Details
-
Bug
-
Status: Confirmed (View Workflow)
-
Major
-
Resolution: Unresolved
-
10.11, 11.4, 11.8, 12.3, 13.1, 13.2
-
MariaDB 13.2.0-MariaDB-asan (Source distribution), @@version_source_revision
f6d172c03bf3cd71fd30ac9cc7b24fd64497b472 (main branch, 2026-10-21).
Build: cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DWITH_ASAN=ON -DWITH_ASAN_SCOPE=ON,
GCC with libsanitizer, Linux x86_64.
Client: python3 + mysql-connector-python (the reproducer sends raw
COM_STMT_PREPARE / COM_STMT_EXECUTE packets).MariaDB 13.2.0-MariaDB-asan (Source distribution), @@version_source_revision f6d172c03bf3cd71fd30ac9cc7b24fd64497b472 (main branch, 2026-10-21). Build: cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DWITH_ASAN=ON -DWITH_ASAN_SCOPE=ON, GCC with libsanitizer, Linux x86_64. Client: python3 + mysql-connector-python (the reproducer sends raw COM_STMT_PREPARE / COM_STMT_EXECUTE packets).
-
Can result in hang or crash
-
Crash when a large binary-protocol PS string parameter is materialized into an Aria temporary table (UNION ALL + ORDER BY)
Description
Summary
When a prepared-statement parameter is declared at the protocol level as
MYSQL_TYPE_STRING (0xfe) and carries a large value (value_bytes * connection
mbmaxlen > 65535), and that parameter is placed directly into an output column
of a UNION ALL with a global ORDER BY, the union result is materialized into an
Aria block-record temporary table. The blob slot of the record written by
select_unit::write_record() then holds a (length, pointer) pair that does not
describe a real buffer, and write_full_pages() memcpys from the wild address.
AddressSanitizer aborts the server; in a non-ASan build this is a read from an
arbitrary address (server crash, potentially copying arbitrary memory into the
temporary table).
Steps to reproduce
- Build the server with -DWITH_ASAN=ON (RelWithDebInfo) and start mariadbd.
- Run the attached reproduce_report.py (requires python3 and
mysql-connector-python):python3 reproduce_report.py -H 127.0.0.1 -P 3306 -u root
The script creates two tiny InnoDB tables, then sends COM_STMT_PREPARE for:
select cast(? as signed) as a1, ? as a2, 1 as a3, 1 as a4, 1 as a5, |
cast(? as double) as a6 from t4 |
union all (select count(t.c3) - cast(? as signed) as a1, ? as a2, |
t.c9 as a3, t.c3 as a4, count(t.c4) as a5, t.c4 as a6 from t3 t) |
order by a5, a6 |
and a COM_STMT_EXECUTE whose parameter for output column a2 has protocol type
byte 0xfe (MYSQL_TYPE_STRING) with a 65536-byte value.
Note on temp table engine: on current main (since MDEV-38975, HEAP blob
support) the internal temp table stays in HEAP with default settings, so the
script forces conversion to Aria with:
SET SESSION tmp_memory_table_size=1024; |
SET SESSION max_heap_table_size=1024; |
Actual result
mariadbd aborts (AddressSanitizer CHECK failed, "Address is not in memory and
not in shadow"):
#8 __interceptor_memcpy
|
#9 write_full_pages storage/maria/ma_blockrec.c:2170
|
#10 write_block_record storage/maria/ma_blockrec.c:3475
|
#11 allocate_and_write_block_record storage/maria/ma_blockrec.c:3580
|
#12 _ma_write_init_block_record storage/maria/ma_blockrec.c:3620
|
#13 maria_write storage/maria/ma_write.c:158
|
#14 handler::ha_write_tmp_row sql/sql_class.h:8238
|
#15 select_unit::write_record sql/sql_union.cc:417
|
#16 select_unit::send_data sql/sql_union.cc:161
|
#17 select_result_sink::send_data_with_check sql/sql_class.cc:3381
|
...
|
#33 mysqld_stmt_execute sql/sql_prepare.cc:3407
|
The full ASan trace is attached (mariadb_error_delta.log).
Expected result
The statement executes (or returns an error) without crashing the server.
Analysis / notes
- The bug requires the binary protocol: the parameter must arrive with protocol
type MYSQL_TYPE_STRING (0xfe). SQL-level PREPARE / EXECUTE ... USING,
CAST/CONVERT and text inlining cannot produce this type handler for a value
of this size and do NOT reproduce the crash. - Size threshold: value_bytes * connection mbmaxlen > 65535 (with utf8mb4,
16384 bytes suffice). - Both UNION branches must scan real tables, and the second branch's aggregate
plus its column sources are needed to keep the record layout on the
full-pages path. (With a single shared table, the same root cause surfaces as
the sibling manifestation in write_tail at ma_blockrec.c:2001.) - Root cause: the blob slot of the temp-table record carries a (length,
pointer) pair that does not describe a real buffer; write_full_pages()
memcpys from the wild address. - Reproduced on 13.2.0-MariaDB-asan @ f6d172c03bf3 (main, 2026-10-21).
Attachments
Issue Links
- relates to
-
MDEV-39562 Assertion `length <= column->length' failed in write_block_record on SELECT...UNION, table corruption
-
- Closed
-