1 Dealing with Untrusted Input in SMM
2 ===================================
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.
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.
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.
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).
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.
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.
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.
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.
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
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.
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
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
79 static inline __attribute__((always_inline))
80 uint32_t pci_read_config32[d](pci_devfn_t dev, unsigned int where)
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)) {
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).
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
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
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.
115 Since this method isn’t generalizable, it has to be an optional
116 compile-time feature.
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
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.
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).