# CVE-2026-43503 (DirtyClone) – Variant Root Cause Analysis

## Summary

This variant stage investigated whether a **different frag-transfer helper** could bypass the CVE-2026-43503 fix.  The candidate chosen was the **Fragnesia** path (CVE-2026-46300): instead of the original DirtyClone netfilter `TEE` → `__pskb_copy_fclone()` route, Fragnesia reaches the same XFRM/ESP in-place decryption sink through **ESP-in-TCP (`espintcp`) over a `veth` pair**, using TCP receive coalescing (`skb_try_coalesce()`) to move file-backed page-cache fragments into an unmarked skb.  On the vulnerable `7.0.9` kernel the variant corrupts `/usr/bin/su` in the page cache and would yield root.  On the fixed `7.0.10` kernel the variant is blocked: the stable backport includes both the CVE-2026-43503 multi-site fix and the CVE-2026-46300 `skb_try_coalesce()` fix, so the shared-frag marker is preserved and `skb_cow_data()` forces a copy before decryption.  **No bypass of the fixed kernel was found.**

## Fix Coverage / Assumptions

The CVE-2026-43503 patch (`48f6a5356a33`) restores the invariant:

> An skb that references externally-owned or file-backed page-cache pages must have `SKBFL_SHARED_FRAG` set.

It does so by OR-ing the source skb's `SKBFL_SHARED_FRAG` bit into the destination skb whenever one of the six audited helpers moves fragment descriptors:

- `__pskb_copy_fclone()`
- `skb_shift()`
- `skb_gro_receive()`
- `skb_gro_receive_list()`
- `tcp_clone_payload()`
- `skb_segment()`

The patch assumes that these are the only frag-transfer sites that can carry page-cache-backed pages to an in-place writer, and that `skb_copy()` / `skb_copy_expand()` need no change because they linearize data into freshly allocated memory.

What the patch does **not** cover:

- `skb_try_coalesce()` (fixed separately by CVE-2026-46300).
- `skb_zerocopy()` and related helpers (no known unprivileged exploit path to the ESP sink).
- Any future kernel code that introduces a new fragment-transfer site without copying the marker.

## Variant / Alternate Trigger

### Candidate 1: Fragnesia / ESP-in-TCP / `skb_try_coalesce()` (tested)

- **Entry point:** Unprivileged user+network namespace; create a `veth` pair, configure an XFRM transport-mode SA/SP for ESP-in-TCP, and splice a page-cache page of `/usr/bin/su` into a TCP socket via `vmsplice()`/`splice()`.
- **Trigger path:** `tcp_v4_rcv()` → TCP receive coalescing → `skb_try_coalesce()` moves shared frags into the ESP-in-TCP skb; `espintcp` receive path calls `skb_cow_data()`, which checks `skb_has_shared_frag()`.
- **Sink:** In-place AES-GCM/AES-CBC decryption over the file-backed page-cache page.
- **Files/functions involved:** `net/core/skbuff.c:skb_try_coalesce()`, `net/ipv4/espintcp.c`, `net/ipv4/tcp_input.c`.
- **Result on v7.0.9:** `RESULT: VULNERABLE (page cache modified)` — the write primitive works.
- **Result on v7.0.10:** `RESULT: NOT VULNERABLE (page cache unchanged)` — the marker is preserved and `skb_cow_data()` makes a copy.

### Candidate 2: Other CVE-2026-43503 helpers with different protocols (not tested separately)

The CVE-2026-43503 fix is applied at the function level, so any path through `__pskb_copy_fclone()`, `skb_shift()`, `skb_gro_receive()`, `skb_gro_receive_list()`, `tcp_clone_payload()`, or `skb_segment()` is covered regardless of the network protocol or driver.  Additional protocol-specific triggers (IPv6 `TEE`, GRO on a different driver, TCP clone probes) are therefore not distinct bypasses; they reach the same now-fixed sink.

### Candidate 3: `skb_zerocopy()` paths (ruled out by analysis)

`skb_zerocopy()` is used by XDP socket TX, Open vSwitch, and `nfnetlink_queue`.  It creates fragment references without propagating `SKBFL_SHARED_FRAG`, but none of these consumers feed the resulting skb into the XFRM ESP input path under an unprivileged local trust boundary.  No public exploit or reliable unprivileged trigger is known, so this remains a theoretical gap rather than a demonstrated bypass.

## Impact

- **Package/component affected:** Linux kernel networking stack (`net/core/skbuff.c`, `net/core/gro.c`, `net/ipv4/tcp_output.c`, `net/ipv4/espintcp.c`).
- **Affected versions as tested:** Ubuntu mainline `7.0.9-070009-generic` (vulnerable to both DirtyClone and Fragnesia); Ubuntu mainline `7.0.10-070010-generic` (blocked).
- **Risk level:** Local privilege escalation.  An unprivileged local user with user and network namespaces can gain root by overwriting the page-cache copy of a setuid binary.  The variant is at least as severe as the original DirtyClone; it is blocked only because the tested fixed kernel includes the sibling CVE-2026-46300 patch.

