# Winmin Kernel Vuln Analyzer Kernel Vuln Analyzer

> Kernel Vulnerability Analyzer

- Skill: `tomevault-io/winmin-kernel-vuln-analyzer-kernel-vuln-analyzer` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add tomevault-io/winmin-kernel-vuln-analyzer-kernel-vuln-analyzer`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tomevault-io/winmin-kernel-vuln-analyzer-kernel-vuln-analyzer/raw
- Safety review: pending (external: skill-scanner PASS, skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: tomevault-io (https://skillmd.com/u/tomevault-io)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/tomevault-io/winmin-kernel-vuln-analyzer-kernel-vuln-analyzer

---


# Kernel Vulnerability Analyzer

A comprehensive skill for analyzing Linux kernel vulnerabilities — from crash log triage through
root cause analysis, exploitability assessment, patch development, and verified fix delivery.

This skill is designed around a **hive-mode subagent architecture**: break the analysis into
parallel workstreams, plan before executing, and coordinate results across agents.

## Core Workflow Overview

The analysis follows seven phases. Each phase builds on the previous, but many sub-tasks within
a phase can run in parallel via subagents.

```
Phase 1: Triage & Planning ──→ Phase 2: Source Acquisition
    │                              │
    │ [1.0: Source Version Check]  │ [GATE: Source Version Verified]
    ↓                              ↓
Phase 3: Root Cause Analysis ←── Phase 4: Dynamic Analysis (QEMU+GDB)
    ↓
Phase 5: Exploitability Assessment
    ↓
Phase 6: Patch Development & Verification
    │
    │ [6.1: Source Version Re-check]
    │ [GATE: QEMU Evidence Required]
    ↓
Phase 7: Report Generation & Artifact Packaging
```

---

## Phase 1: Triage & Planning

**Goal**: Understand what we're dealing with and plan the analysis strategy.

Before writing a single line of analysis, enter Plan mode and create a structured plan.
This is non-negotiable — kernel bugs are complex and a wrong turn wastes significant time.

### 1.0 Verify Source Tree Is Current (FIRST — Before Anything Else)

**This is a BLOCKING prerequisite.** Before parsing the bug report, before reading source
code, before planning — verify the kernel source tree is up-to-date. This takes 5 seconds
and prevents all downstream version mismatch issues.

```bash
# Step 1: Fetch latest from remote
cd /path/to/kernel-src
git fetch origin --tags

# Step 2: Check how far behind we are
BEHIND=$(git log --oneline HEAD..origin/master | wc -l)
LATEST_TAG=$(git describe --tags --abbrev=0 origin/master 2>/dev/null || echo "unknown")
CURRENT=$(git describe --tags HEAD 2>/dev/null || echo "unknown")
echo "Current: $CURRENT | Latest upstream: $LATEST_TAG | Commits behind: $BEHIND"

# Step 3: If behind → update to latest tag
if [ "$BEHIND" -gt 0 ]; then
    echo "WARNING: Local tree is $BEHIND commits behind upstream ($LATEST_TAG)"
    echo "Updating to latest tag..."
    git checkout "$LATEST_TAG"
fi

