Hi Evgeniy,

> In case of out_of_memory, the ecpg_strdup function may return NULL.
> Checks should be added in src/interfaces/ecpg/ecpglib/execute.c.
> Patch attached.
>
> Found by Linux Verification Center (linuxtesting.org) with SVACE.

The patch looks correct, but I believe it's incomplete. It misses
several other places where ecpg_strdup() is called without proper
checks. A correct patch would look like the one attached.

While working on it I noticed a potentially problematic strcmp call,
marked with XXX in the patch. I didn't address this issue in v2.

Thoughts?
From 4af966774361eac189e011d92e985883ee03ee5a Mon Sep 17 00:00:00 2001
From: Aleksander Alekseev <aleksan...@timescale.com>
Date: Fri, 11 Jul 2025 17:59:50 +0300
Subject: [PATCH v2] Add proper checks for ecpg_strdup() return value

Evgeniy Gorbanev, Aleksander Alekseev. Reviewed by TODO FIXME.
---
 src/interfaces/ecpg/ecpglib/connect.c | 26 ++++++++++++++++------
 src/interfaces/ecpg/ecpglib/execute.c | 17 +++++++++++++++
 src/interfaces/ecpg/ecpglib/prepare.c | 31 +++++++++++++++++++++++++++
 3 files changed, 68 insertions(+), 6 deletions(-)

diff --git a/src/interfaces/ecpg/ecpglib/connect.c b/src/interfaces/ecpg/ecpglib/connect.c
index 2bbb70333dc..fc1034ccd7d 100644
--- a/src/interfaces/ecpg/ecpglib/connect.c
+++ b/src/interfaces/ecpg/ecpglib/connect.c
@@ -58,6 +58,7 @@ ecpg_get_connection_nr(const char *connection_name)
 
 		for (con = all_connections; con != NULL; con = con->next)
 		{
+			/* XXX strcmp() will segfault if con->name is NULL */
 			if (strcmp(connection_name, con->name) == 0)
 				break;
 		}
