# DF-0303 VERDICT: CARP HMAC Precomputed Context Torn-Read Race

## Verdict: REPRODUCED (code-confirmed)

## Mechanism
`carp_hmac_prepare()` at ip_carp.c:504 rewrites `sc->sc_sha1` (a SHA1_CTX)
in-place on whatever CPU calls it (typically CPU 0 during configuration via
SIOCSVH). The code itself admits the race at line 517:

```c
/* XXX: possible race here */
```

`carp_hmac_generate()` at ip_carp.c:556 does a `bcopy(&sc->sc_sha1,
&sha1ctx, sizeof(sha1ctx))` on the packet-input CPU (which can be any CPU).
`carp_hmac_verify()` at ip_carp.c:575 calls `carp_hmac_generate()`.

If `carp_hmac_prepare()` is running on CPU 0 (rewriting sc_sha1) while
`carp_hmac_generate()` is running on another CPU (reading sc_sha1 via
bcopy), the bcopy can read a partially-updated (torn) SHA1_CTX. This
produces an incorrect HMAC, causing legitimate advertisements to fail
verification. The result: a BACKUP rejects a valid MASTER's advertisement,
potentially disrupting failover.

The `sc_pad` array (used for the outer hash) has the same issue: it's
rewritten by `carp_hmac_prepare` (lines 520-523, 551-552) and read by
`carp_hmac_generate` (line 569) without synchronization.

## Impact
Timing-dependent failover disruption. An administrator reconfiguring CARP
(e.g., changing the key or addresses via ifconfig) while advertisements are
being processed can cause a torn SHA1_CTX, leading to HMAC verification
failure of legitimate advertisements. The BACKUP may fail to track the
MASTER, causing unnecessary failover or black-holing. Requires concurrent
configuration change + packet input.

## Dynamic Demonstration
This is a timing-dependent race condition. On this single QEMU guest:
- The CARP receive path cannot be exercised (QEMU doesn't loopback multicast)
- The race requires concurrent SIOCSVH ioctl + packet input on different CPUs
- The code explicitly admits the race with `/* XXX: possible race here */`

Confirmed by code analysis: `carp_hmac_prepare` writes `sc_sha1`/`sc_pad`
without holding any lock, while `carp_hmac_generate` reads them via bcopy
on a potentially different CPU.

## Fix
Added `crit_enter()/crit_exit()` around the critical sections in both
`carp_hmac_prepare()` (protecting the sc_sha1/sc_pad writes) and
`carp_hmac_generate()` (protecting the bcopy of sc_sha1). Combined with
the ASSERT_NETISR0 fix from DF-0302 (which serializes the input path to
CPU 0), this closes the torn-read window. See `fix.diff`.

## Kernel Refs
- sys/netinet/ip_carp.c:517 — `/* XXX: possible race here */`
- sys/netinet/ip_carp.c:526-530 — SHA1Init/Update writes to sc_sha1 in-place
- sys/netinet/ip_carp.c:551-552 — sc_pad rewrite
- sys/netinet/ip_carp.c:562 — `bcopy(&sc->sc_sha1, &sha1ctx, sizeof(sha1ctx))` — torn-read
