Skip to content

feat(kernel-doctor): report Linux kernel CVE evidence, never a verdict - #520

Merged
hironow merged 6 commits into
mainfrom
feat/kernel-doctor
Oct 30, 2026
Merged

hironow merged 6 commits into
mainfrom
feat/kernel-doctor

Conversation

@hironow

@hironow hironow commented Oct 30, 2026 •

Copy link
Copy Markdown
Owner

目的

CVE-2026-98432(Linux カーネル IPv4 TCP の use-after-free)への対応。

このリポジトリは Linux カーネルを同梱・ビルド・固定していないため、「修正を適用する」対応は存在しません。できるのは検出と助言だけです。本 PR はそれを、既存の ca-doctor / brotli-doctor と同じ規律(証拠であって判定ではない)で追加します。

チケットの前提の訂正

チケットは「CVSS 9.8 / リモート / 認証不要 / 容易」でしたが、深刻度には争いがあります。

情報源 スコア 攻撃複雑度
NVD / Tenable / Rapid7 9.8 AC:L(容易)
Oracle の自社記録(暫定と明記) 8.1 AC:H(競合を勝ち抜く必要)

Red Hat の優先度は critical ではなく high(修正版は未確定)。EPSS 0.00388、観測時点で KEV 未収載。到達には「SYNACK 組み立て中の競合」か「net.ipv4.tcp_migrate_req=1 / BPF reuseport」が必要です。runbook と記録 JSON に両論を併記しました。

露出面

面 カーネルの供給元 dotfiles の管轄
macOS ホスト Apple 対象外
WSL2 と self-hosted runner Microsoft(wsl --update) なし
devcontainer / emulator / telemetry ホスト VM / WSL2 を共有 なし

WSL カーネルと .wslconfig を管理しない方針は維持します(ADR-0058 / 0064 は変更していません)。

変更

  • tools/kernel-doctor/(Go、stdlib のみ)— 実行中または報告されたカーネル版を、レビュー済みの出典つき修正床と突き合わせる
  • scripts/doctor.sh — advisory ブロック(CA ブロックには終端マーカーを追加)
  • justfile — kernel-doctor-build / kernel-doctor / kernel-doctor-build-all
  • docs/runbook/kernel-doctor.md — 面ごとの是正手順
  • docs/adr/0065-kernel-cve-is-evidence-only.md — Proposed(decision-queue に決裁待ちで起票)
  • テスト、.gitattributes(LF 固定)、.gitignore、tools/README.md

設計の核

D2 — ベンダー / ディストロのカーネルは比較しない。 Ubuntu 6.8.0-45-generic や Debian 6.1.0-28-amd64 の第 3 成分は上流の patch level ではなく、数値比較は両方向に間違えます。比較するのは文字列全体がクリーンな上流形 x.y.z で、かつ major.minor がレビュー済み branch のときだけ。それ以外は unverified とし、未判定を修正済みとして数えません。

D1 — kernel の所見は doctor の err に入れない。 just doctor の exit 1 は just self-check と test_doctor_sandbox を赤くします。カーネルはこのリポジトリが直せないので、operator がリポジトリ外でしか直せない条件で repo の gate を赤くしません。ツールの自己申告に頼らず、doctor.sh 側でも ERR を単一の WARN に落とします。

証拠の提示を一箇所に。 evaluate() が line findings と structured assessments を一緒に返すので、鮮度検査が通らなければ両出力とも比較を出しません(実装レビューで見つかった、JSON だけが比較を配っていた欠陥の修正)。

レビュー

計画段階と実装後の 2 回(Codex 0.164.0)。記録は docs/plan/kernel-cve-evidence.md の §独立レビュー記録 / §実装レビュー記録 に、各指摘をどう確認して採用・反論したかつきで残しています。

  • 計画レビュー: REVISE、8 件すべて実測で確認して採用
  • 実装レビュー: REVISE、3 件 → 2 件採用 / 1 件は再現せず反論
    • 採用①: 追加テストが macOS 前提で、Linux では just go-test を落とす(ローカル緑 → sandbox 赤)。collector を注入してホスト非依存に
    • 採用②: --json が鮮度検査を迂回し、stale な binary でも比較を配っていた
    • 反論: here-string の一時ファイル説。書き込み不可を実証した TMPDIR でも bash 5.3.20 は 20 MB まで一時ファイルを作らないことを実測。加えて同じ形は CA ブロックにも既存なので、doctor.sh 全体の制約として別件化

検証

通った: Go テスト / just go-test / just go-lint(11 module、0 issues) / ruff format・check / ty / shellcheck / markdownlint / semgrep / D2 受入表 13 行 / kernel-doctor-build-all(6 ターゲット、file(1) で ELF・Mach-O・PE32+) / just lint(repo 全体)

走っていない(重要):

  • just test(sandbox) — このホストで Docker が実行できません(docker probe が PermissionError で 111 errors)。test_doctor_sandbox は sandbox 内で just doctor の exit 0 を要求するので、この leg は ready にして CI が回って初めて実行されます
  • just check-all(repo が定める push gate)— just test を含むため完走できません
  • Linux 実機の /proc 経路 / Windows・WSL 実機のカーネル版(クロスビルド成功は実機観測の証明ではありません)

既存赤(本ブランチの base で pristine な worktree を作り、同一に再現させて無関係と確認済み):

  • tests/unit/test_wsl_clipboard.py::test_root_installs_and_repeat_is_noop(macOS 固有)
  • Docker 不在による sandbox leg

なお just lint / just check を落としてどの commit も通せなかった check_uv_exclude_newer は、本ブランチの 973f7d4 が解消しました(期限切れ override 5 件を削除、lock の差分は 7 削除・version 変更 0)。

Draft → ready の条件

  • Windows 機でのチェック検証: 所有者より、更新・再起動・既存処理の停止なしで検証 OK と報告(2026-10-30)。これを受け ready 化を承認。実行コマンド・生ログ・カーネル版は未提示であり、修正済みカーネルへの更新確認や CVE 解消の証明ではない。
  • 結果を 計画の §検証記録 + 本 PR のコメントに記録済み(commit bfd8d469)。observation にはしない(incidents/*.json の全 observation は公開 https の出典を要する。ローカルな uname -r の計測をそこに入れると、出典を捏造するか検証に落ちて全比較が拒否されるかのどちらかになる。所有者の判断で、機体固有の検証は検証記録側に置く)
  • ready 化で CI が回るので、sandbox leg(test_doctor_sandbox)の初回結果を確認し、取りこぼしを直す
  • 上の既存赤 2 件の扱いを決める(本 PR の範囲外だが CI の緑に影響)

マージ方針

このブランチは 4 commit で、squash merge すると PR タイトルの 1 commit に畳まれます。973f7d4(chore(deps) の quarantine cleanup)もこの feat に含まれる扱いとしました(所有者の判断)。commit discipline の「1 commit 1 type」からは外れますが、この cleanup が無いと just lint がどの commit もブロックしていたため、Tidy First として先頭に置いてあります。

The 7-day release quarantine lets a security fix through per package with an
absolute `exclude-newer-package` cutoff. That cutoff never expires by itself,
so once the admitted release is older than the window the entry only freezes
the package there and later Dependabot bumps become silent no-ops. That is
what scripts/check_uv_exclude_newer.py exists to catch, and it now fails:

  emulator/pyproject.toml: `certifi` cutoff 2026-10-20 is 9 days old
  emulator/pyproject.toml: `mlflow` cutoff 2026-10-21 is 8 days old
  ... mlflow-skinny, mlflow-tracing, and telemetry/examples `certifi`

certifi 2026.10.19 (the eMudhra CA removal) and mlflow / mlflow-skinny /
mlflow-tracing 3.17.2 (GHSA-wqmh-qccw-pqwf) were admitted on 2026-10-26 with a
comment saying to remove them on or after 2026-10-28. Dropping them leaves the
repo-authoritative `exclude-newer = "7 days"` span in charge, which now admits
those releases on its own.

`uv lock` in both projects removes only the lock's
`[options.exclude-newer-package]` metadata: no resolved version changes (the
diff is 7 deletions, 0 version lines).

The `ruff = false, ty = false` entries stay; those are a different mechanism
(skip the span so an exact pin can move without waiting out the window).
CVE-2026-98432 (ipv4: free inet_opt and ireq_opt after an RCU grace period)
is a real Linux kernel use-after-free, but this repository ships, builds and
pins no Linux kernel: the kernel belongs to Microsoft (WSL and its
self-hosted runner), to the distribution, or to the container host. There is
nothing here to patch, so the honest response is detection and advice.

tools/kernel-doctor compares the kernel version a host reports against
reviewed, source-cited fix floors in one embedded record. It deliberately
refuses to compare anything that is not a clean upstream x.y.z on a reviewed
branch: Ubuntu 6.8.0-45-generic and Debian 6.1.0-28-amd64 carry the vendor's
own ABI revision, not the upstream patch level, so numeric comparison is
wrong in both directions. Unverified is never counted as fixed, and
patch_status/backport_state stay unverified on every line.

scripts/doctor.sh wires it as advisory only. This repository cannot fix a
kernel, so no kernel finding may reach doctor's err count: `just doctor`
exiting non-zero would break `just self-check` and test_doctor_sandbox over a
condition only the operator can fix, and only outside this repository. The
block therefore adds findings to warn only, and downgrades any ERR the
checker emits to a single WARN.

docs/runbook/kernel-doctor.md records the dispute over severity (NVD/Tenable
9.8 with AC:L versus Oracle's preliminary 8.1 with AC:H; Red Hat priority
high, not critical; EPSS 0.00388; not in KEV, which is not proof that no
public exploit exists) and the remedy per surface. The WSL kernel and
.wslconfig stay unmanaged (ADR-0058, ADR-0064); the runbook carries
`wsl --update` as an operator step, to run when no runner job is in flight.

ADR-0065 records the decision and is Proposed, tracked in the decision queue.
An independent review of 41551b2 found two real defects and one over-claim.
This fixes them. Its third finding did not reproduce; that is recorded rather
than patched.

1. The JSON output bypassed the source-freshness check. With a stale or
   absent --source, the findings said only "run just kernel-doctor-build
   before trusting any finding", while the same report still carried
   `at-or-above-reviewed-fix-floor` in `assessments`. Both outputs now come
   from one evaluate() result, so a comparison is withheld for exactly the
   reason the line findings are withheld. Reverting the change makes the new
   test fail, so it does pin the behaviour.

2. A test asserted a macOS-only outcome. It required the assessment list to
   be empty when no version is obtained, which holds here only because there
   is no /proc; on Linux the collector succeeds and the list is not empty.
   The test would have failed `just go-test` on the sandbox after passing
   locally. run() now takes the host OS and the collector, so every host
   shape is driven from a test: readable -> one assessment, unreadable ->
   none.

   That also makes the documented --osrelease contract literally true. The
   help says the local /proc and /etc/os-release are not read, but Collect
   ran unconditionally and only its result was discarded. With injection the
   override performs no local read at all, which a test asserts by counting
   collector calls.

3. The CISA KEV observation claimed "CISA has no record of exploitation in
   the wild". Absence from KEV establishes nothing about exploitation, about
   public exploits, or about CISA's assessment status. The record and the
   runbook now say only what was observed: not listed at observation time.

The review also reported that a large checker output aborts doctor before the
WARN downgrade, via a here-string temp file. Not reproduced: with a
verified-unwritable TMPDIR, bash 5.3.20 here-strings handle 20 MB without
creating a temp file. The same construct already exists in the CA block, so
this is an open constraint for doctor.sh as a whole, not something to patch
in one place. docs/plan/kernel-cve-evidence.md records the evidence.
The verification record predated the Tidy First commit and still described the
quarantine gate as blocking; 973f7d4 resolved it, so the section now states the
SHA it was measured against and what actually remains red.

Adds the commit-time review record: the re-measurement of the eight plan-review
findings, the three new findings with what confirmed each, the one finding I
could not reproduce and why, and the falsifications the review attempted and
failed to land.
hironow added a commit that referenced this pull request Oct 30, 2026
- fix(install): find the mise prek through the latest symlink
- fix(install): keep going when corepack enable fails
- fix(skills): drop the two skills their upstreams no longer ship
- fix(doctor): prune pyright installed as a uv tool
- chore(rtk): follow rtk 1.1.0
- test(ollama): skip the real-bind serve tests where ::1 cannot bind
- fix(emulator): pin a2a-inspector to its last Python/TypeScript commit
- chore(deps): drop expired exclude-newer-package overrides (from #520)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0147hurtFmppBfdZ3M8n8e4V
@hironow

hironow commented Oct 30, 2026

Copy link
Copy Markdown
Owner Author

Windows 側のチェック検証と ready 化の承認(2026-10-30)

所有者より「Windows 側で、更新・再起動・既存処理の停止なしでのチェック検証は OK」と報告され、ready 化の指示を受けました。

  • これは所有者の検証完了報告です。agent が Windows 実機を観測したという意味ではありません。
  • wsl --update / wsl --shutdown / 既存処理の停止は行っていません。
  • 実行コマンド・生ログ・カーネル版は未提示のため、修正済みカーネルへの更新や CVE 解消は確認していません。
  • 計画の検証記録にも同じ区別を記載し、incident observations には追加しません。

記録の push 後に ready 化し、CI の検証は別途確認します。マージは行いません。

@hironow
hironow marked this pull request as ready for review October 30, 2026 13:31
@hironow
hironow merged commit 78846ac into main Oct 30, 2026
8 checks passed
@hironow
hironow deleted the feat/kernel-doctor branch October 30, 2026 14:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant