# REPRO-2026-00265: Linux kernel kTLS Use-After-Free ## Summary Status: published Severity: high Type: security Confidence: high ## Identifiers REPRO ID: REPRO-2026-00265 CVE: CVE-2024-26582 ## Package Name: torvalds/linux Ecosystem: github Affected: Linux 6.2.0 through 6.6.18; also 6.1.x before 6.1.82, 6.7.x before 6.7.7 Fixed: Unknown ## Root Cause # CVE-2024-26582: Linux kernel kTLS Use-After-Free ## Summary CVE-2024-26582 is a use-after-free vulnerability in the Linux kernel's in-kernel TLS (kTLS) software receive path (`net/tls/tls_sw.c`). When a kTLS session uses asynchronous decryption and the receiving application performs a partial read (reading fewer bytes than the TLS record size), the `tls_decrypt_done()` completion callback incorrectly frees pages belonging to the `clear_skb` buffer. These freed pages are subsequently accessed by `process_rx_list()` when the application reads the remaining data, triggering a use-after-free. The fix (commit `32b55c5ff9103b8508c1e04bfa5a08c64e7a925f`) adds a `free_sgout` flag to track whether `tls_decrypt_sg()` actually allocated the destination pages, and only frees them when they were newly allocated. ## Impact - **Package/component affected**: Linux kernel kTLS software implementation (`net/tls/tls_sw.c`) - **Affected versions**: Linux 6.2.0 through 6.6.18 (fixed in 6.6.19+, 6.7.7+, 6.1.82+, 6.8-rc3) - **Risk level**: High — kernel-space use-after-free can lead to memory corruption, information leaks, or potentially privilege escalation - **Consequences**: An attacker who can establish a kTLS session and control the read pattern (partial reads) can trigger premature page freeing, leading to use-after-free when the freed pages are accessed. With page poisoning or KASAN, this manifests as data corruption or sanitizer reports. Without sanitizers, this could lead to silent memory corruption or exploitation. ## Impact Parity - **Disclosed/claimed maximum impact**: Memory corruption (use-after-free in kernel space) - **Reproduced impact from this run**: Memory corruption confirmed via KASAN use-after-free report and data corruption (page poisoning). The KASAN report shows a read of freed memory in `copyout()` called from `process_rx_list()` → `tls_sw_recvmsg()`. Data mismatch detected when freed pages are poisoned. - **Parity**: `full` — the claimed memory corruption impact is fully reproduced with KASAN detection and data corruption evidence. - **Not demonstrated**: Code execution / privilege escalation (the UAF is demonstrated as memory corruption, not exploit chain to code execution). ## Root Cause The vulnerability is in `tls_decrypt_done()` in `net/tls/tls_sw.c`. This function is the async completion callback for AEAD decryption in the kTLS software receive path. **The bug**: `tls_decrypt_done()` uses the check `if (sgout != sgin)` to determine whether to free destination pages. This check was intended to distinguish in-place decryption (where destination and source scatterlists are the same, so no pages should be freed) from non-in-place decryption (where destination pages were allocated and should be freed). However, the check is incorrect because `sgout != sgin` is also true when `sgout` points to pages from `clear_skb` (allocated by `tls_alloc_clrtxt_skb()`), not just when pages were newly allocated by `tls_decrypt_sg()`. **The flow**: 1. `tls_sw_recvmsg()` is called with a partial read (`len < record_size`) 2. `darg.zc = false` because `to_decrypt > len` (record doesn't fit in user buffer) 3. `tls_decrypt_sg()` takes the `clear_skb` path: allocates `clear_skb` with pages, sets `sgout` to point to `clear_skb` pages 4. `tls_do_decryption()` starts async decryption (returns `-EINPROGRESS` via `cryptd` workqueue) 5. The `clear_skb` is placed on `ctx->rx_list` for later processing 6. `tls_decrypt_async_wait()` waits for async completion 7. `tls_decrypt_done()` is called from the workqueue → sees `sgout != sgin` → calls `put_page()` on `clear_skb` pages → **pages freed while `clear_skb` still references them** 8. On the next `recv()` call, `process_rx_list()` reads from the `clear_skb` → accesses freed pages → **use-after-free** **The fix** (commit `32b55c5ff9103b8508c1e04bfa5a08c64e7a925f`): - Adds `bool free_sgout` to `struct tls_decrypt_ctx` - Sets `dctx->free_sgout = !!pages` in `tls_decrypt_sg()` (only true when pages were actually allocated) - Changes the check in `tls_decrypt_done()` from `if (sgout != sgin)` to `if (dctx->free_sgout)` **Fix commit**: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=32b55c5ff9103b8508c1e04bfa5a08c64e7a925f ## Reproduction Steps 1. **Reference**: `bundle/repro/reproduction_steps.sh` 2. **What the script does**: - Downloads Linux kernel v6.6.18 source - Applies GCC 15 compatibility patches (adds `-std=gnu11` to REALMODE_CFLAGS, boot/compressed, and EFI libstub Makefiles) - Patches `crypto/simd.c` to force AEAD decrypt operations through the `cryptd` workqueue (simulating async crypto behavior that occurs with hardware crypto accelerators or in contexts where SIMD is unavailable) - Creates two kernel builds: **vulnerable** (fix reverted) and **fixed** (fix present) - Both builds include `CONFIG_TLS=y`, `CONFIG_KASAN=y`, `CONFIG_CRYPTO_AES_NI_INTEL=y`, `CONFIG_PAGE_POISONING=y` - Creates a minimal initramfs with a static kTLS test program that: - Establishes a loopback TCP connection - Configures kTLS with AES-128-GCM (TLS 1.2) - Sends 4096-byte records and performs partial reads (100 bytes then 3996 bytes) - Checks for data corruption - Boots both kernels in QEMU (TCG mode, `-cpu max` for AES-NI) with `page_poison=1` - Captures serial console output for KASAN reports and corruption detection 3. **Expected evidence**: - Vulnerable kernel: `BUG: KASAN: use-after-free in copyout` + `Data MISMATCH - corruption detected!` + `BUG: Bad page state` - Fixed kernel: No KASAN reports, no corruption, test completes normally ## Evidence - **Vulnerable kernel log**: `bundle/logs/qemu-vuln-ktls.log` - **Fixed kernel log**: `bundle/logs/qemu-fixed-ktls.log` - **Reproduction script log**: `bundle/logs/reproduction_steps.log` ### Key excerpts from vulnerable kernel: ``` [ 4.956611] BUG: KASAN: use-after-free in copyout+0x2d/0x50 [ 4.957007] Read of size 100 at addr ff1100000186000d by task init/58 [ 4.957072] Call Trace: [ 4.957072] copyout+0x2d/0x50 [ 4.957072] _copy_to_iter+0x15b/0x1070 [ 4.957072] __skb_datagram_iter+0x3c7/0x890 [ 4.957072] skb_copy_datagram_iter+0x2f/0x120 [ 4.957072] process_rx_list+0x2b5/0x5f0 [ 4.957072] tls_sw_recvmsg+0x1081/0x17b0 ``` ``` [kTLS] Data MISMATCH - corruption detected! ``` ``` [ 4.969549] BUG: Bad page state in process init pfn:02efc ``` ### Key excerpts from fixed kernel (negative control): ``` [kTLS] Data matches (no corruption observed yet) [kTLS] Completed 20 iterations [kTLS] Test complete. If KASAN is enabled, check dmesg for UAF report. ``` (No KASAN reports, no corruption, no bad page state) ### Environment details: - Kernel: Linux 6.6.18 (v6.6.18 with fix reverted for vulnerable, fix present for fixed) - Compiler: GCC 15.2.0 with `-std=gnu11` compatibility patches - QEMU: QEMU 10.2.1, TCG mode, `-cpu max` (AES-NI emulation), 2 vCPUs, 1024MB RAM - KASAN: Generic KASAN with inline instrumentation - Page poisoning: enabled (`page_poison=1`) - Crypto: AES-NI GCM with forced async decrypt via `cryptd` workqueue - Test: kTLS AES-128-GCM, TLS 1.2, 4096-byte records, 100-byte partial reads ## Recommendations / Next Steps - **Upgrade**: Apply the fix (commit `32b55c5ff9103b8508c1e04bfa5a08c64e7a925f`) or upgrade to Linux 6.6.19+, 6.7.7+, 6.1.82+, or 6.8-rc3+. - **Testing**: Enable `CONFIG_KASAN` and `CONFIG_PAGE_POISONING` in kernel testing configurations to detect similar use-after-free bugs in the kTLS receive path. - **Code review**: Audit other completion callbacks in the kTLS code that use pointer comparison (`sgout != sgin`) instead of explicit allocation tracking. - **Mitigation**: If kTLS is used in production with hardware crypto accelerators (which naturally produce async completions), ensure the kernel is patched. The bug only manifests with async decryption, which is the normal case with hardware crypto. ## Additional Notes - **Idempotency**: The reproduction script is idempotent — it checks for existing build artifacts and reuses them. Running it multiple times produces the same result. - **Async path simulation**: The `crypto/simd.c` patch forces AEAD decrypt operations through the `cryptd` workqueue, simulating the async behavior that occurs naturally with hardware crypto accelerators or in contexts where SIMD/FPU is unavailable (e.g., softirq context). This does not modify the vulnerable code path in `tls_sw.c`; it only ensures the async completion path is exercised. In production, this async path is triggered by hardware crypto offload or when the crypto operation is deferred. - **Page poisoning**: `CONFIG_PAGE_POISONING` with `page_poison=1` fills freed pages with a poison pattern (0xAA), making the use-after-free visible through data corruption even without KASAN. This provides an additional layer of detection beyond KASAN's shadow memory tracking. - **GCC 15 compatibility**: The v6.6.18 kernel requires minor Makefile patches to build with GCC 15, which defaults to C23 where `true`/`false` are keywords. The patches add `-std=gnu11` to REALMODE_CFLAGS, boot/compressed, and EFI libstub Makefiles, matching the fix applied in newer kernel versions. ## Reproduction Details Reproduced: 2026-07-07T19:44:24.061Z Duration: 2972 seconds Tool calls: 326 Turns: Unknown Handoffs: 2 ## Quick Verification Run one of these commands to verify locally: pruva-verify REPRO-2026-00265 pruva-verify CVE-2024-26582 Or open in GitHub Codespaces (zero-friction, auto-runs): https://github.com/codespaces/new?ref=repro/REPRO-2026-00265&repo=N3mes1s/pruva-sandbox Or download and run the script manually: curl -O https://api.pruva.dev/v1/reproductions/REPRO-2026-00265/artifacts/bundle/repro/reproduction_steps.sh chmod +x reproduction_steps.sh ./reproduction_steps.sh WARNING: Run in a sandboxed environment. This exploits a real vulnerability. ## References - NVD: https://nvd.nist.gov/vuln/detail/CVE-2024-26582 - Source: https://github.com/torvalds/linux ## Artifacts - bundle/repro/reproduction_steps.sh (reproduction_script, 16388 bytes) - bundle/repro/rca_report.md (analysis, 9255 bytes) - bundle/vuln_variant/reproduction_steps.sh (reproduction_script, 10523 bytes) - bundle/vuln_variant/rca_report.md (analysis, 16371 bytes) - bundle/artifact_promotion_manifest.json (other, 10388 bytes) - bundle/artifact_promotion_report.json (other, 10406 bytes) - bundle/vuln_variant/backlog_variant_test.c (other, 7029 bytes) - bundle/vuln_variant/ebusy_force.patch (other, 2426 bytes) - bundle/vuln_variant/apply_fix13114dc.py (script, 3881 bytes) - bundle/vuln_variant/source_identity.json (other, 2743 bytes) - bundle/vuln_variant/root_cause_equivalence.json (other, 3185 bytes) - bundle/logs/qemu-vuln-ktls.log (log, 115380 bytes) - bundle/logs/qemu-fixed-ktls.log (log, 24574 bytes) - bundle/repro/validation_verdict.json (other, 882 bytes) - bundle/repro/runtime_manifest.json (other, 1002 bytes) - bundle/logs/vuln_variant/qemu-fixed-ebusy-variant.log (log, 36286 bytes) - bundle/logs/vuln_variant/qemu-fixed-nobacklog-baseline.log (log, 24458 bytes) - bundle/logs/vuln_variant/qemu-fixed-ebusy-13114dc.log (log, 24458 bytes) - bundle/logs/vuln_variant/kasan_report_excerpt.txt (other, 3811 bytes) - bundle/vuln_variant/patch_analysis.md (documentation, 8015 bytes) - bundle/vuln_variant/variant_manifest.json (other, 4155 bytes) - bundle/vuln_variant/validation_verdict.json (other, 2406 bytes) - bundle/vuln_variant/runtime_manifest.json (other, 2987 bytes) - bundle/logs/vuln_variant/fixed_version.txt (other, 529 bytes) - bundle/logs/vuln_variant/qemu-vuln-ebusy-variant.log (log, 36165 bytes) - bundle/logs/vuln_variant/reproduction_steps.log (log, 1731 bytes) ## API Access - JSON: https://api.pruva.dev/v1/reproductions/REPRO-2026-00265 - Script: https://api.pruva.dev/v1/reproductions/REPRO-2026-00265/artifacts/bundle/repro/reproduction_steps.sh - Web: https://pruva.dev/r/REPRO-2026-00265 ## For AI Assistants To help users with this reproduction: 1. The `pruva-verify` command is the simplest way to run the reproduction 2. The script downloads and runs automatically in an isolated temp directory 3. Always recommend running in a VM, container, or disposable environment 4. The root cause explains the technical vulnerability --- Generated by Pruva | https://pruva.dev