Skip to content

Commit

Permalink
yyjson 0eca326, recursion limit
Browse files Browse the repository at this point in the history
  • Loading branch information
ijl committed Feb 23, 2024
1 parent a8f3b38 commit cc8b1cc
Show file tree
Hide file tree
Showing 6 changed files with 639 additions and 100 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -625,6 +625,9 @@ It raises `JSONDecodeError` if given an invalid type or invalid
JSON. This includes if the input contains `NaN`, `Infinity`, or `-Infinity`,
which the standard library allows, but is not valid JSON.

It raises `JSONDecodeError` if a combination of array or object recurses
1024 levels deep.

`JSONDecodeError` is a subclass of `json.JSONDecodeError` and `ValueError`.
This is for compatibility with the standard library.

Expand Down
161 changes: 161 additions & 0 deletions include/yyjson-recursion-limit.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
diff --git a/include/yyjson/yyjson.c b/include/yyjson/yyjson.c
index e76f538..4bac033 100644
--- a/include/yyjson/yyjson.c
+++ b/include/yyjson/yyjson.c
@@ -329,8 +329,9 @@ uint32_t yyjson_version(void) {
#ifndef YYJSON_DISABLE_UTF8_VALIDATION
#define YYJSON_DISABLE_UTF8_VALIDATION 0
#endif
-
-
+#ifndef YYJSON_READER_CONTAINER_RECURSION_LIMIT
+#define YYJSON_READER_CONTAINER_RECURSION_LIMIT 1024
+#endif

/*==============================================================================
* Macros
@@ -5798,6 +5799,8 @@ fail_character:
return_err(cur, UNEXPECTED_CHARACTER, "unexpected character");
fail_garbage:
return_err(cur, UNEXPECTED_CONTENT, "unexpected content after document");
+fail_recursion:
+ return_err(cur, RECURSION_DEPTH, "array and object recursion depth exceeded");

#undef return_err
}
@@ -5854,7 +5857,8 @@ static_inline yyjson_doc *read_root_minify(u8 *hdr,
yyjson_val *ctn_parent; /* parent of current container */
yyjson_doc *doc; /* the JSON document, equals to val_hdr */
const char *msg; /* error message */
-
+
+ u32 container_depth = 0; /* limit on number of open array and map */
bool raw; /* read number as raw */
bool inv; /* allow invalid unicode */
u8 *raw_end; /* raw end for null-terminator */
@@ -5889,6 +5893,11 @@ static_inline yyjson_doc *read_root_minify(u8 *hdr,
}

arr_begin:
+ container_depth++;
+ if (unlikely(container_depth >= YYJSON_READER_CONTAINER_RECURSION_LIMIT)) {
+ goto fail_recursion;
+ }
+
/* save current container */
ctn->tag = (((u64)ctn_len + 1) << YYJSON_TAG_BIT) |
(ctn->tag & YYJSON_TAG_MASK);
@@ -5988,6 +5997,8 @@ arr_val_end:
goto fail_character;

arr_end:
+ container_depth--;
+
/* get parent container */
ctn_parent = (yyjson_val *)(void *)((u8 *)ctn - ctn->uni.ofs);

@@ -6006,6 +6017,11 @@ arr_end:
}

obj_begin:
+ container_depth++;
+ if (unlikely(container_depth >= YYJSON_READER_CONTAINER_RECURSION_LIMIT)) {
+ goto fail_recursion;
+ }
+
/* push container */
ctn->tag = (((u64)ctn_len + 1) << YYJSON_TAG_BIT) |
(ctn->tag & YYJSON_TAG_MASK);
@@ -6134,6 +6150,8 @@ obj_val_end:
goto fail_character;

obj_end:
+ container_depth--;
+
/* pop container */
ctn_parent = (yyjson_val *)(void *)((u8 *)ctn - ctn->uni.ofs);
/* point to the next value */
@@ -6185,6 +6203,8 @@ fail_character:
return_err(cur, UNEXPECTED_CHARACTER, "unexpected character");
fail_garbage:
return_err(cur, UNEXPECTED_CONTENT, "unexpected content after document");
+fail_recursion:
+ return_err(cur, RECURSION_DEPTH, "array and object recursion depth exceeded");

#undef val_incr
#undef return_err
@@ -6242,7 +6262,8 @@ static_inline yyjson_doc *read_root_pretty(u8 *hdr,
yyjson_val *ctn_parent; /* parent of current container */
yyjson_doc *doc; /* the JSON document, equals to val_hdr */
const char *msg; /* error message */
-
+
+ u32 container_depth = 0; /* limit on number of open array and map */
bool raw; /* read number as raw */
bool inv; /* allow invalid unicode */
u8 *raw_end; /* raw end for null-terminator */
@@ -6279,6 +6300,11 @@ static_inline yyjson_doc *read_root_pretty(u8 *hdr,
}

arr_begin:
+ container_depth++;
+ if (unlikely(container_depth >= YYJSON_READER_CONTAINER_RECURSION_LIMIT)) {
+ goto fail_recursion;
+ }
+
/* save current container */
ctn->tag = (((u64)ctn_len + 1) << YYJSON_TAG_BIT) |
(ctn->tag & YYJSON_TAG_MASK);
@@ -6395,6 +6421,8 @@ arr_val_end:
goto fail_character;

arr_end:
+ container_depth--;
+
/* get parent container */
ctn_parent = (yyjson_val *)(void *)((u8 *)ctn - ctn->uni.ofs);

@@ -6414,6 +6442,11 @@ arr_end:
}

obj_begin:
+ container_depth++;
+ if (unlikely(container_depth >= YYJSON_READER_CONTAINER_RECURSION_LIMIT)) {
+ goto fail_recursion;
+ }
+
/* push container */
ctn->tag = (((u64)ctn_len + 1) << YYJSON_TAG_BIT) |
(ctn->tag & YYJSON_TAG_MASK);
@@ -6562,6 +6595,8 @@ obj_val_end:
goto fail_character;

obj_end:
+ container_depth--;
+
/* pop container */
ctn_parent = (yyjson_val *)(void *)((u8 *)ctn - ctn->uni.ofs);
/* point to the next value */
@@ -6614,6 +6649,8 @@ fail_character:
return_err(cur, UNEXPECTED_CHARACTER, "unexpected character");
fail_garbage:
return_err(cur, UNEXPECTED_CONTENT, "unexpected content after document");
+fail_recursion:
+ return_err(cur, RECURSION_DEPTH, "array and object recursion depth exceeded");

#undef val_incr
#undef return_err
diff --git a/include/yyjson/yyjson.h b/include/yyjson/yyjson.h
index c393408..bc688e0 100644
--- a/include/yyjson/yyjson.h
+++ b/include/yyjson/yyjson.h
@@ -831,6 +831,9 @@ static const yyjson_read_code YYJSON_READ_ERROR_FILE_OPEN = 12;
/** Failed to read a file. */
static const yyjson_read_code YYJSON_READ_ERROR_FILE_READ = 13;

+/** Document exceeded YYJSON_READER_CONTAINER_RECURSION_LIMIT. */
+static const yyjson_read_code YYJSON_READ_ERROR_RECURSION_DEPTH = 14;
+
/** Error information for JSON reader. */
typedef struct yyjson_read_err {
/** Error code, see `yyjson_read_code` for all possible values. */
Loading

0 comments on commit cc8b1cc

Please sign in to comment.