vkernel64 genassym.c emits only 5 of the 26 GD_* macros global.s consumes β 13 restorable per-CPU exports (gd_currentldt, gd_reqflags, gd_common_tss, gd_savefpu, gd_fpu_lock, gd_fpending/gd_ipending/gd_spending, gd_cnt, gd_ss_eflags, gd_intr_nesting_level, gd_common_tssd, gd_tss_gdt) silently vanish from every VKERNEL64 build (DF-5596 twin at 21x scale, task_log lead confirmed)
Summary
global.s builds its .globl export block from 26 GD_* macros but genassym defines only 5: gas resolves each missing macro as an undefined external, the .set alias AND its .globl export silently vanish, assembly exits 0 under -Werror, and the vkernel's userland cc link tolerates zero-relocation undefineds. Proven end-to-end on the real build tree: nm shows U for all 21 missing macros in global.o AND the final binary, link exit 0. 13 of the 21 back existing mdglobaldata fields and are restored by fix.diff (57 defines, 18 exports, 0 dangling, values independent-TU verified). No runtime consumer today, but this is the C-asm offset contract's only build-time guard failing silently at scale on the platform with the weakest link gate. DF-5596 twin at 21x; the same shape in a relocation-consumed macro is the silent memory-corruption class.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-5658 Β· 28 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 3.8 KB | β raw | |
| VERDICT.md | β | 8.0 KB | β raw | |
| verdict.json | β | 4.7 KB | view raw | |
| manifest.json | β | 1.5 KB | view raw | |
| offset_check.c | β | 6.7 KB | view raw | |
| offset_check.out | β | 1.2 KB | view raw | |
| nm_global_pristine.txt | β | 1.0 KB | view raw | |
| final_binary_baseline.txt | β | 1.1 KB | view raw | |
| final_binary_fixed.txt | β | 688 B | view raw | |
| assym.pristine.s | β | 1022 B | view raw | |
| assym.fixed.s | β | 1.3 KB | view raw | |
| checker.cmd | β | 885 B | β download | |
| checker_build.log | β | 0 B | β download | |
| build.log | β | 2.0 KB | view raw | |
| run.log | β | 2.2 KB | view raw | |
| vkbuild.log | β | 497.5 KB | β download | |
| link.log | β | 53 B | view raw | |
| link2.log | β | 0 B | β download | |
| pristbuild.log | β | 1.9 KB | view raw | |
| fixbuild.log | β | 1.9 KB | view raw | |
| fixbuild2.log | β | 994 B | view raw | |
| fix_build.log | β | 2.9 KB | view raw | |
| fix_run.log | β | 852 B | view raw | |
| genassym.fixed.c | β | 6.1 KB | view raw | |
| global.fixed.s | β | 3.6 KB | view raw | |
| fix.diff | β | 2.7 KB | view raw | |
| hashes.txt | β | 156 B | view raw | |
| env.txt | β | 563 B | view raw |
DF-5658 β vkernel64 genassym.c emits only 5 of the 26 GD_ macros global.s consumes: 13 silently-vanished .set exports for fields that EXIST (DF-5596 twin, 13Γ), plus the 8 GD_PRV_ dangles split out as DF-5659
Evidence class: build-diff on the guest's VKERNEL64 build tree (the vkernel
is a user program linked with plain cc β see sys/platform/vkernel64/conf/Makefile:23-25).
What to run
Everything was executed on the QEMU guest (DragonFly 6.5-DEVELOPMENT,
env.txt) whose /usr/src is MD5-identical to the audit tree for all 9 files
involved (genassym.c, global.s, swtch.s, locore.s, fork_tramp.s, conf/files,
VKERNEL64 config, globaldata.h, thread.h β MD5s in env.txt).
# 1. create the VKERNEL64 build tree
cd /usr/src/sys/config && config VKERNEL64 # -> /usr/src/sys/compile/VKERNEL64
cd /usr/src/sys/compile/VKERNEL64
# 2. regen assym.s through the REAL kern.post.mk rules (see build.log)
make assym.s global.o # both exit 0, -Werror
nm global.o # -> nm_global_pristine.txt / run.log
# 3. independent value check (offset_check.c, exact genassym CFLAGS)
sh $(sed 's|genassym.c|/tmp/offset_check.c|' checker.cmd) -Wno-nested-externs -o /tmp/oc.o
cc /tmp/oc.o -o /tmp/oc && /tmp/oc # -> offset_check.out (65 lines)
# 4. end-to-end link proof (build-id stub works around the unrelated
# kern_mib.c __build_id buster β see VERDICT.md "Incidental")
cc -g -Wl,--export-dynamic -o /tmp/kernel.dangleproof $(make -V SYSTEM_OBJS) \
vers.o /tmp/bidstub.o -pthread -Wl,--hash-style=sysv -Wl,-z,max-page-size=0x200000
nm /tmp/kernel.dangleproof | grep ' U GD_' # -> 21 undefined
objdump -r /tmp/kernel.dangleproof | grep -c GD_ # -> 0 relocations
# 5. fix validation
cd /usr/src && patch -p1 < fix.diff # genassym.c +13 ASSYM, global.s -8 PRV
cd sys/compile/VKERNEL64 && rm -f genassym.o assym.s global.o
make assym.s global.o locore.o swtch.o fork_tramp.o # -> fix_build.log
# relink as in step 4 -> /tmp/kernel.fixed
nm /tmp/kernel.fixed | grep -c ' U GD_' # -> 0
nm /tmp/kernel.fixed | grep -E ' [ADB] gd_' # -> 18 exports (fix_run.log)
Expected results
| Step | pristine | patched |
|---|---|---|
assym.s defines |
44 | 57 |
global.o defined gd_*/globaldata |
5+1 | 18+1 |
global.o undefined ASSYM macros |
21 (U GD_CURRENTLDT, β¦) |
0 |
| final binary undefined GD_* | 21 | 0 |
| final binary gd_* exports | 5 | 18 |
| relocations against GD_* | 0 | 0 |
| checker vs assym.s values | 44/44 match | β |
Key restored values (independent-TU verified, offset_check.out):
gd_currentldt=0x3f14 (right after gd_spending=0x3f0c+gd_sdelayed=0x3f10,
matching globaldata.h:74-78 field order), gd_reqflags=0x18, gd_cnt=0xa8,
gd_intr_nesting_level=0xa0, gd_common_tssd=0x3a80, gd_tss_gdt=0x3a88,
gd_common_tss=0x3a98, gd_savefpu=0x3b00, gd_fpu_lock=0x3f00,
gd_fpending=0x3f04, gd_ipending=0x3f08, gd_ss_eflags=0x3f48.
Files
run.log/nm_global_pristine.txt/final_binary_baseline.txtβ the dangle proof (object + final binary)build.log/checker.cmd/checker_build.logβ regen + independent-TU buildoffset_check.c/offset_check.outβ 44 ASSYM values verified;CHECK_*/MISSING_*extrasassym.pristine.s/assym.fixed.sβ 44 β 57 definesvkbuild.logβ FULL untrimmed 547-object VKERNEL64 build incl. the link failure (__build_id_*)link.log/link2.logβ baseline/patched final-binary linksfixbuild.log/fixbuild2.log/fix_run.log/final_binary_fixed.txtβ fix validationfix.diffβ git-apply-clean against pristinesys/(verified withgit apply --check)env.txtβ guest uname/cc/ncpu + source MD5 identity
DF-5658 VERDICT
Status: reproduced (build-diff evidence class). Impact: none at runtime
today β no relocation or C consumer references the vanished exports; the
defect is a silently-broken Cβassembly contract at 21Γ the scale of the pc64
twin (DF-5596), on the platform whose link (plain userland cc) has the
weakest undefined-symbol gate. Confidence: certain.
The claim under test
Pass-2 task_log lead (2026-10-08): "vkernel64 global.s:65 has the identical GD_CURRENTLDT dangle". Verified β and it is not alone: an exhaustive used-vs-defined inventory over ALL assym.s consumers in the VKERNEL64 build (global.s, locore.s, swtch.s, fork_tramp.s β plus in_cksum2.s/monitor.s/ bzeront.s which include assym.s but consume no macros) shows:
sys/platform/vkernel64/x86_64/global.sconsumes 26 distinct GD_* macros (lines 52-97) for its.globl/.setexport block;sys/platform/vkernel64/x86_64/genassym.cdefines only 5 of them (GD_CURTHREAD :77, GD_CPUID :78, GD_CPUMASK :79, GD_OTHER_CPUS :80, GD_NPXTHREAD :121);- 21 dangle, split by restorability into this finding (13) and DF-5659 (8):
| macro | field exists? | location |
|---|---|---|
| GD_REQFLAGS | yes β mi.gd_reqflags | global.s:54 |
| GD_NPXTHREAD | yes (defined) | global.s:53 |
| GD_COMMON_TSSD | yes β globaldata.h:68 | global.s:61 |
| GD_TSS_GDT | yes β globaldata.h:69 | global.s:62 |
| GD_CURRENTLDT | yes β globaldata.h:78 | global.s:65 β the lead |
| GD_FPU_LOCK | yes β globaldata.h:73 | global.s:68 |
| GD_SAVEFPU | yes β globaldata.h:72 | global.s:69 |
| GD_SS_EFLAGS | yes β globaldata.h:83 | global.s:84 |
| GD_INTR_NESTING_LEVEL | yes β mi.gd_intr_nesting_level | global.s:85 |
| GD_FPENDING/GD_IPENDING/GD_SPENDING | yes β globaldata.h:74-76 | global.s:94-96 |
| GD_CNT | yes β mi.gd_cnt | global.s:97 |
| GD_PRV_CMAP1-3/PMAP1/CADDR1-3/PADDR1 | NO β absent from vkernel64 struct privatespace | global.s:77-78,86-93 β DF-5659 |
Every live displacement use in the real consumers resolves (swtch.o,
fork_tramp.o, locore.o: zero ASSYM-shaped undefined symbols β the
PCB_CR3/PCB_EXT/MSR_FSBASE/MSR_KGSBASE/PCB_USERLDT uses in swtch.s:348,369,
429,440,464 are all inside #if 0 dead blocks). The entire defect class is
concentrated in global.s's .set export block, where gas resolves the missing
macro to an undefined external symbol: the alias and its .globl export
silently vanish, assembly exits 0, -Werror stays silent.
Reproduction (guest, DragonFly 6.5-DEVELOPMENT #0, cc 8.3, 6 vCPU)
- Source identity. All 9 involved files MD5-identical between audit tree
and guest
/usr/src(env.txt). - Real-rule regen.
config VKKERNEL64β/usr/src/sys/compile/VKERNEL64;make assym.svia kern.post.mk:73-79 emitted exactly 44 defines (assym.pristine.s), regeneration byte-identical (determinism).make global.o(kern.post.mk .s rule,-Werror) β exit 0, andnm global.o(nm_global_pristine.txt): exactly 5 backed exports (gd_curthread=0x8, gd_cpuid=0x48, gd_cpumask=0x50, gd_npxthread=0x3a90, gd_other_cpus=0x70) +Ufor all 21 missing macros βU GD_CURRENTLDTamong them, the lead confirmed at the object level β while gd_reqflags, gd_common_tss, gd_currentldt, β¦ (the 21.globlexports) are absent from nm entirely. - End-to-end link. The vkernel is linked as a user program
(conf/Makefile:23-25:
cc -Wl,--export-dynamic β¦ -pthread, no-T ldscript). Linking all 547 SYSTEM_OBJS + vers.o that way (link.log/final_binary_baseline.txt) β exit 0 with the 21U GD_*riding into the final binary with zero relocations (objdump -r/readelf -rβ 0 GD_* entries) β precisely why the userland link never fails on them. - Fix.
fix.diffadds the 13 restorable ASSYM lines to genassym.c and deletes the 8 GD_PRV_ aliases from global.s (DF-5659). Rebuilt with the same rules (fix_build.log): assym.s 44β57 defines (assym.fixed.s);global.onow exports 18 gd_* symbols with zero undefined ASSYM macros; relinked final binary (fix_run.log,final_binary_fixed.txt): 0U GD_*, 18 exports*, link exit 0. Every restored value matches the independent TU checker exactly, e.g.gd_currentldt=0x3f14β struct-consistent (gd_spending 0x3f0c + gd_sdelayed 0x3f10 + int β 0x3f14, globaldata.h:74-78).
Contract-wide cross-checks (pass-2 checklist)
- (b) offset-vs-struct correctness:
offset_check.cre-evaluates every one of the 44 emitted ASSYM expressions in a separate TU compiled with the exact genassym CFLAGS β 44/44 values matchassym.s(only the0xpresentation differs). No stale offsets. Negative result, with evidence. - (c) used-vs-defined inventory: every CAPS macro token extracted from
all 4 platform .s consumers + 3 cpu/x86_64/misc .s consumers; the ONLY
unmatched set is the 21 GD_* in global.s. swtch.s's MSR_FSBASE/MSR_KGSBASE
uses are inside
#if 0and additionally resolvable from specialreg.h (swtch.s includes/ chains). - (d) vkernel-specific fields:
TD_SAVEFPU=0x628verified ==offsetof(struct thread, td_mach.mtd_savefpu)(thread.h:43,49), consumed live by swtch.s:558,701 β correct. The DF-5655 TLS-aliasing chain (td_tls==td_mach.mtd_savevext.vx_tls, thread.h:50) is C-side only:CHECK_td_tls=CHECK_mtd_savevext=0x630,CHECK_vx_tls=0, and NO assembly consumer references it (swtch.s's TLS-restore block at :477-484 is#if 0). No genassym exposure β negative result, chain documented. - DF-5597 twin (ioapic stride): zero IOAPIC_* tokens in any vkernel64 .s consumer β not applicable on this platform.
Incidental discovery (out-of-file lead, task_log'd)
The full VKERNEL64 build fails at link on
kern_mib.c:111/132 β undefined reference to '__build_id_start/__build_id_end'
(vkbuild.log): kern.pre.mk's default SYSTEM_LD passes
-Wl,-T,$S/platform/$P/conf/ldscript.$M (which PROVIDEs both symbols,
vkernel64/conf/ldscript.x86_64:75), but vkernel64's conf/Makefile SYSTEM_LD
override drops -T, so the vkernel64 kernel has been unbuildable since the
build-id sysctl landed. This failure is also what empirically delimits the
DF-5596 mechanism on the vkernel link: the link DOES fail hard on
relocation-referenced undefined symbols (__build_id_ has relocations) while
tolerating the 21 zero-relocation GD_ dangles silently.
Why it matters / impact ceiling
- Today: zero runtime effect β nothing references the vanished symbols by
relocation; C code uses
mdcpu->(and vkernel64'sset_user_ldtpanics, userldt.c:44 β no LDT support). The.globlexports that ddb/kgdb scripts and future assembly would rely on simply do not exist, on the platform whose debugger (in-process ddb) reads the vkernel's own symtab. - Systemically: the genassym contract is the ONLY build-time guard for the Cβasm offset agreement (the silent memory-corruption class this audit family tracks β DF-5586/5335 lineage). This file shows the guard failing silently 21 ways at once on the platform with the weakest link gate, and 8 of the 21 cannot even be "restored" (fields absent) β a maintainer attempting the obvious fix hits a compile error and is likely to abandon all 21.
Classification
Reproduced: yes β object level (nm global.o), final-binary level (21 U,
0 relocations, link exit 0), and controlled fix (0 U, 18 exports, values
independent-TU verified). Exploit chain: none β build-time contract defect,
no attacker-reachable primitive. Recommended fix validated: fix.diff
(git-apply-clean against pristine sys/, never applied to the audit tree).
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.
Fix verification
fixedBaseline: 21 undefined GD_ symbols in global.o and the final VKERNEL64 binary, only 5 backed gd_ exports, zero build diagnostics. After fix.diff (guest /usr/src copy, same kern.post.mk rules): assym.s 44->57 defines, global.o exports all 18 field-backed gd_ symbols with zero undefined ASSYM macros, final relinked binary has 0 U GD_ and 18 exports, every value matching the independent-TU checker (gd_currentldt=0x3f14). Previously-observed bad behavior gone; link green.
fix.diff (git-apply-clean vs pristine sys/), fixbuild.log, fixbuild2.log, fix_run.log, final_binary_fixed.txt, assym.fixed.s vs assym.pristine.s
Confirmed kernel references
- sys/platform/vkernel64/x86_64/genassym.c:72-121
- sys/platform/vkernel64/x86_64/global.s:51-97
- sys/platform/vkernel64/x86_64/global.s:65
- sys/platform/vkernel64/include/globaldata.h:66-84
- sys/platform/vkernel64/conf/Makefile:23-25
- sys/conf/kern.post.mk:73-79
Detail
Evidence (decisive lines)
run.log + nm_global_pristine.txt (U GD_CURRENTLDT + 20 siblings at object level); final_binary_baseline.txt (21 U GD_*, 0 relocations, link exit 0); assym.pristine.s (44 defines) vs assym.fixed.s (57); fix_run.log + final_binary_fixed.txt (0 U, 18 exports after fix); offset_check.out (44/44 emitted values independently verified + MISSING_* targets); vkbuild.log (full untrimmed build incl. the unrelated __build_id link buster that delimits the tolerance mechanism)
PoC changes
No seed PoC existed (pass-2 lead). Authored offset_check.c (independent-TU re-evaluation of all 44 ASSYM expressions + CHECK_/MISSING_ extras) and the build-tree regeneration/probe procedure; had to relax -Werror/-Wnested-externs for the checker harness only (production flags untouched).
Verified recommended fix
Add the 13 ASSYM(GD_, offsetof(struct mdglobaldata, ...)) lines to vkernel64 genassym.c and delete the 8 GD_PRV_ aliases from global.s (fix.diff, validated)
Verdict
Verified on the guest's VKERNEL64 build tree (source MD5-identical): global.s consumes 26 GD_ macros but genassym.c defines only 5, so 21 .set exports silently vanish β nm on the real-rule-built global.o shows U for all 21 missing macros (incl. the task_log lead GD_CURRENTLDT at global.s:65) with the .globl'd gd_ aliases absent entirely, assembly exit 0 under -Werror; the exact SYSTEM_LD userland-cc link completes exit 0 with all 21 U GD_ riding into the final vkernel64 binary carrying ZERO relocations, which is precisely why nothing ever fails. 13 of the 21 are restorable (fields exist in struct mdglobaldata); the other 8 (GD_PRV_) alias fields that do not exist on this platform and are split to DF-5659. fix.diff (13 ASSYM additions + 8 alias deletions) validated: assym.s 44->57 defines, global.o exports 18 gd_ with zero undefined ASSYM macros, relinked final binary 0 U GD_ / 18 exports, every restored value matching an independent-TU checker (gd_currentldt=0x3f14 struct-consistent).
No comments yet.