@@ -460,7 +461,21 @@ ECPGconnect(int lineno, int c, const char *name, const char *user, const char *p
 	 */
 	conn_keywords = (const char **) ecpg_alloc((connect_params + 1) * sizeof(char *), lineno);
 	conn_values = (const char **) ecpg_alloc(connect_params * sizeof(char *), lineno);
-	if (conn_keywords == NULL || conn_values == NULL)
+
+	/* Allocate memory for connection name */
+	if (connection_name != NULL)
+		this->name = ecpg_strdup(connection_name, lineno);
+	else
+		this->name = ecpg_strdup(realname, lineno);
+
+	/*
+	 * Note that NULL is a correct value for realname and ecpg_strdup(NULL,
+	 * ...) just returns NULL. For named reasons the ckecks for this->name are
+	 * a bit complicated here.
+	 */
+	if (conn_keywords == NULL || conn_values == NULL ||
+		(connection_name != NULL && this->name == NULL) ||	/* first call failed */
+		(connection_name == NULL && realname != NULL && this->name == NULL))	/* second call failed */
 	{
 		if (host)
 			ecpg_free(host);
@@ -476,6 +491,8 @@ ECPGconnect(int lineno, int c, const char *name, const char *user, const char *p
 			ecpg_free(conn_keywords);
 		if (conn_values)
 			ecpg_free(conn_values);
+		if (this->name)
+			ecpg_free(this->name);
 		free(this);
 		return false;
 	}
@@ -510,17 +527,14 @@ ECPGconnect(int lineno, int c, const char *name, const char *user, const char *p
 				ecpg_free(conn_keywords);
 			if (conn_values)
 				ecpg_free(conn_values);
+			if (this->name)
+				ecpg_free(this->name);
 			free(this);
 			return false;
 		}
 	}
 #endif
 
-	if (connection_name != NULL)
-		this->name = ecpg_strdup(connection_name, lineno);
-	else
-		this->name = ecpg_strdup(realname, lineno);
-
 	this->cache_head = NULL;
 	this->prep_stmts = NULL;
 
diff --git a/src/interfaces/ecpg/ecpglib/execute.c b/src/interfaces/ecpg/ecpglib/execute.c
index f52da06de9a..6524c646c13 100644
--- a/src/interfaces/ecpg/ecpglib/execute.c
+++ b/src/interfaces/ecpg/ecpglib/execute.c
@@ -2030,7 +2030,14 @@ ecpg_do_prologue(int lineno, const int compat, const int force_indicator,
 		statement_type = ECPGst_execute;
 	}
 	else
+	{
 		stmt->command = ecpg_strdup(query, lineno);
+		if (!stmt->command)
+		{
+			ecpg_do_epilogue(stmt);
+			return false;
+		}
+	}
 
 	stmt->name = NULL;
 
@@ -2043,6 +2050,11 @@ ecpg_do_prologue(int lineno, const int compat, const int force_indicator,
 		{
 			stmt->name = stmt->command;
 			stmt->command = ecpg_strdup(command, lineno);
+			if (!stmt->command)
+			{
+				ecpg_do_epilogue(stmt);
+				return false;
+			}
 		}
 		else
 		{
@@ -2176,6 +2188,11 @@ ecpg_do_prologue(int lineno, const int compat, const int force_indicator,
 			if (!is_prepared_name_set && stmt->statement_type == ECPGst_prepare)
 			{
 				stmt->name = ecpg_strdup(var->value, lineno);
+				if (!stmt->name)
+				{
+					ecpg_do_epilogue(stmt);
+					return false;
+				}
 				is_prepared_name_set = true;
 			}
 		}
diff --git a/src/interfaces/ecpg/ecpglib/prepare.c b/src/interfaces/ecpg/ecpglib/prepare.c
index ea1146f520f..7b6019deed7 100644
--- a/src/interfaces/ecpg/ecpglib/prepare.c
+++ b/src/interfaces/ecpg/ecpglib/prepare.c
@@ -86,8 +86,21 @@ ecpg_register_prepared_stmt(struct statement *stmt)
 	prep_stmt->lineno = lineno;
 	prep_stmt->connection = con;
 	prep_stmt->command = ecpg_strdup(stmt->command, lineno);
+	if (!prep_stmt->command)
+	{
+		ecpg_free(prep_stmt);
+		ecpg_free(this);
+		return false;
+	}
 	prep_stmt->inlist = prep_stmt->outlist = NULL;
 	this->name = ecpg_strdup(stmt->name, lineno);
+	if (!this->name)
+	{
+		ecpg_free(prep_stmt->command);
+		ecpg_free(prep_stmt);
+		ecpg_free(this);
+		return false;
+	}
 	this->stmt = prep_stmt;
 	this->prepared = true;
 
@@ -178,6 +191,12 @@ prepare_common(int lineno, struct connection *con, const char *name, const char
 	stmt->lineno = lineno;
 	stmt->connection = con;
 	stmt->command = ecpg_strdup(variable, lineno);
+	if (!stmt->command)
+	{
+		ecpg_free(stmt);
+		ecpg_free(this);
+		return false;
+	}
 	stmt->inlist = stmt->outlist = NULL;
 
 	/* if we have C variables in our statement replace them with '?' */
@@ -185,6 +204,13 @@ prepare_common(int lineno, struct connection *con, const char *name, const char
 
 	/* add prepared statement to our list */
 	this->name = ecpg_strdup(name, lineno);
+	if (!this->name)
+	{
+		ecpg_free(stmt->command);
+		ecpg_free(stmt);
+		ecpg_free(this);
+		return false;
+	}
 	this->stmt = stmt;
 
 	/* and finally really prepare the statement */
@@ -541,6 +567,8 @@ AddStmtToCache(int lineno,		/* line # of statement */
 	entry = &stmtCacheEntries[entNo];
 	entry->lineno = lineno;
 	entry->ecpgQuery = ecpg_strdup(ecpgQuery, lineno);
+	if (!entry->ecpgQuery)
+		return -1;
 	entry->connection = connection;
 	entry->execs = 0;
 	memcpy(entry->stmtID, stmtID, sizeof(entry->stmtID));
@@ -595,6 +623,9 @@ ecpg_auto_prepare(int lineno, const char *connection_name, const int compat, cha
 		*name = ecpg_strdup(stmtID, lineno);
 	}
 
+	if (!*name)
+		return false;
+
 	/* increase usage counter */
 	stmtCacheEntries[entNo].execs++;
 
-- 
2.43.0

Reply via email to