# DF-0803 — Integer-truncation in `e2fs_gcount`

Integer-truncation bug in `ext2_vfsops.c:620` (`ext2_compute_sb_data`).
The kernel computes the number of block groups as

```c
fs->e2fs_gcount = howmany(fs->e2fs_bcount - first_dblock,
                          EXT2_BLOCKS_PER_GROUP(fs));
```

but `e2fs_gcount` is `uint32_t` (`ext2fs.h:175`) while `e2fs_bcount` is
`uint64_t` (`ext2fs.h:159`). `howmany` returns `uint64_t`, so the
implicit narrowing to `uint32_t` discards the high 32 bits **before**
the post-assignment check at `ext2_vfsops.c:622` ever sees the value.
That check (`if (gcount > 2^32 - DESC_PER_BLOCK)`) only catches the top
64 values of the `uint32_t` range — every wrap-through-zero (or any
other wrap) bypasses it.

## Variants

| Variant | `howmany` true value | `gcount` stored | Effect |
|---------|---------------------|-----------------|--------|
| `g0`    | `2^32`              | 0               | `malloc(0)` → `ZERO_LENGTH_PTR (-8)`; mount "succeeds"; first `ls` derefs `e2fs_gd[0]` at addr `0x20` → **panic** |
| `g1`    | `2^32 + 1`          | 1               | `malloc(4096)` (64 GD entries); only GD[0] validated; mount proceeds silently |
| `g64`   | `2^32 + 64`         | 64              | fills the 4096-byte allocation exactly |

## Files in this folder

- `craft_img.py` — produces crafted ext2 images from a `mke2fs` base.
- `harness.c` — deterministic userspace transcription of the kernel arithmetic.
- `df0803_g0.img`, `df0803_g1.img` — crafted images (built via `craft_img.py`).
- `build.sh`, `run.sh` — reproducible build & run.
- `VERDICT.md` — full narrative analysis.
- `panic.txt` — kernel panic signature from `boot.log` (baseline #0).
- `fix.diff` — the verified fix (`git apply`-able).
- `fix_build.log`, `fix_run.log` — Phase 8 build + validation logs.
- `manifest.json` — artifact catalog.

## How to reproduce

```sh
# 1. Craft the images (host with mke2fs)
python3 craft_img.py gcount=0 df0803_g0.img
python3 craft_img.py gcount=1 df0803_g1.img

# 2. On the DragonFly guest (as root, with /dev/vn available)
vnconfig -c vn0 /path/to/df0803_g0.img
mount_ext2fs -o ro /dev/vn0 /mnt     # succeeds
ls /mnt                              # PANIC: e2fs_gd_get_i_tables @ movl 0x28(%rdi)
```

## Expected outcome (per variant)

- **Unpatched kernel (`#0` GENERIC):** `gcount=0` panics on first
  `ls` with `Stopped at e2fs_gd_get_i_tables: movl 0x28(%rdi),%eax`,
  fault virtual address `0x20`.
- **Patched kernel + module (this `fix.diff`):** both `gcount=0` and
  `gcount=1` are rejected with `mount_ext2fs: …: Invalid argument`
  (`EINVAL`); no panic, no silent mount. Normal ext2 images mount
  unchanged.

## Fix summary

1. Compute `gcount` as `uint64_t` *before* the narrowing assignment;
   reject `gcount64 == 0` and the existing upper bound.
2. Add `ext2_vget` bounds check: `if (ino < EXT2_ROOTINO ||
   ino_to_cg(fs, ino) >= fs->e2fs_gcount) return EINVAL;` before any
   `e2fs_gd[…]` dereference.

See `fix.diff` and `VERDICT.md` for details.
