https://bugs.gentoo.org/977642 From 33d66d22d8bf9c86af307307004ed43850face5f Mon Sep 17 00:00:00 2001 From: Harin Vadodaria Date: Mon, 1 Jun 2026 10:15:59 +0200 Subject: [PATCH 1/2] Bug#39116965: MySQL Out-of-Bounds Read Description: Connection attribute parsing read a length-encoded size field before checking that the complete field was present in the packet. Fix: A size check was added before reading the field. Debug coverage was added for short length-encoded attribute packets during pre-authentication. Change-Id: Ia90e387a219556bb040a26853946c022f73de730 --- a/sql/auth/sql_authentication.cc +++ b/sql/auth/sql_authentication.cc @@ -2262,9 +2262,20 @@ static bool read_client_connect_attrs(THD *thd, char **ptr, size_t length, length_length; char *ptr_save; - /* not enough bytes to hold the length */ + /* Need one byte to determine the length-encoded field size. */ if (*max_bytes_available < 1) return true; + uchar *pos = (uchar *)*ptr; + DBUG_EXECUTE_IF("connect_attrs_too_short_3", *pos = 252; + *max_bytes_available = 2;); + DBUG_EXECUTE_IF("connect_attrs_too_short_4", *pos = 253; + *max_bytes_available = 3;); + DBUG_EXECUTE_IF("connect_attrs_too_short_9", *pos = 254; + *max_bytes_available = 8;); + + const size_t required_length = (size_t)net_field_length_size(pos); + if (*max_bytes_available < required_length) return true; + /* read the length */ ptr_save = *ptr; length = static_cast(net_field_length_ll((uchar **)ptr)); -- 2.54.0 From 2747a03d7a522a4f880df5c978f2e5c1db17b119 Mon Sep 17 00:00:00 2001 From: Lukasz Kotula Date: Fri, 17 Apr 2026 09:12:57 +0200 Subject: [PATCH 2/2] Bug#39204635 - Unauthenticated repeated X Protocol TLS upgrade crashes MySQL Router Description For X Protocol routing with `client_ssl_mode=PREFERRED` and `server_ssl_mode=AS_CLIENT`, a client can send `CON_CAPABILITIES_SET(tls=true)` after TLS is already active. Router accepted this second TLS capability request and re-entered the TLS upgrade path. During `tls_accept_finalize()`, the server-side TLS channel could already be initialized, which led to a `logic_error` and connection termination. Fix === Reject repeated TLS capability upgrades when client-side TLS is already active. For a second `tls=true` capability request, Router now returns error 5001 (`Capability prepare failed for 'tls'`) and continues normal session handling. Change-Id: Ifaaa93eeec1019160c7a6db50daa7e1af8f383c5 --- a/router/src/routing/src/x_connection.cc +++ b/router/src/routing/src/x_connection.cc @@ -1471,6 +1471,7 @@ void MysqlRoutingXConnection::client_cap_set() { if (switch_to_tls) { bool continue_with_tls{false}; + switch (source_ssl_mode()) { case SslMode::kDisabled: { continue_with_tls = false; @@ -1525,6 +1526,11 @@ void MysqlRoutingXConnection::client_cap_set() { discard_current_msg(src_channel, src_protocol); std::vector out_buf; + // The client tries to activate TLS when it is already enabled. + if (continue_with_tls && src_channel->ssl()) { + continue_with_tls = false; + } + if (!continue_with_tls) { #ifdef DEBUG_IO std::cerr << __LINE__ << ": " --- a/router/tests/component/test_routing.cc +++ b/router/tests/component/test_routing.cc @@ -122,6 +122,23 @@ static xcl::XError make_x_connection(XProtocolSession &session, password.c_str(), ""); } +static xcl::XError make_x_raw_connection( + XProtocolSession &session, const std::string &host, const uint16_t port, + int64_t connect_timeout = 10000 /*10s*/) { + session = xcl::create_session(); + xcl::XError err = setup_x_session(session, connect_timeout, "PREFERRED"); + if (err) return err; + + return session->get_protocol().get_connection().connect( + host, port, xcl::Internet_protocol::Any); +} + +static std::string format_xerror(const xcl::XError &e) { + if (!e) return "no-error"; + + return "(code:" + std::to_string(e.error()) + ", message:" + e.what() + ")"; +} + TEST_F(RouterRoutingTest, RoutingOk) { const auto server_port = port_pool_.get_next_available(); const auto router_port = port_pool_.get_next_available(); @@ -1882,6 +1899,78 @@ static size_t xproto_frame_encode(const T &msg, uint8_t msg_type, return msg.SerializeToCodedStream(&codecouts); } +/** + * @test Verify that repeated TLS activation requests are properly rejected + * and do not crash the Router. + */ +TEST_F(RouterRoutingTest, XProtocolRepeatedTlsUpgrade) { + const auto server_classic_port = port_pool_.get_next_available(); + const auto server_x_port = port_pool_.get_next_available(); + const auto router_x_rw_port = port_pool_.get_next_available(); + + const std::string json_stmts = get_data_dir().join("bootstrap_gr.js").str(); + + launch_mysql_server_mock(json_stmts, server_classic_port, EXIT_SUCCESS, false, + /*http_port*/ 0, server_x_port, /*module_prefix*/ "", + /*bind_address*/ "127.0.0.1", + /*wait_for_notify_ready*/ std::chrono::seconds(30), + /*enable_ssl*/ true); + + const std::string routing_x_section = get_static_routing_section( + "x", router_x_rw_port, "", {server_x_port}, "x"); + + TempDirectory conf_dir("conf"); + const std::string ssl_conf = + "client_ssl_mode=PREFERRED\n" + "server_ssl_mode=AS_CLIENT\n" + "client_ssl_key=" SSL_TEST_DATA_DIR + "/server-key-sha512.pem\n" + "client_ssl_cert=" SSL_TEST_DATA_DIR "/server-cert-sha512.pem"; + std::string conf_file = + create_config_file(conf_dir.name(), routing_x_section, nullptr, + "mysqlrouter.conf", ssl_conf, true); + + launch_router({"-c", conf_file}); + + Mysqlx::Connection::CapabilitiesSet switch_tls_msg; + auto *cap = switch_tls_msg.mutable_capabilities()->add_capabilities(); + cap->set_name("tls"); + auto *cap_value = cap->mutable_value(); + cap_value->set_type(Mysqlx::Datatypes::Any_Type::Any_Type_SCALAR); + auto *cap_scalar = cap_value->mutable_scalar(); + cap_scalar->set_type(Mysqlx::Datatypes::Scalar_Type::Scalar_Type_V_BOOL); + cap_scalar->set_v_bool(true); + + XProtocolSession x_session; + const auto x_connect_error = + make_x_raw_connection(x_session, "127.0.0.1", router_x_rw_port); + ASSERT_FALSE(x_connect_error) << format_xerror(x_connect_error); + + const auto x_cap_set_error = + x_session.get()->get_protocol().execute_set_capability(switch_tls_msg); + ASSERT_FALSE(x_cap_set_error) << format_xerror(x_cap_set_error); + + const auto x_tls_error = + x_session.get()->get_protocol().get_connection().activate_tls(); + + ASSERT_FALSE(x_tls_error) << format_xerror(x_tls_error); + + const auto x_cap_set2_error = + x_session.get()->get_protocol().execute_set_capability(switch_tls_msg); + ASSERT_EQ(x_cap_set2_error.error(), 5001) << format_xerror(x_cap_set2_error); + + const auto x_login_error = + x_session.get()->get_protocol().execute_authenticate("root", "fake-pass", + "", "SHA256_MEMORY"); + ASSERT_FALSE(x_login_error) << format_xerror(x_login_error); + + // Router should still accept a fresh X Protocol connection after the loop. + XProtocolSession x_session2; + const auto res = make_x_connection(x_session2, "127.0.0.1", router_x_rw_port, + "root", "fake-pass"); + EXPECT_THAT(res.error(), ::testing::AnyOf(0, 3159)); +} + /** * @test Check that if the x protocol client sends CONCLOSE message the Router * replies with OK{bye!} message. -- 2.54.0