Skip to content

Commit b8c8692

Browse files
committed
MDEV-24620 ASAN heap-buffer-overflow in btr_pcur_restore_position()
Between btr_pcur_store_position() and btr_pcur_restore_position() it is possible that purge empties a table and enlarges index->n_core_fields and index->n_core_null_bytes. Therefore, we must cache index->n_core_fields in btr_pcur_t::old_n_core_fields so that btr_pcur_t::old_rec can be parsed correctly. Unfortunately, this is a huge change, because we will replace "bool leaf" parameters with "ulint n_core" (passing index->n_core_fields, or 0 for non-leaf pages). For special cases where we know that index->is_instant() cannot hold, we may also pass index->n_fields.
1 parent 6e6318b commit b8c8692

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+614
-421
lines changed

mysql-test/suite/innodb/r/instant_alter_debug.result

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,5 +317,33 @@ SELECT * FROM t1 WHERE c<>1 ORDER BY c DESC;
317317
c d
318318
DROP TABLE t1;
319319
SET GLOBAL innodb_limit_optimistic_insert_debug = @saved_limit;
320+
#
321+
# MDEV-24620 ASAN heap-buffer-overflow in btr_pcur_restore_position()
322+
#
323+
CREATE TABLE t1 (a VARCHAR(1) PRIMARY KEY) ENGINE=InnoDB;
324+
INSERT INTO t1 VALUES (1);
325+
connect stop_purge,localhost,root,,;
326+
START TRANSACTION WITH CONSISTENT SNAPSHOT;
327+
connection default;
328+
ALTER TABLE t1 ADD c INT;
329+
BEGIN;
330+
DELETE FROM t1;
331+
connect dml,localhost,root,,test;
332+
SET DEBUG_SYNC='row_mysql_handle_errors SIGNAL s1 WAIT_FOR s2';
333+
UPDATE t1 SET c=1;
334+
connection default;
335+
SET DEBUG_SYNC='now WAIT_FOR s1';
336+
COMMIT;
337+
connection stop_purge;
338+
COMMIT;
339+
disconnect stop_purge;
340+
connection default;
341+
InnoDB 0 transactions not purged
342+
SET DEBUG_SYNC='now SIGNAL s2';
343+
connection dml;
344+
disconnect dml;
345+
connection default;
346+
SET DEBUG_SYNC=RESET;
347+
DROP TABLE t1;
320348
# End of 10.3 tests
321349
SET GLOBAL innodb_purge_rseg_truncate_frequency = @save_frequency;

mysql-test/suite/innodb/t/instant_alter_debug.test

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,44 @@ DROP TABLE t1;
364364

365365
SET GLOBAL innodb_limit_optimistic_insert_debug = @saved_limit;
366366

367+
--echo #
368+
--echo # MDEV-24620 ASAN heap-buffer-overflow in btr_pcur_restore_position()
369+
--echo #
370+
371+
CREATE TABLE t1 (a VARCHAR(1) PRIMARY KEY) ENGINE=InnoDB;
372+
INSERT INTO t1 VALUES (1);
373+
connect (stop_purge,localhost,root,,);
374+
START TRANSACTION WITH CONSISTENT SNAPSHOT;
375+
376+
connection default;
377+
ALTER TABLE t1 ADD c INT;
378+
BEGIN;
379+
DELETE FROM t1;
380+
381+
connect (dml,localhost,root,,test);
382+
SET DEBUG_SYNC='row_mysql_handle_errors SIGNAL s1 WAIT_FOR s2';
383+
send UPDATE t1 SET c=1;
384+
385+
connection default;
386+
SET DEBUG_SYNC='now WAIT_FOR s1';
387+
COMMIT;
388+
389+
connection stop_purge;
390+
COMMIT;
391+
disconnect stop_purge;
392+
393+
connection default;
394+
--source include/wait_all_purged.inc
395+
SET DEBUG_SYNC='now SIGNAL s2';
396+
397+
connection dml;
398+
reap;
399+
disconnect dml;
400+
401+
connection default;
402+
SET DEBUG_SYNC=RESET;
403+
DROP TABLE t1;
404+
367405
--echo # End of 10.3 tests
368406

369407
SET GLOBAL innodb_purge_rseg_truncate_frequency = @save_frequency;