# Step 4: Record the base for the report
echo "Analysis base: $(git describe --tags HEAD)"
```

**DO NOT proceed to Phase 1.1 until the source tree is on the latest upstream tag.**

If `git fetch` fails (no network, wrong remote), warn the user and document that the
analysis is based on a potentially stale tree. But ALWAYS attempt the fetch first.

### 1.1 Parse the Input

The user may provide:
- A raw KASAN/UBSAN/BUG/panic log (most common)
- A syzbot report URL or crash description
- A CVE identifier
- A verbal description of a kernel bug
- A PoC (C code, syzkaller repro, etc.)

**For crash logs**, extract these key signals:
- **Bug type**: KASAN (UAF, OOB-read, OOB-write, double-free), UBSAN, BUG(), WARNING, NULL ptr deref, GPF, etc.
- **Faulting address and access type**: Read/Write, address pattern (NULL page, kernel text, slab, etc.)
- **Call stack**: The full decoded call trace — this is the most important piece
- **Slab cache name**: e.g., `kmalloc-256`, `skbuff_head_cache`, `task_struct` — hints at the object type
- **Allocated/Freed stacks**: KASAN often shows where the object was allocated and freed
- **Kernel version and config**: What kernel is this running? What configs are relevant?
- **Subsystem**: Derived from the call stack — is this networking (net/), filesystem (fs/), drivers, etc.?

### 1.1.1 Decode the Stack Trace (Required)

If the crash log contains raw addresses (not resolved to source lines), decode it immediately:

```bash
./scripts/decode_stacktrace.sh vmlinux < crash.log > decoded_crash.log
```

If `vmlinux` is not available, use `addr2line` on individual addresses:

```bash
addr2line -e vmlinux -fip 0xffffffff81234567
```

The **decoded backtrace** is a critical artifact — it is needed for:
1. The root cause analysis (Phase 3) — source file:line references
2. The commit message (Phase 6) — upstream convention requires the decoded trace in the commit body
3. The final report (Phase 7) — annotated trace with file:line

Save the decoded trace to `logs/decoded_crash.log` in the report directory. Keep both the
raw and decoded versions — raw for reproduction, decoded for analysis.

Read `references/crash-log-analysis.md` for detailed patterns and parsing guidance.

### 1.2 Acquire the PoC

If the user provides a crash log but no PoC, you need to obtain or create one.

**From syzbot**: Read `references/syzbot-workflow.md` for details.
```bash
# Download C reproducer from syzbot
curl -sL '<syzbot-repro-url>' -o poc.c
gcc -o poc -static -lpthread poc.c   # always static-link for QEMU rootfs
```

**From CVE databases**: Search for public PoCs on GitHub, Exploit-DB, or the CVE references.

**Write from scratch**: If no PoC exists, write a minimal trigger based on the crash call trace:
1. Identify the syscall entry point from the bottom of the stack trace
2. Set up required preconditions (namespaces, sysctl, devices)
3. Issue the triggering syscall sequence
4. For race conditions: use pthreads to run concurrent paths

**PoC validation checklist**:
- [ ] Compiles with `gcc -static` (needed for minimal QEMU rootfs)
- [ ] Does it need root? Network? Specific sysctl?
- [ ] Does it need `unshare(CLONE_NEWUSER|CLONE_NEWNET)` for namespaces?
- [ ] Is the crash reliable or does it need loop/stress testing?

### 1.3 Identify the Kernel Subsystem and Source Tree

**Do NOT guess the tree from the top-level directory name alone.** Many subsystems have
independent maintainer trees even though their code lives under a shared parent directory.
The canonical source of truth is `scripts/get_maintainer.pl` and the `MAINTAINERS` file.

**Step 1: Run `get_maintainer.pl` on the affected file**

```bash
./scripts/get_maintainer.pl --scm --web <path/to/affected/file>
# The "SCM:" line tells you the correct git tree
# The "W:" line tells you the web page / mailing list
```

**Step 2: Cross-reference with the subsystem tree mapping**

Some paths are deceptive — always match **most-specific path first**:

| Source path | Subsystem | Git tree (fixes) | Prefix |
|---|---|---|---|
| `net/bluetooth/` | Bluetooth | `bluetooth/bluetooth.git` | `PATCH bluetooth` |
| `net/wireless/`, `drivers/net/wireless/` | WiFi | `wireless/wifi.git` | `PATCH wifi` |
| `net/mac80211/` | WiFi (mac80211) | `wireless/wifi.git` | `PATCH wifi` |
| `net/netfilter/`, `net/ipv4/netfilter/` | Netfilter | `netfilter/nf.git` | `PATCH nf` |
| `net/bridge/` | Bridge | `netdev/net.git` | `PATCH net` |
| `net/ipv4/`, `net/ipv6/`, `net/core/` | Networking core | `netdev/net.git` | `PATCH net` |
| `net/sctp/`, `net/dccp/`, `net/tipc/` | Networking | `netdev/net.git` | `PATCH net` |
| `net/can/` | CAN | `linux-can/linux.git` | `PATCH can` |
| `net/nfc/` | NFC | `sameo/nfc.git` | `PATCH nfc` |
| `kernel/bpf/`, `net/bpf/` | BPF | `bpf/bpf.git` | `PATCH bpf` |
| `drivers/net/ethernet/` | Network drivers | `netdev/net.git` | `PATCH net` |
| `drivers/bluetooth/` | Bluetooth drivers | `bluetooth/bluetooth.git` | `PATCH bluetooth` |
| `drivers/gpu/drm/` | DRM/GPU | `drm/drm.git` | `PATCH drm` |
| `drivers/usb/` | USB | `usb/usb.git` | `PATCH usb` |
| `sound/` | Sound/ALSA | `tiwai/sound.git` | `PATCH sound` |
| `fs/ext4/` | ext4 | `tytso/ext4.git` | `PATCH ext4` |
| `fs/btrfs/` | Btrfs | `kdave/btrfs.git` | `PATCH btrfs` |
| `fs/xfs/` | XFS | `djwong/xfs-linux.git` | `PATCH xfs` |
| `mm/` | Memory management | `akpm/mm.git` | `PATCH mm` |
| `io_uring/` | io_uring | `axboe/linux-block.git` | `PATCH io_uring` |
| `security/apparmor/` | AppArmor | `jj/linux-apparmor.git` | `PATCH apparmor` |
| Others | General | `torvalds/linux.git` | `PATCH` |

**The trap**: `net/bluetooth/` is under `net/` but does NOT go to `netdev/net.git`.
Bluetooth patches go to `bluetooth/bluetooth.git` and are picked by the Bluetooth
maintainer (Luiz Augusto von Dentz). Eventually they flow through `netdev/net.git`
into mainline, but patches must be submitted to the Bluetooth tree directly.

```
WRONG:  net/bluetooth/l2cap_core.c → "this is net/ → netdev/net.git → PATCH net"
RIGHT:  net/bluetooth/l2cap_core.c → get_maintainer.pl → bluetooth.git → PATCH bluetooth
```

**Another trap**: Many subsystems also maintain a `-next` tree for development patches
(e.g., `netfilter/nf-next.git`, `netdev/net-next.git`, `bpf/bpf-next.git`).
A fix may exist in **either** the fixes tree or the -next tree but not yet in
`torvalds/linux.git`. **You MUST fetch and search both subsystem trees during
deduplication — searching only `origin` (torvalds) will miss fixes that are
queued in the subsystem but not yet merged into mainline.**

```
WRONG:  git log origin/master --grep='nf_tables' -- net/netfilter/   ← only searches torvalds
RIGHT:  git fetch nf; git fetch nf-next
        git log nf/main nf-next/main origin/master --grep='nf_tables' -- net/netfilter/
