| From: | PG Bug reporting form <noreply(at)postgresql(dot)org> |
|---|---|
| To: | pgsql-bugs(at)lists(dot)postgresql(dot)org |
| Cc: | 1950233439(at)qq(dot)com |
| Subject: | BUG #19672: int8shl/int8shr Undefined Behavior on Out-of-Range Shift Amounts |
| Date: | 2026-09-07 14:10:42 |
| Message-ID: | 19672-33e7d9d1be2229ea@postgresql.org |
| Views: | Whole Thread | Raw Message | Download mbox | Resend email |
| Thread: | |
| Lists: | pgsql-bugs |
The following bug has been logged on the website:
Bug reference: 19672
Logged by: Tianyu Shi
Email address: 1950233439(at)qq(dot)com
PostgreSQL version: 19beta3
Operating system: Ubuntu22.04
Description:
### Summary
`int8shl()` and `int8shr()` in `src/backend/utils/adt/int8.c` (lines
1255–1270) apply `arg1 << arg2` and `arg1 >> arg2` directly on `int64`
without validating the shift amount `arg2`. Under C11 §6.5.7, shifting by a
negative count, by a count ≥ 64, or left-shifting a signed value into
overflow are all undefined behavior. Every other bigint arithmetic operator
in PostgreSQL (`+`, `-`, `*`, unary `-`) raises `ERROR: bigint out of range`
on overflow, making the shift operators the sole exception and creating a
semantic inconsistency that can silently corrupt permission bitmasks or
financial calculations.
### PoC
Pure SQL — any authenticated database user can trigger the issue without
special privileges.
```sql
-- Shift count >= bit width: returns 1 instead of 0 or ERROR
SELECT 1::bigint << 64;
-- Negative shift count: returns INT64_MIN instead of ERROR
SELECT 1::bigint << -1;
-- Left-shift MAX_INT64 by 1: silently returns -2 instead of raising "bigint
out of range"
SELECT 9223372036854775807::bigint << 1;
-- Differential: multiplication raises a proper error for the same value
SELECT 9223372036854775807::bigint * 2;
-- ERROR: bigint out of range
-- Negative right-shift: returns 0 instead of ERROR
SELECT 1::bigint >> -1;
-- Right-shift count >= bit width: returns 1 instead of 0 or ERROR
SELECT 1::bigint >> 64;
```
```bash
# Initialize and start a UBSAN-instrumented PostgreSQL instance
./build/bin/initdb -D pgdata --no-locale -E UTF8
mkdir -p ./build/run
./build/bin/pg_ctl -D pgdata -l pgdata/postgres.log \
-o "-p 55433 -k ./build/run" start
# Run the PoC queries (Test 4 will error; that is expected)
./build/bin/psql -h ./build/run -p 55433 postgres <<'EOF'
SELECT 1::bigint << 64;
SELECT 1::bigint << -1;
SELECT 9223372036854775807::bigint << 1;
SELECT 9223372036854775807::bigint * 2;
SELECT 1::bigint >> -1;
SELECT 1::bigint >> 64;
EOF
# Inspect server log for UBSAN output
grep -E "runtime error|UndefinedBehaviorSanitizer|SUMMARY"
pgdata/postgres.log
./build/bin/pg_ctl -D pgdata stop
```
### Result
UndefinedBehaviorSanitizer fired in the PostgreSQL server log at both shift
sites: `int8.c:1260:2: runtime error: shift exponent 64 is too large for
64-bit type 'int64' (aka 'long')` and `int8.c:1269:2: runtime error: shift
exponent -1 is negative`. The SQL output confirms wrong values: `1::bigint
<< 64` returns `1` (expected `0` or error), `1::bigint << -1` returns
`-9223372036854775808` (expected error), and `9223372036854775807::bigint <<
1` returns `-2` (expected `ERROR: bigint out of range`). The differential
confirms the inconsistency: `9223372036854775807::bigint * 2` correctly
raises `ERROR: bigint out of range`, while the semantically equivalent
left-shift silently wraps.
| From | Date | Subject | |
|---|---|---|---|
| Next Message | PG Bug reporting form | 2026-09-07 14:12:15 | BUG #19673: inetmi_int8 Signed Integer Overflow Returns Wrong IPv6 Address |
| Previous Message | PG Bug reporting form | 2026-09-07 14:09:09 | BUG #19671: IPv4 CIDR Prefix Integer Overflow Bypasses Validation in inet/cidr Casts |