DF-4605 / extract_and_test.sh
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 | #!/bin/sh # DF-4605 โ verbatim control-flow proof (runs ON THE GUEST). # # Proves, from the LIVE kernel source at /usr/src (not a hand copy), that: # 1. the event guard in amdgpu_atif_handler (amdgpu_acpi.c:374-385) has an # EMPTY body โ its only statements are inside `#if 0`, so the preprocessor # removes them and the `if` becomes a no-op; # 2. control therefore falls through to `atif->functions.sbios_requests` # (amdgpu_acpi.c:387), which reads offset 0x12 of a NULL pointer when # adev->atif == NULL (the normal state on ATIF-less systems, e.g. all # discrete desktop AMD GPUs โ see README.md reachability analysis); # 3. the proposed fix (restore the early `return;`) removes the fault. # # The kernel itself cannot run this path on this guest (no AMD GPU, amdgpu.ko # not built) โ this harness compiles the EXACT extracted lines with the exact # struct layout instead, so the control-flow claim is mechanically verified. set -u SRC=/usr/src/sys/dev/drm/amd/amdgpu/amdgpu_acpi.c [ -f "$SRC" ] || { echo "FATAL: kernel source not present: $SRC"; exit 1; } WORK=$(mktemp -d /tmp/df4605.XXXXXX) cd "$WORK" # --- extract the exact lines under test from the live kernel source ------- sed -n '374,385p' "$SRC" > guard.inc # the guard (if (...) { #if 0 ... #endif }) sed -n '387p' "$SRC" > deref.inc # `if (atif->functions.sbios_requests) {` echo "===== extracted guard.inc (amdgpu_acpi.c:374-385) =====" cat -n guard.inc echo "===== extracted deref.inc (amdgpu_acpi.c:387) =====" cat -n deref.inc # fixed variant: same guard with the #if 0..#endif block (frag lines 6-11) # replaced by a real early return; the closing brace (frag line 12) is kept sed '6,11c\ return;' guard.inc > guard_fixed.inc echo "===== guard_fixed.inc (proposed fix applied) =====" cat -n guard_fixed.inc # --- harness with exact struct layout from amdgpu_acpi.c:38-75 ------------- cat > guard_test.c <<'EOF' /* * DF-4605 control-flow harness. The two #include fragments below are * sed-extracted verbatim from the guest's live kernel source: * guard.inc = amdgpu_acpi.c lines 374-385 (the gutted guard) * deref.inc = amdgpu_acpi.c line 387 (the first deref) * Struct definitions replicate sys/dev/drm/amd/amdgpu/amdgpu_acpi.c:38-75 * byte-for-byte in size/offset (ACPI_HANDLE = pointer, bool = 1 byte), * so `functions.sbios_requests` sits at offset 18 (0x12), exactly as in * the kernel build. */ #include <stdio.h> #include <signal.h> #include <stdint.h> #include <stdlib.h> #include <string.h> #include <unistd.h> typedef unsigned int UINT32; struct amdgpu_atif_notification_cfg { char enabled; int command_code; }; struct amdgpu_atif_notifications { char display_switch, expansion_mode_change, thermal_state, forced_power_state, system_power_state, display_conf_change, px_gfx_switch, brightness_change, dgpu_display_event; /* 9 x 1B */ }; struct amdgpu_atif_functions { char system_params, sbios_requests, select_active_disp, lid_state, get_tv_standard, set_tv_standard, get_panel_expansion_mode, set_panel_expansion_mode, temperature_change, graphics_device_types; /* 10 x 1B */ }; struct amdgpu_atif { void *handle; /* ACPI_HANDLE @0 */ struct amdgpu_atif_notifications notifications; /* @8..16 */ struct amdgpu_atif_functions functions; /* @17..26 */ struct amdgpu_atif_notification_cfg notification_cfg; }; static int fixed_mode = 0; /* * Opaque barrier: prevents the optimizer from proving atif==NULL and * deleting the deref as dead code (UB elision). In the kernel, atif comes * from adev->atif at notify-dispatch time and is never foldable. */ static struct amdgpu_atif * volatile atif_arg = NULL; __attribute__((noinline)) static void handler_under_test(struct amdgpu_atif *atif, UINT32 type) { if (fixed_mode) { #include "guard_fixed.inc" } else { #include "guard.inc" } #include "deref.inc" /* kernel body (amdgpu_acpi.c:388-431) calls amdgpu_atif_get_sbios_requests() * โ a real side effect. The sink replicates that so the compiler cannot * drop the (dead-looking) empty-guard + load. */ printf("KERNEL-EQUIV: sbios_requests path entered " "(kernel evaluates ATIF GET_SYSTEM_BIOS_REQUESTS here)\n"); } } static void segv(int sig, siginfo_t *si, void *uc) { (void)sig; (void)uc; printf("RESULT: SIGSEGV at address %p " "(=&atif->functions.sbios_requests with atif==NULL, expected 0x12)\n", si->si_addr); if (fixed_mode) printf("FIXED-MODE FAULT: unexpected โ fix did NOT work\n"); else printf("VERDICT: guard body compiled to NOTHING; control fell " "through the if() into the NULL deref\n"); exit(fixed_mode ? 1 : 42); } int main(int argc, char **argv) { struct sigaction sa; fixed_mode = (argc > 1); memset(&sa, 0, sizeof(sa)); sa.sa_sigaction = segv; sa.sa_flags = SA_SIGINFO; sigaction(SIGSEGV, &sa, NULL); printf("sizeof(struct amdgpu_atif)=%zu " "offsetof(functions.sbios_requests)=%zu\n", sizeof(struct amdgpu_atif), (size_t)&((struct amdgpu_atif *)0)->functions.sbios_requests); /* exactly the kernel state on ATIF-less systems: adev->atif == NULL. * The NULL is produced at RUNTIME (getenv) so the optimizer cannot * constant-fold it and delete the deref as UB-dead-code (which gcc 8 * demonstrably does otherwise โ see VERDICT.md; in the kernel atif is * adev->atif, an opaque heap field, equally unfoldable). */ { const char *e = getenv("DF4605_ATIF"); struct amdgpu_atif *atif = (struct amdgpu_atif *)(uintptr_t)(e ? strtol(e, NULL, 0) : 0); handler_under_test(atif, 0x81); } printf("RESULT: no fault โ guard returned early (finding REFUTED in this mode)\n"); return 0; } EOF echo "===== build (kernel-style -O2) =====" cc -O2 -Wall -o guard_test guard_test.c || exit 1 echo "===== run BASELINE (gutted guard, verbatim kernel lines) =====" ./guard_test; rc=$? echo "baseline exit=$rc" echo "===== run FIXED (guard with restored return) =====" ./guard_test fixed; rc2=$? echo "fixed exit=$rc2" if [ "$rc" = "42" ] && [ "$rc2" = "0" ]; then echo "PROOF-OK: baseline faults at NULL+0x12; fixed variant returns cleanly" else echo "PROOF-UNEXPECTED: baseline=$rc fixed=$rc2" fi echo "workdir: $WORK" |