```

**Step 3: When in doubt, always trust `get_maintainer.pl`**

```bash
# It handles all the edge cases in MAINTAINERS
./scripts/get_maintainer.pl --scm net/bluetooth/l2cap_core.c
# Output will show bluetooth.git, not net.git
```

**Step 4: Identify the relevant kernel version**
- Check if the bug exists in mainline, stable, or LTS
- Parse from crash log: `grep 'Not tainted' crash.log`

### 1.4 Create the Analysis Plan

Use Plan mode to structure the work. A typical plan:

```
1. Clone source tree and checkout relevant version
2. [Parallel] Spawn subagents for:
   a. Static analysis: read the vulnerable code path, trace data flow
   b. Git archaeology: find the commit that introduced the bug (git log, git bisect)
   c. Cross-reference: search kernelctf knowledge base for similar vulnerability patterns
3. Set up QEMU environment with matching kernel config
4. Reproduce the crash with PoC
5. Dynamic analysis with GDB to confirm root cause
6. Assess exploitability
7. Develop and test patch
8. Package report and artifacts
```

### 1.5 Subagent Dispatch Strategy

This skill makes heavy use of subagents (the Agent tool) to parallelize work.
The guiding principle: **plan centrally, execute in parallel, synthesize results**.

**Parallel-safe tasks** (can run as concurrent subagents):
- Source code reading of different files/functions
- Git log / git blame on different paths
- Searching the kernelctf knowledge base
- Compiling kernel in a worktree
- Web research for related CVEs or patches

**Sequential tasks** (must wait for prior results):
- Dynamic analysis depends on QEMU environment being ready
- Patch writing depends on confirmed root cause
- Patch verification depends on patch being applied

When spawning subagents, always provide:
- Clear, self-contained task description
- All file paths and context needed (the subagent has no memory of your conversation)
- Expected output format
- Use `isolation: "worktree"` for tasks that modify files (compilation, patching)

---

## Phase 2: Source Acquisition & Static Analysis

### 2.1 Acquire and Update Source

In practice, the user usually already has a local kernel source tree. Do NOT blindly
clone a fresh repo every time — check what's available first.

**Case A: User already has a local kernel source tree (most common)**

```bash
cd /path/to/existing/kernel-src

# 1. Check current state
git status                           # any uncommitted changes?
git describe --tags --abbrev=0       # what version is this?
git remote -v                        # what remote does it track?

# 2. Fetch latest from upstream — ALWAYS do this
git fetch origin
git fetch --tags origin

# 3. Check how far behind we are
git log --oneline HEAD..origin/master | head -20
# If significantly behind (>100 commits), strongly recommend updating

# 4. Update to latest (if tree is clean)
git pull --rebase origin master
# Or if on a specific branch:
git pull --rebase origin <branch>
```

If the user has uncommitted changes (their own annotations, previous patches, etc.):
- `git stash` first, then fetch/pull, then `git stash pop` after analysis
- Or work on a detached HEAD at the latest tag: `git checkout <latest-tag>`

**Case B: No local source — clone fresh**

```bash
# For the appropriate subsystem tree:
git clone <tree-url> /path/to/analysis/kernel-src
cd /path/to/analysis/kernel-src
git checkout <version-tag>

# For full history (needed for git bisect / git blame):
git clone <tree-url> /path/to/analysis/kernel-src

# Shallow clone is faster but limits git bisect:
git clone --depth=1 --branch <version-tag> <tree-url> /path/to/analysis/kernel-src
```

**Case C: Local source exists but tracks a different tree**

Sometimes the user has `torvalds/linux.git` but the bug is in a subsystem tree
(e.g., `netdev/net.git`). Add it as a second remote:

```bash
git remote add net git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net.git
git fetch net
git log net/master --oneline | head -5
```

### 2.2 Verify Source Version (Required — Do This Before Any Analysis)

Writing a patch against stale source is a critical mistake: the patch may not apply to
mainline, may fix an already-fixed bug, or may be semantically wrong due to context changes.
Always verify the source is current before proceeding.

**Step 1: Check if the local tree is up-to-date with remote AND fetch subsystem trees**

```bash
git fetch origin
git log HEAD..origin/master --oneline | head -10
# If output is non-empty → the local tree is behind. Update it:
git pull --rebase origin master

