Uploaded image for project: 'MariaDB Server'
  1. MariaDB Server
  2. MDEV-40710

JSON_SEARCH writes array indices in a path as raw single-byte ASCII digits, corrupting the path in every wide character set

    XMLWordPrintable

Details

    • Bug
    • Status: Open (View Workflow)
    • Major
    • Resolution: Unresolved
    • 10.11, 11.4, 11.8, 12.3, 13.0
    • None
    • JSON

    Description

      JSON_SEARCH builds the path it returns out of punctuation it writes,
      key names it copies from the document, and the index of any array step.
      The punctuation is written through the converting append, so in ucs2 a
      bracket is the two bytes 005B. The index is not: its decimal digits go
      in as single bytes, whatever the character set.

      SET NAMES utf8mb4;
      SET @d = CONVERT('[["V"],["V"]]' USING ucs2);
      SELECT HEX(JSON_SEARCH(@d, 'one', CONVERT('V' USING ucs2))) AS h;
      h
      00220024005B30005D005B30005D0022

      Reading that across: 0022 is a quote, 0024 a dollar, 005B an opening
      bracket - and then 30, one byte, where a ucs2 character is two. Every
      character after it is read from the wrong place: what comes back is not
      the path, and the closing quote is no longer where the string thinks it
      is.

      How to repeat

      SET NAMES utf8mb4;
      SET @d = CONVERT('[["V"],["V"]]' USING ucs2);
      SELECT HEX(JSON_SEARCH(@d, 'one', CONVERT('V' USING ucs2))) AS with_an_index;
      -- 00220024005B30005D005B30005D0022  <- 30 is one byte
       
      -- a path with no array step comes out correctly
      SET @o = CONVERT('{"a":"V"}' USING ucs2);
      SELECT HEX(JSON_SEARCH(@o, 'one', CONVERT('V' USING ucs2))) AS keys_only;
      -- 00220024002E00610022             <- every character two bytes
       
      -- an index of two digits puts two raw bytes in
      SET @d11 = CONVERT(CONCAT('[', REPEAT('"x",', 10), '"V"]') USING ucs2);
      SELECT HEX(JSON_SEARCH(@d11, 'one', CONVERT('V' USING ucs2))) AS two_digits;

      Root cause

      `append_json_path()` writes the brackets with `str->append('[')` and
      `str->append(']')`, which convert into the character set of the result,
      and the number between them with `str->append_ulonglong()`, which writes
      decimal digits as bytes. Only the array-index branch is affected; a
      path made of member names is composed correctly.

      Contract, `scripts/fill_help_tables.sql:462` (topic 388): *"Returns the
      path to the given string within a JSON document."*

      Note

      JSON_VALID() still answers 1 for the returned value, because the
      corrupted run happens to lie between the surrounding quotes. So nothing
      downstream has any reason to reject it.

      Affected versions

      JSON_SEARCH was added in 10.2.3 and has written the index this way
      throughout.

      Verified on 10.11 only (10.11.19-MariaDB-debug, commit 1dab253482d).
      Other branches were not tested.

      Note

      Found while auditing the JSON functions against their documented
      contracts. None. No test in the suite writes a JSON_SEARCH
      array-index step in a character set of more than one byte.

      Attachments

        Issue Links

          Activity

            People

              Unassigned Unassigned
              arcivanov Arcadiy Ivanov
              Votes:
              0 Vote for this issue
              Watchers:
              1 Start watching this issue

              Dates

                Created:
                Updated:

                Git Integration

                  Error rendering 'com.xiplink.jira.git.jira_git_plugin:git-issue-webpanel'. Please contact your Jira administrators.