From eea3a3ef56262fe864b79c1d8cf66a891c9c2069 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze@samba.org>
Date: Thu, 23 Apr 2026 21:11:27 +0200
Subject: [PATCH 23/38] CVE-2026-4480/CVE-2026-4408: s3:lib: let
 realloc_string_sub2() use realloc_string_sub_raw()

We don't need this logic more than once!

But we leave the strange calling convention of
realloc_string_sub2(), where the caller it
not allowed to use the passed pointer when
NULL is returned...

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>
---
 source3/lib/substitute_generic.c | 81 ++++++++++----------------------
 1 file changed, 24 insertions(+), 57 deletions(-)

--- a/source3/lib/substitute_generic.c
+++ b/source3/lib/substitute_generic.c
@@ -37,71 +37,38 @@ char *realloc_string_sub2(char *string,
 			bool remove_unsafe_characters,
 			bool allow_trailing_dollar)
 {
-	char *p, *in;
-	char *s;
-	ssize_t ls,lp,li,ld, i;
+	const char *unsafe_characters = NULL;
+	char safe_character = '\0';
+	bool ok;
 
 	if (!insert || !pattern || !*pattern || !string || !*string)
 		return NULL;
 
-	s = string;
+	if (remove_unsafe_characters) {
+		unsafe_characters = STRING_SUB_UNSAFE_CHARACTERS;
+		safe_character = '_';
+	}
 
-	in = talloc_strdup(talloc_tos(), insert);
-	if (!in) {
-		DEBUG(0, ("realloc_string_sub: out of memory!\n"));
+	ok = realloc_string_sub_raw(&string,
+				    pattern,
+				    insert,
+				    false,  /* replace_once */
+				    allow_trailing_dollar,
+				    unsafe_characters,
+				    safe_character);
+	if (!ok) {
+		DBG_ERR("out of memory, realloc_string_sub_raw()!\n");
+		/*
+		 * The calling convention of realloc_string_sub2()
+		 * is very strange regarding stale string pointers.
+		 *
+		 * It is assumed the given string was allocated
+		 * on talloc_tos(), so we just don't touch
+		 * it at all here...
+		 */
 		return NULL;
 	}
-	ls = (ssize_t)strlen(s);
-	lp = (ssize_t)strlen(pattern);
-	li = (ssize_t)strlen(insert);
-	ld = li - lp;
-	for (i=0;i<li;i++) {
-		switch (in[i]) {
-			case '$':
-				/* allow a trailing $
-				 * (as in machine accounts) */
-				if (allow_trailing_dollar && (i == li - 1 )) {
-					break;
-				}
-				FALL_THROUGH;
-			case '`':
-			case '"':
-			case '\'':
-			case ';':
-			case '%':
-			case '\r':
-			case '\n':
-				if ( remove_unsafe_characters ) {
-					in[i] = '_';
-					break;
-				}
-				FALL_THROUGH;
-			default:
-				/* ok */
-				break;
-		}
-	}
 
-	while ((p = strstr_m(s,pattern))) {
-		if (ld > 0) {
-			int offset = PTR_DIFF(s,string);
-			string = talloc_realloc(NULL, string, char, ls + ld + 1);
-			if (!string) {
-				DEBUG(0, ("realloc_string_sub: "
-					"out of memory!\n"));
-				talloc_free(in);
-				return NULL;
-			}
-			p = string + offset + (p - s);
-		}
-		if (li != lp) {
-			memmove(p+li,p+lp,strlen(p+lp)+1);
-		}
-		memcpy(p, in, li);
-		s = p + li;
-		ls += ld;
-	}
-	talloc_free(in);
 	return string;
 }
 
