Skip to content

Commit

Permalink
MDEV-16982 Server crashes in mem_heap_dup upon DELETE from table with…
Browse files Browse the repository at this point in the history
… virtual columns

An uninitialized buffer is passed to row_sel_store_mysql_rec() but
InnoDB may not initialize everything. Looks like it's ok in most cases
but not always.
The partially initialized buffer was later passed to
ha_innobase::write_row() which reads random NULL bit values for
virtual columns and random stuff happens.

No test case for MariaDB 10.2 was found.
The test case for MariaDB 10.3 involves partitioning,
system versioning and the TRASH_ALLOC fill pattern 0xA5.
Test case depends very much on the number and layout of columns.
Think about 0xA5 byte for a NULL bit mask.

row_sel_store_mysql_rec(): always initialize virtual columns NULL bit

Closes #1144
  • Loading branch information
dr-m committed Feb 5, 2019
1 parent f53e795 commit 625994b
Showing 1 changed file with 25 additions and 20 deletions.
45 changes: 25 additions & 20 deletions storage/innobase/row/row0sel.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
Copyright (c) 1997, 2017, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2008, Google Inc.
Copyright (c) 2015, 2018, MariaDB Corporation.
Copyright (c) 2015, 2019, MariaDB Corporation.
Portions of this file contain modifications contributed and copyrighted by
Google, Inc. Those modifications are gratefully acknowledged and are described
Expand Down Expand Up @@ -3132,20 +3132,20 @@ row_sel_store_mysql_field_func(
Note that the template in prebuilt may advise us to copy only a few
columns to mysql_rec, other columns are left blank. All columns may not
be needed in the query.
@param[out] mysql_rec row in the MySQL format
@param[in] prebuilt prebuilt structure
@param[in] rec Innobase record in the index
which was described in prebuilt's
template, or in the clustered index;
must be protected by a page latch
@param[in] vrow virtual columns
@param[in] rec_clust whether the rec in the clustered index
@param[in] index index of rec
@param[in] offsets array returned by rec_get_offsets(rec)
@return TRUE on success, FALSE if not all columns could be retrieved */
static MY_ATTRIBUTE((warn_unused_result))
ibool
row_sel_store_mysql_rec(
@param[out] mysql_rec row in the MySQL format
@param[in] prebuilt cursor
@param[in] rec Innobase record in the index
which was described in prebuilt's
template, or in the clustered index;
must be protected by a page latch
@param[in] vrow virtual columns
@param[in] rec_clust whether index must be the clustered index
@param[in] index index of rec
@param[in] offsets array returned by rec_get_offsets(rec)
@retval true on success
@retval false if not all columns could be retrieved */
MY_ATTRIBUTE((warn_unused_result))
static bool row_sel_store_mysql_rec(
byte* mysql_rec,
row_prebuilt_t* prebuilt,
const rec_t* rec,
Expand All @@ -3167,13 +3167,18 @@ row_sel_store_mysql_rec(
const mysql_row_templ_t*templ = &prebuilt->mysql_template[i];

if (templ->is_virtual && dict_index_is_clust(index)) {
/* Virtual columns are never declared NOT NULL. */
ut_ad(templ->mysql_null_bit_mask);

/* Skip virtual columns if it is not a covered
search or virtual key read is not requested. */
if (!dict_index_has_virtual(prebuilt->index)
if (!rec_clust
|| !prebuilt->index->has_virtual()
|| (!prebuilt->read_just_key
&& !prebuilt->m_read_virtual_key)
|| !rec_clust) {
&& !prebuilt->m_read_virtual_key)) {
/* Initialize the NULL bit. */
mysql_rec[templ->mysql_null_byte_offset]
|= (byte) templ->mysql_null_bit_mask;
continue;
}

Expand Down Expand Up @@ -3247,7 +3252,7 @@ row_sel_store_mysql_rec(
rec, index, offsets,
field_no, templ)) {

DBUG_RETURN(FALSE);
DBUG_RETURN(false);
}
}

Expand All @@ -3264,7 +3269,7 @@ row_sel_store_mysql_rec(
}
}

DBUG_RETURN(TRUE);
DBUG_RETURN(true);
}

/*********************************************************************//**
Expand Down

0 comments on commit 625994b

Please sign in to comment.