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

From: Jiri Olsa
Date: Fri Oct 08 2021 - 09:12:25 EST


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@xxxxxxx>
> >> ---
> >> 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@xxxxxxxxxx>

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
> >>
> >
>