From 4ea54eef2dc2d7e8ad6579c2158ea310d64bcd62 Mon Sep 17 00:00:00 2001
From: Joshua Shanks <jjshanks@gmail.com>
Date: Sat, 1 Nov 2025 14:22:42 -0700
Subject: [PATCH] Improve error handling in passwordFromFile()

Previously, passwordFromFile() returned NULL for both valid cases
(no matching password found) and actual errors (out of memory).
This made it impossible for callers like pqConnectOptions2() to
distinguish between these scenarios and fail the connection
appropriately when OOM errors occurred.

This patch extends passwordFromFile() with a "const char **errmsg"
parameter to signal actual errors. When an OOM error occurs, errmsg
is set to an error message string. The caller checks if errmsg is
set and fails the connection with CONNECTION_BAD status.

This follows the established pattern used by pqAddTuple() in
fe-exec.c, where NULL return with NULL errmsg indicates "not found"
while NULL return with non-NULL errmsg indicates an actual error.

Additionally, removed the PGconn *conn parameter from passwordFromFile()
since it was only used for error reporting, which is now handled by
the caller through the errmsg parameter. This makes the function
cleaner and more focused.

Per feedback from Michael Paquier.
---
 src/interfaces/libpq/fe-connect.c | 33 ++++++++++++++++++++++++-------
 1 file changed, 26 insertions(+), 7 deletions(-)

diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index a3d12931fff..28cc5b36bf3 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -501,8 +501,9 @@ static int	parseServiceFile(const char *serviceFile,
 							 PQExpBuffer errorMessage,
 							 bool *group_found);
 static char *pwdfMatchesString(char *buf, const char *token);
-static char *passwordFromFile(const char *hostname, const char *port, const char *dbname,
-							  const char *username, const char *pgpassfile);
+static char *passwordFromFile(const char *hostname, const char *port,
+							  const char *dbname, const char *username,
+							  const char *pgpassfile, const char **errmsg);
 static void pgpassfileWarning(PGconn *conn);
 static void default_threadlock(int acquire);
 static bool sslVerifyProtocolVersion(const char *version);
@@ -1454,6 +1455,7 @@ pqConnectOptions2(PGconn *conn)
 				 * least one of them is guaranteed nonempty by now).
 				 */
 				const char *pwhost = conn->connhost[i].host;
+				const char *password_errmsg = NULL;
 
 				if (pwhost == NULL || pwhost[0] == '\0')
 					pwhost = conn->connhost[i].hostaddr;
@@ -1463,7 +1465,15 @@ pqConnectOptions2(PGconn *conn)
 									 conn->connhost[i].port,
 									 conn->dbName,
 									 conn->pguser,
-									 conn->pgpassfile);
+									 conn->pgpassfile,
+									 &password_errmsg);
+
+				if (password_errmsg != NULL)
+				{
+					conn->status = CONNECTION_BAD;
+					libpq_append_conn_error(conn, "%s", password_errmsg);
+					return false;
+				}
 			}
 		}
 	}
@@ -7942,10 +7952,16 @@ pwdfMatchesString(char *buf, const char *token)
 	return NULL;
 }
 
-/* Get a password from the password file. Return value is malloc'd. */
+/*
+ * Get a password from the password file. Return value is malloc'd.
+ *
+ * On error, *errmsg can be set to an error string to be returned.
+ * If it is left NULL, the NULL return indicates no matching password was found.
+ */
 static char *
-passwordFromFile(const char *hostname, const char *port, const char *dbname,
-				 const char *username, const char *pgpassfile)
+passwordFromFile(const char *hostname, const char *port,
+				 const char *dbname, const char *username,
+				 const char *pgpassfile, const char **errmsg)
 {
 	FILE	   *fp;
 #ifndef WIN32
@@ -8019,7 +8035,10 @@ passwordFromFile(const char *hostname, const char *port, const char *dbname,
 	{
 		/* Make sure there's a reasonable amount of room in the buffer */
 		if (!enlargePQExpBuffer(&buf, 128))
+		{
+			*errmsg = "out of memory";
 			break;
+		}
 
 		/* Read some data, appending it to what we already have */
 		if (fgets(buf.data + buf.len, buf.maxlen - buf.len, fp) == NULL)
@@ -8058,7 +8077,7 @@ passwordFromFile(const char *hostname, const char *port, const char *dbname,
 
 				if (!ret)
 				{
-					/* Out of memory. XXX: an error message would be nice. */
+					*errmsg = "out of memory";
 					return NULL;
 				}
 
-- 
2.34.1

