On Sun, Jul 12, 2020 at 09:57:02AM +0430, Ali Farzanrad wrote:
> Hi @tech,
>
> I was comparing jsmn.c in acme-client with jsmn.c in FreeBSD [1].
> I found a switch without a default case which is an undefined behavior:
>
> @@ -69,6 +69,8 @@
> case '\t' : case '\r' : case '\n' : case ' ' :
> case ',' : case ']' : case '}' :
> goto found;
> + default:
> + break;
> }
> if (js[parser->pos] < 32 || js[parser->pos] >= 127) {
> parser->pos = start;
>
> I have patched that undefined behavior + some style fix.
It is bad practise to intermix style changes with bug fixes.
Please post the fix seperately.
-Otto
>
> [1] https://svnweb.freebsd.org/base/head/lib/libpmc/pmu-events/jsmn.c
>
> Index: jsmn.c
> ===================================================================
> RCS file: /cvs/src/usr.sbin/acme-client/jsmn.c,v
> retrieving revision 1.1
> diff -u -p -r1.1 jsmn.c
> --- jsmn.c 31 Aug 2016 22:01:42 -0000 1.1
> +++ jsmn.c 12 Jul 2020 05:10:34 -0000
> @@ -1,31 +1,33 @@
> /*
> - Copyright (c) 2010 Serge A. Zaitsev
> -
> - Permission is hereby granted, free of charge, to any person obtaining a copy
> - of this software and associated documentation files (the "Software"), to
> deal
> - in the Software without restriction, including without limitation the rights
> - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> - copies of the Software, and to permit persons to whom the Software is
> - furnished to do so, subject to the following conditions:
> -
> - The above copyright notice and this permission notice shall be included in
> - all copies or substantial portions of the Software.
> -
> - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
> - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> FROM,
> - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> - THE SOFTWARE.*
> + * Copyright (c) 2010 Serge A. Zaitsev
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> copy
> + * of this software and associated documentation files (the "Software"), to
> deal
> + * in the Software without restriction, including without limitation the
> rights
> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> + * copies of the Software, and to permit persons to whom the Software is
> + * furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> THE
> + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> FROM,
> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> + * THE SOFTWARE.
> */
> +
> #include "jsmn.h"
>
> -/**
> - * Allocates a fresh unused token from the token pull.
> +/*
> + * 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) {
> +static jsmntok_t *
> +jsmn_alloc_token(jsmn_parser *parser, jsmntok_t *tokens, size_t num_tokens)
> +{
> jsmntok_t *tok;
> if (parser->toknext >= num_tokens) {
> return NULL;
> @@ -39,22 +41,25 @@ static jsmntok_t *jsmn_alloc_token(jsmn_
> return tok;
> }
>
> -/**
> +/*
> * Fills token type and boundaries.
> */
> -static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type,
> - int start, int end) {
> +static void
> +jsmn_fill_token(jsmntok_t *token, jsmntype_t type, int start, int end)
> +{
> token->type = type;
> token->start = start;
> token->end = end;
> token->size = 0;
> }
>
> -/**
> +/*
> * Fills next available token with JSON primitive.
> */
> -static int jsmn_parse_primitive(jsmn_parser *parser, const char *js,
> - size_t len, jsmntok_t *tokens, size_t num_tokens) {
> +static int
> +jsmn_parse_primitive(jsmn_parser *parser, const char *js,
> + size_t len, jsmntok_t *tokens, size_t num_tokens)
> +{
> jsmntok_t *token;
> int start;
>
> @@ -63,12 +68,19 @@ static int jsmn_parse_primitive(jsmn_par
> for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
> switch (js[parser->pos]) {
> #ifndef JSMN_STRICT
> - /* In strict mode primitive must be followed by "," or
> "}" or "]" */
> - case ':':
> + /* In strict mode primitive must be followed by "," or "}" or
> "]" */
> + case ':':
> #endif
> - case '\t' : case '\r' : case '\n' : case ' ' :
> - case ',' : case ']' : case '}' :
> - goto found;
> + case '\t':
> + case '\r':
> + case '\n':
> + case ' ':
> + case ',':
> + case ']':
> + case '}':
> + goto found;
> + default:
> + break;
> }
> if (js[parser->pos] < 32 || js[parser->pos] >= 127) {
> parser->pos = start;
> @@ -99,13 +111,14 @@ found:
> return 0;
> }
>
> -/**
> +/*
> * Fills next token with JSON string.
> */
> -static int jsmn_parse_string(jsmn_parser *parser, const char *js,
> - size_t len, jsmntok_t *tokens, size_t num_tokens) {
> +static int
> +jsmn_parse_string(jsmn_parser *parser, const char *js,
> + size_t len, jsmntok_t *tokens, size_t num_tokens)
> +{
> jsmntok_t *token;
> -
> int start = parser->pos;
>
> parser->pos++;
> @@ -137,28 +150,34 @@ static int jsmn_parse_string(jsmn_parser
> parser->pos++;
> switch (js[parser->pos]) {
> /* Allowed escaped symbols */
> - case '\"': case '/' : case '\\' : case 'b' :
> - case 'f' : case 'r' : case 'n' : case 't' :
> - break;
> + case '\"':
> + case '/':
> + case '\\':
> + case 'b':
> + case 'f':
> + case 'r':
> + case 'n':
> + case 't':
> + break;
> /* Allows escaped symbol \uXXXX */
> - case 'u':
> - parser->pos++;
> - for(i = 0; i < 4 && parser->pos < len
> && js[parser->pos] != '\0'; i++) {
> - /* If it isn't a hex character
> we have an error */
> - if(!((js[parser->pos] >= 48 &&
> js[parser->pos] <= 57) || /* 0-9 */
> -
> (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */
> -
> (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */
> - parser->pos = start;
> - return JSMN_ERROR_INVAL;
> - }
> - parser->pos++;
> + case 'u':
> + parser->pos++;
> + for(i = 0; i < 4 && parser->pos < len &&
> js[parser->pos] != '\0'; i++) {
> + /* If it isn't a hex character we have
> an error */
> + if(!((js[parser->pos] >= 48 &&
> js[parser->pos] <= 57) || /* 0-9 */
> +
> (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */
> +
> (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */
> + parser->pos = start;
> + return JSMN_ERROR_INVAL;
> }
> - parser->pos--;
> - break;
> + parser->pos++;
> + }
> + parser->pos--;
> + break;
> /* Unexpected symbol */
> - default:
> - parser->pos = start;
> - return JSMN_ERROR_INVAL;
> + default:
> + parser->pos = start;
> + return JSMN_ERROR_INVAL;
> }
> }
> }
> @@ -166,11 +185,13 @@ static int jsmn_parse_string(jsmn_parser
> return JSMN_ERROR_PART;
> }
>
> -/**
> +/*
> * Parse JSON string and fill tokens.
> */
> -int jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
> - jsmntok_t *tokens, unsigned int num_tokens) {
> +int
> +jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
> + jsmntok_t *tokens, unsigned int num_tokens)
> +{
> int r;
> int i;
> jsmntok_t *token;
> @@ -182,128 +203,147 @@ int jsmn_parse(jsmn_parser *parser, cons
>
> c = js[parser->pos];
> switch (c) {
> - case '{': case '[':
> - count++;
> - if (tokens == NULL) {
> - break;
> - }
> - token = jsmn_alloc_token(parser, tokens,
> num_tokens);
> - if (token == NULL)
> - return JSMN_ERROR_NOMEM;
> - if (parser->toksuper != -1) {
> - tokens[parser->toksuper].size++;
> + case '{':
> + case '[':
> + count++;
> + if (tokens == NULL) {
> + break;
> + }
> + token = jsmn_alloc_token(parser, tokens, num_tokens);
> + if (token == NULL)
> + return JSMN_ERROR_NOMEM;
> + if (parser->toksuper != -1) {
> + tokens[parser->toksuper].size++;
> #ifdef JSMN_PARENT_LINKS
> - token->parent = parser->toksuper;
> + token->parent = parser->toksuper;
> #endif
> - }
> - token->type = (c == '{' ? JSMN_OBJECT :
> JSMN_ARRAY);
> - token->start = parser->pos;
> - parser->toksuper = parser->toknext - 1;
> + }
> + token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
> + token->start = parser->pos;
> + parser->toksuper = parser->toknext - 1;
> + break;
> + case '}':
> + case ']':
> + if (tokens == NULL)
> break;
> - case '}': case ']':
> - if (tokens == NULL)
> - break;
> - type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
> + type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
> #ifdef JSMN_PARENT_LINKS
> - if (parser->toknext < 1) {
> - return JSMN_ERROR_INVAL;
> - }
> - token = &tokens[parser->toknext - 1];
> - for (;;) {
> - if (token->start != -1 && token->end ==
> -1) {
> - if (token->type != type) {
> - return JSMN_ERROR_INVAL;
> - }
> - token->end = parser->pos + 1;
> - parser->toksuper =
> token->parent;
> - break;
> - }
> - if (token->parent == -1) {
> - break;
> + if (parser->toknext < 1) {
> + return JSMN_ERROR_INVAL;
> + }
> + token = &tokens[parser->toknext - 1];
> + for (;;) {
> + if (token->start != -1 && token->end == -1) {
> + if (token->type != type) {
> + return JSMN_ERROR_INVAL;
> }
> - token = &tokens[token->parent];
> + token->end = parser->pos + 1;
> + parser->toksuper = token->parent;
> + break;
> + }
> + if (token->parent == -1) {
> + break;
> }
> + token = &tokens[token->parent];
> + }
> #else
> - for (i = parser->toknext - 1; i >= 0; i--) {
> - token = &tokens[i];
> - if (token->start != -1 && token->end ==
> -1) {
> - if (token->type != type) {
> - return JSMN_ERROR_INVAL;
> - }
> - parser->toksuper = -1;
> - token->end = parser->pos + 1;
> - break;
> + for (i = parser->toknext - 1; i >= 0; i--) {
> + token = &tokens[i];
> + if (token->start != -1 && token->end == -1) {
> + if (token->type != type) {
> + return JSMN_ERROR_INVAL;
> }
> + parser->toksuper = -1;
> + token->end = parser->pos + 1;
> + break;
> }
> - /* Error if unmatched closing bracket */
> - if (i == -1) return JSMN_ERROR_INVAL;
> - for (; i >= 0; i--) {
> - token = &tokens[i];
> - if (token->start != -1 && token->end ==
> -1) {
> - parser->toksuper = i;
> - break;
> - }
> + }
> + /* Error if unmatched closing bracket */
> + if (i == -1)
> + return JSMN_ERROR_INVAL;
> + for (; i >= 0; i--) {
> + token = &tokens[i];
> + if (token->start != -1 && token->end == -1) {
> + parser->toksuper = i;
> + break;
> }
> + }
> #endif
> - break;
> - 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;
> - case '\t' : case '\r' : case '\n' : case ' ':
> - break;
> - case ':':
> - parser->toksuper = parser->toknext - 1;
> - break;
> - case ',':
> - if (tokens != NULL && parser->toksuper != -1 &&
> - tokens[parser->toksuper].type
> != JSMN_ARRAY &&
> - tokens[parser->toksuper].type
> != JSMN_OBJECT) {
> + break;
> + 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;
> + case '\t':
> + case '\r':
> + case '\n':
> + case ' ':
> + break;
> + case ':':
> + parser->toksuper = parser->toknext - 1;
> + break;
> + case ',':
> + if (tokens != NULL && parser->toksuper != -1 &&
> + tokens[parser->toksuper].type !=
> JSMN_ARRAY &&
> + tokens[parser->toksuper].type !=
> JSMN_OBJECT) {
> #ifdef JSMN_PARENT_LINKS
> - parser->toksuper =
> tokens[parser->toksuper].parent;
> + parser->toksuper =
> tokens[parser->toksuper].parent;
> #else
> - for (i = parser->toknext - 1; i >= 0;
> i--) {
> - if (tokens[i].type ==
> JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) {
> - if (tokens[i].start !=
> -1 && tokens[i].end == -1) {
> -
> parser->toksuper = i;
> - break;
> - }
> + for (i = parser->toknext - 1; i >= 0; i--) {
> + if (tokens[i].type == JSMN_ARRAY ||
> tokens[i].type == JSMN_OBJECT) {
> + if (tokens[i].start != -1 &&
> tokens[i].end == -1) {
> + parser->toksuper = i;
> + break;
> }
> }
> -#endif
> }
> - break;
> +#endif
> + }
> + break;
> #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)) {
> - return JSMN_ERROR_INVAL;
> - }
> + 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)) {
> + return JSMN_ERROR_INVAL;
> }
> + }
> #else
> /* In non-strict mode every unquoted value is a
> primitive */
> - default:
> + 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;
> + 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;
> + default:
> + return JSMN_ERROR_INVAL;
> #endif
> }
> }
> @@ -320,11 +360,13 @@ int jsmn_parse(jsmn_parser *parser, cons
> return count;
> }
>
> -/**
> +/*
> * Creates a new parser based over a given buffer with an array of tokens
> * available.
> */
> -void jsmn_init(jsmn_parser *parser) {
> +void
> +jsmn_init(jsmn_parser *parser)
> +{
> parser->pos = 0;
> parser->toknext = 0;
> parser->toksuper = -1;
>