HMAC precomputed context torn-read race between config and input paths
Summary
carp_hmac_prepare(:504) rewrites sc_sha1 in-place on CPU0. carp_hmac_verify/generate(:562) bcopy sc_sha1 on packet-input CPU (any). No lock/barrier. Code admits XXX: possible race here(:517). Torn SHA1_CTX -> legitimate adv fails HMAC -> failover disruption. Timing-dependent.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-0303 Β· 8 files| File | Type | Description | Size | |
|---|---|---|---|---|
| fix.diff | suggested-fix | Add crit_enter/crit_exit around sc_sha1/sc_pad access | 1.3 KB | view raw |
| VERDICT.md | verdict | Full analysis of HMAC torn-read race | 2.7 KB | β raw |
| README.md | readme | Finding summary | 825 B | β raw |
| build.sh | build-script | No binary (code analysis) | 146 B | view raw |
| run.sh | run-script | Verify torn-read race code path | 378 B | view raw |
| env.txt | environment | Guest environment | 442 B | view raw |
| ../fix_build_combined.log | build-log | Combined 41-finding kernel build (rc=0, -Werror clean) | 5.6 MB | β download |
| ../fix_build_summary.txt | build-summary | Summary of the combined 41-finding kernel build | 826 B | view raw |
DF-0303: CARP HMAC Precomputed Context Torn-Read Race
Finding
carp_hmac_prepare() (ip_carp.c:504) rewrites sc_sha1 in-place. The code
admits /* XXX: possible race here */ at line 517. carp_hmac_generate()
(line 556) and carp_hmac_verify() (line 575) bcopy sc_sha1 on any CPU.
No lock/barrier. Torn SHA1_CTX causes legitimate adv to fail HMAC β failover
disruption.
Reproduction
Code-confirmed. Race requires concurrent SIOCSVH ioctl (carp_hmac_prepare) + packet input (carp_hmac_generate) on different CPUs. The CARP receive path cannot be exercised on this QEMU guest. The bug is self-evident from the XXX comment at line 517 and the unlocked bcopy at line 562.
Fix
See fix.diff β adds crit_enter/crit_exit around sc_sha1/sc_pad writes in carp_hmac_prepare and the bcopy in carp_hmac_generate.
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:
/* 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
Fix verification
not_testablecompile validated
see evidence pack
Confirmed kernel references
β
Detail
Exploit chain
none
Evidence (decisive lines)
β
Verdict
Source-confirmed. carp_hmac_prepare in-place rewrite with / XXX: possible race here / + no lock. Torn SHA1_CTX read. QEMU no multicast.
No comments yet.