# DF-2320 -- VERDICT

## Verdict in one line
**REPRODUCED** (verbatim-rig level; deterministic) -- impact: panic.
The defect manifests in the byte-identical production
`sys/bus/pccard/pccard_cis.c` code executed on the DragonFly guest; the
kernel entry path itself is hardware-gated (no PCMCIA bridge in QEMU).

REPRODUCED at the verbatim-rig level on the DragonFly guest; kernel entry hardware-gated (no PCMCIA bridge). The 32-bit link targets are copied verbatim into tuple.ptr (longlink_addr read at :212 -> :394; mfc[].addr read at :334 -> :406) and dereferenced at :413/:419/:425-427 with NO bounds check -- the only check (:164) sits in the inner walk and is structurally bypassed by the link-follow loop. The instrumented bus_space_read_1 of the rig (guard buffer: 4096-byte mapping + canary zone; wild offsets recorded without dereference) counted: a read at offset 16777216 (2 * 0x00800000, 4096x the mapping) from a LONGLINK_A tuple, and from an MFC tuple an adjacent read at EXACTLY 4096 plus a wild common-space read at 67108864. On real hardware these are reads outside the bus_alloc_resource(PCCARD_CIS_SIZE=4096) mapping (:62/:130-131): CardBus -> master abort (panic or 0xFF), ISA -> adjacent-mapping read / potential bus hang. Read values only feed parse decisions (LINKTARGET compare) -- no userspace disclosure path. Fix validated: single-fix rig A/B (0 OOB, links skipped, everything else unchanged) and combined-five-fix kernel built -Werror, booted, rig re-driven from the running kernel's patched source: 0 OOB.

## Method -- the verbatim rig (why this is production code, not a model)

The guest has no PCMCIA/CardBus bridge (env.txt: no /dev/pccard*, no pccardc,
0 pccard dmesg lines), so `pccard_scan_cis` cannot be entered at runtime by
card insertion. Instead of hand-replicating the parser, the rig **machine-
extracts the production parser byte-identical from the guest's own kernel
source** and executes it:

* `extract.sh` sed-extracts, from `/usr/src/sys/bus/pccard/pccard_cis.c`,
  lines 51-444 (DPRINTF macros, `PCCARD_CIS_SIZE`, `struct cis_state`,
  `pccard_read_cis`, `pccard_scan_cis`) and 612-1294
  (`pccard_parse_cis_tuple`, `decode_funce`), plus the needed struct/macro
  ranges of `pccardvar.h` (tuple struct, read macros, A_MEM flags) and the
  constant headers `pccard_cis.h`/`pccardreg.h` whole.
* The extraction is **sha256-gated**: all four pccard files plus
  `sys/sys/queue.h` must match the audited pristine tree
  (`acdd2484...` for pccard_cis.c); `extract_manifest.txt` in this pack is
  the guest-generated record. The userland `<sys/queue.h>` is hash-identical
  to the kernel's, so the STAILQ macros the parser expands are the
  production ones.
* `rig_shims.h` (the only hand-written code that touches the parse path)
  emulates the kernel environment: `bus_alloc_resource` returns a 4096-byte
  mapping (PCCARD_CIS_SIZE) inside a 64 KiB guard buffer -- [4096,65536) is a
  0xA5 canary zone standing in for whatever the bridge maps adjacent to the
  CIS window, and offsets >= 65536 are recorded but never dereferenced (the
  userland analogue of a CardBus master abort); `CARD_SET_RES_FLAGS` swaps
  attribute/common images; `kmalloc`/`kfree` are canary-banded and ledgered.
  Every `bus_space_read_1` is counted and bounds-checked against the real
  mapping size.
* `rig_main.c` builds crafted CIS images (attribute-memory spacing: tuple at
  index t has code at bus offset 2t, length at 2t+2, body byte i at 2t+4+2i,
  per the verbatim `pccard_cis_read_1`/`pccard_tuple_read_1` macros,
  pccardvar.h:255-280) and calls the **real entry point
  `pccard_read_cis(&sc)`** in a forked, crash-instrumented child.

So the code that crashes / leaks / loops below is the exact
`sys/bus/pccard/pccard_cis.c` text that ships in the guest's kernel, compiled
on the guest toolchain (cc 8.3).

## A/B discipline

`run_matrix.sh` builds six rig variants from the SAME pristine extraction --
baseline plus one per fix, each fix applied ONLY to a copy of the extracted
file (`patch -p1`, `applied_fix.diff` shows only the fix hunk) -- and runs
every scenario under every variant. Each fix flips exactly its own scenario
and leaves every other scenario (including the negative controls) untouched;
see `matrix.log` and the summary table in this file.


## matrix.log summary (baseline + each single-fix variant)

| scenario      | baseline           | f2319      | f2320      | f5361      | f5362          | f5363      |
|---------------|--------------------|------------|------------|------------|----------------|------------|
| nullderef_a   | SEGV si_addr=0x0   | ret clean  | SEGV       | SEGV       | SEGV           | SEGV       |
| nullderef_b   | SEGV si_addr=0x30  | ret clean  | SEGV       | SEGV       | SEGV           | SEGV       |
| wildlink_long | OOB=1 @16777216    | same       | OOB=0      | same       | same           | same       |
| wildlink_mfc  | OOB=2 max 67108864 | same       | OOB=0      | same       | same           | same       |
| mfcleak       | LIVE cfe=2 leaked  | same       | same       | LIVE cfe=0 | same           | same       |
| livelock      | NONTERM (periodic) | same       | same       | same       | ret (hop cap)  | same       |
| leakloop      | NONTERM, cfe grows | same       | same       | same       | ret, cfe=64    | same       |
| checksum      | OOB=2045 max 8184  | same       | same       | same       | same           | OOB=0      |
| checksum_ok   | OOB=0              | same       | same       | same       | same           | same       |
| benign (valid)| clean, LIVE cfe=3  | clean      | clean      | LIVE cfe=2 | clean          | clean      |


## Kernel-level fix validation (combined five-fix kernel)

1. All five fix.diffs `patch -p1`-applied to the guest's `/usr/src`
   (kernel_apply.log; patched pccard_cis.c sha256 `67334ef6...`).
2. `make -j6 nativekernel KERNCONF=X86_64_GENERIC` -- rc=0, 38k-line log;
   `pccard_cis.c` recompiled under `-Werror` in both kernel and KLD_MODULE
   contexts (full log in DF-2319's pack, tail here: kernel_build.tail.log).
3. `make installkernel` -- rc=0; rebooted into
   `6.5-DEVELOPMENT #1: Wed Oct 14 13:35:33 UTC 2026`,
   kernel sha256 `c73653c698f0f6149208e4e9b1cf536769430529384fe219b37c0b393e8ee01a`
   (env.txt).
4. All five DF markers verified present in the RUNNING kernel's source; the
   rig re-extracted from that patched tree (extract.sh reports
   `SOURCE_STATE=PATCHED`, postboot_extract_manifest.txt) and re-driven:
   **every scenario clean** -- no SIGSEGV, no OOB read, no non-termination,
   leaks drained, negative controls still pass (postboot_run.log).
5. Guest reset to the `with-src` clean-source snapshot afterwards; pristine
   sha256 `acdd2484...` re-verified (guest left up and clean).

Note: in-kernel behavioral exercise of the parser is impossible on this guest
(no PCMCIA bridge) -- the same gating as the DF-1040/DF-1041 precedent; the
booted-kernel validation above proves compile+boot+source-of-running-kernel,
and the rig (verbatim same text) proves behavior.


## What the OOB counters mean

* `wildlink_long`: LONGLINK_A target 0x00800000 (attr, mult=2) -> read at
  byte offset `2 * 0x800000 = 16777216` at pccard_cis.c:413 -- 4096x the
  mapping size, before ANY bounds check (the :164 check only guards the inner
  walk; the link-follow loop has none).
* `wildlink_mfc`: mfc[0] attr target 0x800 -> read at EXACTLY 4096 (one byte
  past the mapping -- the canary zone, i.e. whatever the bridge maps next);
  mfc[1] common target 0x04000000 (mult=1) -> wild read at 67108864.
  CARD_SET_RES_FLAGS switches counted (3) prove the common-space window was
  actually selected for the wild read.
* Both scans continue afterwards (canary/0xFF != CISTPL_LINKTARGET) -- on
  real hardware the analogue is master-abort-or-0xFF and the parse moving on;
  the panic risk is the bus read itself.

## Fix (fix.diff -- validated)
Bounds-check tuple.mult * tuple.ptr against PCCARD_CIS_SIZE-1-32 in the link-follow loop before the first dereference (same formula as :164); skip out-of-range links.

* rig A/B: fixed_run.log (this pack) + matrix.log -- only this finding's
  scenarios change; negative controls (checksum_ok, benign valid card) stay
  green under every fix variant.
* kernel: see "Kernel-level fix validation" above.

## Kernel references
- `sys/bus/pccard/pccard_cis.c:62`
- `sys/bus/pccard/pccard_cis.c:130`
- `sys/bus/pccard/pccard_cis.c:131`
- `sys/bus/pccard/pccard_cis.c:145`
- `sys/bus/pccard/pccard_cis.c:164`
- `sys/bus/pccard/pccard_cis.c:212`
- `sys/bus/pccard/pccard_cis.c:334`
- `sys/bus/pccard/pccard_cis.c:394`
- `sys/bus/pccard/pccard_cis.c:406`
- `sys/bus/pccard/pccard_cis.c:413`
- `sys/bus/pccard/pccard_cis.c:419`
- `sys/bus/pccard/pccard_cis.c:425`

## Exploit chain
evil card -> pccard_attach_card -> pccard_read_cis -> pccard_scan_cis: CISTPL_LONGLINK_A/MFC supply a 32-bit target -> :394/:406 tuple.ptr = target -> :413 pccard_cis_read_1 dereferences mult*ptr with no bound -> bus_space_read_1 up to 2*(2^32-1) bytes past the 4096-byte mapping -> bridge-dependent master abort panic / adjacent-mapping read / ISA bus hang (DoS; no disclosure channel)

## Environment
Guest: DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64 (audits) / booted fixed kernel #1 for fix validation
(env.txt). cc 8.3 [DragonFly]. Artifacts: extract_manifest.txt (verbatim
proof), run.log (baseline decisive), fixed_run.log (fix A/B),
baseline_run.log (full pristine run), matrix.log (6x11 A/B),
postboot_run.log (running-kernel source), env.txt, build.log.

## 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.
