Skip to content

Commit

Permalink
Bug#35769610 statement with open comment crashes connection sharing
Browse files Browse the repository at this point in the history
When connection sharing is enabled, a statement like:

  DO 1 /*

crashes mysqlrouter.

ASAN reports a heap-buffer-overrun.

Change
------

- handle ABORT_SYM in the lexer as end-symbol.
- add a fuzzer for the SqlLexer

Change-Id: Ice21cac61f9e04208a8d0b6492a3af02ff078a12
  • Loading branch information
weigon authored and dahlerlend committed Sep 20, 2023
1 parent 7d09fd1 commit a8a1ba6
Show file tree
Hide file tree
Showing 7 changed files with 107 additions and 4 deletions.
2 changes: 2 additions & 0 deletions router/src/routing/src/classic_query_forwarder.cc
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ static void dump_token(SqlLexer::iterator::Token tkn) {
std::cerr << std::quoted(tkn.text);
} else if (tkn.id == NUM) {
std::cerr << tkn.text;
} else if (tkn.id == ABORT_SYM) {
std::cerr << "<ABORT>";
} else if (tkn.id == END_OF_INPUT) {
std::cerr << "<END>";
}
Expand Down
18 changes: 14 additions & 4 deletions router/src/routing/src/sql_lexer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1297,18 +1297,28 @@ SqlLexer::iterator::Token SqlLexer::iterator::next_token() {
return {get_token_text(token_id), token_id};
}

static bool is_final_token(const SqlLexer::iterator::Token &tkn) {
switch (tkn.id) {
case END_OF_INPUT: // end-of-input
case ABORT_SYM: // broken comment, string, hex-number, ...
return true;
}

return false;
}

SqlLexer::iterator SqlLexer::iterator::operator++(int) {
// the last token as END_OF_INPUT, +1 is past the "end()"
if (token_.id == END_OF_INPUT) {
// the last token is END_OF_INPUT, +1 is past the "end()"
if (is_final_token(token_)) {
return {nullptr};
}

return {session_, next_token()};
}

SqlLexer::iterator &SqlLexer::iterator::operator++() {
// the last token as END_OF_INPUT, +1 is past the "end()"
if (token_.id == END_OF_INPUT) {
// the last token is END_OF_INPUT, +1 is past the "end()"
if (is_final_token(token_)) {
token_ = {};
} else {
token_ = next_token();
Expand Down
12 changes: 12 additions & 0 deletions router/src/routing/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -126,4 +126,16 @@ IF(LIBFUZZER_COMPILE_FLAGS)
)
LIBFUZZER_ADD_TEST(routertest_fuzz_parser_show_warnings
INITIAL_CORPUS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/corpus/show_warnings)

MYSQL_ADD_EXECUTABLE(routertest_fuzz_routing_sql_lexer
fuzz_sql_lexer.cc
../src/sql_lexer.cc
${CMAKE_SOURCE_DIR}/sql/sql_lex_hash.cc
COMPONENT Router
DEPENDENCIES GenServerSource
LINK_LIBRARIES harness_stdx;mysys
SKIP_INSTALL
)
LIBFUZZER_ADD_TEST(routertest_fuzz_routing_sql_lexer
INITIAL_CORPUS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/corpus/sql_lexer)
ENDIF()
1 change: 1 addition & 0 deletions router/src/routing/tests/corpus/sql_lexer/open_comment
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
DO 1 /*;
1 change: 1 addition & 0 deletions router/src/routing/tests/corpus/sql_lexer/select_1
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
SELECT 1
55 changes: 55 additions & 0 deletions router/src/routing/tests/fuzz_sql_lexer.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
Copyright (c) 2023, Oracle and/or its affiliates.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License, version 2.0,
as published by the Free Software Foundation.
This program is also distributed with certain software (including
but not limited to OpenSSL) that is licensed under separate terms,
as designated in a particular file or component or in included license
documentation. The authors of MySQL hereby grant you an additional
permission to link the program and your derivative works with the
separately licensed software that they have included with MySQL.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/

#include "router/src/routing/src/show_warnings_parser.h"

#include <iostream>
#include <string_view>

#include "hexify.h"

extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
// last char must be a \0 and the parser will overwrite the buffer.
auto stmt = std::string(reinterpret_cast<const char *>(Data), Size);

MEM_ROOT mem_root;
THD session;
session.mem_root = &mem_root;

{
Parser_state parser_state;
parser_state.init(&session, stmt.data(), stmt.size());
session.m_parser_state = &parser_state;
SqlLexer lexer{&session};

for (auto tkn : lexer) {
switch (tkn.id) {
default:
break;
}
}
}

return 0;
}
22 changes: 22 additions & 0 deletions router/tests/integration/test_routing_sharing.cc
Original file line number Diff line number Diff line change
Expand Up @@ -7111,6 +7111,28 @@ TEST_P(ShareConnectionTest, select_overlong) {
ASSERT_NO_ERROR(cli.query("DO 1"));
}

TEST_P(ShareConnectionTest, aborted_lexing) {
RecordProperty("Description",
"Check that lexing a statement with a non-closed comment "
"fails properly.");

MysqlClient cli;

auto account = SharedServer::caching_sha2_empty_password_account();

cli.username(account.username);
cli.password(account.password);

ASSERT_NO_ERROR(
cli.connect(shared_router()->host(), shared_router()->port(GetParam())));

auto query_res = cli.query("DO 1 /*");
ASSERT_ERROR(query_res);

// parse-error at /*
EXPECT_EQ(query_res.error().value(), 1064) << query_res.error();
}

INSTANTIATE_TEST_SUITE_P(Spec, ShareConnectionTest,
::testing::ValuesIn(share_connection_params),
[](auto &info) {
Expand Down

0 comments on commit a8a1ba6

Please sign in to comment.