Skip to content

Commit c5f91bd

Browse files
committed
WL#15130 Socket-level TLS patch mysql#2: SSL Socket Table
The SSL socket table is a big table of pointers to SSL objects, indexed by socket number. When an NdbSocket is initialized from an ndb_socket_t, it will fetch any saved SSL * associated with the socket from the SSL socket table. If SSL_TABLE_ABORT_ON_HIT in ssl_socket_table.h is set to 1, debug builds will abort when an SSL pointer is found but not expected. This is a intended as a tool to help find and fix code that might lose the association between a socket and its SSL. The table is protected by a read-write lock. As the code base evolves toward use of NdbSocket with correct life cycles, the contention on the lock should become less and less. Once SecureSockets are correctly used everywhere, the table can be disabled. Change-Id: Ibb9b858146b8d983ca99f05b61ba0abee11b3ee6
1 parent 98fa081 commit c5f91bd

File tree

5 files changed

+186
-5
lines changed

5 files changed

+186
-5
lines changed

storage/ndb/include/util/NdbSocket.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "portlib/ndb_socket.h"
2929
#include "portlib/ndb_socket_poller.h"
3030
#include "portlib/NdbMutex.h"
31+
#include "util/ssl_socket_table.h"
3132

3233
static constexpr int TLS_BUSY_TRY_AGAIN = -2;
3334

