Skip to content

Commit f365d0e

Browse files
committed
Fix mysqlnd memory leak
The actual leak is observed in ext/pdo_mysql/tests/bug_74376.phpt. The persistent connection leaks because a refcount decrement on a result is missed. The refcount decrement is missed because free_result_contents is used, rather than free_result. Looking at other uses of free_result_contents, it looks like they could also suffer from this problem. Apart from one case, free_result_contents is always used to release the result entirely (I've adjusted the one differing case to only free meta), so I'm moving most of the logic from free_result into free_result_contents. The only difference is now that free_result will skip_result first.
1 parent a78adce commit f365d0e

File tree

2 files changed

+10
-12
lines changed

2 files changed

+10
-12
lines changed

ext/mysqlnd/mysqlnd_ps.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,6 @@ MYSQLND_METHOD(mysqlnd_stmt, store_result)(MYSQLND_STMT * const s)
122122
} else {
123123
COPY_CLIENT_ERROR(conn->error_info, result->stored_data->error_info);
124124
stmt->result->m.free_result_contents(stmt->result);
125-
mysqlnd_mempool_destroy(stmt->result->memory_pool);
126125
stmt->result = NULL;
127126
stmt->state = MYSQLND_STMT_PREPARED;
128127
}
@@ -341,7 +340,6 @@ mysqlnd_stmt_prepare_read_eof(MYSQLND_STMT * s)
341340
if (FAIL == (ret = PACKET_READ(conn, &fields_eof))) {
342341
if (stmt->result) {
343342
stmt->result->m.free_result_contents(stmt->result);
344-
mnd_efree(stmt->result);
345343
/* XXX: This will crash, because we will null also the methods.
346344
But seems it happens in extreme cases or doesn't. Should be fixed by exporting a function
347345
(from mysqlnd_driver.c?) to do the reset.

ext/mysqlnd/mysqlnd_result.c

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,13 @@ void MYSQLND_METHOD(mysqlnd_res, free_result_contents_internal)(MYSQLND_RES * re
302302

303303
result->m.free_result_buffers(result);
304304

305+
if (result->conn) {
306+
result->conn->m->free_reference(result->conn);
307+
result->conn = NULL;
308+
}
309+
310+
mysqlnd_mempool_destroy(result->memory_pool);
311+
305312
DBG_VOID_RETURN;
306313
}
307314
/* }}} */
@@ -312,17 +319,10 @@ static
312319
void MYSQLND_METHOD(mysqlnd_res, free_result_internal)(MYSQLND_RES * result)
313320
{
314321
DBG_ENTER("mysqlnd_res::free_result_internal");
315-
result->m.skip_result(result);
316322

323+
result->m.skip_result(result);
317324
result->m.free_result_contents(result);
318325

319-
if (result->conn) {
320-
result->conn->m->free_reference(result->conn);
321-
result->conn = NULL;
322-
}
323-
324-
mysqlnd_mempool_destroy(result->memory_pool);
325-
326326
DBG_VOID_RETURN;
327327
}
328328
/* }}} */
@@ -355,7 +355,8 @@ MYSQLND_METHOD(mysqlnd_res, read_result_metadata)(MYSQLND_RES * result, MYSQLND_
355355

356356
/* It's safe to reread without freeing */
357357
if (FAIL == result->meta->m->read_metadata(result->meta, conn, result)) {
358-
result->m.free_result_contents(result);
358+
result->meta->m->free_metadata(result->meta);
359+
result->meta = NULL;
359360
DBG_RETURN(FAIL);
360361
}
361362
/* COM_FIELD_LIST is broken and has premature EOF, thus we need to hack here and in mysqlnd_res_meta.c */
@@ -517,7 +518,6 @@ mysqlnd_query_read_result_set_header(MYSQLND_CONN_DATA * conn, MYSQLND_STMT * s)
517518
if (FAIL == (ret = PACKET_READ(conn, &fields_eof))) {
518519
DBG_ERR("Error occurred while reading the EOF packet");
519520
result->m.free_result_contents(result);
520-
mysqlnd_mempool_destroy(result->memory_pool);
521521
if (!stmt) {
522522
conn->current_result = NULL;
523523
} else {

0 commit comments

Comments
 (0)