Skip to content

Commit 1452928

Browse files
luqunfacebook-github-bot
authored andcommitted
Better handle bad db_metadata value
Summary: There is implicit require for db_metadata: it should be a json object , such as "{"shard": "<shard_name>", "rs": "<replicaset_id>"}" If The db_metadata value is a json array, such as "[{"shard": "<shard_name>", "rs": "<replicaset_id>"}]", rapidjson will SIGSEGV when treating a json array as a json object and accessing members in the "object". add checks to verify the db_metadata is a json object. Reviewed By: abhinav04sharma, zhichengzhu Differential Revision: D16569064 fbshipit-source-id: 61c3761
1 parent 85c6ed5 commit 1452928

File tree

8 files changed

+159
-7
lines changed

8 files changed

+159
-7
lines changed

mysql-test/r/db_metadata.result

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ drop database if exists test7;
77
drop database if exists test8;
88
drop database if exists test9;
99
drop database if exists test10;
10+
drop database if exists test11;
11+
drop database if exists test12;
12+
drop database if exists test13;
13+
drop database if exists test14;
1014
create database test2;
1115
show create database test2;
1216
Database Create Database
@@ -157,7 +161,7 @@ def test7 {"shard":"test7_shard"}
157161
def test8
158162
def test9 {"shard":"test9_shard"}
159163
create database test10 db_metadata = "invalid_json";
160-
ERROR HY000: Invalid JSON for DB_METADATA attribute: invalid_json.
164+
ERROR HY000: Invalid JSON object for DB_METADATA attribute: invalid_json.
161165
select catalog_name, schema_name, db_metadata from information_schema.schemata_ext;
162166
catalog_name schema_name db_metadata
163167
def information_schema
@@ -174,7 +178,7 @@ def test7 {"shard":"test7_shard"}
174178
def test8
175179
def test9 {"shard":"test9_shard"}
176180
create database test11 db_metadata = "{\'shard\':\'test11_shard\'}";
177-
ERROR HY000: Invalid JSON for DB_METADATA attribute: {'shard':'test11_shard'}.
181+
ERROR HY000: Invalid JSON object for DB_METADATA attribute: {'shard':'test11_shard'}.
178182
select catalog_name, schema_name, db_metadata from information_schema.schemata_ext;
179183
catalog_name schema_name db_metadata
180184
def information_schema
@@ -216,6 +220,25 @@ CREATE DATABASE `test13` /*!40100 DEFAULT CHARACTER SET latin1 DB_METADATA '{"sh
216220
show create database test13;
217221
Database Create Database
218222
test13 CREATE DATABASE `test13` /*!40100 DEFAULT CHARACTER SET latin1 DB_METADATA '{"sha''\\"rd":"test13''_sh\\"ard"}' */
223+
create database test14 db_metadata = "[{\"shard\":\"test14_shard\", \"data\":\"1234\"}]";
224+
ERROR HY000: Invalid JSON object for DB_METADATA attribute: [{"shard":"test14_shard", "data":"1234"}].
225+
select catalog_name, schema_name, db_metadata from information_schema.schemata_ext;
226+
catalog_name schema_name db_metadata
227+
def information_schema
228+
def mtr
229+
def mysql
230+
def performance_schema
231+
def test
232+
def test12 {"sha'rd":"test12\"_shard"}
233+
def test13 {"sha'\"rd":"test13'_sh\"ard"}
234+
def test2
235+
def test3
236+
def test4
237+
def test5 {"shard":"test5_shard"}
238+
def test6 {"shard":"test6_shard"}
239+
def test7 {"shard":"test7_shard"}
240+
def test8
241+
def test9 {"shard":"test9_shard"}
219242
alter database test3 character set ascii;
220243
show create database test3;
221244
Database Create Database
@@ -543,7 +566,7 @@ def test7 {"shard":"test7_shard_altered"}
543566
def test8
544567
def test9 {"shard":"test9_shard_altered"}
545568
alter database test9 db_metadata = "invalid_json";
546-
ERROR HY000: Invalid JSON for DB_METADATA attribute: invalid_json.
569+
ERROR HY000: Invalid JSON object for DB_METADATA attribute: invalid_json.
547570
select catalog_name, schema_name, db_metadata from information_schema.schemata_ext;
548571
catalog_name schema_name db_metadata
549572
def information_schema
@@ -611,6 +634,26 @@ default-character-set=latin1
611634
default-collation=latin1_swedish_ci
612635
db-read-only=0
613636
db-metadata={"shard":"test10_shard_altered"}
637+
alter database test10 db_metadata = "[{\"shard\":\"test10_shard\", \"data\":\"1234\"}]";
638+
ERROR HY000: Invalid JSON object for DB_METADATA attribute: [{"shard":"test10_shard", "data":"1234"}].
639+
select catalog_name, schema_name, db_metadata from information_schema.schemata_ext;
640+
catalog_name schema_name db_metadata
641+
def information_schema
642+
def mtr
643+
def mysql
644+
def performance_schema
645+
def test
646+
def test10 {"shard":"test10_shard_altered"}
647+
def test12 {"sha'rd":"test12\"_shard"}
648+
def test13 {"sha'\"rd":"test13'_sh\"ard"}
649+
def test2
650+
def test3 {"shard":"test3_shard_altered"}
651+
def test4 {"shard":"test4_shard_altered"}
652+
def test5 {"shard":"Really long shard name. Really long shard name. Really long shard name. Really long shard name. Really long shard name. Really long shard name. Really long shard name. Really long shard name. Really long shard name. Really long shard name. Really long shard name. Really long shard name. Really long shard name. Really long shard name. Really long shard name. Really long shard name. Really long shard name. Really long shard name. Really long shard name. Really long shard name. Really long shard name. Really long shard name. Really long shard name. Really long shard name. Really long shard name. Really long shard name. Really long shard name. Really long shard name. Really long shard name. Really long shard name. Really long shard name. Really long shard name. Really long shard name. Really long shard name. Really long shard name. Really long shard name. Really long shard name. Really long shard name. Really long shard name. Really long shard name. Really long shard name. Really long shard name. Fin"}
653+
def test6 {"shard":"test6_shard_altered"}
654+
def test7 {"shard":"test7_shard_altered"}
655+
def test8
656+
def test9 {"shard":"test9_shard_altered"}
614657
drop database if exists test2;
615658
drop database if exists test3;
616659
drop database if exists test4;
@@ -623,3 +666,4 @@ drop database if exists test10;
623666
drop database if exists test11;
624667
drop database if exists test12;
625668
drop database if exists test13;
669+
drop database if exists test14;

