diff options
author | Marko Mäkelä <marko.makela@oracle.com> | 2012-10-08 16:01:50 +0300 |
---|---|---|
committer | Marko Mäkelä <marko.makela@oracle.com> | 2012-10-08 16:01:50 +0300 |
commit | b06620868ea840beffcfaf36e905bfecd27cfa85 (patch) | |
tree | ab07e1ee2bc167a0245e32bc23ffdce8281d73bf /storage | |
parent | 540d0cd28eeb11abab578d189bbe748b930d8edc (diff) | |
download | mariadb-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.c | 18 | ||||
-rw-r--r-- | storage/innobase/include/dict0mem.h | 7 | ||||
-rw-r--r-- | storage/innodb_plugin/dict/dict0dict.c | 18 | ||||
-rw-r--r-- | storage/innodb_plugin/include/dict0mem.h | 7 |
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 |