Signed-int overflow in oversized kmalloc size reconstruction (*kup << PAGE_SHIFT)
| Field | Value |
|---|---|
| ID | DF-0021 |
| Status | new |
| Severity | Low |
| CVSS 3.1 | CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:L/I:N/A:H |
| CWE | CWE-190 Integer Overflow or Wraparound |
| File | sys/kern/kern_slaballoc.c |
| Lines | 1202, 1261, 1432 |
| Area | kern |
| Confidence | likely |
| Discovered | 2026-06-29 |
| Reported | pending |
Summary
The oversized-allocation size is reconstructed in three places as
*kup << PAGE_SHIFT. btokup() returns int * (vm_page->ku_pagecnt is
int), and PAGE_SHIFT is int, so the shift is performed as a 32-bit
signed int. For any oversized allocation whose pagecount *kup >= 2^19
(i.e. any kmalloc() >= 2 GiB), the shift overflows โ wrapping to
INT_MIN sign-extended at exactly 2 GiB, or to 0 at 4 GiB+. The corrupted
size then drives krealloc's bcopy length, _kfree's
kmem_slab_free/vm_map_remove range, and kmalloc_usable_size's return
value. This is a latent memory-safety defect: no unprivileged kernel
interface is currently known to issue a โฅ2 GiB kmalloc, but the allocator
permits it (x86_64 ks_limit โ KvaSize/10), so any future/unaudited caller
activates it.
Root cause
btokup() (sys/kern/kern_slaballoc.c:136):
#define btokup(z) (&pmap_kvtom((vm_offset_t)(z))->ku_pagecnt) /* int * */
ku_pagecnt is int (sys/vm/vm_page.h:189). The size reconstruction:
sys/kern/kern_slaballoc.c:1202(krealloc):osize = *kup << PAGE_SHIFT;โbcopy(ptr, nptr, min(size, osize));(:1207), thenkfree(ptr)(:1208) takes the oversized path which recomputes the same wrapped size at:1432.sys/kern/kern_slaballoc.c:1432(_kfree oversized):size = *kup << PAGE_SHIFT;โkmem_slab_free(ptr, size)โvm_map_remove(kernel_map, ptr, ptr+size)with a bogus/wrappedsize.sys/kern/kern_slaballoc.c:1261(kmalloc_usable_size):size = *kup << PAGE_SHIFT;โ returned to callers that use it for bounds accounting.
_kmalloc stores *kup = size / PAGE_SIZE (:954), which does not truncate
for realistic KVA sizes, so the bug is purely in the readback/shift, not
the store. Threshold: any kmalloc >= 0x80000000 (2 GiB) takes the oversized
path (:942 size >= ZoneLimit), writes *kup >= 2^19, and overflows on
readback.
Threat model & preconditions
- Attacker position: no demonstrated attacker-reachable trigger.
Requires a kernel subsystem to
kmalloc() >= 2 GiBand thenkrealloc/kfreeit. Most large-buffer paths usekmem_alloc/contigmallocinstead. The defect is latent โ recorded so it is tracked and the trivial fix applied before an unaudited/future caller turns it into a live vulnerability. - Privileges gained or impact (if reached): on
krealloca read-OOBbcopy(info leak of neighboring kmem or page-fault panic); onkfreeavm_map_removewith a wrapped range (panic / vm_map corruption); onkmalloc_usable_sizea wrong return enabling a write overflow in a caller that trusts it. - Required config or capabilities: x86_64 pc64 (KvaSize large enough that
ks_limitpermits โฅ2 GiB allocations); a willing caller. - Reachability: latent; needs a โฅ2 GiB
kmalloccaller (not known to be attacker-reachable today).
Proof of concept
PoC source: findings/poc/DF-0021/kmalloc_shift.c (kernel module; root to
kldload).
Build & run (root, disposable VM with โฅ~2 GiB free KVA)
cc -I/sys -DKERNEL -c findings/poc/DF-0021/kmalloc_shift.c ld -r kmalloc_shift.o -o kmalloc_shift.ko kldload ./kmalloc_shift.ko
Expected output (bug present)
On krealloc(p, 4 GiB) the osize wraps, bcopy reads 4 GiB from a 2 GiB
object (read-OOB / page-fault panic), and the subsequent kfree feeds a
wrapped size into kmem_slab_free (vm_map_remove bogus range โ panic /
vm_map corruption).
Impact
Latent. The code defect is certain and the threshold (โฅ2 GiB kmalloc) is
permitted by the allocator on x86_64, so it is a real landmine for any caller
that requests such an allocation. Rated Low because no attacker-reachable
trigger is known; the one-line fix removes the risk entirely.
Recommended fix
Cast to size_t before the shift at all three sites:
--- a/sys/kern/kern_slaballoc.c
+++ b/sys/kern/kern_slaballoc.c
@@ -1202 +1202 @@
- osize = *kup << PAGE_SHIFT;
+ osize = (size_t)*kup << PAGE_SHIFT;
@@ -1261 +1261 @@
- size = *kup << PAGE_SHIFT;
+ size = (size_t)*kup << PAGE_SHIFT;
@@ -1432 +1432 @@
- size = *kup << PAGE_SHIFT;
+ size = (size_t)*kup << PAGE_SHIFT;
References
sys/kern/kern_slaballoc.c:1202,1261,1432โ the overflowing shift.sys/kern/kern_slaballoc.c:136โbtokupreturnsint *.sys/vm/vm_page.h:189โint ku_pagecnt.- CWE-190 Integer Overflow or Wraparound.
Timeline
- 2026-06-29 Discovered during automated file-by-file audit of
sys/kern/kern_slaballoc.c. - pending Reported to DragonFlyBSD security contact.