3 Memory safety is hard to achieve. We, as humans, are bound to make mistakes in
4 our code. While it may be straightforward to detect memory corruption bugs in
5 few lines of code, it becomes quite challenging to find those bugs in a massive
6 code. In such cases, 'Address Sanitizer' may prove to be useful and could help
9 [Address Sanitizer](https://github.com/google/sanitizers/wiki/AddressSanitizer)
10 , also known as ASan, is a runtime memory debugger designed to find
11 out-of-bounds accesses and use-after-scope bugs. coreboot has an in-built
12 Address Sanitizer. Therefore, it is advised to take advantage of this debugging
13 tool while working on large patches. This would further help to ensure code
14 quality and make runtime code more robust.
16 ## Types of errors detected
17 ASan in coreboot catches the following types of memory bugs:
19 ### Stack buffer overflow
20 Example stack-out-of-bounds:
24 int stack_array[5] = {0};
26 for (i = 0; i < 10; i++)
30 In this example, the array is of length 5 but it is being read even beyond the
33 ### Global buffer overflow
34 Example global-out-of-bounds:
36 char a[] = "I use coreboot";
40 char b[] = "proprietary BIOS";
45 > well, you are replacing coreboot with proprietary BIOS. In any case, that's
48 Let's come to the memory bug. The string 'a' is of length 14 but it is being
49 written to even beyond that.
52 Example use-after-scope:
64 In this example, the value 5 is written to an undefined address instead of the
65 variable 'x'. This happens because 'x' can't be accessed outside its scope.
69 In order to enable ASan on a supported platform,
70 select `Address sanitizer support` from `General setup` menu while configuring
73 Then build coreboot and run the image as usual. If your code contains any of the
74 above-mentioned memory bugs, ASan will report them in the console log as shown
77 ASan: <bug type> in <ip>
78 <access type> of <access size> bytes at addr <access address>
82 `bug type` is either `stack-out-of-bounds`, `global-out-of-bounds` or
85 `ip` is the address of the last good instruction before the bad access,
87 `access type` is either `Read` or `Write`,
89 `access size` is the number of bytes read or written, and
91 `access address` is the memory location which is accessed while the error
94 Next, you have to use `ip` to retrieve the instruction which causes the error.
95 Since stages in coreboot are relocated, you need to normalize `ip`. For this,
96 first subtract the start address of the stage from `ip`. Then, read the section
97 headers from `<stage>.debug` file to determine the offset of the text segment.
98 Add this offset to the difference you calculated earlier. Let's call the
99 resultant address `ip'`.
101 Next, read the contents of the symbol table and search for a function having
102 an address closest to `ip'`. This is the function in which our memory bug is
103 present. Let's denote the address of this function by `ip''`.
105 Finally, read the assembly contents of the object file where this function is
106 present. Look for the affected function. Here, the instruction which exists at
107 the offset `ip' - ip''` corresponds to the address `ip`. Therefore, the very
108 next instruction is the one which causes the error.
110 To see ASan in action, let's take an example. Suppose, there is a
111 stack-out-of-bounds error in cbfs.c that we aren’t aware of and we want ASan
112 to help us detect it.
114 int cbfs_boot_region_device(struct region_device *rdev)
119 for (i = 10; i > 0; i--)
122 return vboot_locate_cbfs(rdev) &&
123 fmap_locate_area_as_rdev("COREBOOT", rdev);
126 First, we enable ASan from the configuration menu as shown above. Then, we
127 build coreboot and run the image.
129 ASan reports the following error in the console log:
131 ASan: stack-out-of-bounds in 0x7f7432fd
132 Write of 4 bytes at addr 0x7f7c2ac8
134 Here 0x7f7432fd is `ip` i.e. the address of the last good instruction before
135 the bad access. First we have to normalize this address as stated above.
136 As per the console log, this error happened in ramstage and the stage starts
137 from 0x7f72c000. So, let’s look at the sections headers of ramstage from
140 $ objdump -h build/cbfs/fallback/ramstage.debug
142 build/cbfs/fallback/ramstage.debug: file format elf32-i386
145 Idx Name Size VMA LMA File off Algn
146 0 .text 00070b20 00e00000 00e00000 00001000 2**12
147 CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
148 1 .ctors 0000036c 00e70b20 00e70b20 00071b20 2**2
149 CONTENTS, ALLOC, LOAD, RELOC, DATA
150 2 .data 0001c8f4 00e70e8c 00e70e8c 00071e8c 2**2
151 CONTENTS, ALLOC, LOAD, RELOC, DATA
152 3 .bss 00012940 00e8d780 00e8d780 0008e780 2**7
154 4 .heap 00004000 00ea00c0 00ea00c0 0008e780 2**0
157 As you can see, the offset of the text segment is 0x00e00000. Let's subtract the
158 start address of the stage from `ip` and add this offset to the difference. The
159 resultant address i.e. `ip'` is 0x00e172fd.
161 Next, we read the contents of the symbol table and search for a function having
162 an address closest to 0x00e172fd.
164 $ nm -n build/cbfs/fallback/ramstage.debug
167 00e17116 t _GLOBAL__sub_I_65535_1_gfx_get_init_done
169 00e171db T cbfs_load_and_decompress
170 00e1729b T cbfs_boot_region_device
171 00e17387 T cbfs_boot_locate
172 00e1740d T cbfs_boot_map_with_leak
173 00e174ef T cbfs_boot_map_optionrom
177 The symbol having an address closest to 0x00e172fd is `cbfs_boot_region_device` and
178 its address i.e. `ip''` is 0x00e1729b.
180 Now, as we know the affected function, let's read the assembly contents of
181 `cbfs_boot_region_device()` which is present in `cbfs.o` to find the faulty
184 $ objdump -d build/ramstage/lib/cbfs.o
187 51: e8 fc ff ff ff call 52 <cbfs_boot_region_device+0x52>
188 56: 83 ec 0c sub $0xc,%esp
190 5a: 83 ef 04 sub $0x4,%edi
191 5d: e8 fc ff ff ff call 5e <cbfs_boot_region_device+0x5e>
192 62: 83 c4 10 add $0x10,%esp
193 65: 89 5f 04 mov %ebx,0x4(%edi)
195 69: 75 eb jne 56 <cbfs_boot_region_device+0x56>
199 Here, we look for the instruction present at the offset 62 i.e. `ip' - ip''`.
200 The instruction is `add $0x10,%esp` and it corresponds to
201 `for (i = 10; i > 0; i--)` in our code. It means the very next instruction
202 i.e. `mov %ebx,0x4(%edi)` is the one that causes the error. Now, as we look at
203 C code of `cbfs_boot_region_device()` again, we find that this instruction
204 corresponds to `array[i] = i`.
206 Voilà! We just caught the memory bug using ASan.
208 ## Supported platforms
209 Presently, the following architectures support ASan in ramstage:
211 +------------------+--------------------------------+
212 | Architecture | Notes |
213 +==================+================================+
214 | x86 | Support for all x86 platforms |
215 +------------------+--------------------------------+
218 And in romstage ASan is available on the following platforms:
220 +---------------------+-----------------------------+
222 +=====================+=============================+
224 +---------------------+-----------------------------+
225 | Intel Apollo Lake | |
226 +---------------------+-----------------------------+
228 +---------------------+-----------------------------+
230 Alternatively, you can use `grep` to view the list of platforms that support
233 $ git grep "select HAVE_ASAN_IN_ROMSTAGE"
235 If the x86 platform you are using is not listed here, there is
236 still a chance that it supports ASan in romstage.
238 To test it, select `HAVE_ASAN_IN_ROMSTAGE` from the Kconfig file in the
239 platform's dedicated directory. Then, enable ASan from the config menu as
240 indicated in the previous section.
242 If you are able to build coreboot without any errors and boot cleanly, that
243 means the platform supports ASan in romstage. In that case, please upload a
244 patch on Gerrit selecting this config option using 'ASan' topic. Also, update
245 the platform name in the table.
247 However, if you end up in compilation errors or the linker error saying that
248 the cache got full, additional steps need to be taken to enable ASan in
249 romstage on the platform. While compile errors could be resolved easily and
250 therefore ASan in romstage has a good chance to be supported, a full cache is
251 an indication that it is way more work or even likely impossible to enable
255 ### Heap buffer overflow
256 Presently, ASan doesn't detect out-of-bounds accesses for the objects defined
259 To add support for these type of memory bugs, you have to make sure that
260 whenever some block of memory is allocated in the heap, the surrounding areas
261 (redzones) are poisoned. Correspondingly, these redzones should be unpoisoned
262 when the memory block is de-allocated.
264 ### ASan on other architectures
265 The following points should help when adding support for ASan to other
266 architectures like ARM or RISC-V:
268 * Enabling ASan in ramstage on other architectures should be easy. You just
269 have to make sure the shadow memory is initialized as early as possible when
270 ramstage is loaded. This can be done by making a function call to `asan_init()`
271 at the appropriate place.
273 * For romstage, you have to find out if there is enough room in the cache to fit
274 the shadow memory region. For this, find the boundary linker symbols for the
275 region you'd want to run ASan on, excluding the hardware mapped addresses.
276 Then define a new linker section named `asan_shadow` of size
277 `(_end - _start) >> 3`, where `_start` and `_end` are the linker symbols you
278 found earlier. This section should be appended to the region already occupied
279 by the coreboot program. Now build coreboot. If you don't see any errors while
280 building with the current translation function, ASan can be enabled on that
283 * The shadow region we currently use consumes memory equal to 1/8th of the
284 program memory. So, if you end up in a linker error saying that the memory got
285 full, you'll have to use a more compact shadow region. In that case, the
286 translation function could be something like
287 `shadow = (mem >> 7) | shadow_offset`. Since the stack buffers are protected by
288 the compiler, you'll also have to create a GCC patch forcing it to use the new
289 translation function for this particular architecture.
291 * Once you are sure that the architecture supports ASan in ramstage, select
292 `HAVE_ASAN_IN_RAMSTAGE` from the Kconfig file of that architecture. Similarly,
293 if the platform supports ASan in romstage, select `HAVE_ASAN_IN_ROMSTAGE` from
294 the platform's dedicated Kconfig file.
296 ### Post-processing script
297 Unlike Linux, coreboot doesn't have `%pS` printk format to dereference pointer
298 to its symbolic name. Therefore, we normalise the pointer address manually to
299 determine the name of the affected function and further use it to find the
300 instruction which causes the error.
302 A custom script can be written to automate this process.