diff options
-rw-r--r-- | jsmn/jsmn.c | 62 | ||||
-rw-r--r-- | jsmn/jsmn.h | 1 | ||||
-rw-r--r-- | regtest.tcl | 11 | ||||
-rw-r--r-- | tests/json.test | 10 |
4 files changed, 52 insertions, 32 deletions
diff --git a/jsmn/jsmn.c b/jsmn/jsmn.c index 2de5ec2..19d48e0 100644 --- a/jsmn/jsmn.c +++ b/jsmn/jsmn.c @@ -1,11 +1,17 @@ #include "jsmn.h" +/* For json-decode we want strict mode so we don't get + * garbage for malformed json + */ +#define JSMN_STRICT + /** * Allocates a fresh unused token from the token pool. */ static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, jsmntok_t *tokens, size_t num_tokens) { jsmntok_t *tok; + parser->count++; if (parser->toknext >= num_tokens) { return NULL; } @@ -61,11 +67,11 @@ static int jsmn_parse_primitive(jsmn_parser *parser, const char *js, #endif found: + token = jsmn_alloc_token(parser, tokens, num_tokens); if (tokens == NULL) { parser->pos--; return 0; } - token = jsmn_alloc_token(parser, tokens, num_tokens); if (token == NULL) { parser->pos = start; return JSMN_ERROR_NOMEM; @@ -95,10 +101,10 @@ static int jsmn_parse_string(jsmn_parser *parser, const char *js, /* Quote: end of string */ if (c == '\"') { + token = jsmn_alloc_token(parser, tokens, num_tokens); if (tokens == NULL) { return 0; } - token = jsmn_alloc_token(parser, tokens, num_tokens); if (token == NULL) { parser->pos = start; return JSMN_ERROR_NOMEM; @@ -153,7 +159,6 @@ int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, int r; int i; jsmntok_t *token; - int count = parser->toknext; for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { char c; @@ -162,11 +167,10 @@ int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, c = js[parser->pos]; switch (c) { case '{': case '[': - count++; + token = jsmn_alloc_token(parser, tokens, num_tokens); if (tokens == NULL) { break; } - token = jsmn_alloc_token(parser, tokens, num_tokens); if (token == NULL) return JSMN_ERROR_NOMEM; if (parser->toksuper != -1) { @@ -235,7 +239,6 @@ int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, case '\"': r = jsmn_parse_string(parser, js, len, tokens, num_tokens); if (r < 0) return r; - count++; if (parser->toksuper != -1 && tokens != NULL) tokens[parser->toksuper].size++; break; @@ -262,35 +265,39 @@ int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, #endif } break; + + default: + /* In non-strict mode every unquoted value is a primitive */ #ifdef JSMN_STRICT - /* In strict mode primitives are: numbers and booleans */ - case '-': case '0': case '1' : case '2': case '3' : case '4': - case '5': case '6': case '7' : case '8': case '9': - case 't': case 'f': case 'n' : - /* And they must not be keys of the object */ - if (tokens != NULL && parser->toksuper != -1) { - jsmntok_t *t = &tokens[parser->toksuper]; - if (t->type == JSMN_OBJECT || - (t->type == JSMN_STRING && t->size != 0)) { + switch (c) { + /* In strict mode primitives are: numbers and booleans */ + case '-': case '0': case '1' : case '2': case '3' : case '4': + case '5': case '6': case '7' : case '8': case '9': + case 't': case 'f': case 'n' : +#ifndef JSMN_FULLY_STRICT + /* Allow Inf and NaN in any mode other than fully strict */ + case 'I': case 'N': +#endif + /* And they must not be keys of the object */ + if (tokens != NULL && parser->toksuper != -1) { + jsmntok_t *t = &tokens[parser->toksuper]; + if (t->type == JSMN_OBJECT || + (t->type == JSMN_STRING && t->size != 0)) { + return JSMN_ERROR_INVAL; + } + } + break; + + default: + /* Unexpected char in strict mode */ return JSMN_ERROR_INVAL; - } } -#else - /* In non-strict mode every unquoted value is a primitive */ - default: #endif r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens); if (r < 0) return r; - count++; if (parser->toksuper != -1 && tokens != NULL) tokens[parser->toksuper].size++; break; - -#ifdef JSMN_STRICT - /* Unexpected char in strict mode */ - default: - return JSMN_ERROR_INVAL; -#endif } } @@ -303,7 +310,7 @@ int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, } } - return count; + return parser->count; } /** @@ -314,5 +321,6 @@ void jsmn_init(jsmn_parser *parser) { parser->pos = 0; parser->toknext = 0; parser->toksuper = -1; + parser->count = 0; } diff --git a/jsmn/jsmn.h b/jsmn/jsmn.h index 01ca99c..9c20657 100644 --- a/jsmn/jsmn.h +++ b/jsmn/jsmn.h @@ -54,6 +54,7 @@ typedef struct { typedef struct { unsigned int pos; /* offset in the JSON string */ unsigned int toknext; /* next token to allocate */ + unsigned int count; /* number of tokens parsed */ int toksuper; /* superior token node, e.g parent object or array */ } jsmn_parser; diff --git a/regtest.tcl b/regtest.tcl index a9ee5eb..5e66aa3 100644 --- a/regtest.tcl +++ b/regtest.tcl @@ -388,6 +388,17 @@ puts "TEST 54 PASSED" apply {{} {info frame 0}} puts "TEST 55 PASSED" +# json decode should not core dump on invalid input +set json { +{ + "fossil":"9c65b5432e4aeecf3556e5550c338ce93fd861cc", + "timestamp":1435827337, + "command":"timeline/checkin", /* this is line 3 */ + "procTimeUs":3333, +}} +catch {json::decode $json} +puts "TEST 56 PASSED" + # TAKE THE FOLLOWING puts AS LAST LINE puts "--- ALL TESTS PASSED ---" diff --git a/tests/json.test b/tests/json.test index ed73401..78a19fa 100644 --- a/tests/json.test +++ b/tests/json.test @@ -60,6 +60,7 @@ test json-decode-012 {default null value} { } {null} test json-decode-1.1 {Number forms} { + # Note that this is not strictly correct JSON, but is usable in practice json::decode {[ 1, 2, 3.0, 4, Infinity, NaN, -Infinity, -0.0, 1e5, -1e-5 ]} } {1 2 3.0 4 Inf NaN -Inf -0.0 1e5 -1e-5} @@ -80,15 +81,15 @@ test json-2.4 {schema tests} { } {obj a num b num} test json-2.5 {schema tests} { - lindex [json::decode -schema {[1, 2, {a:"b", c:false}, "hello"]}] 1 + lindex [json::decode -schema {[1, 2, {"a":"b", "c":false}, "hello"]}] 1 } {mixed num num {obj a str c bool} str} test json-2.6 {schema tests} { - lindex [json::decode -schema {[1, 2, {a:["b", 1, true, Infinity]}]}] 1 + lindex [json::decode -schema {[1, 2, {"a":["b", 1, true, Infinity]}]}] 1 } {mixed num num {obj a {mixed str num bool num}}} test json-2.7 {schema tests} { - lindex [json::decode -schema {[1, 2, {a:["b", 1, true, ["d", "e", "f"]]}]}] 1 + lindex [json::decode -schema {[1, 2, {"a":["b", 1, true, ["d", "e", "f"]]}]}] 1 } {mixed num num {obj a {mixed str num bool {list str}}}} test json-2.8 {schema tests} { @@ -96,10 +97,9 @@ test json-2.8 {schema tests} { } {mixed num num bool bool} test json-2.9 {schema tests} { - lindex [json::decode -schema {[{a:1},{b:2}]}] 1 + lindex [json::decode -schema {[{"a":1},{"b":2}]}] 1 } {mixed {obj a num} {obj b num}} - test json-3.1 {-index array} { json::decode -index \ {[null, 1, 2, true, false, "hello"]} |