v2-0001-Fix-incorrect-fprintf-usage-in-log_error-FRONTEND-pa.patch

text/plain

Filename: v2-0001-Fix-incorrect-fprintf-usage-in-log_error-FRONTEND-pa.patch
Type: text/plain
Part: 0
Message: Re: [PATCH] Fix incorrect fprintf usage in log_error FRONTEND path

Patch

Format: format-patch
Series: patch v2-0001
Subject: Fix va_list handling in log_error() and add vwrite_stderr()
File+
src/backend/utils/error/elog.c 13 3
src/include/utils/elog.h 1 0
src/port/win32security.c 1 1
From de3ed61590ef80e2d28649aab722dc549b6576ea Mon Sep 17 00:00:00 2001
From: Bryan Green <dbryan.green@gmail.com>
Date: Mon, 13 Oct 2025 14:53:58 -0500
Subject: [PATCH] Fix va_list handling in log_error() and add vwrite_stderr()

The log_error() function in win32security.c was incorrectly passing
a va_list to variadic functions, which would cause the va_list pointer
to be treated as a single argument rather than expanding the arguments
properly.

Specifically:
- In the non-FRONTEND path, it called write_stderr(fmt, ap) where
  write_stderr() expects variadic arguments (...), not a va_list
- In the FRONTEND path, it called fprintf(stderr, fmt, ap) where
  fprintf() also expects variadic arguments, not a va_list

This bug likely went undetected because the error paths in
pgwin32_is_admin() are rarely exercised - they only trigger when
Windows security API calls fail catastrophically (out of memory,
security subsystem corruption, etc.).

To fix this, add a new vwrite_stderr() function that accepts a
va_list parameter, following the standard C library pattern of
providing both variadic and va_list versions (like printf/vprintf,
fprintf/vfprintf, etc.).

Changes:
- Add vwrite_stderr() to handle va_list arguments
- Refactor write_stderr() to call vwrite_stderr() internally
- Fix log_error() to use vwrite_stderr() for non-FRONTEND path
- Fix log_error() to use vfprintf() for FRONTEND path

This ensures proper argument expansion in both code paths.
---
 src/backend/utils/error/elog.c | 16 +++++++++++++---
 src/include/utils/elog.h       |  1 +
 src/port/win32security.c       |  2 +-
 3 files changed, 15 insertions(+), 4 deletions(-)

diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index b7b9692f8c..399ab67fcf 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -3786,13 +3786,24 @@ write_stderr(const char *fmt,...)
 {
 	va_list		ap;
 
+	va_start(ap, fmt);
+	vwrite_stderr(fmt, ap);
+	va_end(ap);
+}
+
+
+/*
+ * Write errors to stderr (or by equal means when stderr is
+ * not available) - va_list version
+ */
+void
+vwrite_stderr(const char *fmt,va_list ap)
+{
 #ifdef WIN32
 	char		errbuf[2048];	/* Arbitrary size? */
 #endif
 
 	fmt = _(fmt);
-
-	va_start(ap, fmt);
 #ifndef WIN32
 	/* On Unix, we just fprintf to stderr */
 	vfprintf(stderr, fmt, ap);
@@ -3815,5 +3826,4 @@ write_stderr(const char *fmt,...)
 		fflush(stderr);
 	}
 #endif
-	va_end(ap);
 }
diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h
index 348dafbf90..001ab93ae6 100644
--- a/src/include/utils/elog.h
+++ b/src/include/utils/elog.h
@@ -528,5 +528,6 @@ extern void write_jsonlog(ErrorData *edata);
  * safely (memory context, GUC load etc)
  */
 extern void write_stderr(const char *fmt,...) pg_attribute_printf(1, 2);
+extern void vwrite_stderr(const char *fmt, va_list ap) pg_attribute_printf(1, 0);
 
 #endif							/* ELOG_H */
diff --git a/src/port/win32security.c b/src/port/win32security.c
index a46b82dd04..af3537ab82 100644
--- a/src/port/win32security.c
+++ b/src/port/win32security.c
@@ -31,7 +31,7 @@ log_error(const char *fmt,...)
 
 	va_start(ap, fmt);
 #ifndef FRONTEND
-	write_stderr(fmt, ap);
+	vwrite_stderr(fmt, ap);
 #else
 	fprintf(stderr, fmt, ap);
 #endif
-- 
2.49.0