From 689d99306a1838728075928fcc61ca3ffc112384 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Tue, 2 Oct 2012 17:31:49 -0400 Subject: [PATCH] Work around unportable behavior of malloc(0) and realloc(NULL, 0). On some platforms these functions return NULL, rather than the more common practice of returning a pointer to a zero-sized block of memory. Hack our various wrapper functions to hide the difference by substituting a size request of 1. This is probably not so important for the callers, who should never touch the block anyway if they asked for size 0 --- but it's important for the wrapper functions themselves, which mistakenly treated the NULL result as an out-of-memory failure. This broke at least pg_dump for the case of no user-defined aggregates, as per report from Matthew Carrington. Back-patch to 9.2 to fix the pg_dump issue. Given the lack of previous complaints, it seems likely that there is no live bug in previous releases, even though some of these functions were in place before that. --- contrib/oid2name/oid2name.c | 80 ++++++++++++++++++------------ contrib/pg_upgrade/util.c | 24 +++++---- contrib/pgbench/pgbench.c | 6 +++ src/backend/utils/misc/guc.c | 6 +++ src/bin/initdb/initdb.c | 3 ++ src/bin/pg_basebackup/streamutil.c | 3 ++ src/bin/pg_ctl/pg_ctl.c | 3 ++ src/bin/pg_dump/dumpmem.c | 6 +++ src/bin/psql/common.c | 3 ++ src/bin/psql/print.c | 3 ++ src/port/dirmod.c | 12 ++++- 11 files changed, 106 insertions(+), 43 deletions(-) diff --git a/contrib/oid2name/oid2name.c b/contrib/oid2name/oid2name.c index e8a389e49e7..74e60f23fba 100644 --- a/contrib/oid2name/oid2name.c +++ b/contrib/oid2name/oid2name.c @@ -49,8 +49,9 @@ struct options /* function prototypes */ static void help(const char *progname); void get_opts(int, char **, struct options *); -void *myalloc(size_t size); -char *mystrdup(const char *str); +void *pg_malloc(size_t size); +void *pg_realloc(void *ptr, size_t size); +char *pg_strdup(const char *str); void add_one_elt(char *eltname, eary *eary); char *get_comma_elts(eary *eary); PGconn *sql_conn(struct options *); @@ -102,7 +103,7 @@ get_opts(int argc, char **argv, struct options * my_opts) { /* specify the database */ case 'd': - my_opts->dbname = mystrdup(optarg); + my_opts->dbname = pg_strdup(optarg); break; /* specify one tablename to show */ @@ -127,17 +128,17 @@ get_opts(int argc, char **argv, struct options * my_opts) /* host to connect to */ case 'H': - my_opts->hostname = mystrdup(optarg); + my_opts->hostname = pg_strdup(optarg); break; /* port to connect to on remote host */ case 'p': - my_opts->port = mystrdup(optarg); + my_opts->port = pg_strdup(optarg); break; /* username */ case 'U': - my_opts->username = mystrdup(optarg); + my_opts->username = pg_strdup(optarg); break; /* display system tables */ @@ -199,26 +200,47 @@ help(const char *progname) } void * -myalloc(size_t size) +pg_malloc(size_t size) { - void *ptr = malloc(size); + void *ptr; + /* Avoid unportable behavior of malloc(0) */ + if (size == 0) + size = 1; + ptr = malloc(size); if (!ptr) { - fprintf(stderr, "out of memory"); + fprintf(stderr, "out of memory\n"); exit(1); } return ptr; } +void * +pg_realloc(void *ptr, size_t size) +{ + void *result; + + /* Avoid unportable behavior of realloc(NULL, 0) */ + if (ptr == NULL && size == 0) + size = 1; + result = realloc(ptr, size); + if (!result) + { + fprintf(stderr, "out of memory\n"); + exit(1); + } + return result; +} + char * -mystrdup(const char *str) +pg_strdup(const char *str) { char *result = strdup(str); if (!result) { - fprintf(stderr, "out of memory"); + fprintf(stderr, "out of memory\n"); exit(1); } return result; @@ -235,22 +257,16 @@ add_one_elt(char *eltname, eary *eary) if (eary->alloc == 0) { eary ->alloc = 8; - eary ->array = (char **) myalloc(8 * sizeof(char *)); + eary ->array = (char **) pg_malloc(8 * sizeof(char *)); } else if (eary->num >= eary->alloc) { eary ->alloc *= 2; - eary ->array = (char **) - realloc(eary->array, eary->alloc * sizeof(char *)); - - if (!eary->array) - { - fprintf(stderr, "out of memory"); - exit(1); - } + eary ->array = (char **) pg_realloc(eary->array, + eary->alloc * sizeof(char *)); } - eary ->array[eary->num] = mystrdup(eltname); + eary ->array[eary->num] = pg_strdup(eltname); eary ->num++; } @@ -270,7 +286,7 @@ get_comma_elts(eary *eary) length = 0; if (eary->num == 0) - return mystrdup(""); + return pg_strdup(""); /* * PQescapeString wants 2 * length + 1 bytes of breath space. Add two @@ -279,7 +295,7 @@ get_comma_elts(eary *eary) for (i = 0; i < eary->num; i++) length += strlen(eary->array[i]); - ret = (char *) myalloc(length * 2 + 4 * eary->num); + ret = (char *) pg_malloc(length * 2 + 4 * eary->num); ptr = ret; for (i = 0; i < eary->num; i++) @@ -384,7 +400,7 @@ sql_exec(PGconn *conn, const char *todo, bool quiet) nfields = PQnfields(res); /* for each field, get the needed width */ - length = (int *) myalloc(sizeof(int) * nfields); + length = (int *) pg_malloc(sizeof(int) * nfields); for (j = 0; j < nfields; j++) length[j] = strlen(PQfname(res, j)); @@ -407,7 +423,7 @@ sql_exec(PGconn *conn, const char *todo, bool quiet) l += length[j] + 2; } fprintf(stdout, "\n"); - pad = (char *) myalloc(l + 1); + pad = (char *) pg_malloc(l + 1); MemSet(pad, '-', l); pad[l] = '\0'; fprintf(stdout, "%s\n", pad); @@ -498,8 +514,8 @@ sql_exec_searchtables(PGconn *conn, struct options * opts) comma_filenodes = get_comma_elts(opts->filenodes); /* 80 extra chars for SQL expression */ - qualifiers = (char *) myalloc(strlen(comma_oids) + strlen(comma_tables) + - strlen(comma_filenodes) + 80); + qualifiers = (char *) pg_malloc(strlen(comma_oids) + strlen(comma_tables) + + strlen(comma_filenodes) + 80); ptr = qualifiers; if (opts->oids->num > 0) @@ -525,7 +541,7 @@ sql_exec_searchtables(PGconn *conn, struct options * opts) free(comma_filenodes); /* now build the query */ - todo = (char *) myalloc(650 + strlen(qualifiers)); + todo = (char *) pg_malloc(650 + strlen(qualifiers)); snprintf(todo, 650 + strlen(qualifiers), "SELECT pg_catalog.pg_relation_filenode(c.oid) as \"Filenode\", relname as \"Table Name\" %s\n" "FROM pg_catalog.pg_class c \n" @@ -565,11 +581,11 @@ main(int argc, char **argv) struct options *my_opts; PGconn *pgconn; - my_opts = (struct options *) myalloc(sizeof(struct options)); + my_opts = (struct options *) pg_malloc(sizeof(struct options)); - my_opts->oids = (eary *) myalloc(sizeof(eary)); - my_opts->tables = (eary *) myalloc(sizeof(eary)); - my_opts->filenodes = (eary *) myalloc(sizeof(eary)); + my_opts->oids = (eary *) pg_malloc(sizeof(eary)); + my_opts->tables = (eary *) pg_malloc(sizeof(eary)); + my_opts->filenodes = (eary *) pg_malloc(sizeof(eary)); my_opts->oids->num = my_opts->oids->alloc = 0; my_opts->tables->num = my_opts->tables->alloc = 0; diff --git a/contrib/pg_upgrade/util.c b/contrib/pg_upgrade/util.c index d879e762fa2..1d4bc89f0bf 100644 --- a/contrib/pg_upgrade/util.c +++ b/contrib/pg_upgrade/util.c @@ -192,33 +192,39 @@ get_user_info(char **user_name) void * -pg_malloc(size_t n) +pg_malloc(size_t size) { - void *p = malloc(n); + void *p; + /* Avoid unportable behavior of malloc(0) */ + if (size == 0) + size = 1; + p = malloc(size); if (p == NULL) pg_log(PG_FATAL, "%s: out of memory\n", os_info.progname); - return p; } void * -pg_realloc(void *ptr, size_t n) +pg_realloc(void *ptr, size_t size) { - void *p = realloc(ptr, n); + void *p; + /* Avoid unportable behavior of realloc(NULL, 0) */ + if (ptr == NULL && size == 0) + size = 1; + p = realloc(ptr, size); if (p == NULL) pg_log(PG_FATAL, "%s: out of memory\n", os_info.progname); - return p; } void -pg_free(void *p) +pg_free(void *ptr) { - if (p != NULL) - free(p); + if (ptr != NULL) + free(ptr); } diff --git a/contrib/pgbench/pgbench.c b/contrib/pgbench/pgbench.c index f2fdc6c56f6..e177e16c413 100644 --- a/contrib/pgbench/pgbench.c +++ b/contrib/pgbench/pgbench.c @@ -295,6 +295,9 @@ xmalloc(size_t size) { void *result; + /* Avoid unportable behavior of malloc(0) */ + if (size == 0) + size = 1; result = malloc(size); if (!result) { @@ -309,6 +312,9 @@ xrealloc(void *ptr, size_t size) { void *result; + /* Avoid unportable behavior of realloc(NULL, 0) */ + if (ptr == NULL && size == 0) + size = 1; result = realloc(ptr, size); if (!result) { diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index 7f54d452eae..9ab13475575 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -3352,6 +3352,9 @@ guc_malloc(int elevel, size_t size) { void *data; + /* Avoid unportable behavior of malloc(0) */ + if (size == 0) + size = 1; data = malloc(size); if (data == NULL) ereport(elevel, @@ -3365,6 +3368,9 @@ guc_realloc(int elevel, void *old, size_t size) { void *data; + /* Avoid unportable behavior of realloc(NULL, 0) */ + if (old == NULL && size == 0) + size = 1; data = realloc(old, size); if (data == NULL) ereport(elevel, diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c index 74046b5dfea..a2f16a01c3c 100644 --- a/src/bin/initdb/initdb.c +++ b/src/bin/initdb/initdb.c @@ -285,6 +285,9 @@ pg_malloc(size_t size) { void *result; + /* Avoid unportable behavior of malloc(0) */ + if (size == 0) + size = 1; result = malloc(size); if (!result) { diff --git a/src/bin/pg_basebackup/streamutil.c b/src/bin/pg_basebackup/streamutil.c index c32c5acb2b9..e5fd6fd7655 100644 --- a/src/bin/pg_basebackup/streamutil.c +++ b/src/bin/pg_basebackup/streamutil.c @@ -54,6 +54,9 @@ xmalloc0(int size) { void *result; + /* Avoid unportable behavior of malloc(0) */ + if (size == 0) + size = 1; result = malloc(size); if (!result) { diff --git a/src/bin/pg_ctl/pg_ctl.c b/src/bin/pg_ctl/pg_ctl.c index 72fc4c1abf6..b288e9a8a63 100644 --- a/src/bin/pg_ctl/pg_ctl.c +++ b/src/bin/pg_ctl/pg_ctl.c @@ -233,6 +233,9 @@ pg_malloc(size_t size) { void *result; + /* Avoid unportable behavior of malloc(0) */ + if (size == 0) + size = 1; result = malloc(size); if (!result) { diff --git a/src/bin/pg_dump/dumpmem.c b/src/bin/pg_dump/dumpmem.c index 3ef10307bc5..cadc89d4447 100644 --- a/src/bin/pg_dump/dumpmem.c +++ b/src/bin/pg_dump/dumpmem.c @@ -42,6 +42,9 @@ pg_malloc(size_t size) { void *tmp; + /* Avoid unportable behavior of malloc(0) */ + if (size == 0) + size = 1; tmp = malloc(size); if (!tmp) exit_horribly(NULL, "out of memory\n"); @@ -64,6 +67,9 @@ pg_realloc(void *ptr, size_t size) { void *tmp; + /* Avoid unportable behavior of realloc(NULL, 0) */ + if (ptr == NULL && size == 0) + size = 1; tmp = realloc(ptr, size); if (!tmp) exit_horribly(NULL, "out of memory\n"); diff --git a/src/bin/psql/common.c b/src/bin/psql/common.c index 330d5ce12cf..c804148cd57 100644 --- a/src/bin/psql/common.c +++ b/src/bin/psql/common.c @@ -60,6 +60,9 @@ pg_malloc(size_t size) { void *tmp; + /* Avoid unportable behavior of malloc(0) */ + if (size == 0) + size = 1; tmp = malloc(size); if (!tmp) { diff --git a/src/bin/psql/print.c b/src/bin/psql/print.c index 8fa5e371284..6da3ba053e0 100644 --- a/src/bin/psql/print.c +++ b/src/bin/psql/print.c @@ -136,6 +136,9 @@ pg_local_malloc(size_t size) { void *tmp; + /* Avoid unportable behavior of malloc(0) */ + if (size == 0) + size = 1; tmp = malloc(size); if (!tmp) { diff --git a/src/port/dirmod.c b/src/port/dirmod.c index 22f5c591b07..514424f82e6 100644 --- a/src/port/dirmod.c +++ b/src/port/dirmod.c @@ -70,7 +70,11 @@ fe_palloc(Size size) { void *res; - if ((res = malloc(size)) == NULL) + /* Avoid unportable behavior of malloc(0) */ + if (size == 0) + size = 1; + res = malloc(size); + if (res == NULL) { fprintf(stderr, _("out of memory\n")); exit(1); @@ -96,7 +100,11 @@ fe_repalloc(void *pointer, Size size) { void *res; - if ((res = realloc(pointer, size)) == NULL) + /* Avoid unportable behavior of realloc(NULL, 0) */ + if (pointer == NULL && size == 0) + size = 1; + res = realloc(pointer, size); + if (res == NULL) { fprintf(stderr, _("out of memory\n")); exit(1); -- 2.39.5