2017-02-dealing-with-untrusted-input-in-smm.md
1 Dealing with Untrusted Input in SMM 2 =================================== 3 4 Objective 5 --------- 6 Intel Security recently held a talk and published 7 [slides](http://www.intelsecurity.com/advanced-threat-research/content/data/REConBrussels2017_BARing_the_system.pdf) 8 on a vulnerability in SMM handlers on x86 systems. They provide examples 9 on how both UEFI and coreboot are affected. 10 11 Background 12 ---------- 13 SMM, the System Management Mode, is a CPU mode that is configured by 14 firmware and survives the system’s initialization phase. On certain 15 events that mode can be triggered and executes code, suspending the 16 current processing that is going on the CPU, no matter whether it’s 17 in kernel or user space. 18 19 In SMM, the CPU has access to memory dedicated to that mode (SMRAM) that 20 is normally inaccessible, and typically some restrictions are lifted as 21 well (eg. in some configurations, certain flash write protection registers 22 are writable in SMM only). This makes SMM a target for attacks which 23 seek to elevate a ring0 (kernel) exploit to something permanent. 24 25 Overview 26 -------- 27 Intel Security showed several places in coreboot’s SMM handler (Slides 28 32+) that could be manipulated into writing data at user-chosen addresses 29 (SMRAM or otherwise), by modifying the BAR (Base Address Register) on 30 certain devices. By picking the right addresses and the right events 31 (and with them, mutators on the data at these addresses), it might 32 be possible to change the SMM handler itself to call into regular RAM 33 (where other code resides that then can work with elevated privileges). 34 35 Their proposed mitigations (Slide 37) revolve around making sure 36 that the BAR entries are reasonable, and point to a device instead of 37 regular memory or SMRAM. They’re not very detailed on how this could 38 be implemented, which is what this document discusses. 39 40 Detailed Design 41 --------------- 42 The attack works because the SMM handler trusts the results of the 43 `pci_read_config32(dev, reg)` function, even though the value read by that 44 function can be modified in kernel mode. 45 46 In the general case it’s not possible to keep the cached value from 47 system initialization because there are legitimate modifications the 48 kernel can do to these values, so the only remedy is to make sure that 49 the value isn’t totally off. 50 51 For applications where hardware changes are limited by design (eg. no 52 user-modifiable PCIe slots) and where the running kernel is known, 53 such as Chromebooks, further efforts include caching the BAR settings 54 at initialization time and comparing later accesses to that. 55 56 What "totally off" means is chipset specific because it requires 57 knowledge of the memory map as seen by the memory controller: which 58 addresses are routed to devices, which are handled by the memory 59 controller itself? 60 The proposal is that in SMM, the `pci_read_config` functions (which 61 aren’t timing critical) _always_ validate the value read from a given 62 set of registers (the BARs) and fail hard (ie. cold reset, potentially 63 after logging the event) if they’re invalid (because that points to 64 a severe kernel bug or an attack). 65 The actual validation is done by a function implemented by the chipset code. 66 67 Another validation that can be done is to make sure that the BAR has the 68 appropriate bits set so it is enabled and points to memory (instead of 69 IO space). 70 71 In terms of implementation, this might look somewhat as follows. There 72 are a bunch of blanks to fill in, in particular how to handle the actual 73 config space access and there will be more registers that need to be 74 checked for correctness, both official BARs (0-4) and per-chipset 75 registers that need to be blacklisted in another chipset specific 76 function: 77 78 ```c 79 static inline __attribute__((always_inline)) 80 uint32_t pci_read_config32[d](pci_devfn_t dev, unsigned int where) 81 { 82 uint32_t val = real_pci_read_config32(dev, where); 83 if (IS_ENABLED(__SMM__) && (where == PCI_BASE_ADDRESS_0) && 84 is_mmio_ptr(dev, where) && !is_address_in_mmio(val)) { 85 cold_reset(); 86 } 87 return val; 88 } 89 ``` 90 91 `is_address_in_mmio(addr)` would be a newly introduced function to be 92 implemented by chipset drivers that returns true if the passed address 93 points into whatever is considered valid MMIO space. 94 `is_mmio_ptr(dev, where)` returns true for PCI config space registers that 95 point to BARs (allowing custom overrides because sometimes additional 96 registers are used to point to addresses). 97 98 For this function what is considered a legal address needs to be 99 documented, in accordance with the chipset design. (For example: AMD 100 K8 has a bunch of registers that define strictly which addresses are 101 "MMIO") 102 103 ### Fully insured (aka “paranoid”) mode 104 For systems with more control over the hardware and kernel (such as 105 Chromebooks), it may be possible to set up the BARs in a way that the 106 kernel isn’t compelled to rewrite them, and store these values for 107 later comparison. 108 109 This avoids attacks such as setting the BAR to point to another device’s 110 MMIO region which the above method can’t catch. Such a configuration 111 would be “illegal”, but depending on the evaluation order of BARs 112 in the chipset, this might effectively only disable the device used for 113 the attack, while still fooling the SMM handler. 114 115 Since this method isn’t generalizable, it has to be an optional 116 compile-time feature. 117 118 Caveats 119 ------- 120 This capability might need to be hidden behind a Kconfig flag 121 because we won’t be able to provide functional implementations of 122 `is_address_in_mmio()` for every chipset supported by coreboot from the 123 start. 124 125 Security Considerations 126 ----------------------- 127 The actual exploitability of the issue is unknown, but fixing it serves 128 as defense in depth, similar to the 129 [Memory Sinkhole mitigation](https://review.coreboot.org/#/c/11519/) for 130 older Intel chipsets. 131 132 Testing Plan 133 ------------ 134 Manual testing can be conducted easily by creating a small payload that 135 provokes the reaction. It should test all conditions that enable the 136 address test (ie. the different BAR offsets if used by SMM handlers).