# DF-5596 VERDICT

**Status: reproduced (build-diff evidence class). Impact: none at runtime
today; the defect is a silently-broken C↔assembly contract and a defeated
build-time guard. Confidence: certain.**

## The claim under test

Pass-2 checklist item (b)/(c) for `sys/platform/pc64/x86_64/genassym.c`:
"any missing ASSYM for fields the assembly touches" / "stale or
removed-field references". An exhaustive extraction of every
`GD_*/TD_*/PCB_*/TF_*/TR_*/PM_*/LWP_*/RQF_*/IOAPIC_*/CPULOCK_*/CPUMASK_*/
MACHINTR_*/SPEC_CTRL_*/SIGF_*/TDF_*/TDPRI_*` token from **all** assym.s
consumers (13 platform .s files, 3 cpu .s files, 2 nvmm module .S files)
against the 179 ASSYM definitions in genassym.c leaves exactly three
unmatched:

- `PCB_USERLDT`  — swtch.s:571, inside `#if 0 /* JG */` (dead code)
- `LWP_SWITCH_OPTIMIZE` — swtch.s:97,110,189 (comment + `#undef`)
- `GD_CURRENTLDT` — **global.s:67, LIVE** (this finding)

## Reproduction (guest, DragonFly 6.5-DEVELOPMENT #0, cc 8.3)

Chain of evidence, all captured in this pack:

1. **Shipped-kernel proof.** The running kernel's own build tree
   (`/usr/obj/usr/src/sys/X86_64_GENERIC`, source MD5-identical to the
   audit tree) contains `global.o` with `U GD_CURRENTLDT` and no
   `gd_currentldt`; the shipped `/boot/kernel/kernel` (EXEC) carries
   `U GD_CURRENTLDT` in its symbol table and zero relocations against it
   (`shipped_kernel_evidence.txt`). The build never failed.
2. **Controlled repro.** Rebuilt `genassym.o` from pristine source with
   the exact kern.post.mk:76-79 command (`make -V CFLAGS`, minus
   `-fno-common`, plus `-fcommon`); `genassym.sh` regenerated `assym.s`
   byte-identical to the build tree's (`diff.buildtree_vs_regen.txt` —
   also proving determinism). `probe_gdcl.s` consumes `GD_CURRENTLDT`
   exactly as `global.s:66-67` does:
   - unpatched assym → `nm_probe_unpatched.txt`: `U GD_CURRENTLDT`,
     `gd_currentldt_probe` **absent**, assembly exit 0. Bug reproduced.
   - a *displacement*-style use (`movq GD_NOSUCH(%rax),%rbx`) also
     assembles cleanly (probe on guest: exit 0) — only the final link of
     a *relocation-referenced* undefined symbol would fail; `.set`-consumed
     macros never even get that far. The loud boundary is narrower than
     the genassym design assumes.
3. **Fix.** One line — `ASSYM(GD_CURRENTLDT, offsetof(struct mdglobaldata,
   gd_currentldt));` (`fix.diff`). Rebuilt with identical flags:
   `diff.fix_delta.txt` shows the regenerated assym.s gains exactly
   `#define GD_CURRENTLDT 0x42d4` (sits correctly between
   `gd_spending 0x42cc` and `gd_private_tss 0x42d8`), and the same probe
   now defines `00000000000042d4 A gd_currentldt_probe`
   (`nm_probe_fixed.txt`). Baseline bad behavior (missing symbol) is gone.
   A full kernel rebuild+reboot is not required for a non-corruption
   finding; the targeted same-flags rebuild exercises the exact production
   code path (kern.post.mk rules for genassym.o/assym.s and the .s
   compile flags verbatim — `asmflags.txt`).
4. **Contract-wide cross-check (checklist a).** `offset_check.c`
   regenerates every ASSYM expression in a *separate* TU compiled with
   the kernel's own CFLAGS: 176/176 values match the generated assym.s
   (`diff.assym_vs_checker.txt`; the two `+` lines are
   SPEC_CTRL_IBRS/STIBP which genassym.c intentionally `#if 0`s — an
   artifact of my checker generator, not a mismatch). All
   `td_/gd_/pcb_/trapframe/trampframe/ioapic` offsets consumed by
   exception.S/swtch.s/ipl.s/locore.s/apic match the compiled kernel
   layout. TF_R10/TF_R11 are defined (genassym.c:152-153) and consumed
   (exception.S:305-306) — that lead resolves clean.

## Why it matters / impact ceiling

- Today: **zero runtime effect** — nothing references the missing symbol
  by relocation; C code touches `gd_currentldt` via `mdcpu->` (e.g.
  mp_machdep.c:287). The `.globl gd_currentldt` export that ddb/kgdb
  scripts and future assembly would rely on simply does not exist, with
  no build diagnostic.
- Systemically: the audit's premise for this file — "a mismatch is a
  silent memory-corruption class" — is *usually* checked by the build
  breaking. This case proves the check is **not unconditional**: an ASSYM
  consumed through `.set` vanishes silently from the shipped kernel. The
  same shape in a field that *is* consumed (e.g. someone "temporarily"
  removing an ASSYM that exception.S still uses through a `.set` alias)
  would not be caught at build time either. The 7 sibling dangling
  symbols from locore.s:41-47 (`dmapbase`, `dmapend`, `loc_PTmap`, … —
  extern *variables* from pmap.h that no longer exist) show the class is
  not hypothetical; they are a separate cleanup in locore.s/pmap.h, not
  genassym.c.
- vkernel64 has the identical dangling use
  (sys/platform/vkernel64/x86_64/global.s:65); its genassym.c needs the
  same line if the export is to be real there (follow-up, out of this
  file's scope).

## Classification

Reproduced: yes (the contract break manifests exactly as described, on
the shipped kernel and under controlled rebuild). Exploit chain: none —
no attacker-reachable primitive (build-time defect, no runtime consumer).
Recommended fix applied and validated: `fix.diff` (one ASSYM line).

## Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest `make -j6 nativekernel KERNCONF=X86_64_GENERIC` completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
