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