From 9a1123dc45dde98c2e84cfa1c44dee882d0d3436 Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Thu, 15 Oct 2026 15:48:47 +0400 Subject: [PATCH] MDEV-41556 Unexpected error: Incorrect ASSOCIATIVE ARRAY KEY value my_strntoull10rnd_8bit() did not set *error when a signed negative value was exactly LONGLONG_MIN (-9223372036854775808): that path returned the value and left *error untouched. Callers passing an uninitialized "int error", such as the associative array key conversion in Type_handler_assoc_array, then read a garbage value and raised a spurious ER_WRONG_VALUE. Field_num::get_int() and Field_year::store() also pass an uninitialized error, and my_strntoull10rnd_mb2_or_mb4() calls this function too. Fix: initialize *error to 0 at function entry, like the sibling my_strntoll_8bit() and my_strntoll_mb2_or_mb4() do, so that no exit path can leave it unset. The bug was introduced by commit 031f11717d9 (Fix all warnings given by UBSAN, 2021-04-18), which started skipping the *error assignment when the value equals LONGLONG_MIN. Co-Authored-By: Claude Sonnet 5.5 --- .../type_assoc_array/sp-assoc-array.result | 14 +++++ .../type_assoc_array/sp-assoc-array.test | 18 +++++++ strings/ctype-simple.c | 2 + unittest/strings/strings-t.c | 51 ++++++++++++++++++- 4 files changed, 84 insertions(+), 1 deletion(-) diff --git a/plugin/type_assoc_array/mysql-test/type_assoc_array/sp-assoc-array.result b/plugin/type_assoc_array/mysql-test/type_assoc_array/sp-assoc-array.result index 180c5487975..6fd0454bf8b 100644 --- a/plugin/type_assoc_array/mysql-test/type_assoc_array/sp-assoc-array.result +++ b/plugin/type_assoc_array/mysql-test/type_assoc_array/sp-assoc-array.result @@ -3122,4 +3122,18 @@ $$ DROP PROCEDURE p1; DROP PROCEDURE p2; DROP PROCEDURE p_inout; +# +# MDEV-41556 Unexpected error: Incorrect ASSOCIATIVE ARRAY KEY value +# +SET sql_mode=ORACLE; +DECLARE +TYPE t IS TABLE OF INT INDEX BY BIGINT; +a t; +BEGIN +a(-9223372036854775807-1):= 10; -- key is LONGLONG_MIN +SELECT a(-9223372036854775807-1) AS c1; +END; +$$ +c1 +10 # End of 12.3 tests diff --git a/plugin/type_assoc_array/mysql-test/type_assoc_array/sp-assoc-array.test b/plugin/type_assoc_array/mysql-test/type_assoc_array/sp-assoc-array.test index bf9ddfa5f99..33c256f5914 100644 --- a/plugin/type_assoc_array/mysql-test/type_assoc_array/sp-assoc-array.test +++ b/plugin/type_assoc_array/mysql-test/type_assoc_array/sp-assoc-array.test @@ -3561,4 +3561,22 @@ DROP PROCEDURE p1; DROP PROCEDURE p2; DROP PROCEDURE p_inout; + +--echo # +--echo # MDEV-41556 Unexpected error: Incorrect ASSOCIATIVE ARRAY KEY value +--echo # + +SET sql_mode=ORACLE; +DELIMITER $$; +DECLARE + TYPE t IS TABLE OF INT INDEX BY BIGINT; + a t; +BEGIN + a(-9223372036854775807-1):= 10; -- key is LONGLONG_MIN + SELECT a(-9223372036854775807-1) AS c1; +END; +$$ +DELIMITER ;$$ + + --echo # End of 12.3 tests diff --git a/strings/ctype-simple.c b/strings/ctype-simple.c index 5d1313d8535..5e4f5baa22f 100644 --- a/strings/ctype-simple.c +++ b/strings/ctype-simple.c @@ -1693,6 +1693,8 @@ my_strntoull10rnd_8bit(CHARSET_INFO *cs __attribute__((unused)), uchar ch; int shift= 0, digits= 0, negative, addon; + *error= 0; + /* Skip leading spaces and tabs */ for ( ; str < end && my_isspace(&my_charset_latin1, *str) ; ) str++; diff --git a/unittest/strings/strings-t.c b/unittest/strings/strings-t.c index 0807f6ef0e8..0e563ea7626 100644 --- a/unittest/strings/strings-t.c +++ b/unittest/strings/strings-t.c @@ -1735,13 +1735,58 @@ static int test_mb_wc_filename_truncated() } +/* + MDEV-41556: my_strntoull10rnd() must always set *error, also when a signed + value equals LONGLONG_MIN exactly. Pre-set "error" to a non-zero value, so + the check does not depend on the caller's uninitialized stack contents. +*/ +static int test_strntoull10rnd_error_set() +{ + static const struct + { + const char *str; + int unsigned_flag; + int expected_error; + } tests[]= + { + {"-9223372036854775808", 0, 0}, + {"-9223372036854775809", 0, ERANGE}, + {"9223372036854775807", 0, 0}, + {"9223372036854775808", 0, ERANGE}, + {"18446744073709551615", 1, 0}, + }; + CHARSET_INFO *charsets[]= {&my_charset_latin1, &my_charset_bin}; + int failed= 0; + size_t i, j; + + for (j= 0; j < array_elements(charsets); j++) + { + for (i= 0; i < array_elements(tests); i++) + { + char *endptr; + int error= 12345; + my_ci_strntoull10rnd(charsets[j], tests[i].str, strlen(tests[i].str), + tests[i].unsigned_flag, &endptr, &error); + if (error != tests[i].expected_error) + { + diag("%s: %s unsigned_flag=%d: error=%d, expected %d", + charsets[j]->coll_name.str, tests[i].str, + tests[i].unsigned_flag, error, tests[i].expected_error); + failed++; + } + } + } + return failed; +} + + int main(int ac, char **av) { size_t i, failed= 0; MY_INIT(av[0]); - plan(5); + plan(6); diag("Testing my_like_range_xxx() functions"); for (i= 0; i < array_elements(charset_list); i++) @@ -1771,6 +1816,10 @@ int main(int ac, char **av) ok(test_mb_wc_filename_truncated() == 0, "filename decoder does not read past the end pointer"); + diag("Testing my_ci_strntoull10rnd() error output"); + ok(test_strntoull10rnd_error_set() == 0, + "my_ci_strntoull10rnd() always sets error"); + my_end(0); return exit_status();