Skip to content

Commit fa703b3

Browse files
committed
Fix error handling with threads on OOM in ECPG connection logic
An out-of-memory failure happening when allocating the structures to store the connection parameter keywords and values would mess up with the set of connections saved, as on failure the pthread mutex would still be hold with the new connection object listed but free()'d. Rather than just unlocking the mutex, which would leave the static list of connections into an inconsistent state, move the allocation for the structures of the connection parameters before beginning the test manipulation. This ensures that the list of connections and the connection mutex remain consistent all the time in this code path. This error is unlikely going to happen, but this could mess up badly with ECPG clients in surprising ways, so backpatch all the way down. Reported-by: ryancaicse Discussion: https://postgr.es/m/17186-b4cfd8f0eb4d1dee@postgresql.org Backpatch-through: 9.6
1 parent fee1040 commit fa703b3

File tree

1 file changed

+39
-32
lines changed

1 file changed

+39
-32
lines changed

src/interfaces/ecpg/ecpglib/connect.c

Lines changed: 39 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -458,37 +458,10 @@ ECPGconnect(int lineno, int c, const char *name, const char *user, const char *p
458458
else
459459
realname = NULL;
460460

461-
/* add connection to our list */
462-
#ifdef ENABLE_THREAD_SAFETY
463-
pthread_mutex_lock(&connections_mutex);
464-
#endif
465-
if (connection_name != NULL)
466-
this->name = ecpg_strdup(connection_name, lineno);
467-
else
468-
this->name = ecpg_strdup(realname, lineno);
469-
470-
this->cache_head = NULL;
471-
this->prep_stmts = NULL;
472-
473-
if (all_connections == NULL)
474-
this->next = NULL;
475-
else
476-
this->next = all_connections;
477-
478-
all_connections = this;
479-
#ifdef ENABLE_THREAD_SAFETY
480-
pthread_setspecific(actual_connection_key, all_connections);
481-
#endif
482-
actual_connection = all_connections;
483-
484-
ecpg_log("ECPGconnect: opening database %s on %s port %s %s%s %s%s\n",
485-
realname ? realname : "<DEFAULT>",
486-
host ? host : "<DEFAULT>",
487-
port ? (ecpg_internal_regression_mode ? "<REGRESSION_PORT>" : port) : "<DEFAULT>",
488-
options ? "with options " : "", options ? options : "",
489-
(user && strlen(user) > 0) ? "for user " : "", user ? user : "");
490-
491-
/* count options (this may produce an overestimate, it's ok) */
461+
/*
462+
* Count options for the allocation done below (this may produce an
463+
* overestimate, it's ok).
464+
*/
492465
if (options)
493466
for (i = 0; options[i]; i++)
494467
if (options[i] == '=')
@@ -499,7 +472,11 @@ ECPGconnect(int lineno, int c, const char *name, const char *user, const char *p
499472
if (passwd && strlen(passwd) > 0)
500473
connect_params++;
501474

502-
/* allocate enough space for all connection parameters */
475+
/*
476+
* Allocate enough space for all connection parameters. These allocations
477+
* are done before manipulating the list of connections to ease the error
478+
* handling on failure.
479+
*/
503480
conn_keywords = (const char **) ecpg_alloc((connect_params + 1) * sizeof(char *), lineno);
504481
conn_values = (const char **) ecpg_alloc(connect_params * sizeof(char *), lineno);
505482
if (conn_keywords == NULL || conn_values == NULL)
@@ -522,6 +499,36 @@ ECPGconnect(int lineno, int c, const char *name, const char *user, const char *p
522499
return false;
523500
}
524501

502+
/* add connection to our list */
503+
#ifdef ENABLE_THREAD_SAFETY
504+
pthread_mutex_lock(&connections_mutex);
505+
#endif
506+
if (connection_name != NULL)
507+
this->name = ecpg_strdup(connection_name, lineno);
508+
else
509+
this->name = ecpg_strdup(realname, lineno);
510+
511+
this->cache_head = NULL;
512+
this->prep_stmts = NULL;
513+
514+
if (all_connections == NULL)
515+
this->next = NULL;
516+
else
517+
this->next = all_connections;
518+
519+
all_connections = this;
520+
#ifdef ENABLE_THREAD_SAFETY
521+
pthread_setspecific(actual_connection_key, all_connections);
522+
#endif
523+
actual_connection = all_connections;
524+
525+
ecpg_log("ECPGconnect: opening database %s on %s port %s %s%s %s%s\n",
526+
realname ? realname : "<DEFAULT>",
527+
host ? host : "<DEFAULT>",
528+
port ? (ecpg_internal_regression_mode ? "<REGRESSION_PORT>" : port) : "<DEFAULT>",
529+
options ? "with options " : "", options ? options : "",
530+
(user && strlen(user) > 0) ? "for user " : "", user ? user : "");
531+
525532
i = 0;
526533
if (realname)
527534
{

0 commit comments

Comments
 (0)