From 3775e4e624549b4add02c831562967dd40464e75 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Sun, 24 Mar 2024 22:21:43 +0200 Subject: [PATCH] spa: json: check object key-value pair parity during parsing Check each object key is associated with value. Disallow object or array valued keys. Add flag tracking whether the parser is at global top-level or not, as there we may either be in object context or in a single-value context. Save depth=0 array flag bit in state, so that spa_json_next preserves its complete state across calls. The higher-depth flag bits can be in temporary stack as they are not needed across calls. --- spa/include/spa/utils/json.h | 83 +++++++++++++++++++++++------- test/test-spa-json.c | 99 +++++++++++++++++++++++++++--------- 2 files changed, 138 insertions(+), 44 deletions(-) diff --git a/spa/include/spa/utils/json.h b/spa/include/spa/utils/json.h index 75f5b9a8c..0202dbe54 100644 --- a/spa/include/spa/utils/json.h +++ b/spa/include/spa/utils/json.h @@ -44,7 +44,7 @@ static inline void spa_json_init(struct spa_json * iter, const char *data, size_ { *iter = SPA_JSON_INIT(data, size); } -#define SPA_JSON_ENTER(iter) ((struct spa_json) { (iter)->cur, (iter)->end, (iter), (iter)->state & 0xf0, 0 }) +#define SPA_JSON_ENTER(iter) ((struct spa_json) { (iter)->cur, (iter)->end, (iter), (iter)->state & 0xff0, 0 }) static inline void spa_json_enter(struct spa_json * iter, struct spa_json * sub) { @@ -60,13 +60,14 @@ static inline int spa_json_next(struct spa_json * iter, const char **value) int utf8_remain = 0; enum { __NONE, __STRUCT, __BARE, __STRING, __UTF8, __ESC, __COMMENT, - __ARRAY_FLAG = 0x10, - __OBJECT_FLAG = 0x20, + __ARRAY_FLAG = 0x10, /* in array context */ + __PREV_ARRAY_FLAG = 0x20, /* depth=0 array context flag */ __ERROR_FLAG = 0x40, - __FLAGS = 0xf0, + __KEY_FLAG = 0x80, /* inside object key */ + __SUB_FLAG = 0x100, /* not at top-level */ + __FLAGS = 0xff0, }; - uint8_t object_stack[16] = {0}; - uint8_t array_stack[SPA_N_ELEMENTS(object_stack)] = {0}; + uint64_t array_stack[8] = {0}; /* array context flags of depths 1...512 */ *value = iter->cur; @@ -81,6 +82,7 @@ static inline int spa_json_next(struct spa_json * iter, const char **value) flag = iter->state & __FLAGS; switch (iter->state & ~__FLAGS) { case __NONE: + flag &= ~(__KEY_FLAG | __PREV_ARRAY_FLAG); iter->state = __STRUCT | flag; iter->depth = 0; goto again; @@ -91,21 +93,48 @@ static inline int spa_json_next(struct spa_json * iter, const char **value) case ':': case '=': if (flag & __ARRAY_FLAG) goto error; + if (!(flag & __KEY_FLAG)) + goto error; + iter->state |= __SUB_FLAG; continue; case '#': iter->state = __COMMENT | flag; continue; case '"': + if (flag & __KEY_FLAG) + flag |= __SUB_FLAG; + if (!(flag & __ARRAY_FLAG)) + SPA_FLAG_UPDATE(flag, __KEY_FLAG, !(flag & __KEY_FLAG)); *value = iter->cur; iter->state = __STRING | flag; continue; case '[': case '{': - iter->state = __STRUCT | (cur == '[' ? __ARRAY_FLAG : __OBJECT_FLAG); - if ((iter->depth >> 3) < SPA_N_ELEMENTS(object_stack)) { - uint8_t mask = 1 << (iter->depth & 0x7); - SPA_FLAG_UPDATE(object_stack[iter->depth >> 3], mask, flag & __OBJECT_FLAG); - SPA_FLAG_UPDATE(array_stack[iter->depth >> 3], mask, flag & __ARRAY_FLAG); + if (!(flag & __ARRAY_FLAG)) { + /* At top-level we may be either in object context + * or in single-item context, and then we need ot + * accept array/object here. + */ + if ((iter->state & __SUB_FLAG) && !(flag & __KEY_FLAG)) + goto error; + SPA_FLAG_CLEAR(flag, __KEY_FLAG); } + iter->state = __STRUCT | __SUB_FLAG | flag; + SPA_FLAG_UPDATE(iter->state, __ARRAY_FLAG, cur == '['); + + /* We need to remember previous array state across calls + * for depth=0, so store that in state. Others bits go to + * temporary stack. + */ + if (iter->depth == 0) { + SPA_FLAG_UPDATE(iter->state, __PREV_ARRAY_FLAG, flag & __ARRAY_FLAG); + } else if (((iter->depth-1) >> 6) < SPA_N_ELEMENTS(array_stack)) { + uint64_t mask = 1ULL << ((iter->depth-1) & 0x3f); + SPA_FLAG_UPDATE(array_stack[(iter->depth-1) >> 6], mask, flag & __ARRAY_FLAG); + } else { + /* too deep */ + goto error; + } + *value = iter->cur; if (++iter->depth > 1) continue; @@ -114,9 +143,13 @@ static inline int spa_json_next(struct spa_json * iter, const char **value) case '}': case ']': if ((flag & __ARRAY_FLAG) && cur != ']') goto error; - if ((flag & __OBJECT_FLAG) && cur != '}') + if (!(flag & __ARRAY_FLAG) && cur != '}') goto error; - iter->state = __STRUCT; + if (flag & __KEY_FLAG) { + /* incomplete key-value pair */ + goto error; + } + iter->state = __STRUCT | __SUB_FLAG | flag; if (iter->depth == 0) { if (iter->parent) iter->parent->cur = iter->cur; @@ -125,12 +158,15 @@ static inline int spa_json_next(struct spa_json * iter, const char **value) return 0; } --iter->depth; - if ((iter->depth >> 3) < SPA_N_ELEMENTS(object_stack)) { - uint8_t mask = 1 << (iter->depth & 0x7); - if (SPA_FLAG_IS_SET(object_stack[iter->depth >> 3], mask)) - iter->state |= __OBJECT_FLAG; - if (SPA_FLAG_IS_SET(array_stack[iter->depth >> 3], mask)) - iter->state |= __ARRAY_FLAG; + if (iter->depth == 0) { + SPA_FLAG_UPDATE(iter->state, __ARRAY_FLAG, flag & __PREV_ARRAY_FLAG); + } else if (((iter->depth-1) >> 6) < SPA_N_ELEMENTS(array_stack)) { + uint64_t mask = 1ULL << ((iter->depth-1) & 0x3f); + SPA_FLAG_UPDATE(iter->state, __ARRAY_FLAG, + SPA_FLAG_IS_SET(array_stack[(iter->depth-1) >> 6], mask)); + } else { + /* too deep */ + goto error; } continue; case '\\': @@ -140,6 +176,10 @@ static inline int spa_json_next(struct spa_json * iter, const char **value) /* allow bare ascii */ if (!(cur >= 32 && cur <= 126)) goto error; + if (flag & __KEY_FLAG) + flag |= __SUB_FLAG; + if (!(flag & __ARRAY_FLAG)) + SPA_FLAG_UPDATE(flag, __KEY_FLAG, !(flag & __KEY_FLAG)); *value = iter->cur; iter->state = __BARE | flag; } @@ -227,6 +267,11 @@ static inline int spa_json_next(struct spa_json * iter, const char **value) return 0; } + if ((iter->state & __SUB_FLAG) && (iter->state & __KEY_FLAG)) { + /* incomplete key-value pair */ + goto error; + } + if ((iter->state & ~__FLAGS) != __STRUCT) { iter->state = __STRUCT | (iter->state & __FLAGS); return iter->cur - *value; diff --git a/test/test-spa-json.c b/test/test-spa-json.c index ff46fe2e1..32ea7c554 100644 --- a/test/test-spa-json.c +++ b/test/test-spa-json.c @@ -180,8 +180,6 @@ static void expect_null(struct spa_json *it) PWTEST(json_parse) { - char buf[1024]; - int i; struct spa_json it[5]; const char *json = " { " "\"foo\": \"bar\", # comment\n" @@ -306,17 +304,6 @@ PWTEST(json_parse) expect_end(&it[0]); expect_end(&it[0]); - /* overflowing parser nesting stack is not an error */ - for (i = 0; i < 256; ++i) - buf[i] = '['; - for (; i < 512; ++i) - buf[i] = ']'; - buf[i++] = '\0'; - - spa_json_init(&it[0], buf, strlen(buf)); - pwtest_int_eq(spa_json_next(&it[0], &value), 1); - expect_end(&it[0]); - /* non-null terminated strings OK */ json = "1.234"; spa_json_init(&it[0], json, 4); @@ -363,12 +350,29 @@ PWTEST(json_parse) expect_string(&it[0], "hello"); expect_end(&it[0]); + /* top-level context */ + json = "x y x y"; + spa_json_init(&it[0], json, strlen(json)); + expect_string_or_bare(&it[0], "x"); + expect_string_or_bare(&it[0], "y"); + expect_string_or_bare(&it[0], "x"); + expect_string_or_bare(&it[0], "y"); + expect_end(&it[0]); + + json = "x = y x = y"; + spa_json_init(&it[0], json, strlen(json)); + expect_string_or_bare(&it[0], "x"); + expect_string_or_bare(&it[0], "y"); + expect_string_or_bare(&it[0], "x"); + expect_string_or_bare(&it[0], "y"); + expect_end(&it[0]); + return PWTEST_PASS; } PWTEST(json_parse_fail) { - char buf[1024]; + char buf[2048]; struct spa_json it[5]; const char *json, *value; int i; @@ -410,17 +414,64 @@ PWTEST(json_parse_fail) expect_parse_error(&it[0], json, 1, 14); /* bad nesting */ - json = "{ {[{[{[{[{[{[{[{[{[{[{[{[ ]}]}]}]}]}]}]}]}]}]}]}]} ]"; + json = "{a: {a:[{a:[{a:[{a:[{a:[{a:[{a:[{a:[{a:[{a:[{a:[{a:[ ]}]}]}]}]}]}]}]}]}]}]}]} ]"; spa_json_init(&it[0], json, strlen(json)); pwtest_int_eq(spa_json_next(&it[0], &value), 1); expect_parse_error(&it[0], json, 1, strlen(json)); /* bad nesting */ - json = "[ {[{[{[{[{[{[{[{[{[{[{[{[ ]}]}]}]}]}]}]}]}]}]}]}]} }"; + json = "[ {a:[{a:[{a:[{a:[{a:[{a:[{a:[{a:[{a:[{a:[{a:[{a:[ ]}]}]}]}]}]}]}]}]}]}]}]} }"; spa_json_init(&it[0], json, strlen(json)); pwtest_int_eq(spa_json_next(&it[0], &value), 1); expect_parse_error(&it[0], json, 1, strlen(json)); + /* bad object key-values */ + json = "{ = }"; + spa_json_init(&it[0], json, strlen(json)); + expect_object(&it[0], &it[1]); + expect_parse_error(&it[1], json, 1, 3); + + json = "{ x }"; + spa_json_init(&it[0], json, strlen(json)); + expect_object(&it[0], &it[1]); + expect_string_or_bare(&it[1], "x"); + expect_parse_error(&it[1], json, 1, 5); + + json = "{ x : }"; + spa_json_init(&it[0], json, strlen(json)); + expect_object(&it[0], &it[1]); + expect_string_or_bare(&it[1], "x"); + expect_parse_error(&it[1], json, 1, 7); + + json = "{ x = y, : }"; + spa_json_init(&it[0], json, strlen(json)); + expect_object(&it[0], &it[1]); + expect_string_or_bare(&it[1], "x"); + expect_string_or_bare(&it[1], "y"); + expect_parse_error(&it[1], json, 1, 10); + + json = "{ x = {1:3}, z : }"; + spa_json_init(&it[0], json, strlen(json)); + expect_object(&it[0], &it[1]); + expect_string_or_bare(&it[1], "x"); + expect_object(&it[1], &it[2]); + expect_string_or_bare(&it[1], "z"); + expect_parse_error(&it[1], json, 1, 18); + + json = "{ x y x }"; + spa_json_init(&it[0], json, strlen(json)); + expect_object(&it[0], &it[1]); + expect_string_or_bare(&it[1], "x"); + expect_string_or_bare(&it[1], "y"); + expect_string_or_bare(&it[1], "x"); + expect_parse_error(&it[1], json, 1, 9); + + json = "x y x"; + spa_json_init(&it[0], json, strlen(json)); + expect_string_or_bare(&it[0], "x"); + expect_string_or_bare(&it[0], "y"); + expect_parse_error(&it[0], json, 1, 6); + /* unclosed string */ json = "\"foo"; spa_json_init(&it[0], json, strlen(json)); @@ -454,11 +505,12 @@ PWTEST(json_parse_fail) expect_parse_error(&it[0], json, 1, 1); /* bad nesting in subparser */ - json = "{[]"; + json = "{a:[]"; spa_json_init(&it[0], json, strlen(json)); expect_object(&it[0], &it[1]); + expect_string_or_bare(&it[1], "a"); expect_array(&it[1], &it[2]); - expect_parse_error(&it[1], json, 1, 4); + expect_parse_error(&it[1], json, 1, 6); /* entered parser assumes nesting */ json = "[]"; @@ -467,17 +519,16 @@ PWTEST(json_parse_fail) expect_array(&it[1], &it[2]); expect_parse_error(&it[1], json, 1, 3); - /* overflowing parser nesting stack */ - for (i = 0; i < 256; ++i) + /* overflowing parser nesting stack is an error*/ + for (i = 0; i < 514; ++i) buf[i] = '['; - for (; i < 511; ++i) + for (; i < 2*514; ++i) buf[i] = ']'; - buf[i++] = '}'; buf[i++] = '\0'; spa_json_init(&it[0], buf, strlen(buf)); pwtest_int_eq(spa_json_next(&it[0], &value), 1); - expect_parse_error(&it[0], buf, 1, strlen(buf)); + expect_parse_error(&it[0], buf, 1, 514); /* bad utf8 */ json = "\"\xc0\""; @@ -898,10 +949,8 @@ PWTEST(json_data) "n_object_unquoted_key.json", "n_object_with_trailing_garbage.json", "n_single_space.json", - "n_structure_double_array.json", "n_structure_no_data.json", "n_structure_null-byte-outside-string.json", - "n_structure_object_with_trailing_garbage.json", "n_structure_trailing_#.json", "n_multidigit_number_then_00.json",