summaryrefslogtreecommitdiff
path: root/storage
diff options
context:
space:
mode:
authorMarko Mäkelä <marko.makela@oracle.com>2012-10-08 16:01:50 +0300
committerMarko Mäkelä <marko.makela@oracle.com>2012-10-08 16:01:50 +0300
commitb06620868ea840beffcfaf36e905bfecd27cfa85 (patch)
treeab07e1ee2bc167a0245e32bc23ffdce8281d73bf /storage
parent540d0cd28eeb11abab578d189bbe748b930d8edc (diff)
downloadmariadb-git-b06620868ea840beffcfaf36e905bfecd27cfa85.tar.gz
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
Diffstat (limited to 'storage')
-rw-r--r--storage/innobase/dict/dict0dict.c18
-rw-r--r--storage/innobase/include/dict0mem.h7
-rw-r--r--storage/innodb_plugin/dict/dict0dict.c18
-rw-r--r--storage/innodb_plugin/include/dict0mem.h7
4 files changed, 42 insertions, 8 deletions
diff --git a/storage/innobase/dict/dict0dict.c b/storage/innobase/dict/dict0dict.c
index b2baa81b73f..7139eb5db95 100644
--- a/storage/innobase/dict/dict0dict.c
+++ b/storage/innobase/dict/dict0dict.c
@@ -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;
+ }
}
}
diff --git a/storage/innobase/include/dict0mem.h b/storage/innobase/include/dict0mem.h
index 83dbf65ea41..8a55fef7f73 100644
--- a/storage/innobase/include/dict0mem.h
+++ b/storage/innobase/include/dict0mem.h
@@ -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
diff --git a/storage/innodb_plugin/dict/dict0dict.c b/storage/innodb_plugin/dict/dict0dict.c
index cfeba015723..fdc93382190 100644
--- a/storage/innodb_plugin/dict/dict0dict.c
+++ b/storage/innodb_plugin/dict/dict0dict.c
@@ -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;
+ }
}
}
diff --git a/storage/innodb_plugin/include/dict0mem.h b/storage/innodb_plugin/include/dict0mem.h
index 1b26aea38c9..8a7984a4375 100644
--- a/storage/innodb_plugin/include/dict0mem.h
+++ b/storage/innodb_plugin/include/dict0mem.h
@@ -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