From 081fa4f3f0df104c47a341a54ffceee8b016cef4 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze@samba.org>
Date: Thu, 30 Apr 2026 14:48:26 +0200
Subject: [PATCH 21/38] CVE-2026-4480/CVE-2026-4408: lib/util: split out
 realloc_string_sub_raw()

This will allow realloc_string_sub2() to use it in order
to have the logic in one place only.

And it will also allow adjacted callers to be
more flexible.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=16033
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16034

Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
---
 lib/util/substitute.c | 85 ++++++++++++++++++++++++++++++-------------
 lib/util/substitute.h | 18 +++++++++
 2 files changed, 78 insertions(+), 25 deletions(-)

diff --git a/lib/util/substitute.c b/lib/util/substitute.c
index b9fe32e993e..465aea86605 100644
--- a/lib/util/substitute.c
+++ b/lib/util/substitute.c
@@ -171,32 +171,24 @@ _PUBLIC_ void all_string_sub(char *s,const char *pattern,const char *insert, siz
  * talloc version of string_sub2.
  */
 
-char *talloc_string_sub2(TALLOC_CTX *mem_ctx, const char *src,
-			const char *pattern,
-			const char *insert,
-			bool remove_unsafe_characters,
-			bool replace_once,
-			bool allow_trailing_dollar)
+bool realloc_string_sub_raw(char **_string,
+			    const char *pattern,
+			    const char *insert,
+			    bool replace_once,
+			    bool allow_trailing_dollar,
+			    const char *unsafe_characters,
+			    char safe_character)
 {
-	const char *unsafe_characters = STRING_SUB_UNSAFE_CHARACTERS;
-	const char safe_character = '_';
-	char *p = NULL,
+	char *p = NULL;
 	char *s = NULL;
 	char *string = NULL;
 	ssize_t ls,lp,li,ld, i;
 
-	if (!insert || !pattern || !*pattern || !src) {
-		return NULL;
+	if (!insert || !pattern || !*pattern || !_string|| !*_string) {
+		return false;
 	}
 
-	string = talloc_strdup(mem_ctx, src);
-	if (string == NULL) {
-		DEBUG(0, ("talloc_string_sub2: "
-			"talloc_strdup failed\n"));
-		return NULL;
-	}
-
-	s = string;
+	s = string = *_string;
 
 	ls = (ssize_t)strlen(s);
 	lp = (ssize_t)strlen(pattern);
@@ -205,14 +197,13 @@ char *talloc_string_sub2(TALLOC_CTX *mem_ctx, const char *src,
 
 	while ((p = strstr_m(s,pattern))) {
 		if (ld > 0) {
-			int offset = PTR_DIFF(s,string);
-			string = (char *)talloc_realloc_size(mem_ctx, string,
-							ls + ld + 1);
+			ptrdiff_t offset = PTR_DIFF(s,string);
+			string = talloc_realloc(NULL, string, char, ls + ld + 1);
 			if (!string) {
-				DEBUG(0, ("talloc_string_sub: out of "
-					  "memory!\n"));
-				return NULL;
+				DBG_ERR("out of memory(realloc)!\n");
+				return false;
 			}
+			*_string = string;
 			p = string + offset + (p - s);
 		}
 		if (li != lp) {
@@ -234,6 +225,50 @@ char *talloc_string_sub2(TALLOC_CTX *mem_ctx, const char *src,
 			break;
 		}
 	}
+	return true;
+}
+
+char *talloc_string_sub2(TALLOC_CTX *mem_ctx,
+			 const char *src,
+			 const char *pattern,
+			 const char *insert,
+			 bool remove_unsafe_characters,
+			 bool replace_once,
+			 bool allow_trailing_dollar)
+{
+	const char *unsafe_characters = NULL;
+	char safe_character = '\0';
+	char *string = NULL;
+	bool ok;
+
+	if (!insert || !pattern || !*pattern || !src) {
+		return NULL;
+	}
+
+	if (remove_unsafe_characters) {
+		unsafe_characters = STRING_SUB_UNSAFE_CHARACTERS;
+		safe_character = '_';
+	}
+
+	string = talloc_strdup(mem_ctx, src);
+	if (string == NULL) {
+		DBG_ERR("out of memory, talloc_strdup(src)!\n");
+		return NULL;
+	}
+
+	ok = realloc_string_sub_raw(&string,
+				    pattern,
+				    insert,
+				    replace_once,
+				    allow_trailing_dollar,
+				    unsafe_characters,
+				    safe_character);
+	if (!ok) {
+		TALLOC_FREE(string);
+		DBG_ERR("out of memory, realloc_string_sub_raw()!\n");
+		return NULL;
+	}
+
 	return string;
 }
 
diff --git a/lib/util/substitute.h b/lib/util/substitute.h
index e1a82859dac..041a649fd18 100644
--- a/lib/util/substitute.h
+++ b/lib/util/substitute.h
@@ -51,6 +51,24 @@ void string_sub(char *s,const char *pattern, const char *insert, size_t len);
 **/
 void all_string_sub(char *s,const char *pattern,const char *insert, size_t len);
 
+/*
+ * If unsafe_characters is NULL all characters are allowed,
+ * if unsafe_characters is not NULL all characters caught
+ * by iscntrl() are also replaced by safe_character.
+ *
+ * *_string might be reallocated!
+ *
+ * On error *_string may still be reallocated and
+ * may contain partial replacements.
+ */
+bool realloc_string_sub_raw(char **_string,
+			    const char *pattern,
+			    const char *insert,
+			    bool replace_once,
+			    bool allow_trailing_dollar,
+			    const char *unsafe_characters,
+			    char safe_character);
+
 char *talloc_string_sub2(TALLOC_CTX *mem_ctx, const char *src,
 			const char *pattern,
 			const char *insert,
-- 
2.43.0


