DF-3856 / harness.c
/* * DF-3856 harness - BIOS-controlled shift counts >= 32 (undefined behavior) * used to build GPIO/PLL write masks. * * Kernel sites (all take an unchecked BIOS byte as the shift count): * sys/dev/drm/radeon/radeon_combios.c:3103-3108 combios_parse_pll_table() * case 1: shift = RBIOS8(offset) * 8; [0..2040] * and_mask = RBIOS8(offset) << shift; * and_mask |= ~(0xff << shift); * or_mask = RBIOS8(offset) << shift; * ... WREG32_PLL(addr, tmp) [garbage masked] * sys/dev/drm/radeon/radeon_combios.c:2752/2760 voltage gpio mask * mask = (1 << tmp); tmp = RBIOS8(...) -> r100_pm_misc sink * sys/dev/drm/radeon/radeon_combios.c:654-658 i2c gpiopad masks * (1 << clk), (1 << data) clk/data = RBIOS8(...) * sys/dev/drm/radeon/radeon_combios.c:2687 thermal clk/data bits * (1 << clk_bit), (1 << data_bit) * * C99 6.5.7p3: shifting a 32-bit value by >= 32 is undefined. This harness * shows the concrete misbehaviour on the audit toolchain (gcc 8.3, x86-64): * the count is silently taken mod 32, so a BIOS byte of 32..255 produces a * WRONG mask that is then written to PLL/GPIO registers - no trap, no * warning at runtime, purely wrong hardware programming. (The compiler is * entitled to do anything here; that is the point.) */ #include <stdio.h> #include <stdint.h> int main(void) { volatile unsigned shift_byte, mask_byte, tmp; printf("== DF-3856: unchecked BIOS shift counts (C99 6.5.7p3 UB) ==\n\n"); /* --- radeon_combios.c:3103-3108, pll table case 1 --- */ shift_byte = 3; /* sane: shift = 24 */ tmp = 0x0f; { uint32_t shift = shift_byte * 8; uint32_t and_mask = (uint32_t)tmp << shift; and_mask |= ~(0xffu << shift); printf("[pll case1] shift_byte=%u -> shift=%u: and_mask=0x%08x (correct: byte lane 3 kept 0x0f)\n", shift_byte, shift, and_mask); } shift_byte = 4; /* BIOS byte 4 -> shift = 32: UB */ tmp = 0x0f; { uint32_t shift = shift_byte * 8; uint32_t and_mask = (uint32_t)tmp << shift; /* UB */ and_mask |= ~(0xffu << shift); /* UB */ printf("[pll case1] shift_byte=%u -> shift=%u: and_mask=0x%08x (UB! count taken mod 32 -> lane 0, not lane 3; expected 0x0fffffff)\n", shift_byte, shift, and_mask); } /* --- radeon_combios.c:2752/2760/654-658/2687, mask = (1 << tmp) --- */ mask_byte = 9; printf("[gpio mask] tmp=%u -> 1<<tmp = 0x%08x (intended single bit)\n", mask_byte, (unsigned)(1u << mask_byte)); mask_byte = 32; /* BIOS byte 32+: UB */ printf("[gpio mask] tmp=%u -> 1<<tmp = 0x%08x (UB! mod-32 -> bit 0; on other arch/codegen: anything)\n", mask_byte, (unsigned)(1u << mask_byte)); mask_byte = 200; printf("[gpio mask] tmp=%u -> 1<<tmp = 0x%08x (UB! mod-32 -> bit 8)\n", mask_byte, (unsigned)(1u << mask_byte)); printf("\nall three sites feed these masks straight into WREG32_PLL / " "r100_pm_misc() RMW / GPIO set_masks with no range check.\n"); return 0; } |