mysql-test/r/db_metadata_reset.result

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,51 @@ test4 CREATE DATABASE `test4` /*!40100 DEFAULT CHARACTER SET latin1 */
6868
show create database test6;
6969
Database Create Database
7070
test6 CREATE DATABASE `test6` /*!40100 DEFAULT CHARACTER SET latin1 */
71+
create database test7 db_metadata = "{\"shard\":\"test7_shard\"}";
72+
show create database test7;
73+
Database Create Database
74+
test7 CREATE DATABASE `test7` /*!40100 DEFAULT CHARACTER SET latin1 DB_METADATA '{"shard":"test7_shard"}' */
75+
set @start_binlog_trx_meta_data= @@global.binlog_trx_meta_data;
76+
set @start_log_warnings = @@global.log_warnings;
77+
set @@global.binlog_trx_meta_data= 1;
78+
set @@global.log_warnings= 2;
79+
select catalog_name, schema_name, default_character_set_name, default_collation_name, sql_path from information_schema.schemata;
80+
catalog_name schema_name default_character_set_name default_collation_name sql_path
81+
def information_schema utf8 utf8_general_ci NULL
82+
def mtr latin1 latin1_swedish_ci NULL
83+
def mysql latin1 latin1_swedish_ci NULL
84+
def performance_schema utf8 utf8_general_ci NULL
85+
def test latin1 latin1_swedish_ci NULL
86+
def test2 latin1 latin1_swedish_ci NULL
87+
def test3 utf8 utf8_general_ci NULL
88+
def test4 latin1 latin1_swedish_ci NULL
89+
def test5 utf16 utf16_general_ci NULL
90+
def test6 latin1 latin1_swedish_ci NULL
91+
def test7 latin1 latin1_swedish_ci NULL
92+
select catalog_name, schema_name, db_metadata from information_schema.schemata_ext;
93+
catalog_name schema_name db_metadata
94+
def information_schema
95+
def mtr
96+
def mysql
97+
def performance_schema
98+
def test
99+
def test2
100+
def test3 {"shard":"test3_shard"}
101+
def test4
102+
def test5 {"shard":"test5_shard"}
103+
def test6
104+
def test7 [{"shard": "test3_shard", "data2": "date2_shard"}]
105+
show create database test7;
106+
Database Create Database
107+
test7 CREATE DATABASE `test7` /*!40100 DEFAULT CHARACTER SET latin1 DB_METADATA '[{"shard": "test3_shard", "data2": "date2_shard"}]' */
108+
use test7;
109+
create table t7 (a int, b int, primary key(a,b));
110+
drop table t7;
111+
set @@global.binlog_trx_meta_data = @start_binlog_trx_meta_data;
112+
set @@global.log_warnings = @start_log_warnings;
71113
drop database if exists test2;
72114
drop database if exists test3;
73115
drop database if exists test4;
74116
drop database if exists test5;
75117
drop database if exists test6;
118+
drop database if exists test7;

