From 3d41f014f1a09d800b40f3da169de8b77c6bc577 Mon Sep 17 00:00:00 2001 From: Fujii Masao Date: Wed, 19 Aug 2026 12:31:00 +0900 Subject: [PATCH] psql: 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() could 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. Backpatch to all supported versions. Reported-by: Junwang Zhao Author: Fujii Masao Reviewed-by: Chao Li Reviewed-by: Junwang Zhao Discussion: https://postgr.es/m/CAHGQGwEh3R3=1tx_a5=fTDJ+ycuwxWMEn6bG_Yt4B5P+hE7AVw@mail.gmail.com Backpatch-through: 14 --- 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 953ebdf761d..2e55c900aa1 100644 --- a/src/bin/psql/command.c +++ b/src/bin/psql/command.c @@ -238,7 +238,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 c739a754cf2..27782eedcad 100644 --- a/src/bin/psql/psqlscanslash.l +++ b/src/bin/psql/psqlscanslash.l @@ -481,7 +481,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) @@ -512,7 +512,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 @@ -520,6 +520,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; }