Details
-
Bug
-
Status: Closed (View Workflow)
-
Major
-
Resolution: Fixed
-
10.11
-
None
-
Can result in hang or crash
-
Q4/2026 Server Maintenance
Description
partition_info::get_clone() returns NULL when allocation fails. Most callers check it:
if (!(tab_part_info= tab_part_info->get_clone(thd))) |
DBUG_RETURN(TRUE);
|
Three callers in sql_partition.cc do not, and dereference the result right away. Out-of-memory during the ALTER crashes the server instead of failing the statement with ER_OUT_OF_RESOURCES.
| Function | Statement | Since |
|---|---|---|
| prep_alter_part_table() | ALTER ... PARTITION BY SYSTEM_TIME with a changed INTERVAL | |
| prep_alter_part_table() | ALTER ... ADD PARTITION under LOCK TABLES | |
| handle_alter_part_error() | any failed fast ALTER of partitions | MySQL legacy, every call since Bug#19784790, 10.5.3 |
prep_alter_part_table(), SYSTEM_TIME INTERVAL
/* If interval is changed we can not do fast alter */
|
tab_part_info= tab_part_info->get_clone(thd);
|
...
|
*tab_part_info->vers_info= *part_info->vers_info;
|
prep_alter_part_table(), ADD PARTITION under LOCK TABLES
saved_part_info= tab_part_info->get_clone(thd);
|
thd->mem_root= old_root;
|
saved_part_info->read_partitions= tab_part_info->read_partitions;
|
handle_alter_part_error()
The clone here is made only to keep the DDL log entries (list, execute_entry) after the table is closed, because closing the table frees lpt->part_info. Besides the missing NULL check, the branch where DDL log recovery fails is wrong:
write_log_completed(lpt, FALSE); // releases entries via lpt->part_info |
release_log_entries(part_info); // releases the same entries via the clone |
- write_log_completed() reads lpt->part_info, which the table close may already have freed.
- The entries are released twice. A released entry gets next_active_log_entry= 0x1, so the second release follows that pointer and crashes.
This branch is currently unreachable: ddl_log_execute_entry() always returns false. It is still wrong and will crash once recovery can report failure.
How to repeat
Needs a debug build and an injection point right before get_clone() that turns on simulate_out_of_memory, so the clone's allocation fails. The fix adds one named prep_alter_part_table_get_clone_oom.
--source include/have_debug.inc
|
--source include/have_partition.inc
|
|
|
create table t1 (x int) with system versioning |
partition by system_time interval 1 hour partitions 3; |
set @@debug_dbug= '+d,prep_alter_part_table_get_clone_oom'; |
--error ER_OUT_OF_RESOURCES
|
alter table t1 partition by system_time interval 2 hour; |
set @@debug_dbug= default; |
drop table t1; |
|
|
create table t1 (x int) partition by range (x) |
(partition p0 values less than (10)); |
lock tables t1 write;
|
set @@debug_dbug= '+d,prep_alter_part_table_get_clone_oom'; |
--error ER_OUT_OF_RESOURCES
|
alter table t1 add partition (partition p1 values less than (20)); |
set @@debug_dbug= default; |
unlock tables;
|
drop table t1; |
Without the fix both ALTERs crash with a NULL pointer dereference in prep_alter_part_table().
Fix
- prep_alter_part_table(): check the get_clone() result like the other callers do.
- handle_alter_part_error(): keep a copy of DDL_LOG_STATE instead of cloning part_info. Nothing is allocated, so nothing can fail. Pass it to write_log_completed() and release_log_entries(), which now take DDL_LOG_STATE *. The first call releases the entries and clears the pointers, so the second call does nothing.
Attachments
Issue Links
- relates to
-
MDEV-41543 Changing INTERVAL of SYSTEM_TIME partitioning does not move history to the new partitions
-
- Closed
-