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

CONV() truncates from_base/to_base to 32 bits before the 2..62 range check: CONV('11', 4294967298, 10) = '3'

    XMLWordPrintable

Details

    Description

      CONV(N, from_base, to_base) returns NULL when a base is outside the allowed range: 2..62 in absolute value since MDEV-30879. For example, CONV('11', 63, 10) is NULL. But Item_func_conv::val_str() reads both bases with

      int from_base= (int) args[1]->val_int();
      int to_base= (int) args[2]->val_int();
      

      (sql/item_strfunc.cc, main 82964737), and the range check is then applied to the low 32 bits only. So:

      • 4294967298 (2^32 + 2) and -4294967294 are accepted as base 2;
      • 4294967286 (2^32 - 10) becomes -10, a negative base, which switches CONV to signed output: CONV('-1', 10, 4294967286) = '-1', as for to_base -10, while to_base 10 gives 18446744073709551615;
      • a BIGINT column holding 4294967312 (2^32 + 16) is used as base 16.

      No warning is given. A base that does not fit in BIGINT (18446744073709551618) does give NULL with warning 1916, so only the values from 2^31 up to the BIGINT range are silently narrowed.

      This is not the MDEV-40989 question (bases 37..62 are documented as legal). These bases are billions, and are accepted only because of the cast.

      It is also not the code of MDEV-40971 / MDEV-40987 (JSON path indexes, PERIOD_ADD, SUBSTRING_INDEX, ELT): the narrowing here is the (int) cast of the two bases in Item_func_conv::val_str() (sql/item_strfunc.cc), and a fix for those issues does not change this one.

      How to repeat

      SELECT CONV('11', 2, 10) AS b2, CONV('11', 4294967298, 10) AS b2p32_2,
             CONV('11', -4294967294, 10) AS bm2p32_2, CONV('11', 63, 10) AS b63;
      -- 3, 3, 3, NULL
       
      SELECT CONV('3', 10, 2) AS t2, CONV('3', 10, 4294967298) AS t2p32_2, CONV('3', 10, 63) AS t63;
      -- 11, 11, NULL
       
      SELECT CONV('-1', 10, 10) AS unsigned_out, CONV('-1', 10, -10) AS signed_out,
             CONV('-1', 10, 4294967286) AS t2p32_m10;
      -- 18446744073709551615, -1, -1
       
      CREATE TABLE t015 (b BIGINT);
      INSERT INTO t015 VALUES (16), (4294967312);
      SELECT b, CONV('ff', b, 10) AS v FROM t015 ORDER BY b;
      -- 16          255
      -- 4294967312  255
      

      +------+---------+----------+------+
      | b2   | b2p32_2 | bm2p32_2 | b63  |
      +------+---------+----------+------+
      | 3    | 3       | 3        | NULL |
      +------+---------+----------+------+
      

      Expected

      NULL for every base whose absolute value is outside 2..62, as for 63. MySQL 26.10 has the same defect (with limit 36). TiDB returns NULL for all of the out-of-range bases above.

      Suggested fix

      Keep the bases as longlong until the range check passes (and take unsigned_flag into account, so that an unsigned value above LONGLONG_MAX does not wrap into the negative range):

      longlong from_base= args[1]->val_int();
      longlong to_base= args[2]->val_int();
      if (... || from_base < -62 || from_base > 62 || (from_base > -2 && from_base < 2) ||
                 to_base < -62 || to_base > 62 || (to_base > -2 && to_base < 2) || ...)
      

      Attachments

        Issue Links

          Activity

            People

              raghunandan.bhat Raghunandan Bhat
              kehan5800 Ke Han
              Votes:
              0 Vote for this issue
              Watchers:
              3 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.