Skip to content

Commit 254913f

Browse files
committed
Bug #27434254: MASTER : MEMORY LEAK WHILE CONNECTION WITH WRONG SSL-CA ATTEMPTED.
More fixes in the Session_builder logic to make sure that dynamically allocated objects are deleted if something goes wrong before the final owner receives them.
1 parent 1cb2b86 commit 254913f

File tree

2 files changed

+108
-26
lines changed

2 files changed

+108
-26
lines changed

cdk/core/session.cc

Lines changed: 102 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -75,36 +75,57 @@ struct Session_builder
7575
3. If a bail-out error was detected, throws that error.
7676
*/
7777

78-
template <class Conn>
79-
Conn* connect(Conn*);
80-
8178
bool operator() (const ds::TCPIP &ds, const ds::TCPIP::Options &options);
8279
#ifndef WIN32
8380
bool operator() (const ds::Unix_socket&ds, const ds::Unix_socket::Options &options);
8481
#endif
8582
bool operator() (const ds::TCPIP_old &ds, const ds::TCPIP_old::Options &options);
8683

84+
/*
85+
Make a connection attempt using the given connection object. Returns true if
86+
connection was established, otherwise returns false or throws error
87+
depending on the value of m_throw_errors;
88+
*/
89+
90+
template <class Conn>
91+
bool connect(Conn&);
92+
8793
#ifdef WITH_SSL
88-
TLS* tls_connect(Socket_base &conn, const TLS::Options &opt);
94+
95+
/*
96+
Given plain connection `conn` (which should be a dynamically allocated
97+
object) create a TLS connection over it, if possible.
98+
99+
If a TLS object is successfully created, it takes the ownership of the
100+
plain connection object passed to this call. The caller of this method
101+
should take responsibility for the returned TLS object in that case.
102+
103+
If this method throws error, then it deletes the plain connection object
104+
first, so that the caller does not need to bother about it. But if this
105+
method returns NULL, then the ownership of the plain connection
106+
object stays with the caller who is responsible of deleting it.
107+
108+
Returns NULL if TLS connections are not supported. Throws errors in case
109+
of other problems.
110+
*/
111+
112+
TLS* tls_connect(Socket_base *conn, const TLS::Options &opt);
89113
#endif
90114
};
91115

92116

93117
template <class Conn>
94-
Conn* Session_builder::connect(Conn* connection)
118+
bool Session_builder::connect(Conn &connection)
95119
{
96120
m_attempts++;
97121

98-
assert(connection);
99-
100122
try
101123
{
102-
connection->connect();
124+
connection.connect();
125+
return true;
103126
}
104127
catch (...)
105128
{
106-
delete connection;
107-
108129
// Use rethrow_error() to wrap arbitrary exception in cdk::Error.
109130

110131
try {
@@ -123,9 +144,8 @@ Conn* Session_builder::connect(Conn* connection)
123144
m_error.reset(err.clone());
124145
}
125146

126-
return NULL;
147+
return false;
127148
}
128-
return connection;
129149
}
130150

131151