mysql-test/t/db_metadata.test

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ drop database if exists test7;
1212
drop database if exists test8;
1313
drop database if exists test9;
1414
drop database if exists test10;
15+
drop database if exists test11;
16+
drop database if exists test12;
17+
drop database if exists test13;
18+
drop database if exists test14;
1519
--enable_warnings
1620

1721
--disable_query_log
@@ -104,6 +108,11 @@ drop database test13;
104108
eval $db_create;
105109
show create database test13;
106110

111+
# create database with JSON array
112+
--error ER_DB_METADATA_INVALID_JSON
113+
create database test14 db_metadata = "[{\"shard\":\"test14_shard\", \"data\":\"1234\"}]";
114+
select catalog_name, schema_name, db_metadata from information_schema.schemata_ext;
115+
107116
# alter database tests
108117

109118
# alter database character set
@@ -235,6 +244,11 @@ alter database test10 read_only = false;
235244
show create database test10;
236245
--exec cat $MYSQLD_DATADIR/test10/db.opt
237246

247+
# alter database with JSON array
248+
--error ER_DB_METADATA_INVALID_JSON
249+
alter database test10 db_metadata = "[{\"shard\":\"test10_shard\", \"data\":\"1234\"}]";
250+
select catalog_name, schema_name, db_metadata from information_schema.schemata_ext;
251+
238252
# cleanup
239253
--disable_warnings
240254
drop database if exists test2;
@@ -249,4 +263,5 @@ drop database if exists test10;
249263
drop database if exists test11;
250264
drop database if exists test12;
251265
drop database if exists test13;
266+
drop database if exists test14;
252267
--enable_warnings

mysql-test/t/db_metadata_reset.test

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Test per-database database-metadata attribute
22
--source include/have_innodb.inc
3+
--source include/have_log_bin.inc
34
connection default;
45

56
--disable_query_log
@@ -51,11 +52,44 @@ select catalog_name, schema_name, db_metadata from information_schema.schemata_e
5152
show create database test4;
5253
show create database test6;
5354

55+
# create database with db_metadatae
56+
create database test7 db_metadata = "{\"shard\":\"test7_shard\"}";
57+
show create database test7;
58+
# modify database with bad db_metadatae
59+
# db_metadata should be a json object, but now we modify it with json array
60+
--exec echo "db-metadata=[{\"shard\": \"test3_shard\", \"data2\": \"date2_shard\"}]" > $MYSQLD_DATADIR/test7/db.opt
61+
62+
# Restart server to get rid of the dboptions cache
63+
--source include/restart_mysqld.inc
64+
set @start_binlog_trx_meta_data= @@global.binlog_trx_meta_data;
65+
set @start_log_warnings = @@global.log_warnings;
66+
67+
# show db_metadata
68+
set @@global.binlog_trx_meta_data= 1;
69+
set @@global.log_warnings= 2;
70+
select catalog_name, schema_name, default_character_set_name, default_collation_name, sql_path from information_schema.schemata;
71+
select catalog_name, schema_name, db_metadata from information_schema.schemata_ext;
72+
show create database test7;
73+
74+
# suppress error during THD::gen_trx_metadata
75+
--disable_query_log
76+
call mtr.add_suppression("Exception while adding meta data");
77+
call mtr.add_suppression("Exception while reading meta data: \\[\\{\"shard\": \"test3_shard\", \"data2\": \"date2_shard\"\\}\\]");
78+
--enable_query_log
79+
80+
# bad db-metadata won't crash mysqld or trigger assert
81+
use test7;
82+
create table t7 (a int, b int, primary key(a,b));
83+
drop table t7;
84+
set @@global.binlog_trx_meta_data = @start_binlog_trx_meta_data;
85+
set @@global.log_warnings = @start_log_warnings;
86+
5487
# cleanup
5588
--disable_warnings
5689
drop database if exists test2;
5790
drop database if exists test3;
5891
drop database if exists test4;
5992
drop database if exists test5;
6093
drop database if exists test6;
94+
drop database if exists test7;
6195
--enable_warnings

