Skip to content

Commit 4e53039

Browse files
committed
MDEV-30732 : wsrep_store_key_val_for_row() may invoke memcpy() on nullptr
Problem was that row_mysql_read_blob_ref can return NULL in case when blob datatype is used in a key and its real value is NULL. This NULL pointer is then used in memcpy function in wsrep_store_key_val_for_row. However, memcpy is defined so that argument 2 must not be NULL. Fixed by adding conditions before memcpy functions so that argument 2 is always non NULL.
1 parent b1c2963 commit 4e53039

File tree

3 files changed

+243
-30
lines changed

3 files changed

+243
-30
lines changed
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
connection node_2;
2+
connection node_1;
3+
SET GLOBAL sql_mode=0;
4+
SET sql_mode=DEFAULT;
5+
CREATE TABLE t (c INT,c2 BLOB,KEY k2 (c2 (1),c)) DEFAULT CHARSET=latin1 ROW_FORMAT=COMPACT;
6+
INSERT INTO t (c) VALUES (1),(1),(1),(1),(1);
7+
SELECT * FROM t;
8+
c c2
9+
1 NULL
10+
1 NULL
11+
1 NULL
12+
1 NULL
13+
1 NULL
14+
DROP TABLE t;
15+
CREATE TABLE t (c INT,c2 BLOB NOT NULL,KEY k2 (c2 (1),c)) DEFAULT CHARSET=latin1 ROW_FORMAT=COMPACT;
16+
INSERT INTO t (c) VALUES (1),(1),(1),(1),(1);
17+
Warnings:
18+
Warning 1364 Field 'c2' doesn't have a default value
19+
SELECT * FROM t;
20+
c c2
21+
1
22+
1
23+
1
24+
1
25+
1
26+
DROP TABLE t;
27+
CREATE TABLE t (c INT,c2 VARCHAR(270),KEY k2 (c2 (1),c)) DEFAULT CHARSET=latin1 ROW_FORMAT=COMPACT;
28+
INSERT INTO t (c) VALUES (1),(1),(1),(1),(1);
29+
SELECT * FROM t;
30+
c c2
31+
1 NULL
32+
1 NULL
33+
1 NULL
34+
1 NULL
35+
1 NULL
36+
DROP TABLE t;
37+
CREATE TABLE t (c INT,c2 VARCHAR(270) NOT NULL,KEY k2 (c2 (1),c)) DEFAULT CHARSET=latin1 ROW_FORMAT=COMPACT;
38+
INSERT INTO t (c) VALUES (1),(1),(1),(1),(1);
39+
Warnings:
40+
Warning 1364 Field 'c2' doesn't have a default value
41+
SELECT * FROM t;
42+
c c2
43+
1
44+
1
45+
1
46+
1
47+
1
48+
DROP TABLE t;
49+
CREATE TABLE t (c INT,c2 CHAR(80),KEY k2 (c2 (1),c)) DEFAULT CHARSET=latin1 ROW_FORMAT=COMPACT;
50+
INSERT INTO t (c) VALUES (1),(1),(1),(1),(1);
51+
SELECT * FROM t;
52+
c c2
53+
1 NULL
54+
1 NULL
55+
1 NULL
56+
1 NULL
57+
1 NULL
58+
DROP TABLE t;
59+
CREATE TABLE t (c INT,c2 CHAR(80) NOT NULL,KEY k2 (c2 (1),c)) DEFAULT CHARSET=latin1 ROW_FORMAT=COMPACT;
60+
INSERT INTO t (c) VALUES (1),(1),(1),(1),(1);
61+
Warnings:
62+
Warning 1364 Field 'c2' doesn't have a default value
63+
SELECT * FROM t;
64+
c c2
65+
1
66+
1
67+
1
68+
1
69+
1
70+
DROP TABLE t;
71+
CREATE TABLE t (c INT,c2 TEXT,KEY k2 (c2 (1),c)) DEFAULT CHARSET=latin1 ROW_FORMAT=COMPACT;
72+
INSERT INTO t (c) VALUES (1),(1),(1),(1),(1);
73+
SELECT * FROM t;
74+
c c2
75+
1 NULL
76+
1 NULL
77+
1 NULL
78+
1 NULL
79+
1 NULL
80+
DROP TABLE t;
81+
CREATE TABLE t (c INT,c2 TEXT NOT NULL,KEY k2 (c2 (1),c)) DEFAULT CHARSET=latin1 ROW_FORMAT=COMPACT;
82+
INSERT INTO t (c) VALUES (1),(1),(1),(1),(1);
83+
Warnings:
84+
Warning 1364 Field 'c2' doesn't have a default value
85+
SELECT * FROM t;
86+
c c2
87+
1
88+
1
89+
1
90+
1
91+
1
92+
DROP TABLE t;
93+
SET GLOBAL sql_mode=DEFAULT;
94+
CREATE TABLE t (c INT,c2 BLOB,KEY k2 (c2 (1),c)) DEFAULT CHARSET=latin1 ROW_FORMAT=COMPACT;
95+
INSERT INTO t (c) VALUES (1),(1),(1),(1),(1);
96+
SELECT * FROM t;
97+
c c2
98+
1 NULL
99+
1 NULL
100+
1 NULL
101+
1 NULL
102+
1 NULL
103+
DROP TABLE t;
104+
CREATE TABLE t (c INT,c2 VARCHAR(270),KEY k2 (c2 (1),c)) DEFAULT CHARSET=latin1 ROW_FORMAT=COMPACT;
105+
INSERT INTO t (c) VALUES (1),(1),(1),(1),(1);
106+
SELECT * FROM t;
107+
c c2
108+
1 NULL
109+
1 NULL
110+
1 NULL
111+
1 NULL
112+
1 NULL
113+
DROP TABLE t;
114+
CREATE TABLE t (c INT,c2 CHAR(80),KEY k2 (c2 (1),c)) DEFAULT CHARSET=latin1 ROW_FORMAT=COMPACT;
115+
INSERT INTO t (c) VALUES (1),(1),(1),(1),(1);
116+
SELECT * FROM t;
117+
c c2
118+
1 NULL
119+
1 NULL
120+
1 NULL
121+
1 NULL
122+
1 NULL
123+
DROP TABLE t;
124+
CREATE TABLE t (c INT,c2 TEXT,KEY k2 (c2 (1),c)) DEFAULT CHARSET=latin1 ROW_FORMAT=COMPACT;
125+
INSERT INTO t (c) VALUES (1),(1),(1),(1),(1);
126+
SELECT * FROM t;
127+
c c2
128+
1 NULL
129+
1 NULL
130+
1 NULL
131+
1 NULL
132+
1 NULL
133+
DROP TABLE t;
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
--source include/galera_cluster.inc
2+
3+
SET GLOBAL sql_mode=0;
4+
SET sql_mode=DEFAULT;
5+
CREATE TABLE t (c INT,c2 BLOB,KEY k2 (c2 (1),c)) DEFAULT CHARSET=latin1 ROW_FORMAT=COMPACT;
6+
INSERT INTO t (c) VALUES (1),(1),(1),(1),(1);
7+
SELECT * FROM t;
8+
DROP TABLE t;
9+
CREATE TABLE t (c INT,c2 BLOB NOT NULL,KEY k2 (c2 (1),c)) DEFAULT CHARSET=latin1 ROW_FORMAT=COMPACT;
10+
INSERT INTO t (c) VALUES (1),(1),(1),(1),(1);
11+
SELECT * FROM t;
12+
DROP TABLE t;
13+
CREATE TABLE t (c INT,c2 VARCHAR(270),KEY k2 (c2 (1),c)) DEFAULT CHARSET=latin1 ROW_FORMAT=COMPACT;
14+
INSERT INTO t (c) VALUES (1),(1),(1),(1),(1);
15+
SELECT * FROM t;
16+
DROP TABLE t;
17+
CREATE TABLE t (c INT,c2 VARCHAR(270) NOT NULL,KEY k2 (c2 (1),c)) DEFAULT CHARSET=latin1 ROW_FORMAT=COMPACT;
18+
INSERT INTO t (c) VALUES (1),(1),(1),(1),(1);
19+
SELECT * FROM t;
20+
DROP TABLE t;
21+
CREATE TABLE t (c INT,c2 CHAR(80),KEY k2 (c2 (1),c)) DEFAULT CHARSET=latin1 ROW_FORMAT=COMPACT;
22+
INSERT INTO t (c) VALUES (1),(1),(1),(1),(1);
23+
SELECT * FROM t;
24+
DROP TABLE t;
25+
CREATE TABLE t (c INT,c2 CHAR(80) NOT NULL,KEY k2 (c2 (1),c)) DEFAULT CHARSET=latin1 ROW_FORMAT=COMPACT;
26+
INSERT INTO t (c) VALUES (1),(1),(1),(1),(1);
27+
SELECT * FROM t;
28+
DROP TABLE t;
29+
CREATE TABLE t (c INT,c2 TEXT,KEY k2 (c2 (1),c)) DEFAULT CHARSET=latin1 ROW_FORMAT=COMPACT;
30+
INSERT INTO t (c) VALUES (1),(1),(1),(1),(1);
31+
SELECT * FROM t;
32+
DROP TABLE t;
33+
CREATE TABLE t (c INT,c2 TEXT NOT NULL,KEY k2 (c2 (1),c)) DEFAULT CHARSET=latin1 ROW_FORMAT=COMPACT;
34+
INSERT INTO t (c) VALUES (1),(1),(1),(1),(1);
35+
SELECT * FROM t;
36+
DROP TABLE t;
37+
38+
SET GLOBAL sql_mode=DEFAULT;
39+
40+
CREATE TABLE t (c INT,c2 BLOB,KEY k2 (c2 (1),c)) DEFAULT CHARSET=latin1 ROW_FORMAT=COMPACT;
41+
INSERT INTO t (c) VALUES (1),(1),(1),(1),(1);
42+
SELECT * FROM t;
43+
DROP TABLE t;
44+
CREATE TABLE t (c INT,c2 VARCHAR(270),KEY k2 (c2 (1),c)) DEFAULT CHARSET=latin1 ROW_FORMAT=COMPACT;
45+
INSERT INTO t (c) VALUES (1),(1),(1),(1),(1);
46+
SELECT * FROM t;
47+
DROP TABLE t;
48+
CREATE TABLE t (c INT,c2 CHAR(80),KEY k2 (c2 (1),c)) DEFAULT CHARSET=latin1 ROW_FORMAT=COMPACT;
49+
INSERT INTO t (c) VALUES (1),(1),(1),(1),(1);
50+
SELECT * FROM t;
51+
DROP TABLE t;
52+
CREATE TABLE t (c INT,c2 TEXT,KEY k2 (c2 (1),c)) DEFAULT CHARSET=latin1 ROW_FORMAT=COMPACT;
53+
INSERT INTO t (c) VALUES (1),(1),(1),(1),(1);
54+
SELECT * FROM t;
55+
DROP TABLE t;

