From 6e8877c3129e3e7f626d3a7aae0f838c9ca03b0b Mon Sep 17 00:00:00 2001 From: Luke Lu Date: Tue, 4 Aug 2026 17:08:45 +0000 Subject: [PATCH] MDEV-40385 use-of-uninitialized-value in Binary_string::c_ptr() SELECT KDF('','',1000,256) triggered an MSAN use-of-uninitialized-value report in Binary_string::c_ptr() (sql/sql_string.h) reached from Item_func_kdf::val_str(). The optional 4th argument (kdf_name) was evaluated into a result buffer and then read as a C string with c_ptr(). When that argument is an integer literal such as 256, Item_int::val_str() writes the digits "256" into the buffer without appending a trailing NUL, and String::alloc() intentionally skips reallocation, so the buffer stays non-"alloced" and unterminated. c_ptr() then reads Ptr[str_length] to test for an existing terminator, reading an uninitialized byte. The buffer is a stack-resident ValueBuffer from Protocol::send_result_set_row, so the byte is validly addressable but never initialized; only MSAN re-poisons the stack scope, which is why the report is MSAN-only and Valgrind does not flag it. Use c_ptr_safe() instead of c_ptr() when reading the kdf_name argument. c_ptr_safe() writes the NUL terminator after a capacity check without first reading Ptr[str_length], while c_ptr() reads that byte to detect an existing terminator. Behaviour is unchanged: a non-matching kdf_name still yields ER_STD_INVALID_ARGUMENT and NULL, and valid names (pbkdf2_hmac, hkdf) still work. The regression cases added to main.func_kdf reproduce the report only under an MSAN-instrumented build; on a normal build they pass on both the unfixed and fixed server because the stray read is harmless without a sanitizer. All new code of the whole pull request, including one or several files that are either new files or modified ones, are contributed under the BSD-new license. I am contributing on behalf of my employer Amazon Web Services, Inc. --- mysql-test/main/func_kdf.result | 22 ++++++++++++++++++++++ mysql-test/main/func_kdf.test | 14 ++++++++++++++ sql/item_strfunc.cc | 4 ++-- 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/mysql-test/main/func_kdf.result b/mysql-test/main/func_kdf.result index fda8c0581d2cb..e334125c6d5f4 100644 --- a/mysql-test/main/func_kdf.result +++ b/mysql-test/main/func_kdf.result @@ -161,3 +161,25 @@ ERROR 42000: Incorrect parameter count in the call to native function 'kdf' # # End of 11.3 tests # +# +# MDEV-40385 use-of-uninitialized-value in Binary_string::c_ptr() +# +SELECT KDF('','',1000,256); +KDF('','',1000,256) +NULL +Warnings: +Warning 3047 Invalid argument error: '256' in function kdf. +SELECT KDF('secret','salt',1000,999); +KDF('secret','salt',1000,999) +NULL +Warnings: +Warning 3047 Invalid argument error: '999' in function kdf. +SELECT KDF('secret','salt',1000,'pbkdf2_hmac') IS NOT NULL; +KDF('secret','salt',1000,'pbkdf2_hmac') IS NOT NULL +1 +SELECT KDF('secret','salt') IS NOT NULL; +KDF('secret','salt') IS NOT NULL +1 +# +# End of 11.4 tests +# diff --git a/mysql-test/main/func_kdf.test b/mysql-test/main/func_kdf.test index 01f53da3c7ecd..279323ede5952 100644 --- a/mysql-test/main/func_kdf.test +++ b/mysql-test/main/func_kdf.test @@ -62,3 +62,17 @@ select kdf(); --echo # End of 11.3 tests --echo # +--echo # +--echo # MDEV-40385 use-of-uninitialized-value in Binary_string::c_ptr() +--echo # +# MSAN only: this test verifies the fix behaviorally (warning + NULL); the +# uninitialized read itself is only observable under an MSAN build. +SELECT KDF('','',1000,256); +SELECT KDF('secret','salt',1000,999); +SELECT KDF('secret','salt',1000,'pbkdf2_hmac') IS NOT NULL; +SELECT KDF('secret','salt') IS NOT NULL; + +--echo # +--echo # End of 11.4 tests +--echo # + diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index f9bd32c111da4..30987598ed1e5 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -489,9 +489,9 @@ String *Item_func_kdf::val_str(String *buf) { if (String *s= args[3]->val_str(buf)) { - if (strcasecmp(s->c_ptr(), "hkdf") == 0) + if (strcasecmp(s->c_ptr_safe(), "hkdf") == 0) use_hkdf= true; - else if (strcasecmp(s->c_ptr(), "pbkdf2_hmac") != 0) + else if (strcasecmp(s->c_ptr_safe(), "pbkdf2_hmac") != 0) { invalid_argument_error(func_name(), ErrConvStringQ(s).ptr()); goto ret_null;