@@ -36,8 +37,8 @@ class NdbSocket {
3637
enum class From { New, Existing };
3738

3839
NdbSocket() = default;
39-
NdbSocket(ndb_socket_t ndbsocket, From /*fromType*/) {
40-
// ssl = socket_table_get_ssl(ndbsocket.s, (fromType == From::Existing));
40+
NdbSocket(ndb_socket_t ndbsocket, From fromType) {
41+
ssl = socket_table_get_ssl(ndbsocket.s, (fromType == From::Existing));
4142
init_from_native(ndbsocket.s);
4243
}
4344
/* NdbSockets cannot be copied, except using NdbSocket::transfer() */
@@ -199,7 +200,7 @@ class NdbSocket {
199200

200201
inline
201202
void NdbSocket::init_from_new(ndb_socket_t ndbsocket) {
202-
// assert(! socket_table_get_ssl(ndbsocket.s, false));
203+
assert(! socket_table_get_ssl(ndbsocket.s, false));
203204
init_from_native(ndbsocket.s);
204205
}
205206

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
Copyright (c) 2022, Oracle and/or its affiliates.
3+
4+
This program is free software; you can redistribute it and/or modify
5+
it under the terms of the GNU General Public License, version 2.0,
6+
as published by the Free Software Foundation.
7+
8+
This program is also distributed with certain software (including
9+
but not limited to OpenSSL) that is licensed under separate terms,
10+
as designated in a particular file or component or in included license
11+
documentation. The authors of MySQL hereby grant you an additional
12+
permission to link the program and your derivative works with the
13+
separately licensed software that they have included with MySQL.
14+
15+
This program is distributed in the hope that it will be useful,
16+
but WITHOUT ANY WARRANTY; without even the implied warranty of
17+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18+
GNU General Public License, version 2.0, for more details.
19+
20+
You should have received a copy of the GNU General Public License
21+
along with this program; if not, write to the Free Software
22+
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23+
*/
24+
#ifndef NDB_UTIL_SSL_SOCKET_TABLE_H
25+
#define NDB_UTIL_SSL_SOCKET_TABLE_H
26+
27+
/* The implementation uses a static-allocated table of size
28+
NDB_SSL_FIXED_TABLE_SIZE, plus a dynamic unordered map for
29+
descriptor values larger than NDB_SSL_FIXED_TABLE_SIZE.
30+
31+
In a data node, dynamic allocation is not allowed, but we have
32+
full understanding of the code base. NDB_SSL_FIXED_TABLE_SIZE
33+
must be big enough so that all lookups in the data node use the
34+
fixed table.
35+
36+
In an API node, dynamic allocation is okay, but the application-level
37+
code could require any arbitrary number of file descriptors. In this
38+
case the overflow table may be used.
39+
*/
40+
#define NDB_SSL_FIXED_TABLE_SIZE 4192
41+
42+
/* Enable or disable the socket table with 0 or 1 here */
43+
#define NDB_USE_SSL_SOCKET_TABLE 1
44+
45+
#if NDB_USE_SSL_SOCKET_TABLE
46+
47+
/* Set SSL for socket */
48+
void socket_table_set_ssl(socket_t, struct ssl_st *);
49+
50+
/* Clear stored SSL for socket. */
51+
void socket_table_clear_ssl(socket_t);
52+
53+
/* Get SSL associated with socket, or nullptr if none.
54+
If an SSL is found when expected is false, this will abort in debug builds.
55+
*/
56+
struct ssl_st * socket_table_get_ssl(socket_t, bool expected);
57+
58+
#else
59+
60+
/* Socket table is disabled */
61+
#define socket_table_set_ssl(A, B)
62+
#define socket_table_clear_ssl(A)
63+
#define socket_table_get_ssl(A, ...) nullptr
64+
65+
#endif /* NDB_USE_SSL_SOCKET_TABLE */
66+
67+
#endif /* NDB_UTIL_SSL_SOCKET_TABLE_H */

storage/ndb/src/common/util/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ ADD_CONVENIENCE_LIBRARY(ndbgeneral
6969
parse_mask.cpp
7070
random.cpp
7171
require.cpp
72+
ssl_socket_table.cpp
7273
socket_io.cpp
7374
uucode.cpp
7475
version.cpp

storage/ndb/src/common/util/NdbSocket.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ bool NdbSocket::associate(SSL * new_ssl)
127127
{
128128
if(ssl) return false; // already associated
129129
if(! SSL_set_fd(new_ssl, s.s)) return false;
130-
// if(! socket_table_set_ssl(s.s, new_ssl)) return false;
130+
socket_table_set_ssl(s.s, new_ssl);
131131
ssl = new_ssl;
132132
return true;
133133
}
@@ -186,7 +186,7 @@ bool NdbSocket::disable_locking() {
186186
void NdbSocket::ssl_close() {
187187
set_nonblocking(false); // set to blocking
188188
SSL_shutdown(ssl); // wait for close
189-
// socket_table_clear_ssl(s.s);
189+
socket_table_clear_ssl(s.s);
190190
SSL_free(ssl);
191191
}
192192

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
/*
2+
Copyright (c) 2022, Oracle and/or its affiliates.
3+
4+
This program is free software; you can redistribute it and/or modify
5+
it under the terms of the GNU General Public License, version 2.0,
6+
as published by the Free Software Foundation.
7+
8+
This program is also distributed with certain software (including
9+
but not limited to OpenSSL) that is licensed under separate terms,
10+
as designated in a particular file or component or in included license
11+
documentation. The authors of MySQL hereby grant you an additional
12+
permission to link the program and your derivative works with the
13+
separately licensed software that they have included with MySQL.
14+
15+
This program is distributed in the hope that it will be useful,
16+
but WITHOUT ANY WARRANTY; without even the implied warranty of
17+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18+
GNU General Public License, version 2.0, for more details.
19+
20+
You should have received a copy of the GNU General Public License
21+
along with this program; if not, write to the Free Software
22+
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23+
*/
24+
#include <assert.h>
25+
#include <mutex>
26+
#include <shared_mutex>
27+
#include <unordered_map>
28+
29+
#include "portlib/ndb_socket.h"
30+
#include "util/ssl_socket_table.h"
31+
32+
#if NDB_USE_SSL_SOCKET_TABLE
33+
34+
static constexpr int ssl_table_size = NDB_SSL_FIXED_TABLE_SIZE;
35+
36+
static struct ssl_st * ssl_table[ssl_table_size];
37+
std::unordered_map<uint, struct ssl_st *> ssl_overflow_table;
38+
std::shared_mutex ssl_table_mutex; // a read/write lock
39+
40+
41+
/* A Windows socket_t is an offset into a table of 32-bit values. So, the first
42+
Unix fds are 0, 1, and 2; but the first Win32 fds are 0x0, 0x4, 0x8.
43+
44+
You could right-shift the Win32 fd two places (to divide it by four) in
45+
calculating the index. This would make more efficient use of the space in
46+
the fixed table, but with the overflow table available it is not strictly
47+
necessary.
48+
*/
49+
#define socket_to_index(s) (uint)s
50+
51+
52+
void socket_table_set_ssl(socket_t s, struct ssl_st *ssl)
53+
{
54+
assert(s != INVALID_SOCKET);
55+
uint fd = socket_to_index(s);
56+
57+
std::unique_lock write_lock(ssl_table_mutex); // unique lock
58+
59+
if(fd < ssl_table_size)
60+
{
61+
assert(ssl_table[fd] == nullptr);
62+
ssl_table[fd] = ssl;
63+
}
64+
else
65+
{
66+
assert(ssl_overflow_table.count(fd) == 0);
67+
ssl_overflow_table[fd] = ssl;
68+
}
69+
}
70+
71+
void socket_table_clear_ssl(socket_t s)
72+
{
73+
assert(s != INVALID_SOCKET);
74+
uint fd = socket_to_index(s);
75+
76+
std::unique_lock write_lock(ssl_table_mutex); // unique lock
77+
78+
if(fd < ssl_table_size)
79+
{
80+
assert(ssl_table[fd]);
81+
ssl_table[fd] = nullptr;
82+
}
83+
else
84+
{
85+
assert(ssl_overflow_table.count(fd) == 1);
86+
ssl_overflow_table.erase(fd);
87+
}
88+
}
89+
90+
struct ssl_st * socket_table_get_ssl(socket_t s, bool expected [[maybe_unused]])
91+
{
92+
struct ssl_st * ptr = nullptr;
93+
if(s == INVALID_SOCKET) return ptr;
94+
uint fd = socket_to_index(s);
95+
96+
std::shared_lock read_lock(ssl_table_mutex); // shared lock
97+
98+
if(fd < ssl_table_size)
99+
{
100+
ptr = ssl_table[fd];
101+
}
102+
else
103+
{
104+
auto it = ssl_overflow_table.find(fd);
105+
if(it != ssl_overflow_table.end()) ptr = it->second;
106+
}
107+
108+
assert(expected || ! ptr);
109+
return ptr;
110+
}
111+
112+
#endif /* NDB_USE_SSL_SOCKET_TABLE */

0 commit comments

Comments
 (0)