# CRITICAL: Also fetch the subsystem tree for the affected file.
# Use the subsystem tree mapping table above, or run:
#   ./scripts/get_maintainer.pl --scm <path/to/affected/file>
# to find the correct git tree URL.
#
# Example for net/netfilter/:
#   git remote add nf https://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf.git 2>/dev/null
#   git remote add nf-next https://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf-next.git 2>/dev/null
#   git fetch nf
#   git fetch nf-next
#
# Example for net/core/, net/ipv4/, drivers/net/:
#   git remote add net https://git.kernel.org/pub/scm/linux/kernel/git/netdev/net.git 2>/dev/null
#   git remote add net-next https://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next.git 2>/dev/null
#   git fetch net
#   git fetch net-next
#
# For other subsystems, follow the same pattern with the tree from the table.
# The 2>/dev/null on `git remote add` suppresses "already exists" errors on re-runs.
```

**Step 2: Check if the bug is already fixed — search BOTH origin AND subsystem trees**

This is the most important check — someone may have already submitted a fix.
**A fix may exist in the subsystem tree (e.g., nf.git, net-next.git) but not yet
in torvalds/linux.git.** Searching only origin will miss these. You MUST search
all fetched remotes.

```bash
# Determine which refs to search (adjust remote names per subsystem)
# Example for netfilter — replace nf/main nf-next/main with your subsystem refs
SEARCH_REFS="origin/master nf/main nf-next/main"

# Search for patches touching the vulnerable function
git log $SEARCH_REFS --oneline -S '<vulnerable_function_name>' -- <file>

# Search for Fixes: tags referencing the introducing commit
git log $SEARCH_REFS --oneline --grep='Fixes: <introducing-commit-short-hash>'

# Search commit messages for the bug description keywords
git log $SEARCH_REFS --oneline --grep='<key_keyword>' -- <file>

# Check the linux-stable tree for backported fixes
git log --all --oneline --grep='<CVE-number>'
```

If a fix already exists (in ANY of the searched refs — origin OR subsystem tree):
- **Report it** — tell the user the bug is already fixed, with the fixing commit hash and which tree it's in
- **Verify the fix** — read the upstream fix to confirm it actually addresses the root cause
- **Skip to Phase 7** — no need to write a new patch; document the existing fix in the report

**Step 3: Two-Stage Workflow — Analyze on Crash Version, Patch on Latest**

This is a **critical distinction** that the skill MUST enforce:

Extract the crash kernel version from the log first:

```bash
# Parse the crash log for the kernel version
CRASH_VERSION=$(grep -oP 'Not tainted \K\S+' crash.log)
# e.g., CRASH_VERSION="6.12.77"

# Find the closest git tag
CRASH_TAG=$(git tag -l "v${CRASH_VERSION}*" | sort -V | tail -1)
# Or for stable kernels: git tag -l "v$(echo $CRASH_VERSION | cut -d. -f1-2)*" | sort -V | tail -1

# Get current latest upstream
git fetch origin --tags
LATEST=$(git describe --tags --abbrev=0 origin/master)
```

```
┌────────────────────────────────────────────────────────────────────┐
│  Crash log says kernel $CRASH_VERSION (e.g., a stable/old release) │
│  Latest upstream is $LATEST (e.g., mainline HEAD)                  │
│                                                                     │
│  WRONG: Write the patch against $CRASH_VERSION and call it done.    │
│  RIGHT: Analyze on $CRASH_VERSION, then rebase the fix onto         │
│         $LATEST mainline/subsystem-tree HEAD before finalizing.     │
└────────────────────────────────────────────────────────────────────┘
```

**Stage 1 — Analyze & Reproduce on the crash version**:
```bash
# Checkout the crash kernel version for analysis and QEMU reproduction
git checkout "$CRASH_TAG"
# Build, boot in QEMU, reproduce the crash, do root cause analysis
# This ensures you understand the bug in the exact context it was reported
```

**Stage 2 — Fetch latest code and check before writing the patch**:
```bash
# ALWAYS fetch BOTH origin AND the subsystem tree before writing ANY patch
git fetch origin
git fetch --tags origin

# Fetch the subsystem remote (must have been added in Phase 2.2 Step 1)
# Example for netfilter:
git fetch nf
git fetch nf-next

# Check: does the vulnerable code still exist in the latest version?
git show origin/master:<path/to/vulnerable/file> | grep '<vulnerable_function>'
# If the code has been refactored or removed → the bug may be moot in mainline

# Check: has someone already fixed this in the latest tree?
# CRITICAL: Search BOTH origin AND subsystem refs — a fix queued in the
# subsystem tree may not have reached torvalds/linux.git yet.
SEARCH_REFS="origin/master nf/main nf-next/main"   # adjust per subsystem
git log $SEARCH_REFS --oneline -S '<vulnerable_function>' -- <file> | head -10
git log $SEARCH_REFS --oneline --grep='<key_keyword>' -- <file> | head -10

# If bug still exists in latest → write patch against latest subsystem HEAD:
git checkout origin/master
# Or for networking fixes:
git checkout net/main       # netdev/net.git main branch
# Or for netfilter fixes:
git checkout nf/main        # netfilter/nf.git main branch
```

**Stage 3 — Write the patch against the latest code**:
```bash
# The patch MUST be based on the latest subsystem tree HEAD
# NOT on the old crash kernel version
git diff > patch.diff      # your fix, based on latest code

# Verify the fix also applies to the crash version (for QEMU testing)
git stash
git checkout "$CRASH_TAG"
git stash pop              # if it applies cleanly
# Or: git cherry-pick / manual port if context differs
```

**Why this matters**:
- Upstream WILL NOT accept patches based on old stable kernels
- The code around the bug may have changed (variable renames, refactors, new callers)
- A patch against an old stable may not apply to the latest mainline at all
- Even if the patch applies, context lines may differ → `git am` fails

| Crash kernel version | Analyze on | Write patch against |
|---|---|---|
| Stable (e.g., X.Y.Z) | `v$CRASH_VERSION` tag | Latest `origin/master` or subsystem HEAD |
| Mainline (e.g., X.Y-rcN) | `v$CRASH_VERSION` tag | Latest `origin/master` |
| Distro (e.g., X.Y.Z-distro) | Distro source | Upstream mainline HEAD |
| net-next / subsystem tree | Subsystem HEAD | Subsystem HEAD (already latest) |

**If the local tree is old and behind upstream**:
```bash
# You MUST update before writing the patch
git fetch origin
git log --oneline HEAD..origin/master | wc -l
# If significantly behind → pull or checkout latest

# Check if the vulnerable file has changed significantly
git diff "$CRASH_TAG"..origin/master -- <path/to/file> | diffstat
# If large diff → the patch context has changed, must write against latest
```

**Step 4: Record the base commit in the report**

Always document the exact commit your patch is based on:

```bash
echo "Patch base: $(git log --oneline -1 HEAD)" >> report_metadata.txt
# e.g., "Patch base: abc123def456 Merge tag 'net-6.12-rc4'"
```

This goes in the report's metadata section so anyone applying the patch knows exactly
which tree state it was developed against.

**If the source tree was already present** (e.g., the user has a local clone):
- Still run Steps 1-3 — don't assume it's current
- `git fetch && git log HEAD..origin/master --oneline` is fast and catches stale trees
- If the tree is weeks/months behind, warn the user before proceeding

---

## GATE CHECK: Before Entering Phase 3 — Source Version Verified

**DO NOT proceed to Phase 3 (Root Cause Analysis) until the source tree version is verified.**
This gate ensures you are analyzing and will patch against the correct code.

### Required Checks (must ALL pass)

```bash
echo "=== Source Version Gate Check ==="

# 1. Was remote fetched?
echo -n "1. Remote fetched: "
git fetch origin --tags 2>&1 | tail -1
echo "DONE"

# 1b. Was SUBSYSTEM remote fetched?
# Identify the subsystem remote from Phase 2.2 Step 1 (e.g., nf, nf-next, net, bpf)
echo -n "1b. Subsystem remote fetched: "
# Replace SUBSYS_REMOTE / SUBSYS_NEXT with the actual remote names
SUBSYS_REMOTE="nf"        # adjust per subsystem
SUBSYS_NEXT="nf-next"     # adjust per subsystem (may not exist for all)
git fetch "$SUBSYS_REMOTE" 2>&1 | tail -1 && echo "  $SUBSYS_REMOTE DONE"
git fetch "$SUBSYS_NEXT"   2>&1 | tail -1 && echo "  $SUBSYS_NEXT DONE"
# If `git remote` does not list the subsystem → go back to Phase 2.2 Step 1

# 2. What is the latest upstream tag?
echo -n "2. Latest upstream tag: "
LATEST=$(git describe --tags --abbrev=0 origin/master 2>/dev/null)
echo "$LATEST"

# 3. Are we on or ahead of the latest tag?
echo -n "3. Source version: "
BEHIND=$(git log --oneline HEAD..origin/master 2>/dev/null | wc -l)
if [ "$BEHIND" -eq 0 ]; then
    echo "PASS — up to date ($(git describe --tags HEAD))"
else
    echo "FAIL — $BEHIND commits behind $LATEST"
    echo "   → Run: git checkout $LATEST"
fi

# 4. Has the vulnerable file changed between current HEAD and latest?
echo -n "4. Vulnerable file changed since HEAD? "
VFILE="<path/to/vulnerable/file>"
CHANGES=$(git diff --stat HEAD..origin/master -- "$VFILE" 2>/dev/null | grep -c '|')
if [ "$CHANGES" -eq 0 ]; then
    echo "PASS — no changes"
else
    echo "WARNING — file changed, MUST patch against latest"
fi