## Impact Parity

- **Disclosed/claimed maximum impact:** Local privilege escalation (`privilege_escalation`) — arbitrary write into read-only page-cache pages of setuid binaries, leading to root code execution.
- **Reproduced impact from this variant run:** Full privilege escalation on `7.0.9` (the variant overwrites `/usr/bin/su` in the page cache).  The impact was **not** reproduced on `7.0.10` because the write primitive was blocked.
- **Parity:** `partial` — the variant is a real, distinct trigger for the same root cause on the vulnerable kernel, but it is not a bypass of the fixed kernel tested.
- **Not demonstrated:** Root shell on the fixed kernel; bypass of the combined CVE-2026-43503 + CVE-2026-46300 mitigation.

## Root Cause

The same root cause underlies both DirtyClone and Fragnesia: the Linux kernel has multiple fragment-transfer helpers that move page references between skbs without moving the `SKBFL_SHARED_FRAG` metadata flag.  When the destination skb reaches an in-place writer (XFRM ESP input), `skb_has_shared_frag()` returns false, `skb_cow_data()` is skipped, and decryption writes directly into file-backed page-cache memory.

The CVE-2026-43503 patch fixes six helpers.  Fragnesia uses a seventh helper, `skb_try_coalesce()`, which was fixed by a separate commit.  The two fixes are siblings; the tested `7.0.10` stable kernel includes both, so the variant is blocked.

## Reproduction Steps

1. Run the variant reproduction script:
   ```bash
   bash bundle/vuln_variant/reproduction_steps.sh
   ```
   The script is idempotent and will exit `1` because the variant is blocked on the fixed kernel.

2. The script performs the following:
   - Builds a minimal initramfs that mounts the Fragnesia rootfs and `switch_root`s into it.
   - Boots the v7.0.9 vulnerable kernel with the Fragnesia rootfs under QEMU.
   - Boots the v7.0.10 fixed kernel with the Fragnesia rootfs under QEMU.
   - Captures serial console output to `bundle/logs/qemu_fragnesia_vuln.log` and `bundle/logs/qemu_fragnesia_fixed.log`.
   - Greps for `RESULT: VULNERABLE` or `RESULT: NOT VULNERABLE` to determine whether the bypass succeeded.

3. Expected evidence:
   - `logs/qemu_fragnesia_vuln.log` contains `RESULT: VULNERABLE (page cache modified)`.
   - `logs/qemu_fragnesia_fixed.log` contains `RESULT: NOT VULNERABLE (page cache unchanged)`.

## Evidence

- **Vulnerable run log:** `bundle/logs/qemu_fragnesia_vuln.log`  
  Key excerpt: `[+] RESULT: VULNERABLE (page cache modified)`
- **Fixed run log:** `bundle/logs/qemu_fragnesia_fixed.log`  
  Key excerpt: `[-] byte at index=24 unchanged after 8 attempts, kernel may be fixed` followed by `[*] RESULT: NOT VULNERABLE (page cache unchanged)`.
- **Runtime manifest:** `bundle/vuln_variant/runtime_manifest.json`
- **Validation verdict:** `bundle/vuln_variant/validation_verdict.json`
- **Patch analysis:** `bundle/vuln_variant/patch_analysis.md`
- **Environment:** QEMU x86_64, Ubuntu mainline kernels `7.0.9-070009-generic` and `7.0.10-070010-generic`, 512 MB RAM, 2 vCPUs.

## Recommendations / Next Steps

1. **Do not treat CVE-2026-43503 as a standalone fix.**  Distribution backports must include both:
   - commit `48f6a5356a33` (CVE-2026-43503, six helpers), and
   - the CVE-2026-46300 `skb_try_coalesce()` commit (Fragnesia).  
   A kernel with only the first commit is still vulnerable to Fragnesia.

2. **Extend static review** to any remaining frag-transfer helpers such as `skb_zerocopy()` and future GRO/offload code.  Even though no unprivileged trigger is known today, the same shape of bug could reappear.

3. **Consider a structural hardening patch** that makes `SKBFL_SHARED_FRAG` propagation automatic when `nr_frags` is increased by moving descriptors, rather than relying on authors to remember each site.  This would close the regression window for future variants.

4. **Regression test:** Add a kernel self-test that verifies `skb_has_shared_frag()` is true after each of the seven known frag-transfer helpers when the source skb has the marker set.

## Additional Notes

- **Idempotency:** The script was run three times; each run produced identical verdicts (`VULNERABLE` on `7.0.9`, `NOT VULNERABLE` on `7.0.10`) and exited `1`.
- **Limitations:** The Fragnesia binary is a prebuilt artifact from the project cache.  This run treats it as an alternate trigger to test the completeness of the fix; it does not claim to have discovered a new kernel bug.  No host-side files were modified; the rootfs images are mounted read-only inside QEMU.
- **Trust boundary:** The variant remains a local privilege escalation under the same trust model as the original CVE (unprivileged user/network namespace).
