Skip to content

Commit 6d69df0

Browse files
committed
Bug#23707238 - PROTOBUF LIMITS THE NUMBER OF NESTED OBJECTS TO 100 RECURSIONS
Description: When X Plugin receives X Protocol message that contains 100 nested objects, the decoding operation fails and client received following error: "Invalid message" and gets disconnected. Analysis: Decoding failure is caused by Protobuf which limits the number of nested objects. There isn't any way to verify the cause of the failure, which leaves us with a generic message. When decoding this message manualy (by "protoc") id doesn't give any usefull information about the cause. For now the limit should be set to higher value. Fix: Fix sets the limit to 400 (higher values cause a crash - stack overflow) It is done by calling following method: google::protobuf::io::CodedInputStream::SetRecursionLimit RB: 13186 Reviewed-by: Grzegorz Szwarc <[email protected]> Reviewed-by: Andrzej Religa <[email protected]>
1 parent 8d556b5 commit 6d69df0

File tree

10 files changed

+154
-13
lines changed

10 files changed

+154
-13
lines changed

rapid/plugin/x/mysqlxtest_src/mysqlxtest.cc

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -822,8 +822,8 @@ class Command
822822
m_commands["enablessl"] = &Command::cmd_enablessl;
823823
m_commands["sleep "] = &Command::cmd_sleep;
824824
m_commands["login "] = &Command::cmd_login;
825-
m_commands["stmtadmin "] = &Command::cmd_stmt_admin;
826-
m_commands["stmtsql "] = &Command::cmd_stmt_sql;
825+
m_commands["stmtadmin "] = &Command::cmd_stmtadmin;
826+
m_commands["stmtsql "] = &Command::cmd_stmtsql;
827827
m_commands["loginerror "] = &Command::cmd_loginerror;
828828
m_commands["repeat "] = &Command::cmd_repeat;
829829
m_commands["endrepeat"] = &Command::cmd_endrepeat;
@@ -1149,7 +1149,7 @@ class Command
11491149
return Continue;
11501150
}
11511151

1152-
Result cmd_stmt_sql(Execution_context &context, const std::string &args)
1152+
Result cmd_stmtsql(Execution_context &context, const std::string &args)
11531153
{
11541154
Mysqlx::Sql::StmtExecute stmt;
11551155

@@ -1165,7 +1165,7 @@ class Command
11651165
}
11661166

11671167

1168-
Result cmd_stmt_admin(Execution_context &context, const std::string &args)
1168+
Result cmd_stmtadmin(Execution_context &context, const std::string &args)
11691169
{
11701170
std::string tmp = args;
11711171
replace_variables(tmp);
@@ -1296,6 +1296,9 @@ class Command
12961296
variable_name = argl[1];
12971297
}
12981298

1299+
// Allow use of variables as a source of number of iterations
1300+
replace_variables(argl[0]);
1301+
12991302
Loop_do loop = {context.m_stream.tellg(), atoi(argl[0].c_str()), 0, variable_name};
13001303

13011304
m_loop_stack.push_back(loop);

rapid/plugin/x/ngs/src/protocol_decoder.cc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,12 @@ Error_code Message_decoder::parse(Request &request)
109109
static_cast<int>(buffer.length()));
110110
// variable 'mysqlx_max_allowed_packet' has been checked when buffer was filling by data
111111
stream.SetTotalBytesLimit(static_cast<int>(buffer.length()), -1 /*no warnings*/);
112+
// Protobuf limits the number of nested objects in decoded message
113+
// 400 nested objects should be enough to write complicated queries.
114+
// Protobuf doesn't print a readable error after reaching the limit
115+
// thus we would like to ensure that user doesn't hit the error
116+
stream.SetRecursionLimit(400);
117+
112118
message->ParseFromCodedStream(&stream);
113119

114120
if (!message->IsInitialized())

