# DF-0916 - smb_time_unix2dos year-computation loop unbounded allows kernel livelock DoS

## Summary

`smb_time_unix2dos()` in `sys/vfs/smbfs/smbfs_subr.c:173` contains an
unbounded year-computation loop. The function takes a `struct timespec *`
from the caller and computes the year by iterating from 1970, subtracting
365/366 days per iteration, with no upper bound. On x86_64, where `u_long`
is 64 bits, an attacker-controlled `tv_sec` of `INT64_MAX` (or `-1`,
reinterpreted as `u_long = UINT64_MAX`) makes `days = t / 86400` astronomically
large and the loop run for ~3e11 / ~6e14 iterations — minutes-to-days of
kernel CPU per syscall. This is a kernel livelock DoS (CWE-834).

Reachability: the function is called from `smbfs_smb_setpattr` /
`smbfs_smb_setftime` (and friends) which are reached from `smbfs_setattr()`
via `VOP_SETATTR` whenever a process calls `utimensat()` / `futimens()` /
`utimes()` on an SMBFS-mounted file. The trigger requires the attacker to
have an SMBFS share mounted (default `vfs.usermount=0` means only root can
mount SMBFS; `vfs.usermount=1` + an attacker-owned SMB share is the
unprivileged path) and write/setattr permission on a file in it.

## Files in this evidence pack

| File | Purpose |
|---|---|
| `trigger.c`         | standalone demonstrator: copies the buggy year-loop body verbatim, instruments it with an iteration cap, and runs it on `tv_sec={0, INT64_MAX, -1}`. Sane value terminates instantly; malicious values hit the 5e8-iteration cap and would livelock the kernel (which has no cap). |
| `trigger_fixed.c`   | same as `trigger.c` but with the proposed 2-line clamp fix applied. Same malicious inputs now terminate in 137 iterations / ~15 µs. |
| `build.sh`          | builds both demonstrators inside the guest as the unprivileged user. |
| `run.sh`            | runs both before/after for three representative inputs. |
| `fix.diff`          | git-apply-able unified diff against `sys/vfs/smbfs/smbfs_subr.c`. |
| `build.log`         | standalone-test build output. |
| `run.log`           | standalone-test run output (before / after for all 3 inputs). |
| `fix_build.log`     | smbfs.ko module build output with the fix applied. |
| `fix_run.log`       | re-run of standalone test on the guest after the fixed smbfs.ko was installed + loaded (proves the same algorithm now terminates). |
| `env.txt`           | guest environment (uname, cc version, vfs.usermount, kldstat). |
| `VERDICT.md`        | the human-readable narrative. |
| `manifest.json`     | machine-readable catalog. |

## How to reproduce

Inside the guest (or any DragonFlyBSD/amd64 host):

```
cd findings/poc/DF-0916
./build.sh
./run.sh
```

The buggy column (`trigger`) shows `RESULT: LIVELOCK` for `INT64_MAX` and
`-1`; the fixed column (`trigger_fixed`) shows `RESULT: TERMINATED in 137
iterations` for the same inputs.

## Why standalone (and not end-to-end)

`smb_time_unix2dos()` is **pure arithmetic**: no locks, no kernel state
beyond the trivial `lasttime`/`lastday` cache, no system calls. Running the
verbatim loop body in userspace produces **exactly** the same iteration
count as the in-kernel call, so the userspace demonstrator is a faithful
reproduction of the algorithmic defect. (End-to-end via `utimensat` on an
SMBFS file additionally requires an SMB server, which is not present in
this audit guest.)

The kernel-side validation (apply fix.diff, build smbfs.ko, load) confirms
the patch compiles cleanly under `-Werror` and that the patched
`smb_time_unix2dos` symbol is present in the loaded module.
