# DF-2810 — parse_dynamic accepts relocation/hash tags without DT_SYMTAB/DT_STRTAB → NULL-base wild reads in symbol_name()/elf_lookup()

## Build
```
python3 gen_exec_ko.py nulldyn-rel.ko nulldyn-rel
```

## Run
```
scp -F dfbsd-qemu/config nulldyn-rel.ko dfbsd:/tmp/
dfbsd-qemu/vm.sh run_root 'kldload /tmp/nulldyn-rel.ko; echo RC=$?'
dfbsd-qemu/vm.sh log 30
```

## Expected (stock kernel, verified 2026-09-01)
```
Fatal trap 12: page fault while in kernel mode
fault virtual address = 0x18
fault code      = supervisor read data, page not present
Stopped at      relocate_file+0x7b:     movl    (%rax),%esi
```

## Expected (patched kernel)
```
kldload: Missing DT_STRTAB/DT_SYMTAB for relocations, RC=1, guest stays up
```

## Why (root cause, sys/kern/link_elf.c)
The module supplies `DT_REL`/`DT_RELSZ` (one R_X86_64_64 entry referencing
symbol index 1) but NO `DT_SYMTAB`, `DT_STRTAB`, or `DT_HASH`. parse_dynamic
(sys/kern/link_elf.c:232-314) stores each tag independently and never
requires the tables that its consumers dereference unconditionally:

```c
691:  symbol_name(elf_file_t ef, Elf_Size r_info)
697:      ref = ef->symtab + ELF_R_SYM(r_info);   /* NULL + 1 */
698:      return ef->strtab + ref->st_name;       /* read at 0x18 -> fault */
```

relocate_file (714-724) → elf_reloc → elf_lookup fails (symidx >= nchains==0,
sys/kern/link_elf.c:994) → the error path at :719 calls symbol_name() which
dereferences the NULL symtab. Fault VA 0x18 = sizeof(Elf64_Sym)*1 +
offsetof(Elf64_Sym, st_name) — matches exactly.

The sibling loaders validate this: link_elf_obj.c's elf_obj_lookup checks
`symidx >= ef->ddbsymcnt` against the section-derived table and its
relocate_file skips entries whose symbol is undefined; FreeBSD's rtld
requires the tag set. Same class reachable via DT_HASH without DT_SYMTAB
(link_elf_lookup_symbol:818 `symp = ef->symtab + symnum` with symtab==NULL
once a bucket yields symnum != STN_UNDEF).
