From 21fe95ee828b0b9b944f6aa0bb02d24fbb981815 Mon Sep 17 00:00:00 2001
From: Martijn van Beurden <mvanb1@gmail.com>
Date: Wed, 3 Aug 2022 13:52:19 +0200
Subject: [PATCH] Add and use _nofree variants of safe_realloc functions

Parts of the code use realloc like

x = safe_realloc(x, somesize);

when this is the case, the safe_realloc variant used must free the
old memory block in case it fails, otherwise it will leak. However,
there are also instances in the code where handling is different:

if (0 == (x = safe_realloc(y, somesize)))
    return false

in this case, y should not be freed, as y is not set to NULL we
could encounter double frees. Here the safe_realloc_nofree
functions are used.

Reference:https://github.com/xiph/flac/commit/21fe95ee828b0b9b944f6aa0bb02d24fbb981815
---
 include/share/alloc.h         | 41 +++++++++++++++++++++++++++++++----
 src/flac/encode.c             |  4 ++--
 src/flac/foreign_metadata.c   |  2 +-
 src/libFLAC/bitwriter.c       |  2 +-
 src/libFLAC/metadata_object.c |  2 +-
 src/plugin_common/tags.c      |  2 +-
 src/share/utf8/iconvert.c     |  2 +-
 7 files changed, 44 insertions(+), 11 deletions(-)

diff --git a/include/share/alloc.h b/include/share/alloc.h
index 9b53b010..74f444d6 100644
--- a/include/share/alloc.h
+++ b/include/share/alloc.h
@@ -161,17 +161,30 @@ static inline void *safe_realloc_(void *ptr, size_t size)
 		free(oldptr);
 	return newptr;
 }
-static inline void *safe_realloc_add_2op_(void *ptr, size_t size1, size_t size2)
+static inline void *safe_realloc_nofree_add_2op_(void *ptr, size_t size1, size_t size2)
+{
+	size2 += size1;
+	if(size2 < size1)
+		return 0;
+	return realloc(ptr, size2);
+}
+
+static inline void *safe_realloc_add_3op_(void *ptr, size_t size1, size_t size2, size_t size3)
 {
 	size2 += size1;
 	if(size2 < size1) {
 		free(ptr);
 		return 0;
 	}
-	return realloc(ptr, size2);
+	size3 += size2;
+	if(size3 < size2) {
+		free(ptr);
+		return 0;
+	}
+	return safe_realloc_(ptr, size3);
 }
 
