sysctl_handle_sb_max() maintains u_long sb_max through sizeof(int) transfers β 8-byte writes silently truncated by 4 GiB, sb_max can never represent its declared type
| Field | Value |
|---|---|
| ID | DF-2837 |
| Status | new |
| Severity | Info |
| CVSS 3.1 | CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:U/C:N/I:N/A:N |
| CWE | CWE-704 Incorrect Type Conversion or Cast |
| File | sys/kern/uipc_socket2.c |
| Lines | 686-704 (decl :75) |
| Area | kern |
| Confidence | certain |
| Discovered | 2026-08-31 |
| Pass | 2 (GLM 5.3 second pass) |
| Bucket | base:kern |
| Reported | pending |
| Known CVE | none |
| CVE match | novel |
Summary
The kern.ipc.maxsockbuf handler does SYSCTL_OUT/SYSCTL_IN of sizeof(int) against the u_long sb_max. Demonstrated as root: an 8-byte sysctl write of 0x100001000 returns success and stores only the low half (sb_max=0x1000); a write of 0x100080000 stores 0x80000 β magnitude silently wrong by exactly 4 GiB with rc=0. The read side reports only the low 32 bits. Because this is sb_max's only writer the high 32 bits are provably always 0, so no out-of-bounds sb_max is reachable β type-correctness/hardening with no demonstrated security impact. Fix: round-trip through an explicit int temporary so stored/validated/reported agree (validated diff in the pack).
Timeline
- 2026-08-31 Discovered during pass-2 audit of uipc_socket2.c (GLM 5.3); truncation demonstrated live same run.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-2837 Β· 10 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 1.7 KB | β raw | |
| VERDICT.md | β | 1.5 KB | β raw | |
| sbmax.c | β | 2.1 KB | view raw | |
| build.sh | β | 33 B | view raw | |
| run.sh | β | 18 B | view raw | |
| run.log | β | 372 B | view raw | |
| fix.diff | β | 662 B | view raw | |
| env.txt | β | 261 B | view raw | |
| manifest.json | β | 594 B | view raw | |
| verdict.json | β | 2.5 KB | view raw |
DF-2837 β sysctl_handle_sb_max() maintains u_long sb_max through sizeof(int) operations
What
sys/kern/uipc_socket2.c:686-704: the handler for kern.ipc.maxsockbuf
does SYSCTL_OUT(req, arg1, sizeof(int)) / SYSCTL_IN(req, arg1,
sizeof(int)) against the u_long sb_max (8 bytes on amd64, declared at
line 75). Type confusion with two observable consequences:
- An 8-byte sysctl write is silently accepted and only its low 4 bytes are stored β the upper half is dropped without error.
sb_maxcan therefore never hold (nor report) a value β₯ 4 GiB; any attempted larger value silently wraps to its low-32 magnitude (subject only to thesb_max < MSIZE + MCLBYTESfloor check applied after truncation).
Root-only writer; hardening/correctness finding, no security boundary crossed (the high 32 bits are provably always 0 because this is the only writer and it only writes the low half).
Reproduce (root)
cc -O -o sbmax sbmax.c && ./sbmax
Observed (stock kernel, run.log):
write #1: 8-byte value 0x0000000100001000 -> sysctl returned 0 (ok) after ... sb_max = 4096 (0x1000) <-- low 32 bits kept, high half silently dropped write #2: 8-byte 0x100080000 -> ret=0, sb_max = 524288 (0x80000) <-- magnitude wrong by 4 GiB restored: sb_max = 524288
Fix
fix.diff β round-trip through an explicit int temporary so the stored
value is always the value that was validated and reported. (Behavior for
well-formed int-width sysctl use is unchanged, so no kernel rebuild
validation was performed for this Info finding; the diff is compile-checked
in-guest as part of the DF-2836/2838 validation build.)
Files
sbmax.c,build.sh,run.sh,run.log,fix.diff,env.txt
DF-2837 VERDICT
Status: reproduced (type confusion manifests exactly as derived; root-only writer) Impact: none β hardening/correctness; no security boundary crossed.
Root cause (path:line)
sys/kern/uipc_socket2.c:686-704 β sysctl_handle_sb_max() operates on the
u_long sb_max (declared at line 75) with sizeof(int) (4-byte)
SYSCTL_OUT/SYSCTL_IN transfers:
- an 8-byte write is accepted silently and only its low 4 bytes stored;
sb_maxcan never represent or report a value β₯ 4 GiB β attempted larger values silently take their low-32 magnitude (thesb_max < MSIZE+MCLBYTESfloor is applied after truncation);- the read side reports only the low 32 bits.
Because this handler is sb_max's only writer, the high 32 bits stay 0
forever, so the confusion cannot be turned into an out-of-bounds sb_max β
the demonstrated impact is silent magnitude corruption of a root-settable
limit (run.log: writing 0x100080000 yields sb_max=0x80000, wrong by
exactly 4 GiB, with return code 0).
Reproduction
./sbmax as root (run.log, restored afterwards). Behavior on the stock
kernel matches the derivation exactly.
Fix
fix.diff round-trips the value through an explicit int temporary so what
is validated, stored and reported are the same quantity. No kernel-rebuild
validation was performed for this Info finding (behavior for well-formed
int-width use is identical by construction); the diff compiles in-guest as
part of the DF-2836/DF-2838 validation build.
Fix verification
not_testableInfo-severity finding; no behavioral kernel validation performed - for well-formed int-width sysctl use the patched behavior is identical by construction. The fix.diff was applied to the guest's /usr/src copy after the DF-2836/DF-2838 validation runs and compile-checked with the kernel's own preprocessor flags in the kernel objdir (cc -fsyntax-only ... rc=0).
['findings/poc/DF-2837/fix.diff']
Confirmed kernel references
Detail
Evidence (decisive lines)
["run.log: 'write #1 ... -> sysctl returned 0 (ok)' then 'sb_max = 4096 (0x1000) <-- low 32 bits kept, high half silently dropped'", "run.log: 'write #2 ... sb_max = 524288 (0x80000) <-- became 0x80000, magnitude wrong by 4 GiB'"]
PoC changes
Seed written fresh; no changes needed after the first run.
Verified recommended fix
Round-trip the sysctl value through an explicit int temporary in sysctl_handle_sb_max so the stored, validated and reported values agree (see fix.diff).
Verdict
sysctl_handle_sb_max (uipc_socket2.c:686-704) maintains the u_long sb_max with sizeof(int) SYSCTL_IN/SYSCTL_OUT transfers. Demonstrated as root: an 8-byte sysctl write of 0x100001000 returns success and stores only the low half (sb_max=0x1000); a write of 0x100080000 stores 0x80000 - magnitude silently wrong by 4 GiB with rc=0. The high 32 bits of sb_max are provably always zero (this handler is the only writer), so no out-of-bounds sb_max is reachable; impact is silent corruption of a root-settable limit plus a truncated read side. Hardening finding: reproduced exactly as derived, no security impact.
No comments yet.