Commit cc057fb3 authored by Michael Paquier's avatar Michael Paquier

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
parent b33283cb
...@@ -458,37 +458,10 @@ ECPGconnect(int lineno, int c, const char *name, const char *user, const char *p ...@@ -458,37 +458,10 @@ ECPGconnect(int lineno, int c, const char *name, const char *user, const char *p
else else
realname = NULL; realname = NULL;
/* add connection to our list */ /*
#ifdef ENABLE_THREAD_SAFETY * Count options for the allocation done below (this may produce an
pthread_mutex_lock(&connections_mutex); * overestimate, it's ok).
#endif */
if (connection_name != NULL)
this->name = ecpg_strdup(connection_name, lineno);
else
this->name = ecpg_strdup(realname, lineno);
this->cache_head = NULL;
this->prep_stmts = NULL;
if (all_connections == NULL)
this->next = NULL;
else
this->next = all_connections;
all_connections = this;
#ifdef ENABLE_THREAD_SAFETY
pthread_setspecific(actual_connection_key, all_connections);
#endif
actual_connection = all_connections;
ecpg_log("ECPGconnect: opening database %s on %s port %s %s%s %s%s\n",
realname ? realname : "<DEFAULT>",
host ? host : "<DEFAULT>",
port ? (ecpg_internal_regression_mode ? "<REGRESSION_PORT>" : port) : "<DEFAULT>",
options ? "with options " : "", options ? options : "",
(user && strlen(user) > 0) ? "for user " : "", user ? user : "");
/* count options (this may produce an overestimate, it's ok) */
if (options) if (options)
for (i = 0; options[i]; i++) for (i = 0; options[i]; i++)
if (options[i] == '=') if (options[i] == '=')
...@@ -499,7 +472,11 @@ ECPGconnect(int lineno, int c, const char *name, const char *user, const char *p ...@@ -499,7 +472,11 @@ ECPGconnect(int lineno, int c, const char *name, const char *user, const char *p
if (passwd && strlen(passwd) > 0) if (passwd && strlen(passwd) > 0)
connect_params++; connect_params++;
/* allocate enough space for all connection parameters */ /*
* Allocate enough space for all connection parameters. These allocations
* are done before manipulating the list of connections to ease the error
* handling on failure.
*/
conn_keywords = (const char **) ecpg_alloc((connect_params + 1) * sizeof(char *), lineno); conn_keywords = (const char **) ecpg_alloc((connect_params + 1) * sizeof(char *), lineno);
conn_values = (const char **) ecpg_alloc(connect_params * sizeof(char *), lineno); conn_values = (const char **) ecpg_alloc(connect_params * sizeof(char *), lineno);
if (conn_keywords == NULL || conn_values == NULL) 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 ...@@ -522,6 +499,36 @@ ECPGconnect(int lineno, int c, const char *name, const char *user, const char *p
return false; return false;
} }
/* add connection to our list */
#ifdef ENABLE_THREAD_SAFETY
pthread_mutex_lock(&connections_mutex);
#endif
if (connection_name != NULL)
this->name = ecpg_strdup(connection_name, lineno);
else
this->name = ecpg_strdup(realname, lineno);
this->cache_head = NULL;
this->prep_stmts = NULL;
if (all_connections == NULL)
this->next = NULL;
else
this->next = all_connections;
all_connections = this;
#ifdef ENABLE_THREAD_SAFETY
pthread_setspecific(actual_connection_key, all_connections);
#endif
actual_connection = all_connections;
ecpg_log("ECPGconnect: opening database %s on %s port %s %s%s %s%s\n",
realname ? realname : "<DEFAULT>",
host ? host : "<DEFAULT>",
port ? (ecpg_internal_regression_mode ? "<REGRESSION_PORT>" : port) : "<DEFAULT>",
options ? "with options " : "", options ? options : "",
(user && strlen(user) > 0) ? "for user " : "", user ? user : "");
i = 0; i = 0;
if (realname) if (realname)
{ {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment