1
0
mirror of https://github.com/MariaDB/server.git synced 2026-10-23 18:49:29 +03:00

MDEV-41667 InnoDB: Failing assertion: buf <= (byte*) original_upd_buff + buff_len

Problem:
=======
An UPDATE on an InnoDB table with several indexed virtual columns
could corrupt the heap in calc_row_difference() because the
write pointer went past the end of the update buffer.

ha_innobase::update_row() sized the buffer from the row length, the
maximum key length and fixed padding. That did not allow for the
extra packed values calc_row_difference() stores for indexed
virtual columns for both the new value and the old value.

Solution:
========
  When allocating the update buffer in ha_innobase::update_row(),
add pack_length() of every virtual column. The row length
already counts each virtual column once, so the buffer can now
hold both the new and old packed values. All virtual columns
are counted, not only the indexed ones, because the buffer
is reused and a column may become indexed after it is allocated.

Based on mysql/mysql-server@1265514061
This commit is contained in:
Thirunarayanan Balathandayuthapani
2026-10-21 20:53:13 +05:30
parent f941fc810c
commit 1d12434a4f
4 changed files with 53 additions and 2 deletions

View File

@@ -364,5 +364,21 @@ disconnect con1;
connection default;
drop table t;
#
# MDEV-41667 InnoDB: Failing assertion: buf <= (byte*)
# original_upd_buff + buff_len
#
CREATE TABLE t1 (a INT PRIMARY KEY, b BIGINT NOT NULL , v1 BIGINT AS (b+1) VIRTUAL, KEY(v1), v2 BIGINT AS (b+2) VIRTUAL, KEY(v2), v3 BIGINT AS (b+3) VIRTUAL, KEY(v3), v4 BIGINT AS (b+4) VIRTUAL, KEY(v4), v5 BIGINT AS (b+5) VIRTUAL, KEY(v5), v6 BIGINT AS (b+6) VIRTUAL, KEY(v6), v7 BIGINT AS (b+7) VIRTUAL, KEY(v7), v8 BIGINT AS (b+8) VIRTUAL, KEY(v8), v9 BIGINT AS (b+9) VIRTUAL, KEY(v9), v10 BIGINT AS (b+10) VIRTUAL, KEY(v10), v11 BIGINT AS (b+11) VIRTUAL, KEY(v11), v12 BIGINT AS (b+12) VIRTUAL, KEY(v12), v13 BIGINT AS (b+13) VIRTUAL, KEY(v13), v14 BIGINT AS (b+14) VIRTUAL, KEY(v14), v15 BIGINT AS (b+15) VIRTUAL, KEY(v15), v16 BIGINT AS (b+16) VIRTUAL, KEY(v16), v17 BIGINT AS (b+17) VIRTUAL, KEY(v17), v18 BIGINT AS (b+18) VIRTUAL, KEY(v18), v19 BIGINT AS (b+19) VIRTUAL, KEY(v19), v20 BIGINT AS (b+20) VIRTUAL, KEY(v20), v21 BIGINT AS (b+21) VIRTUAL, KEY(v21), v22 BIGINT AS (b+22) VIRTUAL, KEY(v22), v23 BIGINT AS (b+23) VIRTUAL, KEY(v23), v24 BIGINT AS (b+24) VIRTUAL, KEY(v24), v25 BIGINT AS (b+25) VIRTUAL, KEY(v25), v26 BIGINT AS (b+26) VIRTUAL, KEY(v26), v27 BIGINT AS (b+27) VIRTUAL, KEY(v27), v28 BIGINT AS (b+28) VIRTUAL, KEY(v28), v29 BIGINT AS (b+29) VIRTUAL, KEY(v29), v30 BIGINT AS (b+30) VIRTUAL, KEY(v30), v31 BIGINT AS (b+31) VIRTUAL, KEY(v31), v32 BIGINT AS (b+32) VIRTUAL, KEY(v32)) ENGINE=InnoDB;
INSERT INTO t1 (a,b) VALUES (1,10),(2,20);
UPDATE t1 SET b=11 WHERE a=1;
UPDATE t1 SET b=b+100;
SELECT a, b, v1, v32 FROM t1;
a b v1 v32
1 111 112 143
2 120 121 152
CHECK TABLE t1;
Table Op Msg_type Msg_text
test.t1 check status OK
DROP TABLE t1;
#
# End of 10.11 tests
#

View File

@@ -197,6 +197,26 @@ disconnect con1;
connection default;
drop table t;
--echo #
--echo # MDEV-41667 InnoDB: Failing assertion: buf <= (byte*)
--echo # original_upd_buff + buff_len
--echo #
let $n= 32;
let $cols= ;
let $i= 1;
while ($i <= $n)
{
let $cols= $cols, v$i BIGINT AS (b+$i) VIRTUAL, KEY(v$i);
inc $i;
}
eval CREATE TABLE t1 (a INT PRIMARY KEY, b BIGINT NOT NULL $cols) ENGINE=InnoDB;
INSERT INTO t1 (a,b) VALUES (1,10),(2,20);
UPDATE t1 SET b=11 WHERE a=1;
UPDATE t1 SET b=b+100;
SELECT a, b, v1, v32 FROM t1;
CHECK TABLE t1;
DROP TABLE t1;
--echo #
--echo # End of 10.11 tests
--echo #

View File

@@ -8266,7 +8266,7 @@ calc_row_difference(
const uchar* new_row,
TABLE* table,
uchar* upd_buff,
ulint buff_len,
size_t buff_len,
row_prebuilt_t* prebuilt,
ib_uint64_t& auto_inc)
{
@@ -8799,6 +8799,21 @@ ha_innobase::update_row(
m_upd_buf_size = table->s->reclength + table->s->max_key_length
+ MAX_REF_PARTS * 3;
/* Reserve an extra packed value for every
virtual column. calc_row_difference() may retain
both new and old values for indexed virtual
columns, including columns indexed by an online
rebuild.
The buffer is reused, so its capacity must also
cover virtual columns that become indexed
after the initial allocation. */
for (uint i = 0; i < table->s->fields; i++) {
Field *field = table->field[i];
if (!field->stored_in_db()) {
m_upd_buf_size += field->pack_length();
}
}
m_upd_buf = reinterpret_cast<uchar*>(
my_malloc(PSI_INSTRUMENT_ME,
m_upd_buf_size,

View File

@@ -518,7 +518,7 @@ protected:
uchar* m_upd_buf;
/** the size of upd_buf in bytes */
ulint m_upd_buf_size;
size_t m_upd_buf_size;
/** Flags that specificy the handler instance (table) capability. */
Table_flags m_int_table_flags;