From 0faff1d27893e3d2bd33aa05a29e66a396ac2eee Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Mon, 19 Oct 2026 11:27:37 +0200 Subject: [PATCH] MDEV-41613 CONNECT jfile_convert crashes on invalid file Assisted-by: Claude:claude-opus-5-5 --- storage/connect/bsonudf.cpp | 4 +++ storage/connect/jsonudf.cpp | 16 ++++++++--- .../connect/r/json_bson_file_priv.result | 27 +++++++++++++++++++ .../connect/t/json_bson_file_priv.test | 26 ++++++++++++++++++ 4 files changed, 70 insertions(+), 3 deletions(-) diff --git a/storage/connect/bsonudf.cpp b/storage/connect/bsonudf.cpp index 3d214d08046..1d58944de0e 100644 --- a/storage/connect/bsonudf.cpp +++ b/storage/connect/bsonudf.cpp @@ -4705,6 +4705,10 @@ char *bfile_convert(UDF_INIT* initid, UDF_ARGS* args, char* result, PUSH_WARNING("Missing LRECL"); *is_null = 1; return NULL; + } else if (*(longlong*)args->args[2] <= 0 || *(longlong*)args->args[2] > INT_MAX32) { + PUSH_WARNING("Invalid LRECL"); + *is_null = 1; + return NULL; } else if (!connect_can_access_file(fn) || !connect_can_access_file(ofn)) { PUSH_WARNING("Access denied: FILE privilege or secure_file_priv violation"); *is_null = 1; diff --git a/storage/connect/jsonudf.cpp b/storage/connect/jsonudf.cpp index 59651c1b617..e68d11f99f3 100644 --- a/storage/connect/jsonudf.cpp +++ b/storage/connect/jsonudf.cpp @@ -5980,6 +5980,10 @@ char *jfile_convert(UDF_INIT* initid, UDF_ARGS* args, char* result, PUSH_WARNING("Missing LRECL"); *is_null = 1; return NULL; + } else if (*(longlong*)args->args[2] <= 0 || *(longlong*)args->args[2] > INT_MAX32) { + PUSH_WARNING("Invalid LRECL"); + *is_null = 1; + return NULL; } else if (!connect_can_access_file(fn) || !connect_can_access_file(ofn)) { PUSH_WARNING("Access denied: FILE privilege or secure_file_priv violation"); *is_null = 1; @@ -6247,9 +6251,15 @@ bool JUP::unPretty(PGLOBAL g, int lrecl) { snprintf(g->Message, sizeof(g->Message), "Void JSON file"); return true; } else if (*s != '[') { - // strcpy(g->Message, "JSON file is not an array"); - s = strchr(s, '['); - // return true; + char *p = (char*)memchr(s, '[', len); + + if (!p) { + snprintf(g->Message, sizeof(g->Message), "JSON file is not an array"); + return true; + } // endif p + + len -= p - s; + s = p; } // endif s i = 1; diff --git a/storage/connect/mysql-test/connect/r/json_bson_file_priv.result b/storage/connect/mysql-test/connect/r/json_bson_file_priv.result index 1eb9309de9c..473d81b3f5e 100644 --- a/storage/connect/mysql-test/connect/r/json_bson_file_priv.result +++ b/storage/connect/mysql-test/connect/r/json_bson_file_priv.result @@ -280,6 +280,33 @@ ok select bfile_bjson('MYSQL_TMP_DIR/bib0.json', 'MYSQL_TMP_DIR/out.json', if(rand() > 1, NULL, 1024)) IS NOT NULL AS ok; ok 1 +# Input file is not a JSON array +select jfile_convert('MYSQL_TMP_DIR/people.csv', 'MYSQL_TMP_DIR/out.json', 256); +jfile_convert('MYSQL_TMP_DIR/people.csv', 'MYSQL_TMP_DIR/out.json', 256) +NULL +Warnings: +Warning 1105 JSON file is not an array +select bfile_convert('MYSQL_TMP_DIR/people.csv', 'MYSQL_TMP_DIR/out.json', 256); +bfile_convert('MYSQL_TMP_DIR/people.csv', 'MYSQL_TMP_DIR/out.json', 256) +NULL +Warnings: +Warning 1105 JSON file is not an array +# JSON array does not start the file +select jfile_convert('MYSQL_TMP_DIR/array.json', 'MYSQL_TMP_DIR/out.json', 256); +jfile_convert('MYSQL_TMP_DIR/array.json', 'MYSQL_TMP_DIR/out.json', 256) +MYSQL_TMP_DIR/out.json +{"a":1} +# Non-positive LRECL +select jfile_convert('MYSQL_TMP_DIR/biblio.json', 'MYSQL_TMP_DIR/out.json', -3); +jfile_convert('MYSQL_TMP_DIR/biblio.json', 'MYSQL_TMP_DIR/out.json', -3) +NULL +Warnings: +Warning 1105 Invalid LRECL +select bfile_convert('MYSQL_TMP_DIR/biblio.json', 'MYSQL_TMP_DIR/out.json', 0); +bfile_convert('MYSQL_TMP_DIR/biblio.json', 'MYSQL_TMP_DIR/out.json', 0) +NULL +Warnings: +Warning 1105 Invalid LRECL # Privileged user: relative path resolving outside secure_file_priv select json_file('../../bib0.json') IS NULL AS denied; denied diff --git a/storage/connect/mysql-test/connect/t/json_bson_file_priv.test b/storage/connect/mysql-test/connect/t/json_bson_file_priv.test index 7dd711b505d..f6c2f1c25f1 100644 --- a/storage/connect/mysql-test/connect/t/json_bson_file_priv.test +++ b/storage/connect/mysql-test/connect/t/json_bson_file_priv.test @@ -139,6 +139,32 @@ select bfile_convert('biblio.json', 'out.json', if(rand() > 1, 1, NULL)) IS NULL --eval select bfile_bjson('$MYSQL_TMP_DIR/bib0.json', '$MYSQL_TMP_DIR/out.json', if(rand() > 1, NULL, 1024)) IS NOT NULL AS ok --remove_file $MYSQL_TMP_DIR/out.json +--echo # Input file is not a JSON array +--copy_file $MTR_SUITE_DIR/std_data/people.csv $MYSQL_TMP_DIR/people.csv +--replace_result $MYSQL_TMP_DIR MYSQL_TMP_DIR +--eval select jfile_convert('$MYSQL_TMP_DIR/people.csv', '$MYSQL_TMP_DIR/out.json', 256) +--remove_file $MYSQL_TMP_DIR/out.json +--replace_result $MYSQL_TMP_DIR MYSQL_TMP_DIR +--eval select bfile_convert('$MYSQL_TMP_DIR/people.csv', '$MYSQL_TMP_DIR/out.json', 256) +--remove_file $MYSQL_TMP_DIR/out.json +--remove_file $MYSQL_TMP_DIR/people.csv + +--echo # JSON array does not start the file +--write_file $MYSQL_TMP_DIR/array.json +x[{"a":1}] +EOF +--replace_result $MYSQL_TMP_DIR MYSQL_TMP_DIR +--eval select jfile_convert('$MYSQL_TMP_DIR/array.json', '$MYSQL_TMP_DIR/out.json', 256) +--cat_file $MYSQL_TMP_DIR/out.json +--remove_file $MYSQL_TMP_DIR/out.json +--remove_file $MYSQL_TMP_DIR/array.json + +--echo # Non-positive LRECL +--replace_result $MYSQL_TMP_DIR MYSQL_TMP_DIR +--eval select jfile_convert('$MYSQL_TMP_DIR/biblio.json', '$MYSQL_TMP_DIR/out.json', -3) +--replace_result $MYSQL_TMP_DIR MYSQL_TMP_DIR +--eval select bfile_convert('$MYSQL_TMP_DIR/biblio.json', '$MYSQL_TMP_DIR/out.json', 0) + --echo # Privileged user: relative path resolving outside secure_file_priv select json_file('../../bib0.json') IS NULL AS denied; select bson_file('../../biblio.json') IS NULL AS denied;