From ba10f2054893a29df5d4f1c0ef4950e4c1841482 Mon Sep 17 00:00:00 2001
From: Fujii Masao <fujii@postgresql.org>
Date: Thu, 13 Aug 2026 00:14:58 +0900
Subject: [PATCH v2 1/3] Avoid returning oom_buffer from psql slash command
 scanner

psql_scan_slash_command() builds the command name in a local
PQExpBufferData and returns the buffer's data pointer to its caller. If
either the initial allocation or a later enlargement failed, that data
pointer could be the static PQExpBuffer OOM buffer rather than malloc-owned
storage. HandleSlashCmds() would then eventually pass it to free(), causing
undefined behavior.

Detect a broken command-name buffer before returning it, report OOM, and
return NULL instead. Teach HandleSlashCmds() to treat a NULL command name
as a command error before trying to compare or dispatch it.
---
 src/bin/psql/command.c       |  4 +++-
 src/bin/psql/psqlscanslash.l | 15 +++++++++++++--
 2 files changed, 16 insertions(+), 3 deletions(-)

diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 190c5c6f77a..f80839266e6 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -249,7 +249,9 @@ HandleSlashCmds(PsqlScanState scan_state,
 	 * If we are in "restricted" mode, the only allowable backslash command is
 	 * \unrestrict (to exit restricted mode).
 	 */
-	if (restricted && strcmp(cmd, "unrestrict") != 0)
+	if (cmd == NULL)
+		status = PSQL_CMD_ERROR;
+	else if (restricted && strcmp(cmd, "unrestrict") != 0)
 	{
 		pg_log_error("backslash commands are restricted; only \\unrestrict is allowed");
 		status = PSQL_CMD_ERROR;
diff --git a/src/bin/psql/psqlscanslash.l b/src/bin/psql/psqlscanslash.l
index e3ec1775e62..9640d6e6a6e 100644
--- a/src/bin/psql/psqlscanslash.l
+++ b/src/bin/psql/psqlscanslash.l
@@ -474,7 +474,7 @@ other			.
  * has been consumed through the leading backslash.
  *
  * The return value is a malloc'd copy of the command name, as parsed off
- * from the input.
+ * from the input, or NULL on out-of-memory.
  */
 char *
 psql_scan_slash_command(PsqlScanState state)
@@ -505,7 +505,7 @@ psql_scan_slash_command(PsqlScanState state)
 	/* And lex. */
 	yylex(NULL, state->scanner);
 
-	/* There are no possible errors in this lex state... */
+	/* There are no possible syntax errors in this lex state... */
 
 	/*
 	 * In case the caller returns to using the regular SQL lexer, reselect the
@@ -513,6 +513,17 @@ psql_scan_slash_command(PsqlScanState state)
 	 */
 	psql_scan_reselect_sql_lexer(state);
 
+	/*
+	 * yylex() appends command-name text to mybuf, so a buffer enlargement
+	 * failure during lexing can leave mybuf broken even if initialization
+	 * succeeded.
+	 */
+	if (PQExpBufferDataBroken(mybuf))
+	{
+		pg_log_error("out of memory");
+		return NULL;
+	}
+
 	return mybuf.data;
 }
 
-- 
2.55.0

