Skip to content

Commit efd8af5

Browse files
committed
MDEV-19526 heap number overflow on innodb_page_size=64k
InnoDB only reserves 13 bits for the heap number in the record header, limiting the heap number to be at most 8191. But, when using innodb_page_size=64k and secondary index records of 7 bytes each, it is possible to exceed the maximum heap number. btr_cur_optimistic_insert(): Let the operation fail if the maximum number of records would be exceeded. page_mem_alloc_heap(): Move to the same compilation unit with the only caller, and let the operation fail if the maximum heap number has been allocated already.
1 parent 7ad4709 commit efd8af5

File tree

10 files changed

+139
-122
lines changed

10 files changed

+139
-122
lines changed

mysql-test/suite/innodb/r/innodb-64k.result

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1084,3 +1084,10 @@ update t2 set col145=@b;
10841084
COMMIT;
10851085
drop table t2;
10861086
DROP TABLE t1;
1087+
#
1088+
# MDEV-19526 heap number overflow
1089+
#
1090+
CREATE TABLE t1(a SMALLINT NOT NULL UNIQUE AUTO_INCREMENT, KEY(a))
1091+
ENGINE=InnoDB;
1092+
INSERT INTO t1 (a) SELECT seq FROM seq_1_to_8191;
1093+
DROP TABLE t1;

mysql-test/suite/innodb/t/innodb-64k.test

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
# Tests for setting innodb-page-size=64k;
33
--source include/have_innodb.inc
44
--source include/have_innodb_64k.inc
5+
--source include/have_sequence.inc
56

67
call mtr.add_suppression("InnoDB: Warning: innodb_page_size has been changed from default value *");
78
call mtr.add_suppression("InnoDB: Resizing redo log from *");
@@ -650,6 +651,15 @@ COMMIT;
650651

651652
drop table t2;
652653
DROP TABLE t1;
654+
655+
--echo #
656+
--echo # MDEV-19526 heap number overflow
657+
--echo #
658+
CREATE TABLE t1(a SMALLINT NOT NULL UNIQUE AUTO_INCREMENT, KEY(a))
659+
ENGINE=InnoDB;
660+
INSERT INTO t1 (a) SELECT seq FROM seq_1_to_8191;
661+
DROP TABLE t1;
662+
653663
#
654664
# restore environment to the state it was before this test execution
655665
#

storage/innobase/btr/btr0cur.cc

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
Copyright (c) 1994, 2016, Oracle and/or its affiliates. All Rights Reserved.
44
Copyright (c) 2008, Google Inc.
55
Copyright (c) 2012, Facebook Inc.
6-
Copyright (c) 2015, 2017, MariaDB Corporation.
6+
Copyright (c) 2015, 2020, MariaDB Corporation.
77
88
Portions of this file contain modifications contributed and copyrighted by
99
Google, Inc. Those modifications are gratefully acknowledged and are described
@@ -1433,17 +1433,23 @@ btr_cur_optimistic_insert(
14331433
}
14341434

14351435
ulint max_size = page_get_max_insert_size_after_reorganize(page, 1);
1436+
if (max_size < rec_size) {
1437+
goto fail;
1438+
}
1439+
1440+
const ulint n_recs = page_get_n_recs(page);
1441+
if (UNIV_UNLIKELY(n_recs >= 8189)) {
1442+
ut_ad(srv_page_size == 65536);
1443+
goto fail;
1444+
}
14361445

14371446
if (page_has_garbage(page)) {
1438-
if ((max_size < rec_size
1439-
|| max_size < BTR_CUR_PAGE_REORGANIZE_LIMIT)
1440-
&& page_get_n_recs(page) > 1
1447+
if (max_size < BTR_CUR_PAGE_REORGANIZE_LIMIT
1448+
&& n_recs > 1
14411449
&& page_get_max_insert_size(page, 1) < rec_size) {
14421450

14431451
goto fail;
14441452
}
1445-
} else if (max_size < rec_size) {
1446-
goto fail;
14471453
}
14481454

14491455
/* If there have been many consecutive inserts to the

storage/innobase/include/page0page.h

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*****************************************************************************
22
33
Copyright (c) 1994, 2016, Oracle and/or its affiliates. All Rights Reserved.
4-
Copyright (c) 2013, 2018, MariaDB Corporation.
4+
Copyright (c) 2013, 2020, 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
@@ -759,21 +759,6 @@ page_mem_alloc_free(
759759
free record list */
760760
ulint need); /*!< in: number of bytes allocated */
761761
/************************************************************//**
762-
Allocates a block of memory from the heap of an index page.
763-
@return pointer to start of allocated buffer, or NULL if allocation fails */
764-
UNIV_INTERN
765-
byte*
766-
page_mem_alloc_heap(
767-
/*================*/
768-
page_t* page, /*!< in/out: index page */
769-
page_zip_des_t* page_zip,/*!< in/out: compressed page with enough
770-
space available for inserting the record,
771-
or NULL */
772-
ulint need, /*!< in: total number of bytes needed */
773-
ulint* heap_no);/*!< out: this contains the heap number
774-
of the allocated record
775-
if allocation succeeds */
776-
/************************************************************//**
777762
Puts a record to free list. */
778763
UNIV_INLINE
779764
void

storage/innobase/page/page0cur.cc

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
33
Copyright (c) 1994, 2013, Oracle and/or its affiliates. All Rights Reserved.
44
Copyright (c) 2012, Facebook Inc.
5+
Copyright (c) 2020, MariaDB Corporation.
56
67
This program is free software; you can redistribute it and/or modify it under
78
the terms of the GNU General Public License as published by the Free Software
@@ -941,6 +942,52 @@ page_cur_parse_insert_rec(
941942
return(ptr + end_seg_len);
942943
}
943944

945+
/************************************************************//**
946+
Allocates a block of memory from the heap of an index page.
947+
@return pointer to start of allocated buffer, or NULL if allocation fails */
948+
static
949+
byte*
950+
page_mem_alloc_heap(
951+
/*================*/
952+
page_t* page, /*!< in/out: index page */
953+
page_zip_des_t* page_zip,/*!< in/out: compressed page with enough
954+
space available for inserting the record,
955+
or NULL */
956+
ulint need, /*!< in: total number of bytes needed */
957+
ulint* heap_no)/*!< out: this contains the heap number
958+
of the allocated record
959+
if allocation succeeds */
960+
{
961+
byte* block;
962+
ulint avl_space;
963+
964+
ut_ad(page && heap_no);
965+
966+
avl_space = page_get_max_insert_size(page, 1);
967+
968+
if (avl_space >= need) {
969+
const ulint h = page_dir_get_n_heap(page);
970+
if (UNIV_UNLIKELY(h >= 8191)) {
971+
/* At the minimum record size of 5+2 bytes,
972+
we can only reach this condition when using
973+
innodb_page_size=64k. */
974+
ut_ad(srv_page_size == 65536);
975+
return(NULL);
976+
}
977+
*heap_no = h;
978+
979+
block = page_header_get_ptr(page, PAGE_HEAP_TOP);
980+
981+
page_header_set_ptr(page, page_zip, PAGE_HEAP_TOP,
982+
block + need);
983+
page_dir_set_n_heap(page, page_zip, 1 + *heap_no);
984+
985+
return(block);
986+
}
987+
988+
return(NULL);
989+
}
990+
944991
/***********************************************************//**
945992
Inserts a record next to page cursor on an uncompressed page.
946993
Returns pointer to inserted record if succeed, i.e., enough

storage/innobase/page/page0page.cc

Lines changed: 1 addition & 39 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) 2018, MariaDB Corporation.
5+
Copyright (c) 2018, 2020, 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
@@ -235,44 +235,6 @@ page_set_max_trx_id(
235235
}
236236
}
237237

238-
/************************************************************//**
239-
Allocates a block of memory from the heap of an index page.
240-
@return pointer to start of allocated buffer, or NULL if allocation fails */
241-
UNIV_INTERN
242-
byte*
243-
page_mem_alloc_heap(
244-
/*================*/
245-
page_t* page, /*!< in/out: index page */
246-
page_zip_des_t* page_zip,/*!< in/out: compressed page with enough
247-
space available for inserting the record,
248-
or NULL */
249-
ulint need, /*!< in: total number of bytes needed */
250-
ulint* heap_no)/*!< out: this contains the heap number
251-
of the allocated record
252-
if allocation succeeds */
253-
{
254-
byte* block;
255-
ulint avl_space;
256-
257-
ut_ad(page && heap_no);
258-
259-
avl_space = page_get_max_insert_size(page, 1);
260-
261-
if (avl_space >= need) {
262-
block = page_header_get_ptr(page, PAGE_HEAP_TOP);
263-
264-
page_header_set_ptr(page, page_zip, PAGE_HEAP_TOP,
265-
block + need);
266-
*heap_no = page_dir_get_n_heap(page);
267-
268-
page_dir_set_n_heap(page, page_zip, 1 + *heap_no);
269-
270-
return(block);
271-
}
272-
273-
return(NULL);
274-
}
275-
276238
#ifndef UNIV_HOTBACKUP
277239
/**********************************************************//**
278240
Writes a log record of page creation. */

storage/xtradb/btr/btr0cur.cc

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
Copyright (c) 1994, 2016, Oracle and/or its affiliates. All Rights Reserved.
44
Copyright (c) 2008, Google Inc.
55
Copyright (c) 2012, Facebook Inc.
6-
Copyright (c) 2015, 2017, MariaDB Corporation.
6+
Copyright (c) 2015, 2020, MariaDB Corporation.
77
88
Portions of this file contain modifications contributed and copyrighted by
99
Google, Inc. Those modifications are gratefully acknowledged and are described
@@ -1542,17 +1542,23 @@ btr_cur_optimistic_insert(
15421542
}
15431543

