3831 Marko Mäkelä 2012-10-08
Bug#14731482 UPDATE OR DELETE CORRUPTS A RECORD WITH A LONG PRIMARY KEY
We did not allocate enough bits for index->trx_id_offset, causing an
UPDATE or DELETE of a table with a PRIMARY KEY longer than 1024 bytes
to corrupt the PRIMARY KEY.
dict_index_t: Allocate enough bits.
dict_index_build_internal_clust(): Check for overflow of
index->trx_id_offset. Trip a debug assertion when overflow occurs.
rb:1380 approved by Jimmy Yang
added:
internal/mysql-test/suite/i_innodb/r/innodb_bug14731482.result
internal/mysql-test/suite/i_innodb/t/innodb_bug14731482.test
internal/mysql-test/suite/i_innodb_plugin/r/innodb_bug14731482.result
internal/mysql-test/suite/i_innodb_plugin/t/innodb_bug14731482.test
modified:
storage/innobase/dict/dict0dict.c
storage/innobase/include/dict0mem.h
storage/innodb_plugin/dict/dict0dict.c
storage/innodb_plugin/include/dict0mem.h
3830 Tor Didriksen 2012-10-01
Bug#14683676 ENDLESS MEMORY CONSUMPTION IN SETUP_REF_ARRAY WITH MAX IN SUBQUERY
n_child_sum_items kept increasing.
Since it is used for calculating the size of ref_pointer_array,
we will allocate larger and larger chunks of memory, until we hit some
operating system limit.
The memory is free()d at disconnect, but is most likely *not*
returned to the operating system.
modified:
sql/sql_lex.cc
=== added file 'internal/mysql-test/suite/i_innodb/r/innodb_bug14731482.result'
--- a/internal/mysql-test/suite/i_innodb/r/innodb_bug14731482.result 1970-01-01 00:00:00 +0000
+++ b/internal/mysql-test/suite/i_innodb/r/innodb_bug14731482.result revid:marko.makela@stripped
@@ -0,0 +1,15 @@
+CREATE TABLE t1(
+a INT,
+c1 CHAR(255) NOT NULL DEFAULT '', c2 CHAR(255) NOT NULL DEFAULT '',
+c3 CHAR(255) NOT NULL DEFAULT '', c4 CHAR(255) NOT NULL DEFAULT '',
+c5 CHAR(255) NOT NULL DEFAULT '', c6 CHAR(255) NOT NULL DEFAULT '',
+c7 CHAR(255) NOT NULL DEFAULT '', c8 CHAR(255) NOT NULL DEFAULT '',
+c9 CHAR(255) NOT NULL DEFAULT '', ca CHAR(255) NOT NULL DEFAULT '',
+cb CHAR(255) NOT NULL DEFAULT '', cc CHAR(255) NOT NULL DEFAULT '',
+cd CHAR(255) NOT NULL DEFAULT '', ce CHAR(255) NOT NULL DEFAULT '',
+cf CHAR(255) NOT NULL DEFAULT '', cg CHAR(255) NOT NULL DEFAULT '',
+PRIMARY KEY(a,c1,c2,c3,c4,c5,c6,c7,c8,c9,ca,cb,cc)
+) ENGINE=InnoDB;
+INSERT INTO t1 (a) VALUES(1),(2);
+DELETE FROM t1;
+DROP TABLE t1;
=== added file 'internal/mysql-test/suite/i_innodb/t/innodb_bug14731482.test'
--- a/internal/mysql-test/suite/i_innodb/t/innodb_bug14731482.test 1970-01-01 00:00:00 +0000
+++ b/internal/mysql-test/suite/i_innodb/t/innodb_bug14731482.test revid:marko.makela@stripped
@@ -0,0 +1,21 @@
+# Bug#14731482 DELETE CORRUPTS A RECORD WITH A LONG PRIMARY KEY
+
+--source include/have_innodb.inc
+
+CREATE TABLE t1(
+ a INT,
+ c1 CHAR(255) NOT NULL DEFAULT '', c2 CHAR(255) NOT NULL DEFAULT '',
+ c3 CHAR(255) NOT NULL DEFAULT '', c4 CHAR(255) NOT NULL DEFAULT '',
+ c5 CHAR(255) NOT NULL DEFAULT '', c6 CHAR(255) NOT NULL DEFAULT '',
+ c7 CHAR(255) NOT NULL DEFAULT '', c8 CHAR(255) NOT NULL DEFAULT '',
+ c9 CHAR(255) NOT NULL DEFAULT '', ca CHAR(255) NOT NULL DEFAULT '',
+ cb CHAR(255) NOT NULL DEFAULT '', cc CHAR(255) NOT NULL DEFAULT '',
+ cd CHAR(255) NOT NULL DEFAULT '', ce CHAR(255) NOT NULL DEFAULT '',
+ cf CHAR(255) NOT NULL DEFAULT '', cg CHAR(255) NOT NULL DEFAULT '',
+ PRIMARY KEY(a,c1,c2,c3,c4,c5,c6,c7,c8,c9,ca,cb,cc)
+) ENGINE=InnoDB;
+
+INSERT INTO t1 (a) VALUES(1),(2);
+
+DELETE FROM t1;
+DROP TABLE t1;
=== added file 'internal/mysql-test/suite/i_innodb_plugin/r/innodb_bug14731482.result'
--- a/internal/mysql-test/suite/i_innodb_plugin/r/innodb_bug14731482.result 1970-01-01 00:00:00 +0000
+++ b/internal/mysql-test/suite/i_innodb_plugin/r/innodb_bug14731482.result revid:marko.makela@oracle.com-20121008130150-4jnvbos1w3e211x1
@@ -0,0 +1,15 @@
+CREATE TABLE t1(
+a INT,
+c1 CHAR(255) NOT NULL DEFAULT '', c2 CHAR(255) NOT NULL DEFAULT '',
+c3 CHAR(255) NOT NULL DEFAULT '', c4 CHAR(255) NOT NULL DEFAULT '',
+c5 CHAR(255) NOT NULL DEFAULT '', c6 CHAR(255) NOT NULL DEFAULT '',
+c7 CHAR(255) NOT NULL DEFAULT '', c8 CHAR(255) NOT NULL DEFAULT '',
+c9 CHAR(255) NOT NULL DEFAULT '', ca CHAR(255) NOT NULL DEFAULT '',
+cb CHAR(255) NOT NULL DEFAULT '', cc CHAR(255) NOT NULL DEFAULT '',
+cd CHAR(255) NOT NULL DEFAULT '', ce CHAR(255) NOT NULL DEFAULT '',
+cf CHAR(255) NOT NULL DEFAULT '', cg CHAR(255) NOT NULL DEFAULT '',
+PRIMARY KEY(a,c1,c2,c3,c4,c5,c6,c7,c8,c9,ca,cb,cc)
+) ENGINE=InnoDB;
+INSERT INTO t1 (a) VALUES(1),(2);
+DELETE FROM t1;
+DROP TABLE t1;
=== added file 'internal/mysql-test/suite/i_innodb_plugin/t/innodb_bug14731482.test'
--- a/internal/mysql-test/suite/i_innodb_plugin/t/innodb_bug14731482.test 1970-01-01 00:00:00 +0000
+++ b/internal/mysql-test/suite/i_innodb_plugin/t/innodb_bug14731482.test revid:marko.makela@stripped1w3e211x1
@@ -0,0 +1,21 @@
+# Bug#14731482 DELETE CORRUPTS A RECORD WITH A LONG PRIMARY KEY
+
+--source include/have_innodb_plugin.inc
+
+CREATE TABLE t1(
+ a INT,
+ c1 CHAR(255) NOT NULL DEFAULT '', c2 CHAR(255) NOT NULL DEFAULT '',
+ c3 CHAR(255) NOT NULL DEFAULT '', c4 CHAR(255) NOT NULL DEFAULT '',
+ c5 CHAR(255) NOT NULL DEFAULT '', c6 CHAR(255) NOT NULL DEFAULT '',
+ c7 CHAR(255) NOT NULL DEFAULT '', c8 CHAR(255) NOT NULL DEFAULT '',
+ c9 CHAR(255) NOT NULL DEFAULT '', ca CHAR(255) NOT NULL DEFAULT '',
+ cb CHAR(255) NOT NULL DEFAULT '', cc CHAR(255) NOT NULL DEFAULT '',
+ cd CHAR(255) NOT NULL DEFAULT '', ce CHAR(255) NOT NULL DEFAULT '',
+ cf CHAR(255) NOT NULL DEFAULT '', cg CHAR(255) NOT NULL DEFAULT '',
+ PRIMARY KEY(a,c1,c2,c3,c4,c5,c6,c7,c8,c9,ca,cb,cc)
+) ENGINE=InnoDB;
+
+INSERT INTO t1 (a) VALUES(1),(2);
+
+DELETE FROM t1;
+DROP TABLE t1;
=== modified file 'storage/innobase/dict/dict0dict.c'
--- a/storage/innobase/dict/dict0dict.c revid:tor.didriksen@stripped
+++ b/storage/innobase/dict/dict0dict.c revid:marko.makela@oracle.com-20121008130150-4jnvbos1w3e211x1
@@ -1629,7 +1629,6 @@ dict_index_build_internal_clust(
{
dict_index_t* new_index;
dict_field_t* field;
- ulint fixed_size;
ulint trx_id_pos;
ulint i;
ibool* indexed;
@@ -1706,7 +1705,7 @@ dict_index_build_internal_clust(
for (i = 0; i < trx_id_pos; i++) {
- fixed_size = dict_col_get_fixed_size(
+ ulint fixed_size = dict_col_get_fixed_size(
dict_index_get_nth_col(new_index, i));
if (fixed_size == 0) {
@@ -1722,7 +1721,20 @@ dict_index_build_internal_clust(
break;
}
- new_index->trx_id_offset += (unsigned int) fixed_size;
+ /* Add fixed_size to new_index->trx_id_offset.
+ Because the latter is a bit-field, an overflow
+ can theoretically occur. Check for it. */
+ fixed_size += new_index->trx_id_offset;
+
+ new_index->trx_id_offset = fixed_size;
+
+ if (new_index->trx_id_offset != fixed_size) {
+ /* Overflow. Pretend that this is a
+ variable-length PRIMARY KEY. */
+ ut_ad(0);
+ new_index->trx_id_offset = 0;
+ break;
+ }
}
}
=== modified file 'storage/innobase/include/dict0mem.h'
--- a/storage/innobase/include/dict0mem.h revid:tor.didriksen@stripped0121001111238-geatvbdun02riank
+++ b/storage/innobase/include/dict0mem.h revid:marko.makela@stripped150-4jnvbos1w3e211x1
@@ -196,10 +196,15 @@ struct dict_index_struct{
unsigned space:32;
/* space where the index tree is placed */
unsigned page:32;/* index tree root page number */
- unsigned trx_id_offset:10;/* position of the the trx id column
+#define MAX_KEY_LENGTH_BITS 12
+ unsigned trx_id_offset:MAX_KEY_LENGTH_BITS;
+ /* position of the trx id column
in a clustered index record, if the fields
before it are known to be of a fixed size,
0 otherwise */
+#if (1<<MAX_KEY_LENGTH_BITS) < MAX_KEY_LENGTH
+# error (1<<MAX_KEY_LENGTH_BITS) < MAX_KEY_LENGTH
+#endif
unsigned n_user_defined_cols:10;
/* number of columns the user defined to
be in the index: in the internal
=== modified file 'storage/innodb_plugin/dict/dict0dict.c'
--- a/storage/innodb_plugin/dict/dict0dict.c revid:tor.didriksen@stripped01111238-geatvbdun02riank
+++ b/storage/innodb_plugin/dict/dict0dict.c revid:marko.makela@stripped0-4jnvbos1w3e211x1
@@ -1959,7 +1959,6 @@ dict_index_build_internal_clust(
{
dict_index_t* new_index;
dict_field_t* field;
- ulint fixed_size;
ulint trx_id_pos;
ulint i;
ibool* indexed;
@@ -2036,7 +2035,7 @@ dict_index_build_internal_clust(
for (i = 0; i < trx_id_pos; i++) {
- fixed_size = dict_col_get_fixed_size(
+ ulint fixed_size = dict_col_get_fixed_size(
dict_index_get_nth_col(new_index, i),
dict_table_is_comp(table));
@@ -2053,7 +2052,20 @@ dict_index_build_internal_clust(
break;
}
- new_index->trx_id_offset += (unsigned int) fixed_size;
+ /* Add fixed_size to new_index->trx_id_offset.
+ Because the latter is a bit-field, an overflow
+ can theoretically occur. Check for it. */
+ fixed_size += new_index->trx_id_offset;
+
+ new_index->trx_id_offset = fixed_size;
+
+ if (new_index->trx_id_offset != fixed_size) {
+ /* Overflow. Pretend that this is a
+ variable-length PRIMARY KEY. */
+ ut_ad(0);
+ new_index->trx_id_offset = 0;
+ break;
+ }
}
}
=== modified file 'storage/innodb_plugin/include/dict0mem.h'
--- a/storage/innodb_plugin/include/dict0mem.h revid:tor.didriksen@stripped238-geatvbdun02riank
+++ b/storage/innodb_plugin/include/dict0mem.h revid:marko.makela@strippedjnvbos1w3e211x1
@@ -286,10 +286,15 @@ struct dict_index_struct{
#endif /* !UNIV_HOTBACKUP */
unsigned type:4; /*!< index type (DICT_CLUSTERED, DICT_UNIQUE,
DICT_UNIVERSAL, DICT_IBUF) */
- unsigned trx_id_offset:10;/*!< position of the trx id column
+#define MAX_KEY_LENGTH_BITS 12
+ unsigned trx_id_offset:MAX_KEY_LENGTH_BITS;
+ /*!< position of the trx id column
in a clustered index record, if the fields
before it are known to be of a fixed size,
0 otherwise */
+#if (1<<MAX_KEY_LENGTH_BITS) < MAX_KEY_LENGTH
+# error (1<<MAX_KEY_LENGTH_BITS) < MAX_KEY_LENGTH
+#endif
unsigned n_user_defined_cols:10;
/*!< number of columns the user defined to
be in the index: in the internal
No bundle (reason: useless for push emails).| Thread |
|---|
| • bzr push into mysql-5.1 branch (marko.makela:3830 to 3831) Bug#14731482 | marko.makela | 8 Oct |