# 5. Bug already fixed — search origin AND subsystem trees?
echo -n "5. Already fixed? "
SEARCH_REFS="origin/master"
# Add subsystem refs if they exist
git rev-parse --verify "$SUBSYS_REMOTE/main" &>/dev/null && SEARCH_REFS="$SEARCH_REFS $SUBSYS_REMOTE/main"
git rev-parse --verify "$SUBSYS_NEXT/main"   &>/dev/null && SEARCH_REFS="$SEARCH_REFS $SUBSYS_NEXT/main"
echo "  Searching: $SEARCH_REFS"
git log $SEARCH_REFS --oneline -S '<vulnerable_function>' -- "$VFILE" | head -5
```

**If check 1b shows subsystem remote not configured → go back to Phase 2.2 Step 1.**
**If check 3 shows FAIL → checkout the latest tag before proceeding.**
**If check 5 shows a fix already exists (in ANY ref — origin OR subsystem tree) → report the existing fix, skip to Phase 7.**

### Common Mistakes This Gate Prevents

| Mistake | Consequence | This gate catches it |
|---|---|---|
| Patching against stale tree | Patch may not apply to mainline | Check 3 |
| Missing a context change | Patch applies but is semantically wrong | Check 4 |
| Duplicate work (mainline) | Bug already fixed in torvalds/linux.git | Check 5 (origin) |
| Duplicate work (subsystem) | Fix queued in subsystem tree, not yet in mainline | Check 5 (subsystem refs) |
| Subsystem remote not fetched | Miss fixes in nf.git / net-next.git / etc. | Check 1b |
| Wrong version in report | Report claims wrong "latest affected" | Check 2 |

---

### 2.3 Static Analysis (Spawn as Subagents)

Launch these in parallel:

**Subagent A — Code Path Analysis**:
- Read the functions in the call stack, starting from the crash point
- Trace the data flow: where does the faulting pointer come from?
- Identify the object lifecycle: allocation, use, free
- Look for missing locks, reference count issues, error path leaks

**Subagent B — Git Archaeology**:
- `git log --oneline <file>` for recent changes to the affected files
- `git blame` on the vulnerable lines to find the introducing commit
- Check if there are already patches in mainline or -next that fix this
- Look for related fixes in the same area (often bugs cluster)

**Subagent C — Knowledge Base Cross-Reference**:
- Search `references/kernelctf-knowledge-base.md` for similar vulnerability patterns
- Search `references/vuln-classification.md` to classify the bug type
- Check if this subsystem has known exploit primitives

---

## Phase 3: Root Cause Analysis

This is the most critical phase. The symptom (what the crash log shows) often differs from
the actual bug.

**Common Symptom-vs-Root-Cause Mismatches**:

| Crash Symptom | Possible True Root Cause |
|---|---|
| NULL pointer dereference | UAF (object freed, memory reused/zeroed) |
| General Protection Fault | UAF (object freed, slab poisoned with 0x6b6b6b6b) |
| KASAN: slab-use-after-free | Straightforward UAF, but find the race condition |
| KASAN: slab-out-of-bounds | Off-by-one, integer overflow leading to undersized allocation |
| BUG: unable to handle page fault | UAF, double-free, or type confusion |
| WARNING in refcount_t | Reference count underflow — likely a UAF waiting to happen |
| UBSAN: shift-out-of-bounds | Integer handling bug, possibly exploitable for info leak |

**Always ask**: "What is the actual invariant violation, not just the symptom?"

Read `references/vuln-classification.md` for the full taxonomy of kernel vulnerability classes
and how to distinguish them.

### 3.1 Determine the True Bug Class

To identify the real root cause:

1. **Trace object lifetime**: When was the object allocated? When freed? Who still holds a reference?
2. **Identify the race window**: For concurrency bugs, what's the race between? (syscall vs IRQ, two CPUs, etc.)
3. **Check error paths**: Many kernel bugs live in error handling — a `goto err` that forgets to unlock or drop a reference
4. **Verify with KASAN alloc/free stacks**: If KASAN provides them, the allocation and free call stacks tell you exactly who created and destroyed the object

### 3.2 Build the Bug Narrative — Source-Level Deep Trace (Required)

A shallow narrative ("Thread A frees, Thread B uses" or "missing NULL check") is NOT sufficient.
After confirming the root cause, you must produce a **source-level deep trace** that combines
the kernel source code and the PoC's behavior to explain exactly how the bug manifests.

This trace must be generated **from the actual source code and PoC for each specific bug**.
The approach varies by vulnerability class — use the matching methodology below.

#### Common Steps (All Vulnerability Types)

**Step A: Map PoC actions to kernel code paths**

Read the PoC and trace what each part does in the kernel:
- `PoC line/action → syscall/packet → kernel entry function (file.c:line)`
- For multi-threaded PoCs: which thread does what, and what's the intended race

**Step B: Read the relevant kernel source with file:line citations**

For every function in the call chain from syscall entry to crash:
- What does it do? What data does it read/write?
- What validation/checks does it perform (or fail to perform)?
- What synchronization (locks, RCU, refcount, memory barriers) does it use?

**Step B.1: Verify reachability — trace call-chain preconditions**

For each code site identified as vulnerable, verify that the bad state can actually
occur there. Trace the full call chain backwards and identify all preconditions that
must hold for execution to reach that site: feature flags, device configuration
constraints, creation-time validation, compile-time guards, caller-enforced invariants.
If a precondition already guarantees the state is valid at that site, the site is not
truly vulnerable — exclude it from the fix. Do NOT assume symmetry (e.g., "if the IPv6
path needs a fix, the IPv4 path must too") without proving each case independently.

**Step C: Build a timeline/flow diagram from source**

Produce a visual that shows the bug's progression. The format depends on the
vulnerability class (see below). Use actual function names and file:line references
from the source, not generic placeholders.

**Step D: Explain why the bug exists**

- What invariant is violated?
- What mechanism was supposed to prevent this? Why did it fail?
- Is this a design flaw or an implementation oversight?

#### Per-Vulnerability-Class Methodology

**Choose the methodology that matches your bug's root cause.** Not every bug involves
refcounts or races — trace what's actually relevant.

**UAF / Double-Free / Refcount bugs**:
- Trace the object's full refcount lifecycle: creation (init → get) → normal state →
  destruction (put → release → free), with refcount value at each step
- Identify who holds each reference and when they release it
- Show the race timeline (side-by-side CPUs) with refcount transitions as `before→after`
- Highlight: missing `kref_get_unless_zero`, premature `put`, or unprotected reader

**Race conditions (non-refcount)**:
- Identify the shared state being raced on (flag, pointer, list, counter)
- Show the TOCTOU window: what's checked, what changes, what's used
- Side-by-side CPU timeline showing interleaving that leads to the bug
- Highlight: missing lock, wrong lock scope, missing memory barrier

**NULL pointer dereference (non-race)**:
- Trace the data flow: where does the NULL pointer originate?
- Is it from a failed allocation? A missing initialization? An error path that
  skips setup? A sparse array lookup (like `inet_protos[]`)?
- Show the call chain from the point where NULL enters to the crash dereference
- Highlight: what validation is missing and where it should be

**Out-of-bounds (OOB) read/write**:
- Trace the buffer allocation: what size, from where, based on what input?
- Trace the access: what index/offset, from where, based on what input?
- Show the arithmetic: `allocated_size` vs `accessed_offset` — why does it overflow?
- If integer overflow: show the multiplication/addition that wraps
- Highlight: missing bounds check, wrong size calculation, signedness confusion

**Logic bugs / State machine errors**:
- Map out the state machine: what states exist, what transitions are valid?
- Show the sequence of operations that reaches an "impossible" state
- Trace the error path that skips a required state transition
- Highlight: missing state check, wrong transition order, error path that forgets cleanup

**Info leaks**:
- Trace the data flow from kernel memory to user space
- Identify the uninitialized field, padding bytes, or stale pointer
- Show the struct layout with `pahole` — which bytes are leaked?
- Highlight: missing memset/initialization, struct padding, wrong copy size

**Type confusion**:
- Show the two types involved and their different layouts
- Trace how the object gets cast/reinterpreted as the wrong type
- Highlight which fields overlap incorrectly (especially function pointers vs data)

#### Step E: Visualize Your Analysis with ASCII Diagrams

Diagrams are NOT a separate step — they are the **visual output of the source analysis above**.
After completing Steps A-D, produce diagrams that summarize what you found. The diagrams must
reference actual function names and file:line from YOUR analysis, not generic templates.

Generate whichever diagram types are relevant to the bug:

- **Call chain + data transformation**: Show how data flows through each function with
  `skb->data` / pointer / buffer state at each layer. Each box = actual function (file:line).
- **Race timeline (for concurrency bugs)**: Side-by-side CPUs with actual function names,
  refcount/state transitions, and the race window marked.
- **Struct layout (for OOB/type confusion/info leak)**: pahole-style field offsets showing
  which field is corrupted/leaked/confused. Use actual struct name from the source.
- **Packet/data format (for protocol bugs)**: Byte-level layout of attacker input showing
  which fields are controlled and where validation is missing.
- **Object lifecycle (for UAF)**: Allocation → use → free → use-after-free with refcount
  values at each step, referencing actual functions.
- **State machine (for logic bugs)**: Valid vs actual state transitions.
- **Memory/slab layout (for heap bugs)**: Slab page showing adjacent objects.

**The diagram must reflect your source code analysis — not be a generic template.**
For example, a call chain diagram should use the real function names you found in Step B,
not placeholder names like `function_a()`.

#### Quality Criteria (All Types)

- Every source reference has a **file:line** citation
- The PoC's behavior is mapped to kernel code paths
- State changes (refcount, lock, flag, pointer) show **before→after** values
- The crash is traced to a specific struct field and offset
- Diagrams use actual function/struct names from the source analysis, not placeholders
- There's a clear explanation of **what's broken and why**

#### What to Avoid

- Generic descriptions without source references ("the object is freed then used")
- Drawing diagrams without doing the source analysis first (diagrams are OUTPUT, not INPUT)
- Using only one methodology for all bug types (not everything is a refcount race)
- Skipping the PoC→kernel mapping (the reader needs to understand HOW the bug triggers)
- Copying template diagrams instead of generating them from your analysis

### 3.3 Determine Affected Version Range

After identifying the introducing commit, determine the exact affected version range:

```bash
# Find the earliest release tag containing the introducing commit
git tag --contains <introducing-commit> | sort -V | head -5
# e.g., v3.13-rc1 → bug exists since v3.13