storage/innobase/handler/ha_innodb.cc

Lines changed: 55 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -6675,8 +6675,9 @@ wsrep_store_key_val_for_row(
66756675
*key_is_null = true;
66766676

66776677
for (; key_part != end; key_part++) {
6678-
uchar sorted[REC_VERSION_56_MAX_INDEX_COL_LEN] = {'\0'};
6678+
uchar sorted[REC_VERSION_56_MAX_INDEX_COL_LEN];
66796679
bool part_is_null = false;
6680+
memset(sorted, 0, sizeof(sorted));
66806681

66816682
if (key_part->null_bit) {
66826683
if (buff_space > 0) {
@@ -6701,11 +6702,11 @@ wsrep_store_key_val_for_row(
67016702

67026703
if (mysql_type == MYSQL_TYPE_VARCHAR) {
67036704
/* >= 5.0.3 true VARCHAR */
6704-
ulint lenlen;
6705-
ulint len;
6706-
const byte* data;
6707-
ulint key_len;
6708-
ulint true_len;
6705+
ulint lenlen=0;
6706+
ulint len=0;
6707+
const byte* data=nullptr;
6708+
ulint key_len=0;
6709+
ulint true_len=0;
67096710
const CHARSET_INFO* cs;
67106711
int error=0;
67116712

@@ -6752,16 +6753,23 @@ wsrep_store_key_val_for_row(
67526753
if (true_len > key_len) {
67536754
true_len = key_len;
67546755
}
6755-
/* cannot exceed max column lenght either, we may need to truncate
6756+
/* cannot exceed max column length either, we may need to truncate
67566757
the stored value: */
67576758
if (true_len > sizeof(sorted)) {
6758-
true_len = sizeof(sorted);
6759+
true_len = sizeof(sorted);
6760+
}
6761+
6762+
/* Note that when true_len == 0 it does not mean
6763+
that data == nullptr */
6764+
if (true_len) {
6765+
ut_ad(data);
6766+
memcpy(sorted, data, true_len);
6767+
6768+
true_len = wsrep_innobase_mysql_sort(
6769+
mysql_type, cs->number, sorted, true_len,
6770+
REC_VERSION_56_MAX_INDEX_COL_LEN);
67596771
}
67606772

6761-
memcpy(sorted, data, true_len);
6762-
true_len = wsrep_innobase_mysql_sort(
6763-
mysql_type, cs->number, sorted, true_len,
6764-
REC_VERSION_56_MAX_INDEX_COL_LEN);
67656773
if (wsrep_protocol_version > 1) {
67666774
/* Note that we always reserve the maximum possible
67676775
length of the true VARCHAR in the key value, though
@@ -6774,7 +6782,7 @@ wsrep_store_key_val_for_row(
67746782
wsrep_thd_query(thd));
67756783
true_len = buff_space;
67766784
}
6777-
memcpy(buff, sorted, true_len);
6785+
memcpy(buff, sorted, true_len);
67786786
buff += true_len;
67796787
buff_space -= true_len;
67806788
} else {
@@ -6789,11 +6797,11 @@ wsrep_store_key_val_for_row(
67896797
|| mysql_type == MYSQL_TYPE_GEOMETRY) {
67906798

67916799
const CHARSET_INFO* cs;
6792-
ulint key_len;
6793-
ulint true_len;
6800+
ulint key_len=0;
6801+
ulint true_len=0;
67946802
int error=0;
6795-
ulint blob_len;
6796-
const byte* blob_data;
6803+
ulint blob_len=0;
6804+
const byte* blob_data=nullptr;
67976805

67986806
ut_a(key_part->key_part_flag & HA_PART_KEY_SEG);
67996807

@@ -6846,11 +6854,16 @@ wsrep_store_key_val_for_row(
68466854
true_len = key_len;
68476855
}
68486856

6849-
memcpy(sorted, blob_data, true_len);
6850-
true_len = wsrep_innobase_mysql_sort(
6851-
mysql_type, cs->number, sorted, true_len,
6852-
REC_VERSION_56_MAX_INDEX_COL_LEN);
6853-
6857+
/* Note that when true_len == 0 it does not mean
6858+
that blob_data == nullptr */
6859+
if (true_len) {
6860+
ut_ad(blob_data);
6861+
memcpy(sorted, blob_data, true_len);
6862+
6863+
true_len = wsrep_innobase_mysql_sort(
6864+
mysql_type, cs->number, sorted, true_len,
6865+
REC_VERSION_56_MAX_INDEX_COL_LEN);
6866+
}
68546867

68556868
/* Note that we always reserve the maximum possible
68566869
length of the BLOB prefix in the key value. */
@@ -6874,9 +6887,9 @@ wsrep_store_key_val_for_row(
68746887
index. */
68756888

68766889
const CHARSET_INFO* cs = NULL;
6877-
ulint true_len;
6878-
ulint key_len;
6879-
const uchar* src_start;
6890+
ulint true_len=0;
6891+
ulint key_len=0;
6892+
const uchar* src_start=nullptr;
68806893
int error=0;
68816894
enum_field_types real_type;
68826895

@@ -6926,10 +6939,17 @@ wsrep_store_key_val_for_row(
69266939
cs->mbmaxlen),
69276940
&error);
69286941
}
6929-
memcpy(sorted, src_start, true_len);
6930-
true_len = wsrep_innobase_mysql_sort(
6931-
mysql_type, cs->number, sorted, true_len,
6932-
REC_VERSION_56_MAX_INDEX_COL_LEN);
6942+
6943+
/* Note that when true_len == 0 it does not mean
6944+
that src_start == nullptr */
6945+
if (true_len) {
6946+
ut_ad(src_start);
6947+
memcpy(sorted, src_start, true_len);
6948+
6949+
true_len = wsrep_innobase_mysql_sort(
6950+
mysql_type, cs->number, sorted, true_len,
6951+
REC_VERSION_56_MAX_INDEX_COL_LEN);
6952+
}
69336953

69346954
if (true_len > buff_space) {
69356955
fprintf (stderr,
@@ -6939,7 +6959,12 @@ wsrep_store_key_val_for_row(
69396959
}
69406960
memcpy(buff, sorted, true_len);
69416961
} else {
6942-
memcpy(buff, src_start, true_len);
6962+
/* Note that when true_len == 0 it does not mean
6963+
that src_start == nullptr */
6964+
if (true_len) {
6965+
ut_ad(src_start);
6966+
memcpy(buff, src_start, true_len);
6967+
}
69436968
}
69446969
buff += true_len;
69456970
buff_space -= true_len;

0 commit comments

Comments
 (0)