โฌข DragonFlyBSD Kernel Audit
โ† dashboard
DF-0021

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), then kfree(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/wrapped size.
  • 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 GiB and then krealloc/kfree it. Most large-buffer paths use kmem_alloc/contigmalloc instead. 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 krealloc a read-OOB bcopy (info leak of neighboring kmem or page-fault panic); on kfree a vm_map_remove with a wrapped range (panic / vm_map corruption); on kmalloc_usable_size a wrong return enabling a write overflow in a caller that trusts it.
  • Required config or capabilities: x86_64 pc64 (KvaSize large enough that ks_limit permits โ‰ฅ2 GiB allocations); a willing caller.
  • Reachability: latent; needs a โ‰ฅ2 GiB kmalloc caller (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.

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

Timeline

  • 2026-06-29 Discovered during automated file-by-file audit of sys/kern/kern_slaballoc.c.
  • pending Reported to DragonFlyBSD security contact.