# If a fix already exists upstream, find when it landed
git tag --contains <fixing-commit> | sort -V | head -5
# e.g., v6.14-rc2 → fixed in v6.14

# Check which stable branches are affected
git branch -r --contains <introducing-commit> | grep 'stable'
# Check which stable branches have the fix backported
git branch -r --contains <fixing-commit> | grep 'stable'
```

Record in the report: `Affected: v3.13 — v6.13 (fixed in v6.14-rc2)`

For stable/LTS impact, check if the fix needs `Cc: stable@vger.kernel.org`.

### 3.4 Diagram Reference (Format Examples)

When producing diagrams in Step E above, use these format conventions. These are
**formatting templates only** — your actual diagrams must use real function names
and data from your source analysis.

See `references/crash-log-analysis.md` for address interpretation patterns and
`references/vuln-classification.md` for the bug classification decision tree.

---

## Phase 4: Dynamic Analysis (QEMU + GDB)

### 4.1 Set Up QEMU Environment

Read `references/qemu-setup.md` for detailed setup instructions.

Key requirements:
- Build kernel with: `CONFIG_KASAN=y`, `CONFIG_DEBUG_INFO=y`, `CONFIG_GDB_SCRIPTS=y`,
  `CONFIG_FRAME_POINTER=y`, `CONFIG_HARDENED_USERCOPY=y` (and relevant subsystem configs)
- Use `virtme-ng` for quick boot if applicable, or full QEMU with custom rootfs
- Prepare a minimal rootfs with the PoC compiled and ready

```bash
# Example QEMU launch (adjust as needed)
qemu-system-x86_64 \
  -kernel arch/x86/boot/bzImage \
  -initrd rootfs.cpio.gz \
  -append "console=ttyS0 root=/dev/ram rdinit=/init nokaslr" \
  -nographic \
  -m 2G \
  -smp 2 \
  -s -S  # GDB stub on port 1234, halt at start
