Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

x86/early_printk: Harden early_serial

Scott found that mem32_serial_in() is an ideal speculation gadget, an
indirectly callable function that takes an adddress and offset and
immediately does a load.

Use static_call() to take away the need for indirect calls and
explicitly seal the functions to ensure they're not callable on IBT
enabled parts.

Reported-by: Scott Constable <scott.d.constable@intel.com>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Reviewed-by: Sami Tolvanen <samitolvanen@google.com>
Link: https://lore.kernel.org/r/20250207122546.919773202@infradead.org

+24 -25
+24 -25
arch/x86/kernel/early_printk.c
··· 19 19 #include <linux/usb/ehci_def.h> 20 20 #include <linux/usb/xhci-dbgp.h> 21 21 #include <asm/pci_x86.h> 22 + #include <linux/static_call.h> 22 23 23 24 /* Simple VGA output */ 24 25 #define VGABASE (__ISA_IO_base + 0xb8000) ··· 95 94 #define DLL 0 /* Divisor Latch Low */ 96 95 #define DLH 1 /* Divisor latch High */ 97 96 98 - static unsigned int io_serial_in(unsigned long addr, int offset) 97 + static __noendbr unsigned int io_serial_in(unsigned long addr, int offset) 99 98 { 100 99 return inb(addr + offset); 101 100 } 101 + ANNOTATE_NOENDBR_SYM(io_serial_in); 102 102 103 - static void io_serial_out(unsigned long addr, int offset, int value) 103 + static __noendbr void io_serial_out(unsigned long addr, int offset, int value) 104 104 { 105 105 outb(value, addr + offset); 106 106 } 107 + ANNOTATE_NOENDBR_SYM(io_serial_out); 107 108 108 - static unsigned int (*serial_in)(unsigned long addr, int offset) = io_serial_in; 109 - static void (*serial_out)(unsigned long addr, int offset, int value) = io_serial_out; 109 + DEFINE_STATIC_CALL(serial_in, io_serial_in); 110 + DEFINE_STATIC_CALL(serial_out, io_serial_out); 110 111 111 112 static int early_serial_putc(unsigned char ch) 112 113 { 113 114 unsigned timeout = 0xffff; 114 115 115 - while ((serial_in(early_serial_base, LSR) & XMTRDY) == 0 && --timeout) 116 + while ((static_call(serial_in)(early_serial_base, LSR) & XMTRDY) == 0 && --timeout) 116 117 cpu_relax(); 117 - serial_out(early_serial_base, TXR, ch); 118 + static_call(serial_out)(early_serial_base, TXR, ch); 118 119 return timeout ? 0 : -1; 119 120 } 120 121 ··· 134 131 { 135 132 unsigned char c; 136 133 137 - serial_out(early_serial_base, LCR, 0x3); /* 8n1 */ 138 - serial_out(early_serial_base, IER, 0); /* no interrupt */ 139 - serial_out(early_serial_base, FCR, 0); /* no fifo */ 140 - serial_out(early_serial_base, MCR, 0x3); /* DTR + RTS */ 134 + static_call(serial_out)(early_serial_base, LCR, 0x3); /* 8n1 */ 135 + static_call(serial_out)(early_serial_base, IER, 0); /* no interrupt */ 136 + static_call(serial_out)(early_serial_base, FCR, 0); /* no fifo */ 137 + static_call(serial_out)(early_serial_base, MCR, 0x3); /* DTR + RTS */ 141 138 142 - c = serial_in(early_serial_base, LCR); 143 - serial_out(early_serial_base, LCR, c | DLAB); 144 - serial_out(early_serial_base, DLL, divisor & 0xff); 145 - serial_out(early_serial_base, DLH, (divisor >> 8) & 0xff); 146 - serial_out(early_serial_base, LCR, c & ~DLAB); 139 + c = static_call(serial_in)(early_serial_base, LCR); 140 + static_call(serial_out)(early_serial_base, LCR, c | DLAB); 141 + static_call(serial_out)(early_serial_base, DLL, divisor & 0xff); 142 + static_call(serial_out)(early_serial_base, DLH, (divisor >> 8) & 0xff); 143 + static_call(serial_out)(early_serial_base, LCR, c & ~DLAB); 147 144 } 148 145 149 146 #define DEFAULT_BAUD 9600 ··· 186 183 /* Convert from baud to divisor value */ 187 184 divisor = 115200 / baud; 188 185 189 - /* These will always be IO based ports */ 190 - serial_in = io_serial_in; 191 - serial_out = io_serial_out; 192 - 193 186 /* Set up the HW */ 194 187 early_serial_hw_init(divisor); 195 188 } 196 189 197 190 #ifdef CONFIG_PCI 198 - static void mem32_serial_out(unsigned long addr, int offset, int value) 191 + static __noendbr void mem32_serial_out(unsigned long addr, int offset, int value) 199 192 { 200 193 u32 __iomem *vaddr = (u32 __iomem *)addr; 201 194 /* shift implied by pointer type */ 202 195 writel(value, vaddr + offset); 203 196 } 197 + ANNOTATE_NOENDBR_SYM(mem32_serial_out); 204 198 205 - static unsigned int mem32_serial_in(unsigned long addr, int offset) 199 + static __noendbr unsigned int mem32_serial_in(unsigned long addr, int offset) 206 200 { 207 201 u32 __iomem *vaddr = (u32 __iomem *)addr; 208 202 /* shift implied by pointer type */ 209 203 return readl(vaddr + offset); 210 204 } 205 + ANNOTATE_NOENDBR_SYM(mem32_serial_in); 211 206 212 207 /* 213 208 * early_pci_serial_init() ··· 279 278 */ 280 279 if ((bar0 & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO) { 281 280 /* it is IO mapped */ 282 - serial_in = io_serial_in; 283 - serial_out = io_serial_out; 284 281 early_serial_base = bar0 & PCI_BASE_ADDRESS_IO_MASK; 285 282 write_pci_config(bus, slot, func, PCI_COMMAND, 286 283 cmdreg|PCI_COMMAND_IO); 287 284 } else { 288 285 /* It is memory mapped - assume 32-bit alignment */ 289 - serial_in = mem32_serial_in; 290 - serial_out = mem32_serial_out; 286 + static_call_update(serial_in, mem32_serial_in); 287 + static_call_update(serial_out, mem32_serial_out); 291 288 /* WARNING! assuming the address is always in the first 4G */ 292 289 early_serial_base = 293 290 (unsigned long)early_ioremap(bar0 & PCI_BASE_ADDRESS_MEM_MASK, 0x10);