dev-db/mysql: backport vulnerability fix

Bug: https://bugs.gentoo.org/977642
Signed-off-by: Alfred Wingate <parona@protonmail.com>
Part-of: https://codeberg.org/gentoo/gentoo/pulls/1186
Signed-off-by: Sam James <sam@gentoo.org>
This commit is contained in:
Alfred Wingate
2026-06-19 01:01:58 +03:00
committed by Sam James
parent 48d6b84bf3
commit 50ca7ead2a
2 changed files with 1522 additions and 0 deletions

View File

@@ -0,0 +1,199 @@
https://bugs.gentoo.org/977642
From 33d66d22d8bf9c86af307307004ed43850face5f Mon Sep 17 00:00:00 2001
From: Harin Vadodaria <harin.vadodaria@oracle.com>
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<size_t>(net_field_length_ll((uchar **)ptr));
--
2.54.0
From 2747a03d7a522a4f880df5c978f2e5c1db17b119 Mon Sep 17 00:00:00 2001
From: Lukasz Kotula <lukasz.kotula@oracle.com>
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<uint8_t> 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

File diff suppressed because it is too large Load Diff