# DF-0476 — ipfw3 register_module strncpy bounded by strlen not sizeof

## Verdict
**REPRODUCED** — confirmed live via KLD module load with a 40-char name. Fix validated.

## Mechanism
`ip_fw3_register_module` (`sys/net/ipfw3/ip_fw3.c:170-186`) registers a module in the global `fw3_modules[]` array. The slot name copy is:

```c
strncpy(tmp->name, module_name, strlen(module_name));
```

`tmp->name` is `char name[20]` (`sys/net/ipfw3/ip_fw3.h:493-497`). The strncpy length argument is `strlen(module_name)` — the **source** length, not `sizeof(tmp->name)`. If `module_name >= 20` chars:
- The write overflows `name[20]` into the adjacent `fw3_modules` slot's `type`/`id` fields (or past the array into `fw3_sync_ctx`).
- `strncpy` with `strlen(src)` **never appends a NUL terminator**, so `name` is left un-terminated.

On a subsequent `IP_FW_MODULE` getsockopt (`ip_fw3_ctl_get_modules`, `ip_fw3.c:972-989`), `strcat(module_str, mod->name)` reads `mod->name` past the 20-byte boundary until it hits a zero byte — leaking adjacent kernel memory into the returned module string, or overflowing the 1024-byte `module_str` buffer.

## Live reproduction (unfixed #0 kernel)
1. Built a tiny KLD module `df0476_mod.ko` that calls `ip_fw3_register_module(7, "AAAAAAAAAAAAAAAAAAAA1234567890123456789012")` (42 chars).
2. `sysctl net.filters_default_to_accept=1; kldload ipfw3; kldload df0476_mod`.
3. The overflow propagated to userspace: the `ipfw3 show` CLI (which `dlopen`s `libipfw3<name>.so` for each registered module) tried to open:
   ```
   /usr/lib/libipfw3AAAAAAAAAAAAAAAAAAAA1234567890123456789012.so
   ```
   — the **full 42-char overflowed name**, proving `name[]` was not NUL-terminated and `strcat` read past the 20-byte boundary. dmesg confirms `"ipfw3 module AAAAAAAAAAAAAAAAAAAA1234567890123456789012 loaded"`.

## Evidence
- `df0476_mod.c` + `Makefile` — the KLD module that triggers the overflow.
- `df0476_read.c` — userspace helper to read back the module list.
- `run.log` — the dmesg + CLI output showing the overflowed name propagation.

## Threat model
Root loads a KLD module whose `init_module` calls `ip_fw3_register_module` with a name ≥ 20 chars. The overflow corrupts adjacent global data. The un-terminated `name[]` then leaks adjacent kernel memory to any process that can issue `IP_FW_MODULE` getsockopt (root via raw socket). Impact: global data corruption + info leak. (A buggy or malicious KLD module is the trigger; this is a root-configurable path, not unprivileged.)

## Fix
`fix.diff` — replace `strncpy(tmp->name, module_name, strlen(module_name))` with `strlcpy(tmp->name, module_name, sizeof(tmp->name))`. strlcpy is bounded by the destination size and always NUL-terminates. Validated: built fixed `ipfw3.ko`, loaded `df0476_mod` — the CLI now tries `libipfw3AAAAAAAAAAAAAAAAAAA.so` (**19 chars**, truncated by `strlcpy` to `sizeof(name)-1 = 19`), proving the overflow is prevented.

## Fix validation
- **Before**: CLI tried `libipfw3AAAAAAAAAAAAAAAAAAAA1234567890123456789012.so` (42 chars — overflowed past `name[20]`).
- **After**: CLI tried `libipfw3AAAAAAAAAAAAAAAAAAA.so` (19 chars — truncated by `strlcpy`).

## Build / Run
```
cd df0476_mod && make                # build the KLD module
cc -o df0476_read df0476_read.c      # build the reader
# sysctl net.filters_default_to_accept=1; kldload ipfw3; kldload ./df0476_mod/df0476_mod.ko
# dmesg | tail ; ipfw3 show 2>&1 | grep libipfw3
```
