# DF-2819 — kmalloc_obj zone-limit accounting uses cacheline-aligned objsize while callers derive limits from unaligned sizeof → tmpfs inode quota exhausted at ~85% of configured cap

## What this is

Reproduction of a resource-accounting defect in the `kmalloc_obj` zone
allocator (`sys/kern/kern_kmalloc.c`). The allocator accounts per-object
memory use as the **cache-line-aligned** `ks_objsize`
(`__VM_CACHELINE_ALIGN(objsize)`, applied at zone creation in
`_kmalloc_create_obj`, sys/kern/kern_slaballoc.c:610; accounting at
sys/kern/kern_kmalloc.c:899 `use->memuse += size`), but callers cannot
learn `ks_objsize` (no accessor exists), so the only caller that derives a
zone limit from an object count — tmpfs — computes it from the
**unaligned** `sizeof(struct tmpfs_node) * tm_nodes_max`
(sys/vfs/tmpfs/tmpfs_vfsops.c:206-207).

`sizeof(struct tmpfs_node) = 272`, aligned objsize = **320**
(measured on the guest with a KLD probe: `SZPROBE node=272 nodealign=320`).
The zone therefore "fills" at `272/320 = 85%` of the promised inode count.

## How to reproduce (as performed on the QEMU guest)

1. Build `szprobe` KLD (optional, proves sizes):
   `cd /root/szprobe && make` → `kldload ./szprobe.ko` → dmesg shows
   `SZPROBE node=272 nodealign=320 dirent=88 direntalign=128`.
2. Mount a tmpfs whose raised limit exceeds the default floor
   (`kmem_lim_size()/10` = 390 MiB on this guest):
   `mount -t tmpfs -o size=8g,inodes=1600000 none /mnt/tfill`
   → raised limit = 1,600,000 × 272 = 435,200,000 B = 415.04 MiB.
3. As an unprivileged user (`nobody`), create empty files until failure:
   `/mnt/tfill/fill /mnt/tfill 1500000` (see `fill.c`).
4. Expected (observed): ENOSPC at **1,360,369** files — exactly the first
   `k` where `k × 320 ≥ 435,200,000` (k = 1,360,000) — i.e. 85.02% of
   the 1,600,000-inode cap. `vmstat -m` at that moment shows
   `tmpfs_node  MemUse 415M  Limit 415M` (memuse == limit).
5. tmpfs's own inode cap never comes into play (`tm_nodes_inuse` = 1.36M
   < 1.6M), so the filesystem reports ENOSPC while it still advertises
   ~240K free inodes.

## Files

- `fill.c` — file-creation loop (the trigger), built with `cc -O2`
- `szprobe.c` — KLD printing `sizeof(struct tmpfs_node)` and its
  cacheline alignment
- `run.log` — decisive run: progress lines + `stopped at 1360369: No space
  left on device`
- `vmstat.txt` — `vmstat -m` after the run: `tmpfs_node` MemUse == Limit
- `env.txt` — guest uname, mount, compiler, sysctls
- `fix.diff` — one-line fix (tmpfs raises the limit using the aligned size)
- `VERDICT.md`, `manifest.json`, `verdict.json`

## Impact

Quota-correctness / availability: any tmpfs (including default-mounted
`/tmp` — the guest's `/tmp` has a default node cap of ~3.14M and would
ENOSPC at ~2.67M files) delivers only ~85% of its configured/default
inode budget. The allocation failure path is graceful (`M_NULLOK` at
sys/vfs/tmpfs/tmpfs_subr.c:110-111 → ENOSPC), so there is **no panic and
no memory-safety impact**; severity Low.
