From a24b20ea67f0fac7ab139769a9ff478d4fd1edda Mon Sep 17 00:00:00 2001 From: Sean Anderson Date: Fri, 27 May 2022 10:02:59 -0400 Subject: [PATCH] doc: sandbox: Add additional valgrind documentation This documents some additional options which can be used with valgrind, as well as directions for future work. It also fixes up inline literals to actually be inline literals (and not italics). The content of this documentation is primarily adapted from [1]. [1] https://lore.kernel.org/u-boot/57cb4b49-fa30-1194-9ac3-faa53e8033bd@gmail.com/ Signed-off-by: Sean Anderson Reviewed-by: Heinrich Schuchardt --- doc/arch/sandbox.rst | 102 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 94 insertions(+), 8 deletions(-) diff --git a/doc/arch/sandbox.rst b/doc/arch/sandbox.rst index bfc9cc6a13..068d4a3be4 100644 --- a/doc/arch/sandbox.rst +++ b/doc/arch/sandbox.rst @@ -495,18 +495,104 @@ It is possible to run U-Boot under valgrind to check memory allocations:: valgrind ./u-boot -For more detailed results, enable `CONFIG_VALGRIND`. There are many false -positives due to `malloc` itself. Suppress these with:: +However, this does not give very useful results. The sandbox allocates a memory +pool via mmap(). U-Boot's internal malloc() and free() work on this memory pool. +Custom allocators and deallocators are invisible to valgrind by default. To +expose U-Boot's malloc() and free() to valgrind, enable ``CONFIG_VALGRIND``. +Enabling this option will inject placeholder assembler code which valgrind +interprets. This is used to annotate sections of memory as safe or unsafe, and +to inform valgrind about malloc()s and free()s. There are currently no standard +placeholder assembly sequences for RISC-V, so this option cannot be enabled on +that architecture. + +Malloc's bookkeeping information is marked as unsafe by default. However, this +will generate many false positives when malloc itself accesses this information. +These warnings can be suppressed with:: valgrind --suppressions=scripts/u-boot.supp ./u-boot -If you are running sandbox SPL or TPL, then valgrind will not by default -notice when U-Boot jumps from TPL to SPL, or from SPL to U-Boot proper. To -fix this, use `--trace-children=yes`. To show who alloc'd some troublesome -memory, use `--track-origins=yes`. To uncover possible errors, try running all -unit tests with:: +Additionally, you may experience false positives if U-Boot is using a smaller +pointer size than your host architecture. This is because the pointers used by +U-Boot will only contain 32 bits of addressing information. When interpreted as +64-bit pointers, valgrind will think that they are not initialized properly. To +fix this, enable ``CONFIG_SANDBOX64`` (such as via ``sandbox64_defconfig``) +when running on a 64-bit host. - valgrind --track-origins=yes --suppressions=scripts/u-boot.supp ./u-boot -Tc 'ut all' +Additional options +^^^^^^^^^^^^^^^^^^ + +The following valgrind options are useful in addition to the above examples: + +``--trace-childen=yes`` + tells valgrind to keep tracking subprocesses, such + as when U-Boot jumps from TPL to SPL, or from SPL to U-Boot proper. + +``--track-origins=yes`` + will (for a small overhead) tell valgrind to keep + track of who allocated some troublesome memory. + +``--error-limit`` + will enable printing more than 1000 errors in a single session. + +``--vgdb=yes --vgdb-error=0`` + will let you use GDB to attach like:: + + gdb -ex "target remote | vgdb" u-boot + + This is very helpful for inspecting the program state when there is + an error. + +The following U-Boot option are also helpful: + +``-Tc 'ut all'`` + lets U-Boot run unit tests automatically. Note + that not all unit tests will succeed in the default configuration. + +``-t cooked`` + will keep the console in a sane state if you + terminate it early (instead of having to run tset). + +Future work +^^^^^^^^^^^ + +The biggest limitation to the current approach is that supressions don't +"un-taint" uninitialized memory accesses. Currently, dlmalloc's bookkeeping +information is marked as a "red zone." This means that all reads to that zone +are marked as illegal by valgrind. This is fine for regular code, but dlmalloc +really does need to access this area, so we suppress its violations. However, if +dlmalloc then passes a result calculated from a "tainted" access, that result is +still tainted. So the first accessor will raise a warning. This means that every +construct like + +.. code-block:: + + foo = malloc(sizeof(*foo)); + if (!foo) + return -ENOMEM; + +will raise a warning when we check the result of malloc. Whoops. + +There are at least four possible ways to address this: + +* Don't mark dlmalloc bookkeeping information as a red zone. This is the + simplest solution, but reduces the power of valgrind immensely, since we can + no longer determine that (e.g.) access past the end of an array is undefined. +* Implement red zones properly. This would involve growing every allocation by a + fixed amount (16 bytes or so) and then using that extra space for a real red + zone that neither regular code nor dlmalloc needs to access. Unfortunately, + this would probably some fairly intensive surgery to dlmalloc to add/remove + the offset appropriately. +* Mark bookkeeping information as valid before we use it in dlmalloc, and then + mark it invalid before returning. This would be the most correct, but it would + be very tricky to implement since there are so many code paths to mark. I + think it would be the most effort out of the three options here. +* Use the host malloc and free instead of U-Boot's custom allocator. This will + eliminate the need to annotate dlmalloc. However, using a different allocator + for sandbox will mean that bugs in dlmalloc will only be tested when running + on read (or emulated) hardware. + +Until one of the above options are implemented, it will remain difficult +to sift through the massive amount of spurious warnings. Testing ------- -- 2.39.5