```

### 4.2 Reproduce and Debug

1. Boot the vulnerable kernel in QEMU
2. Run the PoC and confirm the crash reproduces
3. Attach GDB: `gdb vmlinux -ex "target remote :1234"`
4. Set breakpoints at key functions identified in static analysis
5. Step through the vulnerable code path
6. Confirm the root cause hypothesis from Phase 3

### 4.3 Key GDB Commands for Kernel Debugging

```
# Kernel-specific
lx-symbols                    # Load kernel module symbols
lx-dmesg                      # Print kernel log
lx-ps                         # List processes
lx-lsmod                      # List modules

# Analysis
p *(struct sk_buff *)$rdi     # Print kernel structures
info threads                   # Check CPU/thread state
bt                             # Backtrace
watch *(int *)0xaddr          # Hardware watchpoint on the vulnerable field
```

### 4.4 Handling Non-Deterministic Reproduction

Race conditions and timing-sensitive bugs may not crash on every run.

**Increase reproduction rate**:
```bash
# Loop the PoC — run 100 times and count crashes
for i in $(seq 1 100); do
    timeout 5 ./poc 2>/dev/null
    echo "Run $i: exit=$?"
done

# Increase CPU count to widen race windows
qemu-system-x86_64 ... -smp 4    # or -smp 8

# Add system stress to increase scheduling pressure
stress-ng --cpu 4 --io 2 --vm 2 --timeout 60 &
./poc

# Use taskset to pin PoC threads to specific CPUs
taskset -c 0,1 ./poc
```

**Record reproduction rate** in the report: e.g., "Triggers 30/100 runs with -smp 4"

**If the PoC never crashes**:
- Verify kernel config matches the crash environment (especially KASAN, PREEMPT, SMP)
- Check if the compiler version matters (see `references/syzbot-workflow.md`)
- Try the exact syzbot kernel config if available
- Add `usleep()` delays in the PoC to manipulate race timing
- Use `ftrace` to confirm the race window exists even if it doesn't crash

---

## Phase 5: Exploitability Assessment

After confirming the root cause, assess whether this bug is exploitable for privilege escalation,
information leak, or denial of service.

Read `references/exploitability-assessment.md` for the full assessment framework.

### 5.1 Key Questions

1. **What primitive does this bug give an attacker?**
   - UAF → potential arbitrary read/write via heap spray
   - OOB write → adjacent object corruption
   - Double-free → overlapping allocations
   - Info leak → KASLR bypass
   - Race condition → how wide is the window? Is it winnable?

2. **What is the attack surface?**
   - Reachable from unprivileged user? Needs `CAP_NET_ADMIN`? Needs user namespaces?
   - Reachable from network? From a container?

3. **What objects share the same slab cache?**
   - For UAF/OOB: what useful kernel objects (e.g., `struct cred`, `struct file`, `msg_msg`,
     `pipe_buffer`, `sk_buff`) live in the same `kmalloc-*` bucket?
   - Can the attacker control allocation/free timing?

4. **What mitigations apply?**
   - KASLR, SMEP, SMAP, CFI, RANDSTRUCT
   - `CONFIG_SLAB_FREELIST_RANDOM`, `CONFIG_SLAB_FREELIST_HARDENED`
   - `CONFIG_HARDENED_USERCOPY`, `CONFIG_USERFAULTFD` availability

5. **Is there precedent?**
   - Search the kernelctf knowledge base for exploits in the same subsystem or using similar primitives
   - Reference known techniques: `msg_msg` spray, `pipe_buffer` ROP, `io_uring` primitives, cross-cache attacks

### 5.2 Permission Gate Analysis (`capable` vs `ns_capable`) — Required

Before rating exploitability, you MUST trace the full permission check chain from
syscall entry to the vulnerable function. This determines the true attack surface.

**Why this matters**: A bug gated by `capable(CAP_NET_ADMIN)` needs real root.
But the SAME capability checked via `ns_capable()` is obtainable by any unprivileged
user through `unshare(CLONE_NEWUSER|CLONE_NEWNET)`. Many analysts miss this distinction,
leading to wrong severity a

…(truncated)
