# DF-2956 — post-2107 tv_sec silently wraps year field / truncates to 32 bits in timespec2fattime

## Where

`sys/kern/subr_fattime.c:157` and `:173` (`timespec2fattime`):

```c
t2 = t1 / DAY;          /* :157  int64 -> unsigned 32-bit TRUNCATION */
...
l = t2 / LYC;
*ddp = (l * 4) << 9;    /* :173  year field is 7 bits; l*4 > 127 wraps mod 2^16 */
```

Two stacked overflows on the write path:

1. **Year-field width**: dates past 2107-12-31 need `l*4 > 127`; `(l*4)<<9`
   wraps modulo 2^16 when stored into the `uint16_t` date word, aliasing
   far-future dates onto *arbitrary* dates in 1980..2107.
2. **64→32 truncation**: for `tv_sec >= 2^32*86400` (~year 14.5M) the
   `unsigned t2 = t1 / DAY` assignment truncates, aliasing astronomically
   far dates onto arbitrary recent ones.

Reachable by an **unprivileged** file owner: `itimespecfix`
(sys/kern/kern_time.c:1047) enforces only `tv_sec >= 0`, no upper bound;
`kern_futimens` gates on file-owner-or-write (vfs_syscalls.c:3847);
`msdosfs_setattr` (msdosfs_vnops.c:400-421) allows explicit utimes when
`cr_uid == pmp->pm_uid` — i.e. any user-uid-mounted msdosfs (desktop
removable-media automounts use the console user's uid).

Distinct from known DF-0200 (which is the *negative* tv_sec path).

## Observed (stock INVARIANTS kernel #0)

| requested utimes() | on-disk MDate | stat shows |
|---|---|---|
| 2108-01-01 00:00Z | `0x0021` (1980-01-01) | 1980-01-01 |
| 4147-08-08 08:00Z | `0xeef8` | 2099-07-24 |
| year 4461763 | `0x5777` (2023-11-23) | **2023-11-23** (plausible!) |

A timestamp of year ~4.4 million is silently stored and reported as
**2023-11-23 05:22:06** — indistinguishable from a genuine recent mtime.

## Reproduce

Same procedure as DF-2955 (see run.sh); use the X cases:

```sh
./fattime_poc /mnt/X1 4354819200       # 2108-01-01 -> 1980-01-01
./fattime_poc /mnt/X3 140737488355327  # year 4.4M  -> 2023-11-23
umount /mnt && ./findentry /tmp/fat.img X1   # MDate bytes 21 00
```

## Fix

`fix.diff` adds a saturating clamp after `t2 -= T1980`:

```c
if (t2 > T2107)		/* ((2108-1980)*YEAR + 31 - 1) */
	t2 = T2107;
```

Validated on rebuilt kernel #1: all post-2107 inputs saturate to 2107-12-31
(preserving time-of-day), on-disk encoding `0xff9f`; the 1980/2023 aliases
are gone (poc_fixed.log).
