From 0b6f1d35290cb98a3263ee95c899aee0a55d90f9 Mon Sep 17 00:00:00 2001
From: Egor Ivkov <e.ivkov@arenadata.io>
Date: Tue, 22 Sep 2026 22:44:05 +0300
Subject: [PATCH v1] pg_combinebackup: make the OID range check in parse_oid()
 effective

parse_oid() assigned the result of strtoul() to an Oid variable before
range-checking it, so the value had already been truncated to 32 bits by
the time "oid > PG_UINT32_MAX" was evaluated.  On platforms where
unsigned long is wider than 32 bits that test is dead code, and an
out-of-range string is accepted as its truncated value instead of being
rejected: "4294967297" is accepted as OID 1, and "-1" is accepted as OID
4294967295.

Keep the parsed value in an unsigned long until it has been checked, and
cast to Oid only afterwards.  This is the same shape as
parse_relfilenumber() in pg_upgrade, which gets it right.

parse_oid() is only fed directory names found under pg_tblspc, so the
practical consequence is limited to pg_combinebackup treating a bogus
directory name as a valid tablespace OID instead of ignoring it.
---
 src/bin/pg_combinebackup/pg_combinebackup.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/bin/pg_combinebackup/pg_combinebackup.c b/src/bin/pg_combinebackup/pg_combinebackup.c
index 254a27b125b..600962f1aec 100644
--- a/src/bin/pg_combinebackup/pg_combinebackup.c
+++ b/src/bin/pg_combinebackup/pg_combinebackup.c
@@ -816,7 +816,7 @@ help(const char *progname)
 static bool
 parse_oid(char *s, Oid *result)
 {
-	Oid			oid;
+	unsigned long oid;
 	char	   *ep;
 
 	errno = 0;
@@ -824,7 +824,7 @@ parse_oid(char *s, Oid *result)
 	if (errno != 0 || *ep != '\0' || oid < 1 || oid > PG_UINT32_MAX)
 		return false;
 
-	*result = oid;
+	*result = (Oid) oid;
 	return true;
 }
 
-- 
2.43.0

