| From: | Andrey Rachitskiy <pl0h0yp1(at)gmail(dot)com> |
|---|---|
| To: | 1950233439(at)qq(dot)com, pgsql-bugs(at)lists(dot)postgresql(dot)org |
| Subject: | Re: BUG #19671: IPv4 CIDR Prefix Integer Overflow Bypasses Validation in inet/cidr Casts |
| Date: | 2026-09-10 12:42:08 |
| Message-ID: | CAB8bMitHLMbrhLkyFHFogAV-W433Db2wrGz80Gmk50Fbcm8JnQ@mail.gmail.com |
| Views: | Whole Thread | Raw Message | Download mbox | Resend email |
| Thread: | |
| Lists: | pgsql-bugs |
чт, 10 сент. 2026 г. в 17:11, PG Bug reporting form <noreply(at)postgresql(dot)org
>:
> The following bug has been logged on the website:
>
> Bug reference: 19671
> Logged by: Tianyu Shi
> Email address: 1950233439(at)qq(dot)com
> PostgreSQL version: 19beta3
> Operating system: Ubuntu22.04
> Description:
>
> ### Summary
>
> 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` <http://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` <http://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` <http://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.
Fix in attachment.
| Attachment | Content-Type | Size |
|---|---|---|
| 0001-Fix-IPv4-CIDR-prefix-length-overflow-in-inet_net_pto.patch | text/x-patch | 10.2 KB |
| From | Date | Subject | |
|---|---|---|---|
| Next Message | Andrey Rachitskiy | 2026-09-10 13:28:22 | Re: Hunspell AF alias count silently wraps to a smaller value |
| Previous Message | mailinglists | 2026-09-10 09:47:31 | unexpected behaviour generating queryid |