DF-5597 / ioapic_drift.c
/* * DF-5597 drift-simulation PoC: the ioapic_irqinfo stride invariant. * * sys/platform/pc64/apic/ioapic_abi.h defines IOAPIC_IRQI_SZSHIFT = 4 as a * HAND constant with only a comment ("Update IOAPIC_IRQI_SZSHIFT after * changing ioapic_irqinfo size") enforcing that * sizeof(struct ioapic_irqinfo) == 1 << IOAPIC_IRQI_SZSHIFT. * genassym.c:276-277 emits BOTH numbers into assym.s, but nothing at build * time relates them. Consumers: * apic/ioapic_ipl.s:80,97 shll $IOAPIC_IRQI_SZSHIFT, %eax (hand const) * apic/apic_vector.s:55-59 IOAPIC_IRQI_SIZE * irq + ... (sizeof) * * This program demonstrates, in userspace, that (1) today the invariant * holds (sizeof==16==1<<4), and (2) a one-field drift (the obvious future * edit: adding an owner cpumask for IRQ balancing) makes the two consumers * disagree: 1<<4 = 16 != sizeof = 24. ioapic_ipl.s would then compute * entry indices into ioapic_irqs[] with the wrong stride and read/write * io_addr/io_idx/io_flags of the WRONG entry (or past the array end for * high IRQ numbers) while apic_vector.s stays correct -- a silent, * build-clean divergence. Compile with -DDRFIFT to see the drifted size. */ #include <stdio.h> #include <stdint.h> #include <stddef.h> struct ioapic_irqinfo_current { uint32_t io_flags; int io_idx; volatile void *io_addr; }; struct ioapic_irqinfo_drifted { uint32_t io_flags; int io_idx; volatile void *io_addr; uint32_t io_owner_cpumask; /* the plausible future field */ }; #define IOAPIC_IRQI_SZSHIFT 4 /* hand constant from ioapic_abi.h:58 */ int main(void) { #ifdef DRIFT size_t sz = sizeof(struct ioapic_irqinfo_drifted); #else size_t sz = sizeof(struct ioapic_irqinfo_current); #endif size_t stride = (size_t)1 << IOAPIC_IRQI_SZSHIFT; printf("sizeof(struct ioapic_irqinfo) = %zu\n", sz); printf("1 << IOAPIC_IRQI_SZSHIFT = %zu\n", stride); if (sz == stride) { printf("INVARIANT HOLDS (matches live kernel: assym.s " "IOAPIC_IRQI_SIZE 0x10 / IOAPIC_IRQI_SZSHIFT 0x4)\n"); return 0; } printf("INVARIANT BROKEN: ioapic_ipl.s (shll $%zu) and apic_vector.s " "(SIZE=%zu) index ioapic_irqs[] differently.\n" "e.g. IRQ 100: ipl.s offset %zu vs vector.s offset %zu " "(array is %s)\n", stride, sz, 100 * stride, 100 * sz, (100 * stride < 100 * sz) ? "under-walked" : "over-walked/OOB"); return 1; } |