From aa1f93a3387ad619c14cea2b8ed01e6f49cb6600 Mon Sep 17 00:00:00 2001 From: Etsuro Fujita Date: Sat, 16 May 2026 17:55:00 +0900 Subject: [PATCH] postgres_fdw: Replace buffers in RemoteAttributeMapping with pointers. Commit 28972b6fc ("Add support for importing statistics from remote servers.") stored the names of local/remote columns for a foreign table into the buffers of NAMEDATALEN bytes in this structure, without accounting for the possibility that the remote column name in particular could be longer than NAMEDATALEN - 1. If it was longer than that, this would leave it unterminated/truncated in the buffer, invoking undefined behavior when match_attrmap() processes it, which assumes that it's fully-contained/terminated in the buffer. To fix, replace the buffers with char pointers, pstrdup the local/remote column names, and store the results into the pointers. This commit also adds a function to clean up the nested data structure. Per Coverity and Tom Lane. Reported-by: Tom Lane Author: Corey Huinker Reviewed-by: Etsuro Fujita Discussion: https://postgr.es/m/342868.1776017700%40sss.pgh.pa.us --- contrib/postgres_fdw/postgres_fdw.c | 32 +++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c index 85be46eb2a2..0a589f8db74 100644 --- a/contrib/postgres_fdw/postgres_fdw.c +++ b/contrib/postgres_fdw/postgres_fdw.c @@ -325,8 +325,8 @@ typedef struct typedef struct { AttrNumber local_attnum; - char local_attname[NAMEDATALEN]; - char remote_attname[NAMEDATALEN]; + char *local_attname; + char *remote_attname; int res_index; } RemoteAttributeMapping; @@ -704,6 +704,7 @@ static PGresult *fetch_attstats(PGconn *conn, int server_version_num, const char *column_list); static RemoteAttributeMapping *build_remattrmap(Relation relation, List *va_cols, int *p_attrcnt, StringInfo column_list); +static void free_remattrmap(RemoteAttributeMapping *map, int len); static bool attname_in_list(const char *attname, List *va_cols); static int remattrmap_cmp(const void *v1, const void *v2); static bool match_attrmap(PGresult *res, @@ -5670,8 +5671,7 @@ postgresImportForeignStatistics(Relation relation, List *va_cols, int elevel) PQclear(remstats.rel); PQclear(remstats.att); - if (remattrmap) - pfree(remattrmap); + free_remattrmap(remattrmap, attrcnt); return ok; } @@ -5957,8 +5957,8 @@ build_remattrmap(Relation relation, List *va_cols, deparseStringLiteral(column_list, remote_attname); remattrmap[attrcnt].local_attnum = attnum; - strncpy(remattrmap[attrcnt].local_attname, attname, NAMEDATALEN); - strncpy(remattrmap[attrcnt].remote_attname, remote_attname, NAMEDATALEN); + remattrmap[attrcnt].local_attname = pstrdup(attname); + remattrmap[attrcnt].remote_attname = pstrdup(remote_attname); remattrmap[attrcnt].res_index = -1; attrcnt++; } @@ -5972,6 +5972,26 @@ build_remattrmap(Relation relation, List *va_cols, return remattrmap; } +/* + * Free the structure created by build_remattrmap(). + */ +static void +free_remattrmap(RemoteAttributeMapping *map, int len) +{ + if (!map) + return; + + for (int i = 0; i < len; i++) + { + Assert(map[i].local_attname); + pfree(map[i].local_attname); + Assert(map[i].remote_attname); + pfree(map[i].remote_attname); + } + + pfree(map); +} + /* * Test if an attribute name is in the list. *