From 99bf6eceafcde4c0498798d69fc0aae3eab7d297 Mon Sep 17 00:00:00 2001
From: Ashutosh Bapat <ashutosh.bapat.oss@gmail.com>
Date: Thu, 19 Jun 2025 10:39:35 +0530
Subject: [PATCH 2/2] Avoid bloating RelfilenumberMap cache by negative entries

RelidByRelfilenumber() has three users, logical replication, autoprewarm and
pg_filenode_relation(). The first two seldom cause negative entries in the cache
since they lookup a valid relation using valid tablespace and relfilenode.
Presence of negative entries doesn't help them.

But pg_filenode_relation(), which is SQL callable, may be invoked with
invalid tablespace and relfilenode pair causing negative entries in the
cache. These entries consume memory which is freed only when an
invalidation message is received. This can lead to bloating of the
cache. The function can be used for a denial of service attack since any
user can execute it.

This commit avoids bloating the cache by not adding negative entries.

Reported by: Ashutosh Bapat
Author: Ashutosh Bapat
---
 src/backend/utils/cache/relfilenumbermap.c | 12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)

diff --git a/src/backend/utils/cache/relfilenumbermap.c b/src/backend/utils/cache/relfilenumbermap.c
index 14c30276e24..f12f79694ad 100644
--- a/src/backend/utils/cache/relfilenumbermap.c
+++ b/src/backend/utils/cache/relfilenumbermap.c
@@ -60,13 +60,15 @@ RelfilenumberMapInvalidateCallback(Datum arg, Oid relid)
 	hash_seq_init(&status, RelfilenumberMapHash);
 	while ((entry = (RelfilenumberMapEntry *) hash_seq_search(&status)) != NULL)
 	{
+		/* Negative cache entries are not expected. */
+		Assert(OidIsValid(entry->relid));
+
 		/*
 		 * If relid is InvalidOid, signaling a complete reset, we must remove
 		 * all entries, otherwise just remove the specific relation's entry.
 		 * Always remove negative cache entries.
 		 */
 		if (relid == InvalidOid ||	/* complete reset */
-			entry->relid == InvalidOid ||	/* negative cache entry */
 			entry->relid == relid)	/* individual flushed relation */
 		{
 			if (hash_search(RelfilenumberMapHash,
@@ -175,7 +177,11 @@ RelidByRelfilenumber(Oid reltablespace, RelFileNumber relfilenumber)
 	entry = hash_search(RelfilenumberMapHash, &key, HASH_FIND, &found);
 
 	if (found)
+	{
+		/* Negative cache entries are not expected. */
+		Assert(OidIsValid(entry->relid));
 		return entry->relid;
+	}
 
 	/* ok, no previous cache entry, do it the hard way */
 
@@ -241,6 +247,10 @@ RelidByRelfilenumber(Oid reltablespace, RelFileNumber relfilenumber)
 			relid = RelationMapFilenumberToOid(relfilenumber, false);
 	}
 
+	/* Avoid bloating cache with negative entries. */
+	if (!OidIsValid(relid))
+		return relid;
+
 	/*
 	 * Only enter entry into cache now, our opening of pg_class could have
 	 * caused cache invalidations to be executed which would have deleted a
-- 
2.34.1

