From 09ac603c36d1c865df68e5abd9dab04bd0fa5e48 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Tue, 2 Oct 2012 17:31:40 -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        | 38 +++++++++++++++++++++---------
 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/scripts/common.c           |  3 +++
 src/port/dirmod.c                  | 12 ++++++++--
 11 files changed, 85 insertions(+), 22 deletions(-)

diff --git a/contrib/oid2name/oid2name.c b/contrib/oid2name/oid2name.c
index 17d4d80fc90..a666731e727 100644
--- a/contrib/oid2name/oid2name.c
+++ b/contrib/oid2name/oid2name.c
@@ -51,6 +51,7 @@ struct options
 static void help(const char *progname);
 void		get_opts(int, char **, struct options *);
 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);
@@ -203,16 +204,37 @@ help(const char *progname)
 void *
 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 *
 pg_strdup(const char *str)
 {
@@ -220,7 +242,7 @@ pg_strdup(const char *str)
 
 	if (!result)
 	{
-		fprintf(stderr, "out of memory");
+		fprintf(stderr, "out of memory\n");
 		exit(1);
 	}
 	return result;
@@ -242,14 +264,8 @@ add_one_elt(char *eltname, eary *eary)
 	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] = pg_strdup(eltname);
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 509472262bc..a669cf406ba 100644
--- a/contrib/pgbench/pgbench.c
+++ b/contrib/pgbench/pgbench.c
@@ -299,6 +299,9 @@ pg_malloc(size_t size)
 {
 	void	   *result;
 
+	/* Avoid unportable behavior of malloc(0) */
+	if (size == 0)
+		size = 1;
 	result = malloc(size);
 	if (!result)
 	{
@@ -313,6 +316,9 @@ 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)
 	{
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 48652b21ea4..6b202e04256 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -3358,6 +3358,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,
@@ -3371,6 +3374,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 11ff554d795..c56f721e08f 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -294,6 +294,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 b6cc2a303e9..22f41281808 100644
--- a/src/bin/pg_basebackup/streamutil.c
+++ b/src/bin/pg_basebackup/streamutil.c
@@ -54,6 +54,9 @@ pg_malloc0(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_ctl/pg_ctl.c b/src/bin/pg_ctl/pg_ctl.c
index 6d74a154ab1..23c47f62752 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 22637e26e48..08e82b269dc 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");
@@ -63,6 +66,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 1cb30088c48..179c1622793 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/scripts/common.c b/src/bin/scripts/common.c
index 4a7d02f7771..7d8875d81cd 100644
--- a/src/bin/scripts/common.c
+++ b/src/bin/scripts/common.c
@@ -304,6 +304,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/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);