diff mbox series

[2/3] perf tools: Make the JSON parser more conformant when in strict mode

Message ID 20211007110543.564963-3-james.clark@arm.com (mailing list archive)
State New, archived
Headers show
Series perf tools: Enable strict JSON parsing | expand

Commit Message

James Clark Oct. 7, 2021, 11:05 a.m. UTC
Return an error when a trailing comma is found or a new item is
encountered before a comma or an opening brace. This ensures that the
perf json files conform more closely to the spec at https://www.json.org

Signed-off-by: James Clark <james.clark@arm.com>
---
 tools/perf/pmu-events/jsmn.c | 42 ++++++++++++++++++++++++++++++++++--
 1 file changed, 40 insertions(+), 2 deletions(-)

Comments

Jiri Olsa Oct. 7, 2021, 5:52 p.m. UTC | #1
On Thu, Oct 07, 2021 at 12:05:41PM +0100, James Clark wrote:
> Return an error when a trailing comma is found or a new item is
> encountered before a comma or an opening brace. This ensures that the
> perf json files conform more closely to the spec at https://www.json.org
> 
> Signed-off-by: James Clark <james.clark@arm.com>
> ---
>  tools/perf/pmu-events/jsmn.c | 42 ++++++++++++++++++++++++++++++++++--
>  1 file changed, 40 insertions(+), 2 deletions(-)
> 
> diff --git a/tools/perf/pmu-events/jsmn.c b/tools/perf/pmu-events/jsmn.c
> index 11d1fa18bfa5..8124d2d3ff0c 100644
> --- a/tools/perf/pmu-events/jsmn.c
> +++ b/tools/perf/pmu-events/jsmn.c
> @@ -176,6 +176,14 @@ jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
>  	jsmnerr_t r;
>  	int i;
>  	jsmntok_t *token;
> +#ifdef JSMN_STRICT

I might have missed some discussion on this, but do we need the
JSMN_STRICT define, if you enable it in the next patch?
why can't we be more strict by default.. do you plan to disable
it in future?

thanks,
jirka

> +	/*
> +	 * Keeps track of whether a new object/list/primitive is expected. New items are only
> +	 * allowed after an opening brace, comma or colon. A closing brace after a comma is not
> +	 * valid JSON.
> +	 */
> +	int expecting_item = 1;
> +#endif
>  
>  	for (; parser->pos < len; parser->pos++) {
>  		char c;
> @@ -185,6 +193,10 @@ jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
>  		switch (c) {
>  		case '{':
>  		case '[':
> +#ifdef JSMN_STRICT
> +			if (!expecting_item)
> +				return JSMN_ERROR_INVAL;
> +#endif
>  			token = jsmn_alloc_token(parser, tokens, num_tokens);
>  			if (token == NULL)
>  				return JSMN_ERROR_NOMEM;
> @@ -196,6 +208,10 @@ jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
>  			break;
>  		case '}':
>  		case ']':
> +#ifdef JSMN_STRICT
> +			if (expecting_item)
> +				return JSMN_ERROR_INVAL;
> +#endif
>  			type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
>  			for (i = parser->toknext - 1; i >= 0; i--) {
>  				token = &tokens[i];
> @@ -219,6 +235,11 @@ jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
>  			}
>  			break;
>  		case '\"':
> +#ifdef JSMN_STRICT
> +			if (!expecting_item)
> +				return JSMN_ERROR_INVAL;
> +			expecting_item = 0;
> +#endif
>  			r = jsmn_parse_string(parser, js, len, tokens,
>  					      num_tokens);
>  			if (r < 0)
> @@ -229,11 +250,15 @@ jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
>  		case '\t':
>  		case '\r':
>  		case '\n':
> -		case ':':
> -		case ',':
>  		case ' ':
>  			break;
>  #ifdef JSMN_STRICT
> +		case ':':
> +		case ',':
> +			if (expecting_item)
> +				return JSMN_ERROR_INVAL;
> +			expecting_item = 1;
> +			break;
>  			/*
>  			 * In strict mode primitives are:
>  			 * numbers and booleans.
> @@ -253,6 +278,9 @@ jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
>  		case 'f':
>  		case 'n':
>  #else
> +		case ':':
> +		case ',':
> +			break;
>  			/*
>  			 * In non-strict mode every unquoted value
>  			 * is a primitive.
> @@ -260,6 +288,12 @@ jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
>  			/*FALL THROUGH */
>  		default:
>  #endif
> +
> +#ifdef JSMN_STRICT
> +			if (!expecting_item)
> +				return JSMN_ERROR_INVAL;
> +			expecting_item = 0;
> +#endif
>  			r = jsmn_parse_primitive(parser, js, len, tokens,
>  						 num_tokens);
>  			if (r < 0)
> @@ -282,7 +316,11 @@ jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
>  			return JSMN_ERROR_PART;
>  	}
>  
> +#ifdef JSMN_STRICT
> +	return expecting_item ? JSMN_ERROR_INVAL : JSMN_SUCCESS;
> +#else
>  	return JSMN_SUCCESS;
> +#endif
>  }
>  
>  /*
> -- 
> 2.28.0
>
James Clark Oct. 8, 2021, 10:08 a.m. UTC | #2
On 07/10/2021 18:52, Jiri Olsa wrote:
> On Thu, Oct 07, 2021 at 12:05:41PM +0100, James Clark wrote:
>> Return an error when a trailing comma is found or a new item is
>> encountered before a comma or an opening brace. This ensures that the
>> perf json files conform more closely to the spec at https://www.json.org
>>
>> Signed-off-by: James Clark <james.clark@arm.com>
>> ---
>>  tools/perf/pmu-events/jsmn.c | 42 ++++++++++++++++++++++++++++++++++--
>>  1 file changed, 40 insertions(+), 2 deletions(-)
>>
>> diff --git a/tools/perf/pmu-events/jsmn.c b/tools/perf/pmu-events/jsmn.c
>> index 11d1fa18bfa5..8124d2d3ff0c 100644
>> --- a/tools/perf/pmu-events/jsmn.c
>> +++ b/tools/perf/pmu-events/jsmn.c
>> @@ -176,6 +176,14 @@ jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
>>  	jsmnerr_t r;
>>  	int i;
>>  	jsmntok_t *token;
>> +#ifdef JSMN_STRICT
> 
> I might have missed some discussion on this, but do we need the
> JSMN_STRICT define, if you enable it in the next patch?
> why can't we be more strict by default.. do you plan to disable
> it in future?

I didn't plan on disabling it, I was just trying to keep to the existing style of the
jsmn project.

I could have added the trailing comma detection by default and not inside any
#ifdef JSMN_STRICT blocks, but I would like to enable JSMN_STRICT anyway, because it
enables some additional built in checking that was already there. So I thought it
made sense to put my new strict stuff inside the existing strict option.

One option would be to remove all (including the existing) #ifdef JSMN_STRICT blocks
and have everything strict by default. But it would be a further deviation from jsmn.

Thanks
James

> 
> thanks,
> jirka
> 
>> +	/*
>> +	 * Keeps track of whether a new object/list/primitive is expected. New items are only
>> +	 * allowed after an opening brace, comma or colon. A closing brace after a comma is not
>> +	 * valid JSON.
>> +	 */
>> +	int expecting_item = 1;
>> +#endif
>>  
>>  	for (; parser->pos < len; parser->pos++) {
>>  		char c;
>> @@ -185,6 +193,10 @@ jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
>>  		switch (c) {
>>  		case '{':
>>  		case '[':
>> +#ifdef JSMN_STRICT
>> +			if (!expecting_item)
>> +				return JSMN_ERROR_INVAL;
>> +#endif
>>  			token = jsmn_alloc_token(parser, tokens, num_tokens);
>>  			if (token == NULL)
>>  				return JSMN_ERROR_NOMEM;
>> @@ -196,6 +208,10 @@ jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
>>  			break;
>>  		case '}':
>>  		case ']':
>> +#ifdef JSMN_STRICT
>> +			if (expecting_item)
>> +				return JSMN_ERROR_INVAL;
>> +#endif
>>  			type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
>>  			for (i = parser->toknext - 1; i >= 0; i--) {
>>  				token = &tokens[i];
>> @@ -219,6 +235,11 @@ jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
>>  			}
>>  			break;
>>  		case '\"':
>> +#ifdef JSMN_STRICT
>> +			if (!expecting_item)
>> +				return JSMN_ERROR_INVAL;
>> +			expecting_item = 0;
>> +#endif
>>  			r = jsmn_parse_string(parser, js, len, tokens,
>>  					      num_tokens);
>>  			if (r < 0)
>> @@ -229,11 +250,15 @@ jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
>>  		case '\t':
>>  		case '\r':
>>  		case '\n':
>> -		case ':':
>> -		case ',':
>>  		case ' ':
>>  			break;
>>  #ifdef JSMN_STRICT
>> +		case ':':
>> +		case ',':
>> +			if (expecting_item)
>> +				return JSMN_ERROR_INVAL;
>> +			expecting_item = 1;
>> +			break;
>>  			/*
>>  			 * In strict mode primitives are:
>>  			 * numbers and booleans.
>> @@ -253,6 +278,9 @@ jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
>>  		case 'f':
>>  		case 'n':
>>  #else
>> +		case ':':
>> +		case ',':
>> +			break;
>>  			/*
>>  			 * In non-strict mode every unquoted value
>>  			 * is a primitive.
>> @@ -260,6 +288,12 @@ jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
>>  			/*FALL THROUGH */
>>  		default:
>>  #endif
>> +
>> +#ifdef JSMN_STRICT
>> +			if (!expecting_item)
>> +				return JSMN_ERROR_INVAL;
>> +			expecting_item = 0;
>> +#endif
>>  			r = jsmn_parse_primitive(parser, js, len, tokens,
>>  						 num_tokens);
>>  			if (r < 0)
>> @@ -282,7 +316,11 @@ jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
>>  			return JSMN_ERROR_PART;
>>  	}
>>  
>> +#ifdef JSMN_STRICT
>> +	return expecting_item ? JSMN_ERROR_INVAL : JSMN_SUCCESS;
>> +#else
>>  	return JSMN_SUCCESS;
>> +#endif
>>  }
>>  
>>  /*
>> -- 
>> 2.28.0
>>
>
Jiri Olsa Oct. 8, 2021, 1:12 p.m. UTC | #3
On Fri, Oct 08, 2021 at 11:08:25AM +0100, James Clark wrote:
> 
> 
> On 07/10/2021 18:52, Jiri Olsa wrote:
> > On Thu, Oct 07, 2021 at 12:05:41PM +0100, James Clark wrote:
> >> Return an error when a trailing comma is found or a new item is
> >> encountered before a comma or an opening brace. This ensures that the
> >> perf json files conform more closely to the spec at https://www.json.org
> >>
> >> Signed-off-by: James Clark <james.clark@arm.com>
> >> ---
> >>  tools/perf/pmu-events/jsmn.c | 42 ++++++++++++++++++++++++++++++++++--
> >>  1 file changed, 40 insertions(+), 2 deletions(-)
> >>
> >> diff --git a/tools/perf/pmu-events/jsmn.c b/tools/perf/pmu-events/jsmn.c
> >> index 11d1fa18bfa5..8124d2d3ff0c 100644
> >> --- a/tools/perf/pmu-events/jsmn.c
> >> +++ b/tools/perf/pmu-events/jsmn.c
> >> @@ -176,6 +176,14 @@ jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
> >>  	jsmnerr_t r;
> >>  	int i;
> >>  	jsmntok_t *token;
> >> +#ifdef JSMN_STRICT
> > 
> > I might have missed some discussion on this, but do we need the
> > JSMN_STRICT define, if you enable it in the next patch?
> > why can't we be more strict by default.. do you plan to disable
> > it in future?
> 
> I didn't plan on disabling it, I was just trying to keep to the existing style of the
> jsmn project.
> 
> I could have added the trailing comma detection by default and not inside any
> #ifdef JSMN_STRICT blocks, but I would like to enable JSMN_STRICT anyway, because it
> enables some additional built in checking that was already there. So I thought it
> made sense to put my new strict stuff inside the existing strict option.
> 
> One option would be to remove all (including the existing) #ifdef JSMN_STRICT blocks
> and have everything strict by default. But it would be a further deviation from jsmn.

ok, I think it makes sense to have JSMN_STRICT then..
thanks for explanation

Acked-by: Jiri Olsa <jolsa@redhat.com>

jirka

> 
> Thanks
> James
> 
> > 
> > thanks,
> > jirka
> > 
> >> +	/*
> >> +	 * Keeps track of whether a new object/list/primitive is expected. New items are only
> >> +	 * allowed after an opening brace, comma or colon. A closing brace after a comma is not
> >> +	 * valid JSON.
> >> +	 */
> >> +	int expecting_item = 1;
> >> +#endif
> >>  
> >>  	for (; parser->pos < len; parser->pos++) {
> >>  		char c;
> >> @@ -185,6 +193,10 @@ jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
> >>  		switch (c) {
> >>  		case '{':
> >>  		case '[':
> >> +#ifdef JSMN_STRICT
> >> +			if (!expecting_item)
> >> +				return JSMN_ERROR_INVAL;
> >> +#endif
> >>  			token = jsmn_alloc_token(parser, tokens, num_tokens);
> >>  			if (token == NULL)
> >>  				return JSMN_ERROR_NOMEM;
> >> @@ -196,6 +208,10 @@ jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
> >>  			break;
> >>  		case '}':
> >>  		case ']':
> >> +#ifdef JSMN_STRICT
> >> +			if (expecting_item)
> >> +				return JSMN_ERROR_INVAL;
> >> +#endif
> >>  			type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
> >>  			for (i = parser->toknext - 1; i >= 0; i--) {
> >>  				token = &tokens[i];
> >> @@ -219,6 +235,11 @@ jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
> >>  			}
> >>  			break;
> >>  		case '\"':
> >> +#ifdef JSMN_STRICT
> >> +			if (!expecting_item)
> >> +				return JSMN_ERROR_INVAL;
> >> +			expecting_item = 0;
> >> +#endif
> >>  			r = jsmn_parse_string(parser, js, len, tokens,
> >>  					      num_tokens);
> >>  			if (r < 0)
> >> @@ -229,11 +250,15 @@ jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
> >>  		case '\t':
> >>  		case '\r':
> >>  		case '\n':
> >> -		case ':':
> >> -		case ',':
> >>  		case ' ':
> >>  			break;
> >>  #ifdef JSMN_STRICT
> >> +		case ':':
> >> +		case ',':
> >> +			if (expecting_item)
> >> +				return JSMN_ERROR_INVAL;
> >> +			expecting_item = 1;
> >> +			break;
> >>  			/*
> >>  			 * In strict mode primitives are:
> >>  			 * numbers and booleans.
> >> @@ -253,6 +278,9 @@ jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
> >>  		case 'f':
> >>  		case 'n':
> >>  #else
> >> +		case ':':
> >> +		case ',':
> >> +			break;
> >>  			/*
> >>  			 * In non-strict mode every unquoted value
> >>  			 * is a primitive.
> >> @@ -260,6 +288,12 @@ jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
> >>  			/*FALL THROUGH */
> >>  		default:
> >>  #endif
> >> +
> >> +#ifdef JSMN_STRICT
> >> +			if (!expecting_item)
> >> +				return JSMN_ERROR_INVAL;
> >> +			expecting_item = 0;
> >> +#endif
> >>  			r = jsmn_parse_primitive(parser, js, len, tokens,
> >>  						 num_tokens);
> >>  			if (r < 0)
> >> @@ -282,7 +316,11 @@ jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
> >>  			return JSMN_ERROR_PART;
> >>  	}
> >>  
> >> +#ifdef JSMN_STRICT
> >> +	return expecting_item ? JSMN_ERROR_INVAL : JSMN_SUCCESS;
> >> +#else
> >>  	return JSMN_SUCCESS;
> >> +#endif
> >>  }
> >>  
> >>  /*
> >> -- 
> >> 2.28.0
> >>
> > 
>
Arnaldo Carvalho de Melo Oct. 8, 2021, 6:56 p.m. UTC | #4
Em Fri, Oct 08, 2021 at 03:12:03PM +0200, Jiri Olsa escreveu:
> On Fri, Oct 08, 2021 at 11:08:25AM +0100, James Clark wrote:
> > 
> > 
> > On 07/10/2021 18:52, Jiri Olsa wrote:
> > > On Thu, Oct 07, 2021 at 12:05:41PM +0100, James Clark wrote:
> > >> Return an error when a trailing comma is found or a new item is
> > >> encountered before a comma or an opening brace. This ensures that the
> > >> perf json files conform more closely to the spec at https://www.json.org
> > >>
> > >> Signed-off-by: James Clark <james.clark@arm.com>
> > >> ---
> > >>  tools/perf/pmu-events/jsmn.c | 42 ++++++++++++++++++++++++++++++++++--
> > >>  1 file changed, 40 insertions(+), 2 deletions(-)
> > >>
> > >> diff --git a/tools/perf/pmu-events/jsmn.c b/tools/perf/pmu-events/jsmn.c
> > >> index 11d1fa18bfa5..8124d2d3ff0c 100644
> > >> --- a/tools/perf/pmu-events/jsmn.c
> > >> +++ b/tools/perf/pmu-events/jsmn.c
> > >> @@ -176,6 +176,14 @@ jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
> > >>  	jsmnerr_t r;
> > >>  	int i;
> > >>  	jsmntok_t *token;
> > >> +#ifdef JSMN_STRICT
> > > 
> > > I might have missed some discussion on this, but do we need the
> > > JSMN_STRICT define, if you enable it in the next patch?
> > > why can't we be more strict by default.. do you plan to disable
> > > it in future?
> > 
> > I didn't plan on disabling it, I was just trying to keep to the existing style of the
> > jsmn project.
> > 
> > I could have added the trailing comma detection by default and not inside any
> > #ifdef JSMN_STRICT blocks, but I would like to enable JSMN_STRICT anyway, because it
> > enables some additional built in checking that was already there. So I thought it
> > made sense to put my new strict stuff inside the existing strict option.
> > 
> > One option would be to remove all (including the existing) #ifdef JSMN_STRICT blocks
> > and have everything strict by default. But it would be a further deviation from jsmn.
> 
> ok, I think it makes sense to have JSMN_STRICT then..
> thanks for explanation
> 
> Acked-by: Jiri Olsa <jolsa@redhat.com>

So, is this for the whole patchset? b4 picked it just for this message.

- Arnaldo
 
> jirka
> 
> > 
> > Thanks
> > James
> > 
> > > 
> > > thanks,
> > > jirka
> > > 
> > >> +	/*
> > >> +	 * Keeps track of whether a new object/list/primitive is expected. New items are only
> > >> +	 * allowed after an opening brace, comma or colon. A closing brace after a comma is not
> > >> +	 * valid JSON.
> > >> +	 */
> > >> +	int expecting_item = 1;
> > >> +#endif
> > >>  
> > >>  	for (; parser->pos < len; parser->pos++) {
> > >>  		char c;
> > >> @@ -185,6 +193,10 @@ jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
> > >>  		switch (c) {
> > >>  		case '{':
> > >>  		case '[':
> > >> +#ifdef JSMN_STRICT
> > >> +			if (!expecting_item)
> > >> +				return JSMN_ERROR_INVAL;
> > >> +#endif
> > >>  			token = jsmn_alloc_token(parser, tokens, num_tokens);
> > >>  			if (token == NULL)
> > >>  				return JSMN_ERROR_NOMEM;
> > >> @@ -196,6 +208,10 @@ jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
> > >>  			break;
> > >>  		case '}':
> > >>  		case ']':
> > >> +#ifdef JSMN_STRICT
> > >> +			if (expecting_item)
> > >> +				return JSMN_ERROR_INVAL;
> > >> +#endif
> > >>  			type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
> > >>  			for (i = parser->toknext - 1; i >= 0; i--) {
> > >>  				token = &tokens[i];
> > >> @@ -219,6 +235,11 @@ jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
> > >>  			}
> > >>  			break;
> > >>  		case '\"':
> > >> +#ifdef JSMN_STRICT
> > >> +			if (!expecting_item)
> > >> +				return JSMN_ERROR_INVAL;
> > >> +			expecting_item = 0;
> > >> +#endif
> > >>  			r = jsmn_parse_string(parser, js, len, tokens,
> > >>  					      num_tokens);
> > >>  			if (r < 0)
> > >> @@ -229,11 +250,15 @@ jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
> > >>  		case '\t':
> > >>  		case '\r':
> > >>  		case '\n':
> > >> -		case ':':
> > >> -		case ',':
> > >>  		case ' ':
> > >>  			break;
> > >>  #ifdef JSMN_STRICT
> > >> +		case ':':
> > >> +		case ',':
> > >> +			if (expecting_item)
> > >> +				return JSMN_ERROR_INVAL;
> > >> +			expecting_item = 1;
> > >> +			break;
> > >>  			/*
> > >>  			 * In strict mode primitives are:
> > >>  			 * numbers and booleans.
> > >> @@ -253,6 +278,9 @@ jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
> > >>  		case 'f':
> > >>  		case 'n':
> > >>  #else
> > >> +		case ':':
> > >> +		case ',':
> > >> +			break;
> > >>  			/*
> > >>  			 * In non-strict mode every unquoted value
> > >>  			 * is a primitive.
> > >> @@ -260,6 +288,12 @@ jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
> > >>  			/*FALL THROUGH */
> > >>  		default:
> > >>  #endif
> > >> +
> > >> +#ifdef JSMN_STRICT
> > >> +			if (!expecting_item)
> > >> +				return JSMN_ERROR_INVAL;
> > >> +			expecting_item = 0;
> > >> +#endif
> > >>  			r = jsmn_parse_primitive(parser, js, len, tokens,
> > >>  						 num_tokens);
> > >>  			if (r < 0)
> > >> @@ -282,7 +316,11 @@ jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
> > >>  			return JSMN_ERROR_PART;
> > >>  	}
> > >>  
> > >> +#ifdef JSMN_STRICT
> > >> +	return expecting_item ? JSMN_ERROR_INVAL : JSMN_SUCCESS;
> > >> +#else
> > >>  	return JSMN_SUCCESS;
> > >> +#endif
> > >>  }
> > >>  
> > >>  /*
> > >> -- 
> > >> 2.28.0
> > >>
> > > 
> >
Arnaldo Carvalho de Melo Oct. 8, 2021, 7:01 p.m. UTC | #5
Em Fri, Oct 08, 2021 at 03:56:26PM -0300, Arnaldo Carvalho de Melo escreveu:
> Em Fri, Oct 08, 2021 at 03:12:03PM +0200, Jiri Olsa escreveu:
> > On Fri, Oct 08, 2021 at 11:08:25AM +0100, James Clark wrote:
> > > 
> > > 
> > > On 07/10/2021 18:52, Jiri Olsa wrote:
> > > > On Thu, Oct 07, 2021 at 12:05:41PM +0100, James Clark wrote:
> > > >> Return an error when a trailing comma is found or a new item is
> > > >> encountered before a comma or an opening brace. This ensures that the
> > > >> perf json files conform more closely to the spec at https://www.json.org
> > > >>
> > > >> Signed-off-by: James Clark <james.clark@arm.com>
> > > >> ---
> > > >>  tools/perf/pmu-events/jsmn.c | 42 ++++++++++++++++++++++++++++++++++--
> > > >>  1 file changed, 40 insertions(+), 2 deletions(-)
> > > >>
> > > >> diff --git a/tools/perf/pmu-events/jsmn.c b/tools/perf/pmu-events/jsmn.c
> > > >> index 11d1fa18bfa5..8124d2d3ff0c 100644
> > > >> --- a/tools/perf/pmu-events/jsmn.c
> > > >> +++ b/tools/perf/pmu-events/jsmn.c
> > > >> @@ -176,6 +176,14 @@ jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
> > > >>  	jsmnerr_t r;
> > > >>  	int i;
> > > >>  	jsmntok_t *token;
> > > >> +#ifdef JSMN_STRICT
> > > > 
> > > > I might have missed some discussion on this, but do we need the
> > > > JSMN_STRICT define, if you enable it in the next patch?
> > > > why can't we be more strict by default.. do you plan to disable
> > > > it in future?
> > > 
> > > I didn't plan on disabling it, I was just trying to keep to the existing style of the
> > > jsmn project.
> > > 
> > > I could have added the trailing comma detection by default and not inside any
> > > #ifdef JSMN_STRICT blocks, but I would like to enable JSMN_STRICT anyway, because it
> > > enables some additional built in checking that was already there. So I thought it
> > > made sense to put my new strict stuff inside the existing strict option.
> > > 
> > > One option would be to remove all (including the existing) #ifdef JSMN_STRICT blocks
> > > and have everything strict by default. But it would be a further deviation from jsmn.
> > 
> > ok, I think it makes sense to have JSMN_STRICT then..
> > thanks for explanation
> > 
> > Acked-by: Jiri Olsa <jolsa@redhat.com>
> 
> So, is this for the whole patchset? b4 picked it just for this message.

Got it from IRC, thanks,

- Arnaldo
diff mbox series

Patch

diff --git a/tools/perf/pmu-events/jsmn.c b/tools/perf/pmu-events/jsmn.c
index 11d1fa18bfa5..8124d2d3ff0c 100644
--- a/tools/perf/pmu-events/jsmn.c
+++ b/tools/perf/pmu-events/jsmn.c
@@ -176,6 +176,14 @@  jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
 	jsmnerr_t r;
 	int i;
 	jsmntok_t *token;
+#ifdef JSMN_STRICT
+	/*
+	 * Keeps track of whether a new object/list/primitive is expected. New items are only
+	 * allowed after an opening brace, comma or colon. A closing brace after a comma is not
+	 * valid JSON.
+	 */
+	int expecting_item = 1;
+#endif
 
 	for (; parser->pos < len; parser->pos++) {
 		char c;
@@ -185,6 +193,10 @@  jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
 		switch (c) {
 		case '{':
 		case '[':
+#ifdef JSMN_STRICT
+			if (!expecting_item)
+				return JSMN_ERROR_INVAL;
+#endif
 			token = jsmn_alloc_token(parser, tokens, num_tokens);
 			if (token == NULL)
 				return JSMN_ERROR_NOMEM;
@@ -196,6 +208,10 @@  jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
 			break;
 		case '}':
 		case ']':
+#ifdef JSMN_STRICT
+			if (expecting_item)
+				return JSMN_ERROR_INVAL;
+#endif
 			type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
 			for (i = parser->toknext - 1; i >= 0; i--) {
 				token = &tokens[i];
@@ -219,6 +235,11 @@  jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
 			}
 			break;
 		case '\"':
+#ifdef JSMN_STRICT
+			if (!expecting_item)
+				return JSMN_ERROR_INVAL;
+			expecting_item = 0;
+#endif
 			r = jsmn_parse_string(parser, js, len, tokens,
 					      num_tokens);
 			if (r < 0)
@@ -229,11 +250,15 @@  jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
 		case '\t':
 		case '\r':
 		case '\n':
-		case ':':
-		case ',':
 		case ' ':
 			break;
 #ifdef JSMN_STRICT
+		case ':':
+		case ',':
+			if (expecting_item)
+				return JSMN_ERROR_INVAL;
+			expecting_item = 1;
+			break;
 			/*
 			 * In strict mode primitives are:
 			 * numbers and booleans.
@@ -253,6 +278,9 @@  jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
 		case 'f':
 		case 'n':
 #else
+		case ':':
+		case ',':
+			break;
 			/*
 			 * In non-strict mode every unquoted value
 			 * is a primitive.
@@ -260,6 +288,12 @@  jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
 			/*FALL THROUGH */
 		default:
 #endif
+
+#ifdef JSMN_STRICT
+			if (!expecting_item)
+				return JSMN_ERROR_INVAL;
+			expecting_item = 0;
+#endif
 			r = jsmn_parse_primitive(parser, js, len, tokens,
 						 num_tokens);
 			if (r < 0)
@@ -282,7 +316,11 @@  jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
 			return JSMN_ERROR_PART;
 	}
 
+#ifdef JSMN_STRICT
+	return expecting_item ? JSMN_ERROR_INVAL : JSMN_SUCCESS;
+#else
 	return JSMN_SUCCESS;
+#endif
 }
 
 /*