Details
-
Bug
-
Status: Confirmed (View Workflow)
-
Major
-
Resolution: Unresolved
-
10.11, 11.4, 11.8, 12.3, 13.1, 12.3.3
-
Linux/amd64; source commit bdf154a3638d54a4d210e2a28962927581c02ef2.
Description
CONNECT's jsonset_grp_size_init() accepts an integer argument whose value is SQL NULL, but jsonset_grp_size() dereferences the corresponding NULL argument pointer. I get SIGSEGV in the recorded standalone harness containing the relevant logic from storage/connect/jsonudf.cpp at commit bdf154a3638d54a4d210e2a28962927581c02ef2.
This occurs when the registered CONNECT group-size setter receives an integer expression whose value is SQL NULL.
The server-level consequence was not observed because the available MariaDB 12.3.3 runtime did not contain ha_connect.so. The following self-contained source harness demonstrates the NULL dereference without requiring the plugin.
Save this as repro.cpp:
#include <cstdint>
|
#include <cstdio>
|
#include <cstring>
|
|
|
using my_bool = bool; |
using uchar = unsigned char; |
using uint = unsigned int; |
|
|
enum Item_result { STRING_RESULT, REAL_RESULT, INT_RESULT }; |
|
|
struct UDF_ARGS { |
unsigned int arg_count; |
Item_result *arg_type;
|
char **args; |
};
|
|
|
struct UDF_INIT {}; |
|
|
static uint JsonGrpSize = 0; |
static uint GetJsonGroupSize() { return JsonGrpSize; } |
|
|
// Exact logic of storage/connect/jsonudf.cpp at target HEAD bdf154a3638d54a.
|
my_bool jsonset_grp_size_init(UDF_INIT *, UDF_ARGS *args, char *message) { |
if (args->arg_count != 1 || args->arg_type[0] != INT_RESULT) { |
std::strcpy(message, "This function must have 1 integer argument"); |
return true; |
} else |
return false; |
}
|
|
|
long long jsonset_grp_size(UDF_INIT *, UDF_ARGS *args, uchar *, uchar *) { |
long long n = *(long long *)args->args[0]; |
JsonGrpSize = (uint)n;
|
return (long long)GetJsonGroupSize(); |
}
|
|
|
int main() { |
UDF_INIT init;
|
Item_result type = INT_RESULT;
|
char *values[] = {nullptr}; // MariaDB's UDF ABI representation of SQL NULL. |
UDF_ARGS args{1, &type, values};
|
char message[256]{}; |
if (jsonset_grp_size_init(&init, &args, message)) { |
std::fprintf(stderr, "unexpected init rejection: %s\n", message); |
return 2; |
}
|
std::puts("init accepted INT_RESULT; invoking with args[0] == NULL"); |
std::fflush(stdout); |
uchar is_null = 0, error = 0;
|
(void)jsonset_grp_size(&init, &args, &is_null, &error); |
std::puts("BUG NOT REPRODUCED: function returned"); |
return 3; |
}
|
Save this as run.sh in the same directory. The oracle accepts only status 139, which is the recorded Bash status for termination by SIGSEGV; an ordinary exit with status 128 does not pass.
#!/bin/sh
|
set -eu |
|
|
cxx="${CXX:-c++}" |
"$cxx" -std=c++11 -O0 -g -Wall -Wextra -o repro repro.cpp |
set +e |
./repro |
status=$?
|
set -e |
|
|
if [ "$status" -eq 139 ]; then |
echo "PASS: null dereference terminated the implementation (status $status)" |
exit 0 |
fi
|
|
|
echo "FAIL: expected SIGSEGV-derived status 139, got $status" >&2 |
exit 1 |
With a C+11 compiler available as c+, run:
bash run.sh |
The recorded output was:
init accepted INT_RESULT; invoking with args[0] == NULL
|
run.sh: line 7: Segmentation fault (core dumped) ./repro
|
PASS: null dereference terminated the implementation (status 139)
|
MariaDB's UDF dispatcher leaves an INT_RESULT argument pointer NULL when its SQL value is NULL. The CONNECT initializer checks only the argument count and type, and the execution function then dereferences args->args[0] without checking it. The function should recognize the UDF ABI's NULL representation and return SQL NULL or a controlled error instead of terminating execution.
For a MariaDB build that contains ha_connect.so, the corresponding end-to-end trigger is:
CREATE FUNCTION jsonset_grp_size RETURNS INTEGER SONAME 'ha_connect.so'; |
SELECT jsonset_grp_size(CAST(NULL AS SIGNED)); |
-- Run after restarting the server if the preceding statement terminates it.
|
DROP FUNCTION IF EXISTS jsonset_grp_size; |
This SQL path is source-supported but was not successfully executed in the recorded environment: registration on MariaDB 12.3.3 failed with ERROR 1126 (HY000), errno 2, because ha_connect.so was unavailable. Consequently, this report does not claim an observed server crash or a runtime-verified affected release. In a server where this CONNECT UDF is registered, the same execution function runs in the server process.