-static inline void *safe_realloc_add_3op_(void *ptr, size_t size1, size_t size2, size_t size3)
+static inline void *safe_realloc_nofree_add_3op_(void *ptr, size_t size1, size_t size2, size_t size3)
 {
 	size2 += size1;
 	if(size2 < size1)
@@ -182,7 +195,7 @@ static inline void *safe_realloc_add_3op_(void *ptr, size_t size1, size_t size2,
 	return realloc(ptr, size3);
 }
 
-static inline void *safe_realloc_add_4op_(void *ptr, size_t size1, size_t size2, size_t size3, size_t size4)
+static inline void *safe_realloc_nofree_add_4op_(void *ptr, size_t size1, size_t size2, size_t size3, size_t size4)
 {
 	size2 += size1;
 	if(size2 < size1)
@@ -207,6 +220,15 @@ static inline void *safe_realloc_mul_2op_(void *ptr, size_t size1, size_t size2)
 	return safe_realloc_(ptr, size1*size2);
 }
 
+static inline void *safe_realloc_nofree_mul_2op_(void *ptr, size_t size1, size_t size2)
+{
+	if(!size1 || !size2)
+		return realloc(ptr, 0); /* preserve POSIX realloc(ptr, 0) semantics */
+	if(size1 > SIZE_MAX / size2)
+		return 0;
+	return realloc(ptr, size1*size2);
+}
+
 /* size1 * (size2 + size3) */
 static inline void *safe_realloc_muladd2_(void *ptr, size_t size1, size_t size2, size_t size3)
 {
@@ -220,4 +242,15 @@ static inline void *safe_realloc_muladd2_(void *ptr, size_t size1, size_t size2,
 	return safe_realloc_mul_2op_(ptr, size1, size2);
 }
 
+/* size1 * (size2 + size3) */
+static inline void *safe_realloc_nofree_muladd2_(void *ptr, size_t size1, size_t size2, size_t size3)
+{
+	if(!size1 || (!size2 && !size3))
+		return realloc(ptr, 0); /* preserve POSIX realloc(ptr, 0) semantics */
+	size2 += size3;
+	if(size2 < size3)
+		return 0;
+	return safe_realloc_nofree_mul_2op_(ptr, size1, size2);
+}
+
 #endif
diff --git a/src/flac/encode.c b/src/flac/encode.c
index a7d1f7b2..b82ced76 100644
--- a/src/flac/encode.c
+++ b/src/flac/encode.c
@@ -1734,10 +1734,10 @@ static void static_metadata_clear(static_metadata_t *m)
 static FLAC__bool static_metadata_append(static_metadata_t *m, FLAC__StreamMetadata *d, FLAC__bool needs_delete)
 {
 	void *x;
-	if(0 == (x = safe_realloc_muladd2_(m->metadata, sizeof(*m->metadata), /*times (*/m->num_metadata, /*+*/1/*)*/)))
+	if(0 == (x = safe_realloc_nofree_muladd2_(m->metadata, sizeof(*m->metadata), /*times (*/m->num_metadata, /*+*/1/*)*/)))
 		return false;
 	m->metadata = (FLAC__StreamMetadata**)x;
-	if(0 == (x = safe_realloc_muladd2_(m->needs_delete, sizeof(*m->needs_delete), /*times (*/m->num_metadata, /*+*/1/*)*/)))
+	if(0 == (x = safe_realloc_nofree_muladd2_(m->needs_delete, sizeof(*m->needs_delete), /*times (*/m->num_metadata, /*+*/1/*)*/)))
 		return false;
 	m->needs_delete = (FLAC__bool*)x;
 	m->metadata[m->num_metadata] = d;
diff --git a/src/flac/foreign_metadata.c b/src/flac/foreign_metadata.c
index 9a1fb96c..c86dff42 100644
--- a/src/flac/foreign_metadata.c
+++ b/src/flac/foreign_metadata.c
@@ -74,7 +74,7 @@ static FLAC__bool copy_data_(FILE *fin, FILE *fout, size_t size, const char **er
 
 static FLAC__bool append_block_(foreign_metadata_t *fm, FLAC__off_t offset, FLAC__uint32 size, const char **error)
 {
-	foreign_block_t *fb = safe_realloc_muladd2_(fm->blocks, sizeof(foreign_block_t), /*times (*/fm->num_blocks, /*+*/1/*)*/);
+	foreign_block_t *fb = safe_realloc_nofree_muladd2_(fm->blocks, sizeof(foreign_block_t), /*times (*/fm->num_blocks, /*+*/1/*)*/);
 	if(fb) {
 		fb[fm->num_blocks].offset = offset;
 		fb[fm->num_blocks].size = size;
diff --git a/src/libFLAC/bitwriter.c b/src/libFLAC/bitwriter.c
index 79ab8649..8865a2f4 100644
--- a/src/libFLAC/bitwriter.c
+++ b/src/libFLAC/bitwriter.c
@@ -133,7 +133,7 @@ FLAC__bool bitwriter_grow_(FLAC__BitWriter *bw, uint32_t bits_to_add)
 	FLAC__ASSERT(new_capacity > bw->capacity);
 	FLAC__ASSERT(new_capacity >= bw->words + ((bw->bits + bits_to_add + FLAC__BITS_PER_WORD - 1) / FLAC__BITS_PER_WORD));
 
-	new_buffer = safe_realloc_mul_2op_(bw->buffer, sizeof(bwword), /*times*/new_capacity);
+	new_buffer = safe_realloc_nofree_mul_2op_(bw->buffer, sizeof(bwword), /*times*/new_capacity);
 	if(new_buffer == 0)
 		return false;
 	bw->buffer = new_buffer;
diff --git a/src/libFLAC/metadata_object.c b/src/libFLAC/metadata_object.c
index 7cc8ee9f..2c7da8db 100644
--- a/src/libFLAC/metadata_object.c
+++ b/src/libFLAC/metadata_object.c
@@ -98,7 +98,7 @@ static FLAC__bool free_copy_bytes_(FLAC__byte **to, const FLAC__byte *from, uint
 /* realloc() failure leaves entry unchanged */
 static FLAC__bool ensure_null_terminated_(FLAC__byte **entry, uint32_t length)
 {
-	FLAC__byte *x = safe_realloc_add_2op_(*entry, length, /*+*/1);
+	FLAC__byte *x = safe_realloc_nofree_add_2op_(*entry, length, /*+*/1);
 	if (x != NULL) {
 		x[length] = '\0';
 		*entry = x;
diff --git a/src/plugin_common/tags.c b/src/plugin_common/tags.c
index e9227444..ffd846b6 100644
--- a/src/plugin_common/tags.c
+++ b/src/plugin_common/tags.c
@@ -317,7 +317,7 @@ FLAC__bool FLAC_plugin__tags_add_tag_utf8(FLAC__StreamMetadata *tags, const char
 		const size_t value_len = strlen(value);
 		const size_t separator_len = strlen(separator);
 		FLAC__byte *new_entry;
-		if(0 == (new_entry = safe_realloc_add_4op_(entry->entry, entry->length, /*+*/value_len, /*+*/separator_len, /*+*/1)))
+		if(0 == (new_entry = safe_realloc_nofree_add_4op_(entry->entry, entry->length, /*+*/value_len, /*+*/separator_len, /*+*/1)))
 			return false;
 		memcpy(new_entry+entry->length, separator, separator_len);
 		entry->length += separator_len;
diff --git a/src/share/utf8/iconvert.c b/src/share/utf8/iconvert.c
index 8ab53c10..876c06e8 100644
--- a/src/share/utf8/iconvert.c
+++ b/src/share/utf8/iconvert.c
@@ -149,7 +149,7 @@ int iconvert(const char *fromcode, const char *tocode,
       iconv_close(cd1);
       return ret;
     }
-    newbuf = safe_realloc_add_2op_(utfbuf, (ob - utfbuf), /*+*/1);
+    newbuf = safe_realloc_nofree_add_2op_(utfbuf, (ob - utfbuf), /*+*/1);
     if (!newbuf)
       goto fail;
     ob = (ob - utfbuf) + newbuf;
-- 
2.27.0

