On 11/24/2014 06:05 PM, Alex Shulgin wrote:
The first patch is not on topic, I just spotted this missing check.
*** a/src/interfaces/libpq/fe-connect.c --- b/src/interfaces/libpq/fe-connect.c *************** conninfo_array_parse(const char *const * *** 4402,4407 **** --- 4402,4415 ---- if (options[k].val) free(options[k].val); options[k].val = strdup(str_option->val); + if (!options[k].val) + { + printfPQExpBuffer(errorMessage, + libpq_gettext("out of memory\n")); + PQconninfoFree(options); + PQconninfoFree(dbname_options); + return NULL; + } break; } }
Oh. There are actually many more places in connection option parsing that don't check the return value of strdup(). The one in fillPGConn even has an XXX comment saying it probably should check it. You can get quite strange behavior if one of them fails. If for example the strdup() on dbname fails, you might end up connecting to different database than intended. And if the "conn->sslmode = strdup(DefaultSSLMode);" call in connectOptions2 fails, you'll get a segfault later because at least connectDBstart assumes that sslmode is not NULL.
I think we need to fix all of those, and backpatch. Per attached. - Heikki
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c index 3fe8c21..d4d8c3b 100644 --- a/src/interfaces/libpq/fe-connect.c +++ b/src/interfaces/libpq/fe-connect.c @@ -333,7 +333,7 @@ static int connectDBStart(PGconn *conn); static int connectDBComplete(PGconn *conn); static PGPing internal_ping(PGconn *conn); static PGconn *makeEmptyPGconn(void); -static void fillPGconn(PGconn *conn, PQconninfoOption *connOptions); +static bool fillPGconn(PGconn *conn, PQconninfoOption *connOptions); static void freePGconn(PGconn *conn); static void closePGconn(PGconn *conn); static PQconninfoOption *conninfo_init(PQExpBuffer errorMessage); @@ -585,7 +585,11 @@ PQconnectStartParams(const char *const * keywords, /* * Move option values into conn structure */ - fillPGconn(conn, connOptions); + if (!fillPGconn(conn, connOptions)) + { + PQconninfoFree(connOptions); + return conn; + } /* * Free the option info - all is in conn now @@ -665,19 +669,19 @@ PQconnectStart(const char *conninfo) return conn; } -static void +/* + * Move option values into conn structure + * + * Don't put anything cute here --- intelligence should be in + * connectOptions2 ... + * + * Returns true on success. On failure, returns false and sets error message. + */ +static bool fillPGconn(PGconn *conn, PQconninfoOption *connOptions) { const internalPQconninfoOption *option; - /* - * Move option values into conn structure - * - * Don't put anything cute here --- intelligence should be in - * connectOptions2 ... - * - * XXX: probably worth checking strdup() return value here... - */ for (option = PQconninfoOptions; option->keyword; option++) { const char *tmp = conninfo_getval(connOptions, option->keyword); @@ -688,9 +692,22 @@ fillPGconn(PGconn *conn, PQconninfoOption *connOptions) if (*connmember) free(*connmember); - *connmember = tmp ? strdup(tmp) : NULL; + if (tmp) + { + *connmember = strdup(tmp); + if (*connmember == NULL) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("out of memory\n")); + return false; + } + } + else + *connmember = NULL; } } + + return true; } /* @@ -723,7 +740,12 @@ connectOptions1(PGconn *conn, const char *conninfo) /* * Move option values into conn structure */ - fillPGconn(conn, connOptions); + if (!fillPGconn(conn, connOptions)) + { + conn->status = CONNECTION_BAD; + PQconninfoFree(connOptions); + return false; + } /* * Free the option info - all is in conn now @@ -753,6 +775,8 @@ connectOptions2(PGconn *conn) if (conn->dbName) free(conn->dbName); conn->dbName = strdup(conn->pguser); + if (!conn->dbName) + goto oom_error; } /* @@ -765,7 +789,12 @@ connectOptions2(PGconn *conn) conn->pgpass = PasswordFromFile(conn->pghost, conn->pgport, conn->dbName, conn->pguser); if (conn->pgpass == NULL) + { conn->pgpass = strdup(DefaultPassword); + if (!conn->pgpass) + goto oom_error; + + } else conn->dot_pgpass_used = true; } @@ -823,7 +852,11 @@ connectOptions2(PGconn *conn) #endif } else + { conn->sslmode = strdup(DefaultSSLMode); + if (!conn->sslmode) + goto oom_error; + } /* * Resolve special "auto" client_encoding from the locale @@ -833,6 +866,8 @@ connectOptions2(PGconn *conn) { free(conn->client_encoding_initial); conn->client_encoding_initial = strdup(pg_encoding_to_char(pg_get_encoding_from_locale(NULL, true))); + if (!conn->client_encoding_initial) + goto oom_error; } /* @@ -843,6 +878,13 @@ connectOptions2(PGconn *conn) conn->options_valid = true; return true; + +oom_error: + conn->options_valid = false; + conn->status = CONNECTION_BAD; + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("out of memory\n")); + return false; } /* @@ -937,6 +979,8 @@ PQsetdbLogin(const char *pghost, const char *pgport, const char *pgoptions, if (conn->dbName) free(conn->dbName); conn->dbName = strdup(dbName); + if (!conn->dbName) + goto oom_error; } } @@ -949,6 +993,8 @@ PQsetdbLogin(const char *pghost, const char *pgport, const char *pgoptions, if (conn->pghost) free(conn->pghost); conn->pghost = strdup(pghost); + if (!conn->pghost) + goto oom_error; } if (pgport && pgport[0] != '\0') @@ -956,6 +1002,8 @@ PQsetdbLogin(const char *pghost, const char *pgport, const char *pgoptions, if (conn->pgport) free(conn->pgport); conn->pgport = strdup(pgport); + if (!conn->pgport) + goto oom_error; } if (pgoptions && pgoptions[0] != '\0') @@ -963,6 +1011,8 @@ PQsetdbLogin(const char *pghost, const char *pgport, const char *pgoptions, if (conn->pgoptions) free(conn->pgoptions); conn->pgoptions = strdup(pgoptions); + if (!conn->pgoptions) + goto oom_error; } if (pgtty && pgtty[0] != '\0') @@ -970,6 +1020,8 @@ PQsetdbLogin(const char *pghost, const char *pgport, const char *pgoptions, if (conn->pgtty) free(conn->pgtty); conn->pgtty = strdup(pgtty); + if (!conn->pgtty) + goto oom_error; } if (login && login[0] != '\0') @@ -977,6 +1029,8 @@ PQsetdbLogin(const char *pghost, const char *pgport, const char *pgoptions, if (conn->pguser) free(conn->pguser); conn->pguser = strdup(login); + if (!conn->pguser) + goto oom_error; } if (pwd && pwd[0] != '\0') @@ -984,6 +1038,8 @@ PQsetdbLogin(const char *pghost, const char *pgport, const char *pgoptions, if (conn->pgpass) free(conn->pgpass); conn->pgpass = strdup(pwd); + if (!conn->pgpass) + goto oom_error; } /* @@ -999,6 +1055,12 @@ PQsetdbLogin(const char *pghost, const char *pgport, const char *pgoptions, (void) connectDBComplete(conn); return conn; + +oom_error: + conn->status = CONNECTION_BAD; + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("out of memory\n")); + return false; } @@ -3760,7 +3822,16 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options, if (strcmp(options[i].keyword, optname) == 0) { if (options[i].val == NULL) + { options[i].val = strdup(optval); + if (!options[i].val) + { + printfPQExpBuffer(errorMessage, + libpq_gettext("out of memory\n")); + free(result); + return 3; + } + } found_keyword = true; break; } @@ -3983,6 +4054,13 @@ parseServiceFile(const char *serviceFile, { if (options[i].val == NULL) options[i].val = strdup(val); + if (!options[i].val) + { + printfPQExpBuffer(errorMessage, + libpq_gettext("out of memory\n")); + fclose(f); + return 3; + } found_keyword = true; break; } @@ -4402,6 +4480,14 @@ conninfo_array_parse(const char *const * keywords, const char *const * values, if (options[k].val) free(options[k].val); options[k].val = strdup(str_option->val); + if (!options[k].val) + { + printfPQExpBuffer(errorMessage, + libpq_gettext("out of memory\n")); + PQconninfoFree(options); + PQconninfoFree(dbname_options); + return NULL; + } break; } } @@ -4596,20 +4682,22 @@ conninfo_uri_parse_options(PQconninfoOption *options, const char *uri, { int prefix_len; char *p; - char *buf = strdup(uri); /* need a modifiable copy of the input - * URI */ - char *start = buf; + char *buf; + char *start; char prevchar = '\0'; char *user = NULL; char *host = NULL; bool retval = false; + /* need a modifiable copy of the input URI */ + buf = strdup(uri); if (buf == NULL) { printfPQExpBuffer(errorMessage, libpq_gettext("out of memory\n")); return false; } + start = buf; /* Skip the URI prefix */ prefix_len = uri_prefix_length(uri); @@ -4951,15 +5039,17 @@ conninfo_uri_parse_params(char *params, static char * conninfo_uri_decode(const char *str, PQExpBuffer errorMessage) { - char *buf = malloc(strlen(str) + 1); - char *p = buf; + char *buf; + char *p; const char *q = str; + buf = malloc(strlen(str) + 1); if (buf == NULL) { printfPQExpBuffer(errorMessage, libpq_gettext("out of memory\n")); return NULL; } + p = buf; for (;;) { @@ -5104,7 +5194,6 @@ conninfo_storeval(PQconninfoOption *connOptions, else { value_copy = strdup(value); - if (value_copy == NULL) { printfPQExpBuffer(errorMessage, libpq_gettext("out of memory\n")); @@ -5672,6 +5761,12 @@ PasswordFromFile(char *hostname, char *port, char *dbname, char *username) ret = strdup(t); fclose(fp); + if (!ret) + { + /* Out of memory. XXX: an error message would be nice. */ + return NULL; + } + /* De-escape password. */ for (p1 = p2 = ret; *p1 != ':' && *p1 != '\0'; ++p1, ++p2) {
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers