# DF-5019 — tbridge GETRESULT without LOADTEST: NULL `tbridge_testcase` → deterministic KKASSERT kernel panic (INVARIANTS builds)

## Files
* `sys/dev/misc/tbridge/tbridge.c` — the defect
* `mytcase.c`   — minimal out-of-tree testcase kmod (registers `tbridge_curtest`)
* `poc.c`       — userland trigger (one ioctl, run as root)
* `build.sh`    — exact guest build commands
* `run.sh`      — exact guest run commands

## Build & run (on the DragonFly guest, as root)

See `build.sh` / `run.sh`. Two build modes matter:

1. **tbridge as a kldload'able kmod** (`/usr/src/sys/dev/misc/tbridge/Makefile`):
   builds fine, `/dev/tbridge` appears (0600 root:wheel). On the stock
   GENERIC this is the *only* way to get the driver — GENERIC does not
   contain `device tbridge` (it is `optional tbridge` in `sys/conf/files:2122`,
   only `LINT64` has it). Result: **no panic** — out-of-tree kmods compile
   without `INVARIANTS` (no `opt_global.h`), so the `KKASSERT` in
   `tbridge_test_done` is a no-op and the whole NULL walk degrades to
   `copyout` returning `ENOTSUP` (errno 45). See `run.kmod.log`.
2. **tbridge compiled into the kernel** (`device tbridge` + `options
   INVARIANTS`, i.e. the dfregress regression-test configuration; stock
   `X86_64_GENERIC` also ships `options INVARIANTS` at
   sys/config/X86_64_GENERIC:56): **deterministic kernel panic** on the
   first `TBRIDGE_GETRESULT` ioctl. See `run.log` / `panic.txt`.

## Expected output (kernel build)

```
GETRESULT returned -1, errno=45 (kernel survived: finding NOT reproduced)   <- kmod build
Fatal trap 9: ... assertion "tbridge_testcase != NULL" failed in
tbridge_test_done at /usr/src/sys/dev/misc/tbridge/tbridge.c:196           <- in-kernel build (panic)
```

## Root cause

`tbridge_dev_ioctl()` (tbridge.c:240) handles `TBRIDGE_GETRESULT`
(tbridge.c:265) without checking that a `TBRIDGE_LOADTEST` ever ran.
With a testcase registered (`tbridge_curtest != NULL`, requires a testcase
kmod) but no LOADTEST, `tbridge_testcase` (the proplib dict) is still NULL:

* tbridge.c:266 `kthread_create(tbridge_curtest->tb_run, ...)` — runs the
  testcase, which calls `tbridge_test_done()` (any real testcase must),
* tbridge.c:196 `KKASSERT(tbridge_testcase != NULL)` — **panics on any
  INVARIANTS kernel**,
* without INVARIANTS the NULL dict then flows through
  `testcase_get_timeout(NULL)` (tbridge.c:274), `testcase_set_result(NULL,..)`
  (tbridge.c:201/289) and `prop_dictionary_copyout_ioctl(NULL)` — all
  NULL-tolerant in libprop — ending in `ENOTSUP`.

Distinct from known DF-2048 (`tbridge_curtest` NULL because *no testcase
module is loaded* — one line earlier, same ioctl).
