[PATCH v3 1/2] 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.co...
Signed-off-by: Sean Anderson seanga2@gmail.com ---
Changes in v3: - Fix some spelling errors - Reword ut all docs for clarity
Changes in v2: - Reworked most documentation to address Heinrich's feedback
doc/arch/sandbox.rst | 90 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 82 insertions(+), 8 deletions(-)
diff --git a/doc/arch/sandbox.rst b/doc/arch/sandbox.rst index e1119492b4..79bcef4f5e 100644 --- a/doc/arch/sandbox.rst +++ b/doc/arch/sandbox.rst @@ -479,18 +479,92 @@ 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 options are useful in addition to the above examples: + +* ``--trace-childen=yes`` will tell 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=no`` 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. +* Passing ``-Tc 'ut all'`` to U-Boot will run unit tests automatically. Note + that not all unit tests will succeed in the default configuration. +* Passing ``-t cooked`` to U-Boot 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 -------

There are no defined instruction sequences in include/valgrind.h for Risc-V, so CONFIG_VALGRIND will do nothing on this arch (and possibly won't compile?). Update Kconfig accordingly.
Signed-off-by: Sean Anderson seanga2@gmail.com ---
(no changes since v2)
Changes in v2: - New
Kconfig | 1 + 1 file changed, 1 insertion(+)
diff --git a/Kconfig b/Kconfig index bdae59e06f..a83b534b34 100644 --- a/Kconfig +++ b/Kconfig @@ -307,6 +307,7 @@ config TPL_SYS_MALLOC_F_LEN
config VALGRIND bool "Inform valgrind about memory allocations" + depends on !RISCV help Valgrind is an instrumentation framework for building dynamic analysis tools. In particular, it may be used to detect memory management bugs

On 5/27/22 16:03, Sean Anderson wrote:
There are no defined instruction sequences in include/valgrind.h for Risc-V, so CONFIG_VALGRIND will do nothing on this arch (and possibly won't compile?). Update Kconfig accordingly.
RISC-V support for valgrind has not been merged upstream yet but is under development.
Reviewed-by: Heinrich Schuchardt xypron.glpk@gmx.de
Signed-off-by: Sean Anderson seanga2@gmail.com
(no changes since v2)
Changes in v2:
New
Kconfig | 1 + 1 file changed, 1 insertion(+)
diff --git a/Kconfig b/Kconfig index bdae59e06f..a83b534b34 100644 --- a/Kconfig +++ b/Kconfig @@ -307,6 +307,7 @@ config TPL_SYS_MALLOC_F_LEN
config VALGRIND bool "Inform valgrind about memory allocations"
- depends on !RISCV help Valgrind is an instrumentation framework for building dynamic analysis tools. In particular, it may be used to detect memory management bugs
participants (2)
-
Heinrich Schuchardt
-
Sean Anderson