sql/binlog.cc

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8718,6 +8718,14 @@ std::string THD::gen_trx_metadata()
87188718
std::string json = buf.GetString();
87198719
boost::trim(json);
87208720

8721+
// verify doc json document
8722+
if (!doc.IsObject())
8723+
{
8724+
// NO_LINT_DEBUG
8725+
sql_print_error("Bad JSON format after adding meta data: %s",
8726+
json.c_str());
8727+
DBUG_RETURN("");
8728+
}
87218729
std::string comment_str= std::string("/*")
87228730
.append(TRX_META_DATA_HEADER)
87238731
.append(json)
@@ -8772,7 +8780,11 @@ bool THD::add_db_metadata(rapidjson::Document &meta_data_root)
87728780
if (!local_db_metadata.empty())
87738781
{
87748782
rapidjson::Document db_metadata_root;
8775-
if (db_metadata_root.Parse(local_db_metadata.c_str()).HasParseError())
8783+
// rapidjson doesn't like calling GetObject() on json non-object value
8784+
// The local_db_metadata format should similar to the following example:
8785+
// {"shard":"<shard_name>", "replicaset":"<replicaset_id>"}
8786+
if (db_metadata_root.Parse(local_db_metadata.c_str()).HasParseError() ||
8787+
!db_metadata_root.IsObject())
87768788
{
87778789
// NO_LINT_DEBUG
87788790
sql_print_error("Exception while reading meta data: %s",

sql/share/errmsg-utf8.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7336,7 +7336,7 @@ ER_DB_METADATA_TOO_LONG
73367336
eng "Metadata for the database is too long. Max length is %d bytes"
73377337

73387338
ER_DB_METADATA_INVALID_JSON
7339-
eng "Invalid JSON for DB_METADATA attribute: %s."
7339+
eng "Invalid JSON object for DB_METADATA attribute: %s."
73407340

73417341
ER_DB_METADATA_READ_ERROR
73427342
eng "Error reading db metadata option: '%-.64s'"

sql/sql_class.cc

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5794,7 +5794,10 @@ static std::string get_shard_id(const std::string& db_metadata)
57945794
try {
57955795
#ifdef HAVE_RAPIDJSON
57965796
rapidjson::Document db_metadata_root;
5797-
if (db_metadata_root.Parse(db_metadata.c_str()).HasParseError()) {
5797+
// The local_db_metadata format should be:
5798+
// {"shard":"<shard_name>", "replicaset":"<replicaset_id>"}
5799+
if (db_metadata_root.Parse(db_metadata.c_str()).HasParseError() ||
5800+
!db_metadata_root.IsObject()) {
57985801
return {};
57995802
}
58005803
const auto iter= db_metadata_root.FindMember("shard");

sql/sql_yacc.yy

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6586,7 +6586,8 @@ db_metadata_str:
65866586
{
65876587
#ifdef HAVE_RAPIDJSON
65886588
rapidjson::Document db_metadata_root;
6589-
if (db_metadata_root.Parse($3.str).HasParseError())
6589+
if (db_metadata_root.Parse($3.str).HasParseError() ||
6590+
!db_metadata_root.IsObject())
65906591
{
65916592
my_error(ER_DB_METADATA_INVALID_JSON, MYF(0), $3.str);
65926593
MYSQL_YYABORT;

0 commit comments

Comments
 (0)