storage/innobase/btr/btr0btr.cc

Lines changed: 33 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
33
Copyright (c) 1994, 2016, Oracle and/or its affiliates. All Rights Reserved.
44
Copyright (c) 2012, Facebook Inc.
5-
Copyright (c) 2014, 2020, MariaDB Corporation.
5+
Copyright (c) 2014, 2021, MariaDB Corporation.
66
77
This program is free software; you can redistribute it and/or modify it under
88
the terms of the GNU General Public License as published by the Free Software
@@ -890,7 +890,7 @@ btr_page_get_father_node_ptr_func(
890890

891891
node_ptr = btr_cur_get_rec(cursor);
892892

893-
offsets = rec_get_offsets(node_ptr, index, offsets, false,
893+
offsets = rec_get_offsets(node_ptr, index, offsets, 0,
894894
ULINT_UNDEFINED, &heap);
895895

896896
if (btr_node_ptr_get_child_page_no(node_ptr, offsets) != page_no) {
@@ -907,10 +907,11 @@ btr_page_get_father_node_ptr_func(
907907
print_rec = page_rec_get_next(
908908
page_get_infimum_rec(page_align(user_rec)));
909909
offsets = rec_get_offsets(print_rec, index, offsets,
910-
page_rec_is_leaf(user_rec),
910+
page_rec_is_leaf(user_rec)
911+
? index->n_core_fields : 0,
911912
ULINT_UNDEFINED, &heap);
912913
page_rec_print(print_rec, offsets);
913-
offsets = rec_get_offsets(node_ptr, index, offsets, false,
914+
offsets = rec_get_offsets(node_ptr, index, offsets, 0,
914915
ULINT_UNDEFINED, &heap);
915916
page_rec_print(node_ptr, offsets);
916917

@@ -2214,7 +2215,9 @@ btr_page_get_split_rec(
22142215
incl_data += insert_size;
22152216
} else {
22162217
offsets = rec_get_offsets(rec, cursor->index, offsets,
2217-
page_is_leaf(page),
2218+
page_is_leaf(page)
2219+
? cursor->index->n_core_fields
2220+
: 0,
22182221
ULINT_UNDEFINED, &heap);
22192222
incl_data += rec_offs_size(offsets);
22202223
}
@@ -2323,7 +2326,9 @@ btr_page_insert_fits(
23232326
space after rec is removed from page. */
23242327

23252328
*offsets = rec_get_offsets(rec, cursor->index, *offsets,
2326-
page_is_leaf(page),
2329+
page_is_leaf(page)
2330+
? cursor->index->n_core_fields
2331+
: 0,
23272332
ULINT_UNDEFINED, heap);
23282333

23292334
total_data -= rec_offs_size(*offsets);
@@ -2610,7 +2615,8 @@ btr_page_tuple_smaller(
26102615
first_rec = page_cur_get_rec(&pcur);
26112616

26122617
*offsets = rec_get_offsets(
2613-
first_rec, cursor->index, *offsets, page_is_leaf(block->frame),
2618+
first_rec, cursor->index, *offsets,
2619+
page_is_leaf(block->frame) ? cursor->index->n_core_fields : 0,
26142620
n_uniq, heap);
26152621

26162622
return(cmp_dtuple_rec(tuple, first_rec, *offsets) < 0);
@@ -2894,7 +2900,9 @@ btr_page_split_and_insert(
28942900
first_rec = move_limit = split_rec;
28952901

28962902
*offsets = rec_get_offsets(split_rec, cursor->index, *offsets,
2897-
page_is_leaf(page), n_uniq, heap);
2903+
page_is_leaf(page)
2904+
? cursor->index->n_core_fields : 0,
2905+
n_uniq, heap);
28982906

28992907
insert_left = !tuple
29002908
|| cmp_dtuple_rec(tuple, split_rec, *offsets) < 0;
@@ -3665,7 +3673,7 @@ btr_compress(
36653673
rec_offs* offsets2 = NULL;
36663674

36673675
/* For rtree, we need to update father's mbr. */
3668-
if (dict_index_is_spatial(index)) {
3676+
if (index->is_spatial()) {
36693677
/* We only support merge pages with the same parent
36703678
page */
36713679
if (!rtr_check_same_block(
@@ -3683,7 +3691,8 @@ btr_compress(
36833691

36843692
offsets2 = rec_get_offsets(
36853693
btr_cur_get_rec(&cursor2), index, NULL,
3686-
page_is_leaf(cursor2.page_cur.block->frame),
3694+
page_is_leaf(cursor2.page_cur.block->frame)
3695+
? index->n_fields : 0,
36873696
ULINT_UNDEFINED, &heap);
36883697

36893698
/* Check if parent entry needs to be updated */
@@ -3857,13 +3866,14 @@ btr_compress(
38573866
#endif /* UNIV_DEBUG */
38583867

38593868
/* For rtree, we need to update father's mbr. */
3860-
if (dict_index_is_spatial(index)) {
3869+
if (index->is_spatial()) {
38613870
rec_offs* offsets2;
38623871
ulint rec_info;
38633872

38643873
offsets2 = rec_get_offsets(
38653874
btr_cur_get_rec(&cursor2), index, NULL,
3866-
page_is_leaf(cursor2.page_cur.block->frame),
3875+
page_is_leaf(cursor2.page_cur.block->frame)
3876+
? index->n_fields : 0,
38673877
ULINT_UNDEFINED, &heap);
38683878

38693879
ut_ad(btr_node_ptr_get_child_page_no(
@@ -4341,7 +4351,7 @@ btr_print_recursive(
43414351
node_ptr = page_cur_get_rec(&cursor);
43424352

43434353
*offsets = rec_get_offsets(
4344-
node_ptr, index, *offsets, false,
4354+
node_ptr, index, *offsets, 0,
43454355
ULINT_UNDEFINED, heap);
43464356
btr_print_recursive(index,
43474357
btr_node_ptr_get_child(node_ptr,
@@ -4490,7 +4500,9 @@ btr_index_rec_validate(
44904500

44914501
page = page_align(rec);
44924502

4493-
if (dict_index_is_ibuf(index)) {
4503+
ut_ad(index->n_core_fields);
4504+
4505+
if (index->is_ibuf()) {
44944506
/* The insert buffer index tree can contain records from any
44954507
other index: we cannot check the number of fields or
44964508
their length */
@@ -4536,7 +4548,8 @@ btr_index_rec_validate(
45364548
}
45374549
}
45384550

4539-
offsets = rec_get_offsets(rec, index, offsets, page_is_leaf(page),
4551+
offsets = rec_get_offsets(rec, index, offsets, page_is_leaf(page)
4552+
? index->n_core_fields : 0,
45404553
ULINT_UNDEFINED, &heap);
45414554

45424555
for (unsigned i = 0; i < index->n_fields; i++) {
@@ -4792,7 +4805,7 @@ btr_validate_level(
47924805
page_cur_move_to_next(&cursor);
47934806

47944807
node_ptr = page_cur_get_rec(&cursor);
4795-
offsets = rec_get_offsets(node_ptr, index, offsets, false,
4808+
offsets = rec_get_offsets(node_ptr, index, offsets, 0,
47964809
ULINT_UNDEFINED, &heap);
47974810

47984811
savepoint2 = mtr_set_savepoint(&mtr);
@@ -4916,10 +4929,12 @@ btr_validate_level(
49164929
right_rec = page_rec_get_next(page_get_infimum_rec(
49174930
right_page));
49184931
offsets = rec_get_offsets(rec, index, offsets,
4919-
page_is_leaf(page),
4932+
page_is_leaf(page)
4933+
? index->n_core_fields : 0,
49204934
ULINT_UNDEFINED, &heap);
49214935
offsets2 = rec_get_offsets(right_rec, index, offsets2,
4922-
page_is_leaf(right_page),
4936+
page_is_leaf(right_page)
4937+
? index->n_core_fields : 0,
49234938
ULINT_UNDEFINED, &heap);
49244939

49254940
/* For spatial index, we cannot guarantee the key ordering

storage/innobase/btr/btr0bulk.cc

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*****************************************************************************
22
33
Copyright (c) 2014, 2019, Oracle and/or its affiliates. All Rights Reserved.
4-
Copyright (c) 2017, 2020, MariaDB Corporation.
4+
Copyright (c) 2017, 2021, MariaDB Corporation.
55
66
This program is free software; you can redistribute it and/or modify it under
77
the terms of the GNU General Public License as published by the Free Software
@@ -193,7 +193,8 @@ PageBulk::insert(
193193
if (!page_rec_is_infimum_low(page_offset(m_cur_rec))) {
194194
rec_t* old_rec = m_cur_rec;
195195
rec_offs* old_offsets = rec_get_offsets(
196-
old_rec, m_index, NULL, is_leaf,
196+
old_rec, m_index, NULL, is_leaf
197+
? m_index->n_core_fields : 0,
197198
ULINT_UNDEFINED, &m_heap);
198199

199200
ut_ad(cmp_rec_rec(rec, old_rec, offsets, old_offsets, m_index)
@@ -447,6 +448,7 @@ PageBulk::getSplitRec()
447448

448449
ut_ad(m_page_zip != NULL);
449450
ut_ad(m_rec_no >= 2);
451+
ut_ad(!m_index->is_instant());
450452

451453
ut_ad(page_get_free_space_of_empty(m_is_comp) > m_free_space);
452454
total_used_size = page_get_free_space_of_empty(m_is_comp)
@@ -456,13 +458,13 @@ PageBulk::getSplitRec()
456458
n_recs = 0;
457459
offsets = NULL;
458460
rec = page_get_infimum_rec(m_page);
461+
const ulint n_core = page_is_leaf(m_page) ? m_index->n_core_fields : 0;
459462

460463
do {
461464
rec = page_rec_get_next(rec);
462465
ut_ad(page_rec_is_user_rec(rec));
463466

464-
offsets = rec_get_offsets(rec, m_index, offsets,
465-
page_is_leaf(m_page),
467+
offsets = rec_get_offsets(rec, m_index, offsets, n_core,
466468
ULINT_UNDEFINED, &m_heap);
467469
total_recs_size += rec_offs_size(offsets);
468470
n_recs++;
@@ -491,9 +493,11 @@ PageBulk::copyIn(
491493
ut_ad(m_rec_no == 0);
492494
ut_ad(page_rec_is_user_rec(rec));
493495

496+
const ulint n_core = page_rec_is_leaf(rec)
497+
? m_index->n_core_fields : 0;
498+
494499
do {
495-
offsets = rec_get_offsets(rec, m_index, offsets,
496-
page_rec_is_leaf(split_rec),
500+
offsets = rec_get_offsets(rec, m_index, offsets, n_core,
497501
ULINT_UNDEFINED, &m_heap);
498502

499503
insert(rec, offsets);
@@ -534,17 +538,18 @@ PageBulk::copyOut(
534538
/* Set last record's next in page */
535539
rec_offs* offsets = NULL;
536540
rec = page_rec_get_prev(split_rec);
537-
offsets = rec_get_offsets(rec, m_index, offsets,
538-
page_rec_is_leaf(split_rec),
541+
const ulint n_core = page_rec_is_leaf(split_rec)
542+
? m_index->n_core_fields : 0;
543+
544+
offsets = rec_get_offsets(rec, m_index, offsets, n_core,
539545
ULINT_UNDEFINED, &m_heap);
540546
page_rec_set_next(rec, page_get_supremum_rec(m_page));
541547

542548
/* Set related members */
543549
m_cur_rec = rec;
544550
m_heap_top = rec_get_end(rec, offsets);
545551

546-
offsets = rec_get_offsets(last_rec, m_index, offsets,
547-
page_rec_is_leaf(split_rec),
552+
offsets = rec_get_offsets(last_rec, m_index, offsets, n_core,
548553
ULINT_UNDEFINED, &m_heap);
549554

550555
m_free_space += ulint(rec_get_end(last_rec, offsets) - m_heap_top)
@@ -975,7 +980,8 @@ BtrBulk::insert(
975980
/* Convert tuple to rec. */
976981
rec = rec_convert_dtuple_to_rec(static_cast<byte*>(mem_heap_alloc(
977982
page_bulk->m_heap, rec_size)), m_index, tuple, n_ext);
978-
offsets = rec_get_offsets(rec, m_index, offsets, !level,
983+
offsets = rec_get_offsets(rec, m_index, offsets, level
984+
? 0 : m_index->n_core_fields,
979985
ULINT_UNDEFINED, &page_bulk->m_heap);
980986

981987
page_bulk->insert(rec, offsets);

0 commit comments

Comments
 (0)