CVE-2020-36429: Out-of-bounds Write (CWE-787)
value: Variant_encodeJson in open62541 1.x before 1.0.4 has an out-of-bounds write for a large recursion depth.
CWE description: The product writes data past the end, or before the beginning, of the intended buffer.
CVSS v3 base score: 5.5
Published: 2021-07-20T07:15Z
Repository: https://github.com/open62541/open62541
Language: C
Files changed: src/ua_types_encoding_json.c
Commit date: 2020-05-19 23:40:55 +0200
Commit message: fix(json): Check max recursion depth in more places

Diff: diff --git a/src/ua_types_encoding_json.c b/src/ua_types_encoding_json.c
index eb8d7026..c0ced08e 100644
--- a/src/ua_types_encoding_json.c
+++ b/src/ua_types_encoding_json.c
@@ -106,10 +106,12 @@ static WRITE_JSON_ELEMENT(Quote) {
     return writeChar(ctx, '\"');
 }
 
 WRITE_JSON_ELEMENT(ObjStart) {
     /* increase depth, save: before first key-value no comma needed. */
+    if(ctx->depth >= UA_JSON_ENCODING_MAX_RECURSION)
+        return UA_STATUSCODE_BADENCODINGERROR;
     ctx->depth++;
     ctx->commaNeeded[ctx->depth] = false;
     return writeChar(ctx, '{');
 }
 
@@ -119,11 +121,14 @@ WRITE_JSON_ELEMENT(ObjEnd) {
     return writeChar(ctx, '}');
 }
 
 WRITE_JSON_ELEMENT(ArrStart) {
     /* increase depth, save: before first array entry no comma needed. */
-    ctx->commaNeeded[++ctx->depth] = false;
+    if(ctx->depth >= UA_JSON_ENCODING_MAX_RECURSION)
+        return UA_STATUSCODE_BADENCODINGERROR;
+    ctx->depth++;
+    ctx->commaNeeded[ctx->depth] = false;
     return writeChar(ctx, '[');
 }
 
 WRITE_JSON_ELEMENT(ArrEnd) {
     ctx->depth--; //decrease depth
@@ -1122,11 +1127,11 @@ Variant_encodeJsonWrapExtensionObject(const UA_Variant *src, const bool isArray,
 static status
 addMultiArrayContentJSON(CtxJson *ctx, void* array, const UA_DataType *type, 
                          size_t *index, UA_UInt32 *arrayDimensions, size_t dimensionIndex, 
                          size_t dimensionSize) {
     /* Check the recursion limit */
-    if(ctx->depth > UA_JSON_ENCODING_MAX_RECURSION)
+    if(ctx->depth >= UA_JSON_ENCODING_MAX_RECURSION)
         return UA_STATUSCODE_BADENCODINGERROR;
     
     /* Stop recursion: The inner Arrays are written */
     status ret;
     if(dimensionIndex == (dimensionSize - 1)) {
@@ -1380,11 +1385,11 @@ ENCODE_JSON(DiagnosticInfo) {
 }
 
 static status
 encodeJsonStructure(const void *src, const UA_DataType *type, CtxJson *ctx) {
     /* Check the recursion limit */
-    if(ctx->depth > UA_JSON_ENCODING_MAX_RECURSION)
+    if(ctx->depth >= UA_JSON_ENCODING_MAX_RECURSION)
         return UA_STATUSCODE_BADENCODINGERROR;
     ctx->depth++;
 
     status ret = writeJsonObjStart(ctx);
 
@@ -3151,11 +3156,11 @@ Array_decodeJson(void * dst, const UA_DataType *type, CtxJson *ctx,
 static status
 decodeJsonStructure(void *dst, const UA_DataType *type, CtxJson *ctx, 
                     ParseCtx *parseCtx, UA_Boolean moveToken) {
     (void) moveToken;
     /* Check the recursion limit */
-    if(ctx->depth > UA_JSON_ENCODING_MAX_RECURSION)
+    if(ctx->depth >= UA_JSON_ENCODING_MAX_RECURSION)
         return UA_STATUSCODE_BADENCODINGERROR;
     ctx->depth++;
 
     uintptr_t ptr = (uintptr_t)dst;
     status ret = UA_STATUSCODE_GOOD;

Vulnerable code:     ctx->commaNeeded[++ctx->depth] = false;
    if(ctx->depth > UA_JSON_ENCODING_MAX_RECURSION)
    if(ctx->depth > UA_JSON_ENCODING_MAX_RECURSION)
    if(ctx->depth > UA_JSON_ENCODING_MAX_RECURSION)
Fixed code:     if(ctx->depth >= UA_JSON_ENCODING_MAX_RECURSION)
        return UA_STATUSCODE_BADENCODINGERROR;
    if(ctx->depth >= UA_JSON_ENCODING_MAX_RECURSION)
        return UA_STATUSCODE_BADENCODINGERROR;
    ctx->depth++;
    ctx->commaNeeded[ctx->depth] = false;
    if(ctx->depth >= UA_JSON_ENCODING_MAX_RECURSION)
    if(ctx->depth >= UA_JSON_ENCODING_MAX_RECURSION)
    if(ctx->depth >= UA_JSON_ENCODING_MAX_RECURSION)