@@ -138,23 +158,56 @@ Session_builder::operator() (
138158
using foundation::connection::TCPIP;
139159
using foundation::connection::Socket_base;
140160

141-
TCPIP* connection = connect(new TCPIP(ds.host(), ds.port()));
161+
unique_ptr<TCPIP> connection(new TCPIP(ds.host(), ds.port()));
142162

143-
if (!connection)
163+
if (!connect(*connection))
144164
return false; // continue to next host if available
145165

146166
#ifdef WITH_SSL
147-
TLS *tls_conn = tls_connect(*connection, options.get_tls());
167+
168+
/*
169+
Note: We must be careful to release the unique_ptr<> if tls_connect()
170+
throws error or returns a TLS object because in that case the plain
171+
connection object is either deleted by tls_connect() or its ownership
172+
is passed to the returned TLS object. However, if tls_connect() returns
173+
NULL, we are still responsible for the plain connection object.
174+
*/
175+
176+
TLS * tls_conn = nullptr;
177+
try {
178+
tls_conn = tls_connect(connection.get(), options.get_tls());
179+
}
180+
catch (...)
181+
{
182+
connection.release();
183+
throw;
184+
}
185+
148186
if (tls_conn)
149187
{
188+
// Now tls_conn owns the plain connection.
189+
connection.release();
190+
191+
/*
192+
Note: Order is important! We need to take ownership of tls_conn before
193+
calling mysqlx::Session() ctor which might throw errors.
194+
*/
195+
150196
m_conn = tls_conn;
151197
m_sess = new mysqlx::Session(*tls_conn, options);
152198
}
153199
else
154200
#endif
155201
{
156-
m_conn = connection;
202+
/*
203+
Note: Order is important! We must construct mysqlx::Session using
204+
connection object of type TCPIP and we must do it before
205+
connection.release() is called. If session ctor fails, the unique_ptr<>
206+
will still take care of deleting the connection object.
207+
*/
208+
157209
m_sess = new mysqlx::Session(*connection, options);
210+
m_conn = connection.release();
158211
}
159212

160213
m_database = options.database();
@@ -171,13 +224,13 @@ Session_builder::operator() (
171224
using foundation::connection::Unix_socket;
172225
using foundation::connection::Socket_base;
173226

174-
Unix_socket* connection = connect(new Unix_socket(ds.path()));
227+
unique_ptr<Unix_socket> connection(new Unix_socket(ds.path()));
175228

176-
if (!connection)
229+
if (!connect(*connection))
177230
return false; // continue to next host if available
178231

179-
m_conn = connection;
180232
m_sess = new mysqlx::Session(*connection, options);
233+
m_conn = connection.release();
181234

182235
m_database = options.database();
183236

@@ -201,8 +254,17 @@ Session_builder::operator() (
201254
#ifdef WITH_SSL
202255

203256
Session_builder::TLS*
204-
Session_builder::tls_connect(Socket_base &connection, const TLS::Options &options)
257+
Session_builder::tls_connect(Socket_base *connection, const TLS::Options &options)
205258
{
259+
/*
260+
Note: In case of throwing any errors, the connection object should be
261+
deleted - this is taken care of by the unique_ptr<>. However, if returning
262+
NULL, the ownership stays with the caller of this method and hence we must
263+
release the unique_ptr<>.
264+
*/
265+
266+
unique_ptr<Socket_base> conn_ptr(connection);
267+
206268
if (!options.get_ca().empty() &&
207269
options.ssl_mode() < TLS::Options::SSL_MODE::VERIFY_CA)
208270
throw Error(cdkerrc::generic_error,
@@ -214,11 +276,16 @@ Session_builder::tls_connect(Socket_base &connection, const TLS::Options &option
214276
"Missing ssl-ca option to verify CA");
215277

216278
if (options.ssl_mode() == TLS::Options::SSL_MODE::DISABLED)
279+
{
280+
conn_ptr.release();
217281
return NULL;
282+
}
283+
284+
assert(connection);
218285

219286
// Negotiate TLS capabilities.
220287

221-
cdk::protocol::mysqlx::Protocol proto(connection);
288+
cdk::protocol::mysqlx::Protocol proto(*connection);
222289

223290
struct : cdk::protocol::mysqlx::api::Any::Document
224291
{
@@ -261,21 +328,30 @@ Session_builder::tls_connect(Socket_base &connection, const TLS::Options &option
261328
proto.rcv_Reply(prc).wait();
262329

263330
if (!prc.m_tls)
331+
{
332+
conn_ptr.release();
264333
return NULL;
334+
}
265335

266336
/*
267337
Capabilites OK, create TLS connection now.
268338
269-
Note: The TLS object is protected by unique_ptr<> for the case that
270-
connection attempt below throws an error - in that case the newly created
271-
object should be deleted.
339+
Note: The TLS object created here takes ownership of the plain connection
340+
object passed to this method. The TLS object itself is protected by a
341+
unique_ptr<> for the case that connection attempt below throws an error
342+
- in that case it will be deleted before leaving this method.
272343
*/
273344

274-
unique_ptr<TLS> tls_conn(new TLS(&connection, options));
345+
unique_ptr<TLS> tls_conn(new TLS(conn_ptr.release(), options));
275346

276-
// TODO: attempt failover if TLS-layer reports network error?
347+
// TODO: attempt fail-over if TLS-layer reports network error?
277348
tls_conn->connect();
278349

350+
/*
351+
Finally we pass the ownership of the created TLS object to the caller
352+
of this method.
353+
*/
354+
279355
return tls_conn.release();
280356
}
281357

cdk/foundation/connection_openssl.cc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,12 @@ class connection_TLS_impl
128128
: public ::cdk::foundation::connection::Socket_base::Impl
129129
{
130130
public:
131+
132+
/*
133+
Note: Once created, the TLS object takes ownership of the plain tcpip
134+
connection object (which is assumed to be dynamically allocated).
135+
*/
136+
131137
connection_TLS_impl(cdk::foundation::connection::Socket_base* tcpip,
132138
cdk::foundation::connection::TLS::Options options)
133139
: m_tcpip(tcpip)

0 commit comments

Comments
 (0)