feat(rules): Dirty Frag class β skb in-place crypto without cow gate#297
Conversation
Three Semgrep-YAML-bridge rules for the Dirty Frag bug class (in-place skcipher/AEAD on skb without cow gate, plus the scatterwalk_map_and_copy STORE primitive abused by Copy Fail and Dirty Frag in crypto_authenc_esn_decrypt). This is the foxguard half of the foxguard-first integration approach proposed in pwnkit issue #263 β kernel/C scanning lives in foxguard, the pwnkit CLI delegates kernel variant-hunt to foxguard rules. Adds: - C as a first-class Language (tree-sitter-c parser, .c/.h detection, // comment marker, semgrep-compat language map). No built-in C rules yet β only YAML loading via --rules. - rules/kernel/dirty-frag-class/skb-inplace-skcipher-no-cow.yaml - rules/kernel/dirty-frag-class/skb-inplace-aead-no-cow.yaml - rules/kernel/dirty-frag-class/scatterwalk-store-on-shared-sgl.yaml - 6 fixtures under tests/fixtures/kernel/dirty-frag/ (positive + negative for each rule), modeling esp_input, rxkad_verify_packet_1, and crypto_authenc_esn_decrypt pre/post the upstream patches - tests/kernel_dirty_frag.rs (calibration test: each rule flags its positive fixture and ignores its negative fixture) Calibration: foxguard --no-builtins --rules rules/kernel/dirty-frag-class \ tests/fixtures/kernel/dirty-frag/ β 3 critical findings (one per *_vulnerable.c). Safe fixtures clean. Path-sensitivity caveat: Rust's regex crate has no backreferences, so these rules cannot syntactically require src == dst on set_crypt's SGL args. The cow-gate suppression is a coarse same-file regex overlap, not a dominating-call analysis. Definitive Dirty Frag class matching needs Coccinelle (path-sensitive) or CodeQL (taint from MSG_SPLICE_PAGES to crypto_*_decrypt). That work is deferred to a follow-up issue per the issue #263 plan. Refs: - oss-security advisory 2026-05-07 (Hyunwoo Kim @V4bel) - upstream ESP patch f4c50a4034e62ab75f1d5cdd191dd5f9c77fdff4
|
Warning Rate limit exceeded
Youβve run out of usage credits. Purchase more in the billing tab. β How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. π¦ How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. βΉοΈ Review infoβοΈ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: β Files ignored due to path filters (1)
π Files selected for processing (16)
β¨ Finishing Touchesπ§ͺ Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Blog post for foxguard.dev/blog covering PR #297: C language support plus three structural rules under rules/kernel/dirty-frag-class/ for the Dirty Frag class disclosed by Hyunwoo Kim (@V4bel) on oss-security 2026-05-07. Frames the rules as structural triage (regex-shaped, not path-sensitive proofs), shows one YAML rule verbatim, lines up the false-positive caveats, and points at foxguard #295 (Coccinelle), #296 (CodeQL), and pwnkit #263 (variant-hunt orchestrator) as the proper homes for dominator analysis.
β¦er 1 + FP shapes) (#305) * test(kernel/dirty-frag): expand fixtures + tighten negative-regex Lifts kernel_dirty_frag test count from 6 to 12 by adding 6 synthetic fixtures (3 Tier 1 sibling-site positives, 3 Tier 2 known-FP negatives) and extends the cow-gate negative regex with two additional kernel primitives. New fixtures (tests/fixtures/kernel/dirty-frag/): - ah_aead_no_cow_vulnerable.c β AH4/AH6 ICV-verify shape (must flag) - ipcomp_skcipher_no_cow_vulnerable.c β IPComp decrypt shape (must flag) - scatterwalk_authenc_store_vulnerable.c β non-ESN authenc STORE (must flag) - tls_aead_cow_safe.c β kTLS-style cow via __skb_cow (must not flag) - macsec_skcipher_cow_safe.c β MACsec via skb_copy_expand (must not flag) - scatterwalk_inplace_read_safe.c β in-place AEAD + READ-back out=0 (must not flag; tests literal-1 tightness of scatterwalk regex) Negative-regex additions (skb-inplace-aead-no-cow, skb-inplace-skcipher-no-cow): - skb_cow β base form of skb_cow_data, used by some non-IPsec callers - __skb_cow β internal helper exposed in skbuff.h, hit by kTLS receive - skb_copy_expand β full reallocation, defeats shared-frag aliasing Skipped on purpose: - skb_clone β duplicates pointers, does NOT make the data writable; including it would over-suppress true positives. - skb_share_check β only handles the cloned case, not non-linear shared frags from MSG_SPLICE_PAGES; same over-suppression risk. All fixtures are minimal synthetic reproducers authored from the patch- hunk shape (oss-security 2026-05-07, commit f4c50a4034e6); no kernel source is copied. Refs foxguard PR #297, pwnkit issue #263. * test(fixtures): drop kernel #include lines, keep clangd quiet The 12 dirty-frag fixtures already self-declare their structs and externs; the kernel headers (linux/skbuff.h, crypto/aead.h, crypto/skcipher.h, crypto/scatterwalk.h) were redundant with the forward decls below them. clangd flagged pp_file_not_found on every fixture because kernel headers aren't on dev machines. These files are scanned as source text by tree-sitter, never compiled, so removing the includes is a no-op for foxguard tests (still 12/12) and a clear win for editor LSP noise.
Three Semgrep-YAML-bridge rules for the Dirty Frag bug class (in-place skcipher/AEAD on skb without cow gate, plus the scatterwalk_map_and_copy STORE primitive abused by Copy Fail and Dirty Frag in crypto_authenc_esn_decrypt). This is the foxguard half of the foxguard-first integration approach proposed in pwnkit issue #263 β kernel/C scanning lives in foxguard, the pwnkit CLI delegates kernel variant-hunt to foxguard rules. Adds: - C as a first-class Language (tree-sitter-c parser, .c/.h detection, // comment marker, semgrep-compat language map). No built-in C rules yet β only YAML loading via --rules. - rules/kernel/dirty-frag-class/skb-inplace-skcipher-no-cow.yaml - rules/kernel/dirty-frag-class/skb-inplace-aead-no-cow.yaml - rules/kernel/dirty-frag-class/scatterwalk-store-on-shared-sgl.yaml - 6 fixtures under tests/fixtures/kernel/dirty-frag/ (positive + negative for each rule), modeling esp_input, rxkad_verify_packet_1, and crypto_authenc_esn_decrypt pre/post the upstream patches - tests/kernel_dirty_frag.rs (calibration test: each rule flags its positive fixture and ignores its negative fixture) Calibration: foxguard --no-builtins --rules rules/kernel/dirty-frag-class \ tests/fixtures/kernel/dirty-frag/ β 3 critical findings (one per *_vulnerable.c). Safe fixtures clean. Path-sensitivity caveat: Rust's regex crate has no backreferences, so these rules cannot syntactically require src == dst on set_crypt's SGL args. The cow-gate suppression is a coarse same-file regex overlap, not a dominating-call analysis. Definitive Dirty Frag class matching needs Coccinelle (path-sensitive) or CodeQL (taint from MSG_SPLICE_PAGES to crypto_*_decrypt). That work is deferred to a follow-up issue per the issue #263 plan. Refs: - oss-security advisory 2026-05-07 (Hyunwoo Kim @V4bel) - upstream ESP patch f4c50a4034e62ab75f1d5cdd191dd5f9c77fdff4
Blog post for foxguard.dev/blog covering PR #297: C language support plus three structural rules under rules/kernel/dirty-frag-class/ for the Dirty Frag class disclosed by Hyunwoo Kim (@V4bel) on oss-security 2026-05-07. Frames the rules as structural triage (regex-shaped, not path-sensitive proofs), shows one YAML rule verbatim, lines up the false-positive caveats, and points at foxguard #295 (Coccinelle), #296 (CodeQL), and pwnkit #263 (variant-hunt orchestrator) as the proper homes for dominator analysis.
β¦er 1 + FP shapes) (#305) * test(kernel/dirty-frag): expand fixtures + tighten negative-regex Lifts kernel_dirty_frag test count from 6 to 12 by adding 6 synthetic fixtures (3 Tier 1 sibling-site positives, 3 Tier 2 known-FP negatives) and extends the cow-gate negative regex with two additional kernel primitives. New fixtures (tests/fixtures/kernel/dirty-frag/): - ah_aead_no_cow_vulnerable.c β AH4/AH6 ICV-verify shape (must flag) - ipcomp_skcipher_no_cow_vulnerable.c β IPComp decrypt shape (must flag) - scatterwalk_authenc_store_vulnerable.c β non-ESN authenc STORE (must flag) - tls_aead_cow_safe.c β kTLS-style cow via __skb_cow (must not flag) - macsec_skcipher_cow_safe.c β MACsec via skb_copy_expand (must not flag) - scatterwalk_inplace_read_safe.c β in-place AEAD + READ-back out=0 (must not flag; tests literal-1 tightness of scatterwalk regex) Negative-regex additions (skb-inplace-aead-no-cow, skb-inplace-skcipher-no-cow): - skb_cow β base form of skb_cow_data, used by some non-IPsec callers - __skb_cow β internal helper exposed in skbuff.h, hit by kTLS receive - skb_copy_expand β full reallocation, defeats shared-frag aliasing Skipped on purpose: - skb_clone β duplicates pointers, does NOT make the data writable; including it would over-suppress true positives. - skb_share_check β only handles the cloned case, not non-linear shared frags from MSG_SPLICE_PAGES; same over-suppression risk. All fixtures are minimal synthetic reproducers authored from the patch- hunk shape (oss-security 2026-05-07, commit f4c50a4034e6); no kernel source is copied. Refs foxguard PR #297, pwnkit issue #263. * test(fixtures): drop kernel #include lines, keep clangd quiet The 12 dirty-frag fixtures already self-declare their structs and externs; the kernel headers (linux/skbuff.h, crypto/aead.h, crypto/skcipher.h, crypto/scatterwalk.h) were redundant with the forward decls below them. clangd flagged pp_file_not_found on every fixture because kernel headers aren't on dev machines. These files are scanned as source text by tree-sitter, never compiled, so removing the includes is a no-op for foxguard tests (still 12/12) and a clear win for editor LSP noise.
Summary
frags[]slot ofstruct sk_buffvia in-place crypto on splice-pinned pages..c/.hfiles at all (it could not before this PR β there is no built-in C rule today, only the new YAML).Rules
All three are loaded with
foxguard --no-builtins --rules rules/kernel/dirty-frag-class:kernel/dirty-frag/skb-inplace-skcipher-no-cowskcipher_request_set_crypt(req, sg, sg, β¦)thencrypto_skcipher_decrypt(req)in the same function, with no dominating cow / unshare / make-writable /pskb_expand_headkernel/dirty-frag/skb-inplace-aead-no-cowaead_request_set_crypt(req, sg, sg, β¦)thencrypto_aead_decrypt(req)in the same function, with no dominating cow gatekernel/dirty-frag/scatterwalk-store-on-shared-sglaead_request_set_crypt(...)thenscatterwalk_map_and_copy(β¦, /*out=*/1)in the same function β the secondary STORE primitive abused by Copy Fail and Dirty Frag incrypto_authenc_esn_decryptCalibration
Six fixtures (positive + negative per rule) model the calibration sites named in the advisory:
aead_no_cow_vulnerable.cβnet/ipv4/esp4.c::esp_input/net/ipv6/esp6.c::esp6_input(pre-patch)skcipher_no_cow_vulnerable.cβnet/rxrpc/rxkad.c::rxkad_verify_packet_1/net/rxrpc/conn_event.c::rxrpc_verify_response(pre-patch)scatterwalk_store_vulnerable.cβcrypto/authencesn.c::crypto_authenc_esn_decrypt*_safe.cfixtures model the same functions post-patch (skb_cow_datadominates) or without=0/ non-aliased SGL.3 of 6 fixtures flagged (the three positives), 3 clean (the three negatives).
What this rule cannot do (deferred to Coccinelle / CodeQL)
The advisory describes a path-sensitive bug β "no
skb_cow_data(or equivalent) call dominates the in-place decrypt on the unsafe path". foxguard's Semgrep YAML bridge is the right tool for structural triage, but it cannot prove dominance of one call over another on every control-flow path. Specific limitations of these rules:arg2 == arg3of*_request_set_crypt(req, src, dst, β¦)to confirmsrc == dst(the in-place property). The rules fire on anyset_cryptthendecryptsequence inside a function. This will produce false positives on anyset_crypt(req, src, dst, β¦)wheresrc != dst(legitimate non-in-place crypto).pattern-not-regexfilters out a positive only when the cow regex overlaps the positive's match span. Since[^}]*?is greedy-lazy across the function body, this approximates "cow appears earlier in the same function" β but it is not a dominating-call analysis. Askb_cow_datacall that is itself behind an unrelated branch will still suppress.pskb_expand_head,pskb_*macros, and inlined skb helpers. These rules enumerate the common direct names; macro-only call paths will be missed.MSG_SPLICE_PAGESsource. The bug requires the page provenance to trace back to userspace viasplice/vmsplice+MSG_SPLICE_PAGES. These rules do not check that β they fire on the in-place idiom regardless of where the SGL came from. Expect false positives on TLS, dm-crypt, fscrypt, and offload-crypto paths.These are deliberate trade-offs for a 1-day spike. The full path-sensitive variant-hunt for Dirty Frag class needs Coccinelle and/or CodeQL, planned as a separate issue per the pwnkit #263 plan (Β§3.1, Β§3.3).
C language enrollment
This PR adds the minimum needed to make the rules above runnable:
tree-sitter-c = "0.24"inCargo.tomlLanguage::Cenum variant insrc/lib.rstree_sitter_c::LANGUAGEinsrc/engine/parser.rs.c/.hextension detection insrc/engine/scanner.rs//comment marker insrc/engine/scanner.rs::comment_markersclanguage string insrc/rules/semgrep_compat.rs::map_languagegen_rules_tsarms (no built-in C rules; the website array is empty for now)No built-in C rules ship in this PR. The scanner will not find anything in C files unless
--rulespoints at a C-language YAML rule set (e.g. these three).Test plan
cargo buildβ cleancargo clippy -- -D warningsβ cleancargo fmtβ cleancargo testβ 580 passing (430 unit + 8 + 109 + 3 + 15 + 1 + 12 + 6 + 6 new), 0 failedcargo test --test kernel_dirty_fragβ all 6 calibration tests pass (positive flags, negative ignored, per rule)cargo test --test rule_inventoryβ generatedwww/src/data/rules.tsmatches (no built-in C rules added, so no inventory drift)foxguard --no-builtins --rules rules/kernel/dirty-frag-class tests/fixtures/kernel/dirty-frag/β 3 findings on positives, 0 on negativesRefs
feat(kernel): pwnkit kernel variant-huntf4c50a4034e62ab75f1d5cdd191dd5f9c77fdff4(extendsskip_cowwith!skb_has_shared_frag(skb))skb->data_lento the gate)