# Dirty Frag Tracking — Claude Code Context This repository contains a living tracking document for **Dirty Frag**, a Linux kernel local privilege escalation chain comprising two page-cache write vulnerabilities: - **xfrm-ESP Page-Cache Write** (`net/ipv4/esp4.c`, `net/ipv6/esp6.c`) - **RxRPC Page-Cache Write** (`net/rxrpc/rxkad.c`) Tracked as **CVE-2026-43284**, assigned shortly after public disclosure on oss-security on 2026-05-07. Disclosure happened after the embargo was broken by an unrelated third party, with linux-distros maintainer agreement on full-publication. Upstream reference: · researcher write-up: . The rendered site is published at . ## Your task Keep `site/content/_index.md` (the canonical tracker) up to date as upstream patches land in stable trees and distro advisories follow. After edits, rebuild with `make build` and publish with `make dist`. A scheduled background agent runs against this repo to refresh the tracker on its own. If you find the file has been edited since you last looked, that's likely why — re-read before assuming stale state. ## Repo layout ``` . ├── site/ # Hugo project │ ├── content/_index.md # the tracker — single source of truth │ ├── hugo.toml # config (subpath baseURL — don't break) │ ├── assets/css/extended/custom.css # CSS overrides (PaperMod extension point) │ ├── layouts/partials/post_meta.html # overrides PaperMod: adds labels + lastmod │ └── go.mod, go.sum # Hugo Modules — pulls PaperMod theme ├── scripts/ # auto-update agent: prompt + driver │ ├── auto-update # wrapper invoked by the systemd timer │ └── auto-update-prompt.txt # prompt fed to headless Claude ├── systemd/ # user-level timer + service units │ ├── dirty-frag-tracker-update.service # runs scripts/auto-update │ └── dirty-frag-tracker-update.timer # twice daily ├── flake.nix, .envrc # Nix dev shell: hugo + go + git ├── Makefile # `make build`, `make dist` ├── LICENSE # CC BY 4.0 ├── README.md # user-facing project README ├── WEBSITE.md # publication plan / decisions log └── CLAUDE.md # this file ``` ## The tracker file (`site/content/_index.md`) — important constraints - It has Hugo front-matter with these required fields: `title`, `description`, `layout: "single"`, `date` (published), `lastmod` (last updated). Keep all five; the rendering depends on them. - The H1 has been stripped — Hugo emits the title from front-matter via PaperMod's single-post layout. Don't add an H1 back. - The TOC is generated by PaperMod's auto-TOC (`ShowToc = true` + `UseHugoToc = true` in `hugo.toml`), which renders between the post header and the body. Don't add a manual TOC. - The "Last updated" date lives in the `lastmod` front-matter field. Bump it on every content edit. ## Update workflow 1. Edit `site/content/_index.md` (or any file under `site/`). 2. Optional: `cd site && hugo server` for a local live preview at . 3. `make build` — emits to `site/public/` (gitignored). 4. `make dist` — runs `make build`, then rsyncs `site/public/` to `haig:.www/sites/kimmo.cloud/htdocs/CVE-2026-43284/` with `--delete`. ## Conventions for status entries - `:white_check_mark:` — fix is confirmed present in the package changelog (not merely announced). - `:warning:` — fix is staged but not in the user-facing channel (e.g. in netdev-next but not in a Linus release; in nixos-unstable but not nixos-stable; or DSA issued but package not in the security repo yet). - `:x:` — vulnerable (no fix or pending). - Record the fixed package version, the date it became available, and the source of confirmation (changelog entries, tracker URL, commit hash). - Each distribution-status table (the per-distro tables and the combined variants table) is the single source for its rows' columns (status, version, fixed-since). Per-distro prose is for notes that don't fit a table — don't restate a row's columns there, and don't add a parallel table that duplicates an existing one; put a newly verified version/status in the table row itself. - A value column holds the value, not a verdict word — don't write "unpatched" / "patched" / "fixed" in a kernel/version cell when the *Status* column already carries the verdict. Label NixOS channels in friendly form (`Unstable`, `Unstable (small)`, `25.11`, `25.11 (small)`), not the raw `nixos-…` identifiers. Per-distro prose is reader-facing caveats (exploitability, mitigation posture) — keep tracking methodology (which distros are reference-only, how channels are structured) in this file, not the tracker. - Whenever you re-verify "still vulnerable" entries, update the `## Verification log` section near the bottom of the tracker rather than adding a new line for each re-check. Bump the `*Last verified .*` line under the heading and edit the appropriate `### Upstream` / `### Distributions` / `### Threat intelligence` subsection in place. Add a new `###` subsection only when a genuinely new topic appears (e.g. another stable-branch backport bug worth calling out). The verified-date lives **only** in the `*Last verified .*` header — don't repeat it inline, neither in the tracker body nor per bullet in the log (no `(verified …)`, no bare `()`); every log entry is already "as of" that date. Method/source attribution without a date is fine (e.g. `(via madison)`, `(checked against ~/src/linux/stable)`). - Dates embedded in the prose (front-matter `lastmod`, the verification log header, and every "as of " / "on " / "released " / "scored " phrase in the body) are **first-seen / last-changed** dates, not "today" dates. Only move a date when the fact it qualifies actually changes. A re-verification that confirms the existing state is the *purpose* of the `*Last verified .*` header — that one line bumps; nothing else does. If the entire run is a no-op (no status, package, version, advisory, or commit-hash change, no movement in EPSS score or percentile, and no KEV listing change), leave the file alone and don't commit at all — don't bump `lastmod`, don't bump `*Last verified*`, and don't insert "re-confirmed " parentheticals. The next real change is what records that everything in between was still current. - EPSS score or percentile movement *is* a real change for these purposes: a shift in either is a fact worth recording — edit the EPSS line in place, bump `lastmod`, and commit. The `scored ` is the first-seen marker for the *current* score/percentile value, not a fact in its own right: move it only when the score or percentile actually changes. EPSS re-scores daily, so FIRST keeps handing you a newer `scored ` for an otherwise-unchanged value — that is a no-op. Don't bump the scored date, `lastmod`, or commit when the score and percentile are unchanged. The previous value's first-seen date is already preserved in `git log`; don't try to encode "down from X on " history into the body indefinitely. ## Build environment - Hugo extended **≥ 0.146.0** (PaperMod's minimum). Debian apt is too old. - Go (any recent version) — needed for Hugo Modules to pull PaperMod. - The Nix flake provides both: `nix develop` (or just `cd` in if direnv is set up). ## Auto-update worktree The systemd timer runs `scripts/auto-update` from a dedicated git worktree at `~/src/auto-update/CVE-2026-43284`, checked out on a single long-lived branch named `auto-update`. The wrapper merges `origin/main` forward into that branch on each run, then hands off to headless Claude, which commits any tracker changes back onto `auto-update` only. The agent must not create per-run branches, switch branches, push, or open PRs — merges of `auto-update` into `main` are done manually by the user. One-time setup (from the primary checkout at `~/src/CVE-2026-43284`): ``` git worktree add -b auto-update ~/src/auto-update/CVE-2026-43284 main ``` The systemd units ship in `systemd/`. They are not in a standard unit search path, so wiring the timer means symlinking both units into `~/.config/systemd/user/` and then enabling the timer. Use `ln -sr` so the links are relative — they survive the home directory being moved or mounted at a different path: ``` ln -sr ~/src/CVE-2026-43284/systemd/dirty-frag-tracker-update.service \ ~/src/CVE-2026-43284/systemd/dirty-frag-tracker-update.timer \ ~/.config/systemd/user/ systemctl --user daemon-reload systemctl --user enable --now dirty-frag-tracker-update.timer ``` ## Tearing down the auto-update To stop the scheduled refresh entirely, unwire it in this order — the sequence matters, because `systemctl disable` needs the unit definition to still be resolvable when it runs. 1. **Disable the timer first**, while the unit symlinks are still in place. This stops the timer and removes the `enable` symlink that lives in `~/.config/systemd/user/timers.target.wants/`: ``` systemctl --user disable --now dirty-frag-tracker-update.timer ``` 2. **Remove the unit-definition symlinks** from the search path: ``` rm ~/.config/systemd/user/dirty-frag-tracker-update.timer \ ~/.config/systemd/user/dirty-frag-tracker-update.service ``` 3. **Reload** so the running user manager drops the units: ``` systemctl --user daemon-reload ``` 4. `systemctl --user status dirty-frag-tracker-update.timer` should now exit non-zero with "could not be found" — that is the expected clean end state, not an error. If the definition symlinks were removed *before* disabling, `disable` can no longer resolve the unit by name and the stale `enable` symlink is left behind, parking the timer in a `failed` state. Recover by deleting that symlink directly, then reloading and clearing the failed residue: ``` rm ~/.config/systemd/user/timers.target.wants/dirty-frag-tracker-update.timer systemctl --user daemon-reload systemctl --user reset-failed dirty-frag-tracker-update.timer ``` Finally, remove the worktree and its branch (run from the primary checkout at `~/src/CVE-2026-43284`): ``` git worktree remove ~/src/auto-update/CVE-2026-43284 git branch -d auto-update ``` `git worktree remove` refuses if the worktree has uncommitted changes; `git branch -d` refuses if `auto-update` holds commits not merged into `main`. Merge or discard that work first, or force with `-D` only once you are sure nothing on `auto-update` is worth keeping. ## Local kernel clones for git.kernel.org git.kernel.org's cgit HTML pages (any URL ending in /log/, /tree/, /commit/, etc.) are now Anubis-gated; WebFetch hits the no-JS challenge and the auto-update agent cannot read them. The agent inspects kernel history via long-living local clones under `~/src/linux/`: | Clone path | Upstream | |----------------------|-----------------------------------------------------------------------| | `~/src/linux/stable` | `https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git` | | `~/src/linux/net` | `https://git.kernel.org/pub/scm/linux/kernel/git/netdev/net.git` | | `~/src/linux/vulns` | `https://git.kernel.org/pub/scm/linux/security/vulns.git` | `vulns` is not a kernel-history clone — it is the kernel CNA's CVE database (one record per assigned CVE); see "Kernel CVE database" below. One-time setup (full clones — kernel.org's git server does not honour `--filter=blob:none`, so a partial clone offers no space saving): ``` mkdir -p ~/src/linux git clone https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git \ ~/src/linux/stable git clone https://git.kernel.org/pub/scm/linux/kernel/git/netdev/net.git \ ~/src/linux/net git clone https://git.kernel.org/pub/scm/linux/security/vulns.git \ ~/src/linux/vulns ``` The auto-update *wrapper* (`scripts/auto-update`) refreshes the clones with `git -C fetch` before invoking the headless agent; the agent itself inspects via the `origin/` remote-tracking refs (e.g. `origin/linux-6.12.y` for stable, `origin/main` for net; `vulns` tracks a single branch — inspect via `origin/master`). The wrapper only *fetches*, so local `HEAD` stays stale; always read the `origin/...` refs, never `HEAD` (a `HEAD`-scoped grep/show can miss a record that has already landed). Manual refresh: `git -C ~/src/linux/stable fetch --all --tags`. The git smart-HTTP protocol is not Anubis-gated, so `git fetch` / `git ls-remote` work from any UA. Anubis only blocks the cgit HTML interface. ### Kernel CVE database (`~/src/linux/vulns`) The kernel has been its own CNA since Feb 2024; `vulns.git` is its CVE database. Two uses here: - **Authoritative fixed versions.** Each published record's `.dyad` lists the `:` commit pair per affected branch — cross-check the *Upstream fixed versions* table against `cve/published//CVE-2026-43284.dyad` and `…/CVE-2026-43500.dyad`: ``` git -C ~/src/linux/vulns show origin/master:cve/published/2026/CVE-2026-43284.dyad ``` - **Variant-CVE discovery.** A kernel-CNA CVE for a new Dirty Frag variant shows up here first. Grep the published records for the fix commits to catch an assignment before the keyed feeds do — the matching filename *is* the CVE ID: ``` git -C ~/src/linux/vulns grep -l f4c50a4034e6 origin/master -- 'cve/published/*' ``` Candidates the CNA's dredge has flagged but not yet assigned sit under `cve/review/proposed/` with no ID. ## Local nixpkgs clone for NixOS channel verification The auto-update agent reads NixOS kernel pins from a local nixpkgs clone at `~/src/nixos/nixpkgs` rather than the security-tracker page (which is JS-rendered and lags). Each NixOS channel has a git-revision pointer at `https://channels.nixos.org//git-revision`; at that commit, `pkgs/os-specific/linux/kernel/kernels-org.json` carries the canonical version + sha256 for every supported mainline.org kernel series. Tracked channels: | Channel | Notes | |-------------------------|--------------------------------------------------| | `nixos-25.11` | default 25.11 channel (full package set) | | `nixos-25.11-small` | server-oriented 25.11 subset; advances faster | | `nixos-unstable` | default unstable channel | | `nixos-unstable-small` | server-oriented unstable subset; advances faster | The `-small` channels typically lead the default channels by one or two stable point releases. Look up the current pinned version with: ``` rev=$(curl -fsSL https://channels.nixos.org//git-revision) git -C ~/src/nixos/nixpkgs show \ "${rev}:pkgs/os-specific/linux/kernel/kernels-org.json" ``` `channels.nixos.org//git-revision` returns a 302 to `releases.nixos.org` — always pass `-L` to curl. The auto-update wrapper refreshes the local clone on every run; for interactive use, refresh with `git -C ~/src/nixos/nixpkgs fetch --quiet origin` before each lookup so commit lookups never miss. ## Key sources to monitor | Source | URL | |---|---| | Upstream reference | | | Upstream technical write-up | | | Researcher write-up | | | oss-security disclosure | | | NVD entry | | | MITRE CVE Awg JSON — CVE-2026-43284 (canonical CNA record) | | | MITRE CVE Awg JSON — CVE-2026-43500 | | | MITRE CVE Awg ID-state probe — CVE-2026-43284 (state-only metadata) | | | MITRE CVE Awg ID-state probe — CVE-2026-43500 | | | cve.org HTML record — CVE-2026-43284 | | | cve.org HTML record — CVE-2026-43500 | | | FIRST EPSS API | | | CISA KEV catalog (JSON) | | | Red Hat CVE page | | | Red Hat JSON (machine-readable) | | | netdev/net.git (xfrm-ESP fix) | | | Linus mainline | | | stable kernel releases | | | Debian security tracker | | | Debian package madison (canonical, dak-backed) | | | Rocky Linux errata (RSS) | | | AlmaLinux blog (sibling RHEL clone, ships ahead of RHT) | | | NixOS security tracker | | | NixOS channel pointer — 25.11 | | | NixOS channel pointer — 25.11-small | | | NixOS channel pointer — unstable | | | NixOS channel pointer — unstable-small | | | Amazon Linux | | | Amazon Linux 2023 advisories | | | Amazon Linux 2 advisories | | | Proxmox advisories (thread) | | | Proxmox advisories (user posts, newest first) | | For machine-readable data, prefer API/feed endpoints over HTML pages — several distro sites are JS-rendered SPAs that don't render via WebFetch. The Red Hat hydra JSON endpoint may 404 until Red Hat publishes their own CVE record. ## Platform-specific notes - **Rocky/RHEL 8, 9, 10:** `esp4`, `esp6`, and `rxrpc` are all loadable modules on the EL family — confirmed by AlmaLinux's blog. The modprobe blacklist is the right mitigation. Rocky tracks Red Hat's schedule, so RLSAs follow RHSAs; AlmaLinux ships ahead of Red Hat in its `testing` repo and is a useful sibling-clone reference for expected fixed-version numbers. - **Debian:** for the *current* default kernel version per suite, query the dak-backed madison API at api.ftp-master.debian.org. Both qa.debian.org/madison.php and sources.debian.org/api/src/linux/ have been observed lagging the archive by a day or more — they should not be used to decide what is currently in sid/forky/trixie/etc. The security tracker page covers advisory status, not current package version. - **NixOS 25.11 / unstable:** Each release has both a default channel (`nixos-25.11`, `nixos-unstable`) and a `-small` server-oriented sibling (`nixos-25.11-small`, `nixos-unstable-small`). Track all four — the `-small` channels often carry fixes one or two stable point releases ahead of the defaults. Determine kernel pins per channel via the local nixpkgs clone + channel git-revision pointer + `kernels-org.json` (see "Local nixpkgs clone for NixOS channel verification" above), not the security-tracker page. - **Amazon Linux 2:** The extras kernel stream (5.4, 5.10, 5.15) may receive fixes independently of the Core 4.14 kernel. Track them separately. - **Fix verification (xfrm-ESP):** the merged fix is upstream commit `f4c50a4034e6` (`net: esp: detect attempts to crypt over user-pinned shared frags`) plus the matching skb shared-frag tagging in `__ip_append_data` / `__ip6_append_data`. Confirm both pieces are present in distro backports — the ESP-side check alone is incomplete if the IP-side flag isn't being set. - **Fix verification (RxRPC):** the merged fix is upstream commit `24481a7f5733` (`rxrpc: Fix conn-level packet handling to unshare RESPONSE packets`) on netdev/net, 2026-04-22. It targets `rxkad_verify_packet_1` and the in-place `pcbc(fcrypt)` decrypt path. Per-branch backport status lives in the tracker's *Upstream fixed versions* table. ## Known harmless warnings during build PaperMod's templates still call `.Language.LanguageDirection` and `.Language.LanguageCode`, which Hugo deprecated in 0.158.0. The build emits `WARN deprecated:` lines for both. Upstream theme issue — don't try to fix it in this repo. ## License The tracker content is licensed under **CC BY 4.0** (see `LICENSE` at the repo root). Copyright © 2026 Kimmo Suominen. The site footer credits both the author and the licence.