From 6bfde9b4291ed1605c75fe961c040bec48d7dd46 Mon Sep 17 00:00:00 2001
From: Iliia Khaprov <i.khaprov@gmail.com>
Date: Tue, 20 Oct 2026 11:57:20 +0200
Subject: [PATCH] Reject IPv4 prefix lengths that overflow int

The IPv4 parsers accumulate the prefix length in an int and check
"bits > 32" only after the last digit, so a long enough number wraps
and passes the check: '192.168.0.0/4294967320'::inet is accepted as
192.168.0.0/24, and '10.0.0.0/4294967304'::cidr as 10.0.0.0/8.

Check the range inside the digit loop, as getbits() does for IPv6.
---
 src/backend/utils/adt/inet_net_pton.c |  8 ++++----
 src/test/regress/expected/inet.out    | 13 +++++++++++++
 src/test/regress/sql/inet.sql         |  3 +++
 3 files changed, 20 insertions(+), 4 deletions(-)

diff --git a/src/backend/utils/adt/inet_net_pton.c b/src/backend/utils/adt/inet_net_pton.c
index 3b0db2a3799..0b8a007c921 100644
--- a/src/backend/utils/adt/inet_net_pton.c
+++ b/src/backend/utils/adt/inet_net_pton.c
@@ -180,11 +180,11 @@ inet_cidr_pton_ipv4(const char *src, u_char *dst, size_t size)
 			assert(n >= 0 && n <= 9);
 			bits *= 10;
 			bits += n;
+			if (bits > 32)
+				goto emsgsize;
 		} while ((ch = *src++) != '\0' && isdigit((unsigned char) ch));
 		if (ch != '\0')
 			goto enoent;
-		if (bits > 32)
-			goto emsgsize;
 	}
 
 	/* Fiery death and destruction unless we prefetched EOS. */
@@ -300,11 +300,11 @@ inet_net_pton_ipv4(const char *src, u_char *dst)
 			assert(n >= 0 && n <= 9);
 			bits *= 10;
 			bits += n;
+			if (bits > 32)
+				goto emsgsize;
 		} while ((ch = *src++) != '\0' && isdigit((unsigned char) ch));
 		if (ch != '\0')
 			goto enoent;
-		if (bits > 32)
-			goto emsgsize;
 	}
 
 	/* Fiery death and destruction unless we prefetched EOS. */
diff --git a/src/test/regress/expected/inet.out b/src/test/regress/expected/inet.out
index 1705bff4dd3..01cc0967e8b 100644
--- a/src/test/regress/expected/inet.out
+++ b/src/test/regress/expected/inet.out
@@ -1156,6 +1156,19 @@ SELECT * FROM pg_input_error_info('192.168.198.200/24', 'cidr');
  invalid cidr value: "192.168.198.200/24" | Value has bits set to right of mask. |      | 22P02
 (1 row)
 
+-- prefix length that overflows int
+SELECT pg_input_is_valid('192.168.0.0/4294967320', 'inet');
+ pg_input_is_valid 
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_is_valid('10.0.0.0/4294967304', 'cidr');
+ pg_input_is_valid 
+-------------------
+ f
+(1 row)
+
 SELECT pg_input_is_valid('1234', 'inet');
  pg_input_is_valid 
 -------------------
diff --git a/src/test/regress/sql/inet.sql b/src/test/regress/sql/inet.sql
index 8f276856df9..104d5eb51fb 100644
--- a/src/test/regress/sql/inet.sql
+++ b/src/test/regress/sql/inet.sql
@@ -270,5 +270,8 @@ SELECT * FROM pg_input_error_info('1234', 'cidr');
 SELECT pg_input_is_valid('192.168.198.200/24', 'cidr');
 SELECT * FROM pg_input_error_info('192.168.198.200/24', 'cidr');
 
+-- prefix length that overflows int
+SELECT pg_input_is_valid('192.168.0.0/4294967320', 'inet');
+SELECT pg_input_is_valid('10.0.0.0/4294967304', 'cidr');
 SELECT pg_input_is_valid('1234', 'inet');
 SELECT * FROM pg_input_error_info('1234', 'inet');
-- 
2.53.0