15441544
ulint max_size = page_get_max_insert_size_after_reorganize(page, 1);
1545+
if (max_size < rec_size) {
1546+
goto fail;
1547+
}
1548+
1549+
const ulint n_recs = page_get_n_recs(page);
1550+
if (UNIV_UNLIKELY(n_recs >= 8189)) {
1551+
ut_ad(srv_page_size == 65536);
1552+
goto fail;
1553+
}
15451554

15461555
if (page_has_garbage(page)) {
1547-
if ((max_size < rec_size
1548-
|| max_size < BTR_CUR_PAGE_REORGANIZE_LIMIT)
1549-
&& page_get_n_recs(page) > 1
1556+
if (max_size < BTR_CUR_PAGE_REORGANIZE_LIMIT
1557+
&& n_recs > 1
15501558
&& page_get_max_insert_size(page, 1) < rec_size) {
15511559

15521560
goto fail;
15531561
}
1554-
} else if (max_size < rec_size) {
1555-
goto fail;
15561562
}
15571563

15581564
/* If there have been many consecutive inserts to the

storage/xtradb/include/page0page.h

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*****************************************************************************
22
33
Copyright (c) 1994, 2016, Oracle and/or its affiliates. All Rights Reserved.
4-
Copyright (c) 2013, 2018, MariaDB Corporation.
4+
Copyright (c) 2013, 2020, 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
@@ -749,21 +749,6 @@ page_mem_alloc_free(
749749
free record list */
750750
ulint need); /*!< in: number of bytes allocated */
751751
/************************************************************//**
752-
Allocates a block of memory from the heap of an index page.
753-
@return pointer to start of allocated buffer, or NULL if allocation fails */
754-
UNIV_INTERN
755-
byte*
756-
page_mem_alloc_heap(
757-
/*================*/
758-
page_t* page, /*!< in/out: index page */
759-
page_zip_des_t* page_zip,/*!< in/out: compressed page with enough
760-
space available for inserting the record,
761-
or NULL */
762-
ulint need, /*!< in: total number of bytes needed */
763-
ulint* heap_no);/*!< out: this contains the heap number
764-
of the allocated record
765-
if allocation succeeds */
766-
/************************************************************//**
767752
Puts a record to free list. */
768753
UNIV_INLINE
769754
void

storage/xtradb/page/page0cur.cc

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
33
Copyright (c) 1994, 2013, Oracle and/or its affiliates. All Rights Reserved.
44
Copyright (c) 2012, Facebook Inc.
5+
Copyright (c) 2020, MariaDB Corporation.
56
67
This program is free software; you can redistribute it and/or modify it under
78
the terms of the GNU General Public License as published by the Free Software
@@ -941,6 +942,52 @@ page_cur_parse_insert_rec(
941942
return(ptr + end_seg_len);
942943
}
943944

945+
/************************************************************//**
946+
Allocates a block of memory from the heap of an index page.
947+
@return pointer to start of allocated buffer, or NULL if allocation fails */
948+
static
949+
byte*
950+
page_mem_alloc_heap(
951+
/*================*/
952+
page_t* page, /*!< in/out: index page */
953+
page_zip_des_t* page_zip,/*!< in/out: compressed page with enough
954+
space available for inserting the record,
955+
or NULL */
956+
ulint need, /*!< in: total number of bytes needed */
957+
ulint* heap_no)/*!< out: this contains the heap number
958+
of the allocated record
959+
if allocation succeeds */
960+
{
961+
byte* block;
962+
ulint avl_space;
963+
964+
ut_ad(page && heap_no);
965+
966+
avl_space = page_get_max_insert_size(page, 1);
967+
968+
if (avl_space >= need) {
969+
const ulint h = page_dir_get_n_heap(page);
970+
if (UNIV_UNLIKELY(h >= 8191)) {
971+
/* At the minimum record size of 5+2 bytes,
972+
we can only reach this condition when using
973+
innodb_page_size=64k. */
974+
ut_ad(srv_page_size == 65536);
975+
return(NULL);
976+
}
977+
*heap_no = h;
978+
979+
block = page_header_get_ptr(page, PAGE_HEAP_TOP);
980+
981+
page_header_set_ptr(page, page_zip, PAGE_HEAP_TOP,
982+
block + need);
983+
page_dir_set_n_heap(page, page_zip, 1 + *heap_no);
984+
985+
return(block);
986+
}
987+
988+
return(NULL);
989+
}
990+
944991
/***********************************************************//**
945992
Inserts a record next to page cursor on an uncompressed page.
946993
Returns pointer to inserted record if succeed, i.e., enough

0 commit comments

Comments
 (0)