rapid/plugin/x/protocol/mysqlx_datatypes.proto

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,14 @@ message Scalar {
3131
required bytes value = 1;
3232
optional uint64 collation = 2;
3333
};
34-
35-
// an opaque octet sequence, with an optional content_type
36-
// See ``Mysqlx.Resultset.ColumnMetadata`` for list of known values.
37-
message Octets {
38-
required bytes value = 1;
39-
optional uint32 content_type = 2;
40-
};
41-
34+
35+
// an opaque octet sequence, with an optional content_type
36+
// See ``Mysqlx.Resultset.ColumnMetadata`` for list of known values.
37+
message Octets {
38+
required bytes value = 1;
39+
optional uint32 content_type = 2;
40+
};
41+
4242
enum Type {
4343
V_SINT = 1;
4444
V_UINT = 2;

rapid/plugin/x/tests/mtr/include/connection_ssl.inc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@
33
# Modified : 19-08-2015 Lalit Choudhary
44

55
--echo Preamble
6+
--source ../include/have_performance_schema_threads.inc
7+
68
--source ../include/xplugin_preamble.inc
9+
710
SET GLOBAL mysqlx_connect_timeout = 300;
811
call mtr.add_suppression("Unsuccessful login attempt");
912

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Do not run the test when PFS thread monitoring is disabled
2+
if (`SELECT COUNT(*)=0 as `running_threads` from performance_schema.threads ;`)
3+
{
4+
skip Needs DISABLE_PSI_THREAD (cmake options) to be set to zero;
5+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
install plugin mysqlx soname "mysqlx.so";
2+
call mtr.add_suppression("Plugin mysqlx reported: .Failed at SSL configuration: .SSL context is not usable without certificate and private key..");
3+
call mtr.add_suppression("Plugin mysqlx reported: .SSL_CTX_load_verify_locations failed.");
4+
5+
command ok
6+
connecting...
7+
active session is now 'test_different_messages'
8+
Try to use value greater than the default limit 100 /2 => 50
9+
doc
10+
command ok
11+
Try to use value equal than the X Protocol limit 400 /2 => 200
12+
doc
13+
command ok
14+
Try to use value greater than the X Protocol limit 400 /2 => 200
15+
Got expected error: Parse error unserializing protobuf message (code 5000)
16+
aborting session test_different_messages
17+
switched to session default
18+
Mysqlx.Ok {
19+
msg: "bye!"
20+
}
21+
ok
22+
uninstall plugin mysqlx;
23+
DROP TABLE IF EXISTS coll;

rapid/plugin/x/tests/mtr/t/connection_nonssl.test

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
# Created : 21-08-2015 Lalit Choudhary
33

44
--echo Preamble
5+
--source ../include/have_performance_schema_threads.inc
6+
57
--source ../include/xplugin_preamble.inc
68

79
SET GLOBAL mysqlx_connect_timeout = 300;

rapid/plugin/x/tests/mtr/t/performance_schema.test

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
## Preamble
44
--source include/not_embedded.inc
5-
--source include/have_perfschema.inc
5+
--source ../include/have_performance_schema_threads.inc
66

77
--source ../include/xplugin_preamble.inc
88

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
--plugin_dir=$MYSQLXPLUGIN_DIR
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
## Preamble
2+
--source ../include/xplugin_preamble.inc
3+
4+
## TEST STARTS HERE
5+
--write_file $MYSQL_TMP_DIR/mysqlx-update_itemremove.tmp
6+
-->stmtadmin create_collection {"schema":"test", "name":"coll"}
7+
-->recvresult
8+
9+
## It tests the limits of protobuf, we do not need any data in the collection
10+
11+
12+
-->macro Send_message_with_repeated_nested_objects %OBJECTS_TO_NEST%
13+
-->varlet %NESTED_OBJECTS%
14+
15+
-->varlet %OBJECTS_TO_REPEAT% %OBJECTS_TO_NEST%
16+
-->varinc %OBJECTS_TO_REPEAT% -3
17+
18+
-->repeat %OBJECTS_TO_REPEAT% %ITERATION%
19+
-->varlet %OBJECT_NUMBER% %ITERATION%
20+
-->varinc %OBJECT_NUMBER% 4
21+
-->varlet %NESTED_OBJECTS% %NESTED_OBJECTS% value { type: ARRAY array { value { type: LITERAL literal { type: V_SINT v_signed_int: %OBJECT_NUMBER% } }
22+
-->endrepeat
23+
24+
-->repeat %OBJECTS_TO_REPEAT%
25+
-->varlet %NESTED_OBJECTS% %NESTED_OBJECTS% } }
26+
-->endrepeat
27+
28+
-->quiet
29+
Mysqlx.Crud.Find {
30+
collection {
31+
name: "coll"
32+
schema: "test"
33+
}
34+
data_model: DOCUMENT
35+
criteria {
36+
type: OPERATOR
37+
operator {
38+
name: "=="
39+
param {
40+
type: IDENT
41+
identifier {
42+
document_path {
43+
type: MEMBER
44+
value: "ARR0"
45+
}
46+
}
47+
}
48+
param {
49+
type: ARRAY
50+
array {
51+
value {
52+
type: LITERAL
53+
literal {
54+
type: V_SINT
55+
v_signed_int: 3
56+
}
57+
}
58+
59+
%NESTED_OBJECTS%
60+
61+
62+
}
63+
64+
}
65+
}
66+
}
67+
}
68+
-->noquiet
69+
-->endmacro
70+
71+
## Current recursion limit is set to 400
72+
## each repeated message in bellow macro contains two messages
73+
## (it increases the current depth by 2)
74+
-->newsession test_different_messages root
75+
-->echo Try to use value greater than the default limit 100 /2 => 50
76+
-->callmacro Send_message_with_repeated_nested_objects 51
77+
-->recvresult
78+
79+
-->echo Try to use value equal than the X Protocol limit 400 /2 => 200
80+
-->callmacro Send_message_with_repeated_nested_objects 200
81+
-->recvresult
82+
83+
-->echo Try to use value greater than the X Protocol limit 400 /2 => 200
84+
-->callmacro Send_message_with_repeated_nested_objects 201
85+
-->expecterror 5000
86+
-->recvresult
87+
-->closesession abort
88+
89+
EOF
90+
91+
--exec $MYSQLXTEST -u root --file=$MYSQL_TMP_DIR/mysqlx-update_itemremove.tmp 2>&1
92+
93+
## Postamble
94+
--remove_file $MYSQL_TMP_DIR/mysqlx-update_itemremove.tmp
95+
96+
uninstall plugin mysqlx;
97+
98+
DROP TABLE IF EXISTS coll;

0 commit comments

Comments
 (0)