Commit 160d97a4 authored by Aleksey Midenkov's avatar Aleksey Midenkov

MDEV-18734 ASAN heap-use-after-free upon sorting by blob column from partitioned table

ha_partition stores records in array of m_ordered_rec_buffer and uses
it for prio queue in ordered index scan. When the records are restored
from the array the blob buffers may be already freed or rewritten.

The solution is to take temporary ownership of cached blob buffers via
String::swap(). When the record is restored from m_ordered_rec_buffer
the ownership is returned to table fields.

Cleanups:

init_record_priority_queue(): removed needless !m_ordered_rec_buffer
check as there is same assertion few lines before.

dbug_print_row() for arbitrary row pointer
parent b8deb028
...@@ -46,6 +46,42 @@ connection slave; ...@@ -46,6 +46,42 @@ connection slave;
drop table federated.t1_1; drop table federated.t1_1;
drop table federated.t1_2; drop table federated.t1_2;
End of 5.1 tests End of 5.1 tests
#
# MDEV-18734 ASAN heap-use-after-free upon sorting by blob column from partitioned table
#
connection slave;
use federated;
create table t1_1 (x int, b text, key(x));
create table t1_2 (x int, b text, key(x));
connection master;
create table t1 (x int, b text, key(x)) engine=federated
partition by range columns (x) (
partition p1 values less than (40) connection='mysql://root@127.0.0.1:SLAVE_PORT/federated/t1_1',
partition pn values less than (maxvalue) connection='mysql://root@127.0.0.1:SLAVE_PORT/federated/t1_2'
);
insert t1 values (1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6), (7, 7), (8, 8);
insert t1 select x + 8, x + 8 from t1;
insert t1 select x + 16, x + 16 from t1;
insert t1 select x + 49, repeat(x + 49, 100) from t1;
flush tables;
# This produces wrong result before MDEV-17573
select x, left(b, 10) from t1 where x > 30 and x < 60 order by b;
x left(b, 10)
31 31
32 32
50 5050505050
51 5151515151
52 5252525252
53 5353535353
54 5454545454
55 5555555555
56 5656565656
57 5757575757
58 5858585858
59 5959595959
drop table t1;
connection slave;
drop table t1_1, t1_2;
connection master; connection master;
DROP TABLE IF EXISTS federated.t1; DROP TABLE IF EXISTS federated.t1;
DROP DATABASE IF EXISTS federated; DROP DATABASE IF EXISTS federated;
......
...@@ -50,4 +50,29 @@ drop table federated.t1_2; ...@@ -50,4 +50,29 @@ drop table federated.t1_2;
--echo End of 5.1 tests --echo End of 5.1 tests
--echo #
--echo # MDEV-18734 ASAN heap-use-after-free upon sorting by blob column from partitioned table
--echo #
connection slave;
use federated;
create table t1_1 (x int, b text, key(x));
create table t1_2 (x int, b text, key(x));
connection master;
--replace_result $SLAVE_MYPORT SLAVE_PORT
eval create table t1 (x int, b text, key(x)) engine=federated
partition by range columns (x) (
partition p1 values less than (40) connection='mysql://root@127.0.0.1:$SLAVE_MYPORT/federated/t1_1',
partition pn values less than (maxvalue) connection='mysql://root@127.0.0.1:$SLAVE_MYPORT/federated/t1_2'
);
insert t1 values (1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6), (7, 7), (8, 8);
insert t1 select x + 8, x + 8 from t1;
insert t1 select x + 16, x + 16 from t1;
insert t1 select x + 49, repeat(x + 49, 100) from t1;
flush tables;
--echo # This produces wrong result before MDEV-17573
select x, left(b, 10) from t1 where x > 30 and x < 60 order by b;
drop table t1;
connection slave;
drop table t1_1, t1_2;
source include/federated_cleanup.inc; source include/federated_cleanup.inc;
...@@ -28,3 +28,76 @@ set statement sql_mode= '' for update t1 set i= 1, v= 2; ...@@ -28,3 +28,76 @@ set statement sql_mode= '' for update t1 set i= 1, v= 2;
Warnings: Warnings:
Warning 1906 The value specified for generated column 'v' in table 't1' has been ignored Warning 1906 The value specified for generated column 'v' in table 't1' has been ignored
drop table t1; drop table t1;
#
# MDEV-18734 ASAN heap-use-after-free in my_strnxfrm_simple_internal upon update on versioned partitioned table
#
# Cover queue_fix() in ha_partition::handle_ordered_index_scan()
create or replace table t1 (
x int auto_increment primary key,
b text, v mediumtext as (b) virtual,
index (v(10))
) partition by range columns (x) (
partition p1 values less than (3),
partition p2 values less than (6),
partition p3 values less than (9),
partition p4 values less than (12),
partition p5 values less than (15),
partition p6 values less than (17),
partition p7 values less than (19),
partition p8 values less than (21),
partition p9 values less than (23),
partition p10 values less than (25),
partition p11 values less than (27),
partition p12 values less than (29),
partition p13 values less than (31),
partition p14 values less than (33),
partition p15 values less than (35),
partition pn values less than (maxvalue));
insert into t1 (b) values
(repeat('q', 8192)), (repeat('z', 8192)), (repeat('a', 8192)), (repeat('b', 8192)),
(repeat('x', 8192)), (repeat('y', 8192));
insert t1 (b) select b from t1;
insert t1 (b) select b from t1;
insert t1 (b) select b from t1;
insert t1 (b) select b from t1;
select x, left(b, 10), left(v, 10) from t1 where x > 30 and x < 60 order by v;
x left(b, 10) left(v, 10)
33 aaaaaaaaaa aaaaaaaaaa
39 aaaaaaaaaa aaaaaaaaaa
45 aaaaaaaaaa aaaaaaaaaa
51 aaaaaaaaaa aaaaaaaaaa
57 aaaaaaaaaa aaaaaaaaaa
34 bbbbbbbbbb bbbbbbbbbb
40 bbbbbbbbbb bbbbbbbbbb
46 bbbbbbbbbb bbbbbbbbbb
52 bbbbbbbbbb bbbbbbbbbb
58 bbbbbbbbbb bbbbbbbbbb
31 qqqqqqqqqq qqqqqqqqqq
37 qqqqqqqqqq qqqqqqqqqq
43 qqqqqqqqqq qqqqqqqqqq
49 qqqqqqqqqq qqqqqqqqqq
55 qqqqqqqqqq qqqqqqqqqq
35 xxxxxxxxxx xxxxxxxxxx
41 xxxxxxxxxx xxxxxxxxxx
47 xxxxxxxxxx xxxxxxxxxx
53 xxxxxxxxxx xxxxxxxxxx
59 xxxxxxxxxx xxxxxxxxxx
36 yyyyyyyyyy yyyyyyyyyy
42 yyyyyyyyyy yyyyyyyyyy
48 yyyyyyyyyy yyyyyyyyyy
54 yyyyyyyyyy yyyyyyyyyy
32 zzzzzzzzzz zzzzzzzzzz
38 zzzzzzzzzz zzzzzzzzzz
44 zzzzzzzzzz zzzzzzzzzz
50 zzzzzzzzzz zzzzzzzzzz
56 zzzzzzzzzz zzzzzzzzzz
update t1 set b= 'bar' where v > 'a' limit 20;
drop table t1;
# Cover return_top_record() in ha_partition::handle_ordered_index_scan()
create table t1 (x int primary key, b tinytext, v text as (b) virtual)
partition by range columns (x) (
partition p1 values less than (4),
partition pn values less than (maxvalue));
insert into t1 (x, b) values (1, ''), (2, ''), (3, 'a'), (4, 'b');
update t1 set b= 'bar' where x > 0 order by v limit 2;
drop table t1;
...@@ -30,3 +30,51 @@ subpartition by hash(v) subpartitions 3 ( ...@@ -30,3 +30,51 @@ subpartition by hash(v) subpartitions 3 (
insert t1 set i= 0; insert t1 set i= 0;
set statement sql_mode= '' for update t1 set i= 1, v= 2; set statement sql_mode= '' for update t1 set i= 1, v= 2;
drop table t1; drop table t1;
--echo #
--echo # MDEV-18734 ASAN heap-use-after-free in my_strnxfrm_simple_internal upon update on versioned partitioned table
--echo #
--echo # Cover queue_fix() in ha_partition::handle_ordered_index_scan()
create or replace table t1 (
x int auto_increment primary key,
b text, v mediumtext as (b) virtual,
index (v(10))
) partition by range columns (x) (
partition p1 values less than (3),
partition p2 values less than (6),
partition p3 values less than (9),
partition p4 values less than (12),
partition p5 values less than (15),
partition p6 values less than (17),
partition p7 values less than (19),
partition p8 values less than (21),
partition p9 values less than (23),
partition p10 values less than (25),
partition p11 values less than (27),
partition p12 values less than (29),
partition p13 values less than (31),
partition p14 values less than (33),
partition p15 values less than (35),
partition pn values less than (maxvalue));
insert into t1 (b) values
(repeat('q', 8192)), (repeat('z', 8192)), (repeat('a', 8192)), (repeat('b', 8192)),
(repeat('x', 8192)), (repeat('y', 8192));
insert t1 (b) select b from t1;
insert t1 (b) select b from t1;
insert t1 (b) select b from t1;
insert t1 (b) select b from t1;
select x, left(b, 10), left(v, 10) from t1 where x > 30 and x < 60 order by v;
update t1 set b= 'bar' where v > 'a' limit 20;
drop table t1;
--echo # Cover return_top_record() in ha_partition::handle_ordered_index_scan()
create table t1 (x int primary key, b tinytext, v text as (b) virtual)
partition by range columns (x) (
partition p1 values less than (4),
partition pn values less than (maxvalue));
insert into t1 (x, b) values (1, ''), (2, ''), (3, 'a'), (4, 'b');
update t1 set b= 'bar' where x > 0 order by v limit 2;
drop table t1;
...@@ -8318,6 +8318,7 @@ int Field_blob::store(const char *from,uint length,CHARSET_INFO *cs) ...@@ -8318,6 +8318,7 @@ int Field_blob::store(const char *from,uint length,CHARSET_INFO *cs)
copy_length= copier.well_formed_copy(field_charset, copy_length= copier.well_formed_copy(field_charset,
(char*) value.ptr(), new_length, (char*) value.ptr(), new_length,
cs, from, length); cs, from, length);
value.length(copy_length);
Field_blob::store_length(copy_length); Field_blob::store_length(copy_length);
bmove(ptr+packlength,(uchar*) &tmp,sizeof(char*)); bmove(ptr+packlength,(uchar*) &tmp,sizeof(char*));
......
...@@ -3465,6 +3465,12 @@ class Field_blob :public Field_longstr { ...@@ -3465,6 +3465,12 @@ class Field_blob :public Field_longstr {
uchar *new_ptr, uint32 length, uchar *new_ptr, uint32 length,
uchar *new_null_ptr, uint new_null_bit); uchar *new_null_ptr, uint new_null_bit);
void sql_type(String &str) const; void sql_type(String &str) const;
/**
Copy blob buffer into internal storage "value" and update record pointer.
@retval true Memory allocation error
@retval false Success
*/
inline bool copy() inline bool copy()
{ {
uchar *tmp= get_ptr(); uchar *tmp= get_ptr();
...@@ -3477,6 +3483,33 @@ class Field_blob :public Field_longstr { ...@@ -3477,6 +3483,33 @@ class Field_blob :public Field_longstr {
memcpy(ptr+packlength, &tmp, sizeof(char*)); memcpy(ptr+packlength, &tmp, sizeof(char*));
return 0; return 0;
} }
void swap(String &inout, bool set_read_value)
{
if (set_read_value)
read_value.swap(inout);
else
value.swap(inout);
}
/**
Return pointer to blob cache or NULL if not cached.
*/
String * cached(bool *set_read_value)
{
char *tmp= (char *) get_ptr();
if (!value.is_empty() && tmp == value.ptr())
{
*set_read_value= false;
return &value;
}
if (!read_value.is_empty() && tmp == read_value.ptr())
{
*set_read_value= true;
return &read_value;
}
return NULL;
}
/* store value for the duration of the current read record */ /* store value for the duration of the current read record */
inline void swap_value_and_read_value() inline void swap_value_and_read_value()
{ {
......
...@@ -608,6 +608,15 @@ const char* dbug_print_table_row(TABLE *table) ...@@ -608,6 +608,15 @@ const char* dbug_print_table_row(TABLE *table)
} }
const char* dbug_print_row(TABLE *table, uchar *rec)
{
table->move_fields(table->field, rec, table->record[0]);
const char* ret= dbug_print_table_row(table);
table->move_fields(table->field, table->record[0], rec);
return ret;
}
/* /*
Print a text, SQL-like record representation into dbug trace. Print a text, SQL-like record representation into dbug trace.
......
This diff is collapsed.
...@@ -21,7 +21,17 @@ ...@@ -21,7 +21,17 @@
#include "sql_partition.h" /* part_id_range, partition_element */ #include "sql_partition.h" /* part_id_range, partition_element */
#include "queues.h" /* QUEUE */ #include "queues.h" /* QUEUE */
struct Ordered_blob_storage
{
String blob;
bool set_read_value;
Ordered_blob_storage() : set_read_value(false)
{}
};
#define PARTITION_BYTES_IN_POS 2 #define PARTITION_BYTES_IN_POS 2
#define ORDERED_PART_NUM_OFFSET sizeof(Ordered_blob_storage **)
#define ORDERED_REC_OFFSET (ORDERED_PART_NUM_OFFSET + PARTITION_BYTES_IN_POS)
/** Struct used for partition_name_hash */ /** Struct used for partition_name_hash */
...@@ -630,6 +640,7 @@ class ha_partition :public handler ...@@ -630,6 +640,7 @@ class ha_partition :public handler
int handle_ordered_next(uchar * buf, bool next_same); int handle_ordered_next(uchar * buf, bool next_same);
int handle_ordered_prev(uchar * buf); int handle_ordered_prev(uchar * buf);
void return_top_record(uchar * buf); void return_top_record(uchar * buf);
void swap_blobs(uchar* rec_buf, Ordered_blob_storage ** storage, bool restore);
public: public:
/* /*
------------------------------------------------------------------------- -------------------------------------------------------------------------
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment