]>
Commit | Line | Data |
---|---|---|
c9a95378 | 1 | --- |
b35ec8de | 2 | title: Testing systemd Using Sanitizers |
4cdca0af | 3 | category: Contributing |
b41a3f66 | 4 | layout: default |
c9a95378 FS |
5 | --- |
6 | ||
b35ec8de | 7 | # Testing systemd Using Sanitizers |
c9a95378 FS |
8 | |
9 | To catch the *nastier* kind of bugs, you can run your code with [Address Sanitizer](https://clang.llvm.org/docs/AddressSanitizer.html) | |
10 | and [Undefined Behavior Sanitizer](https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html). | |
11 | This is mostly done automagically by various CI systems for each PR, but you may | |
12 | want to do it locally as well. The process slightly varies depending on the | |
13 | compiler you want to use and which part of the test suite you want to run. | |
14 | ||
15 | ## gcc | |
16 | gcc compiles in sanitizer libraries dynamically by default, so you need to get | |
17 | the shared libraries first - on Fedora these are shipped as a separate packages | |
18 | (`libasan` for Address Sanitizer and `libubsan` for Undefined Behavior Sanitizer). | |
19 | ||
20 | The compilation itself is then a matter of simply adding `-Db_sanitize=address,undefined` | |
21 | to `meson`. That's it - following executions of `meson test` and integrations tests | |
22 | under `test/` subdirectory will run with sanitizers enabled. However, to get | |
23 | truly useful results, you should tweak the runtime configuration of respective | |
24 | sanitizers; e.g. in systemd we set the following environment variables: | |
25 | ||
26 | ```bash | |
27 | ASAN_OPTIONS=strict_string_checks=1:detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1 | |
28 | UBSAN_OPTIONS=print_stacktrace=1:print_summary=1:halt_on_error=1 | |
29 | ``` | |
30 | ## clang | |
31 | In case of clang things are somewhat different - the sanitizer libraries are | |
32 | compiled in statically by default. This is not an issue if you plan to run | |
33 | only the unit tests, but for integration tests you'll need to convince clang | |
34 | to use the dynamic versions of sanitizer libraries. | |
35 | ||
36 | First of all, pass `-shared-libsan` to both `clang` and `clang++`: | |
37 | ||
38 | ```bash | |
39 | CFLAGS=-shared-libasan | |
40 | CXXFLAGS=-shared-libasan | |
41 | ``` | |
42 | ||
43 | The `CXXFLAGS` are necessary for `src/libsystemd/sd-bus/test-bus-vtable-cc.c`. Compilation | |
44 | is then the same as in case of gcc, simply add `-Db_sanitize=address,undefined` | |
45 | to the `meson` call and use the same environment variables for runtime configuration. | |
46 | ||
47 | ```bash | |
48 | ASAN_OPTIONS=strict_string_checks=1:detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1 | |
49 | UBSAN_OPTIONS=print_stacktrace=1:print_summary=1:halt_on_error=1 | |
50 | ``` | |
51 | ||
52 | After this, you'll probably notice that all compiled binaries complain about | |
53 | missing `libclang_rt.asan*` library. To fix this, you have to install clang's | |
54 | runtime libraries, usually shipped in the `compiler-rt` package. As these libraries | |
55 | are installed in a non-standard location (non-standard for `ldconfig`), you'll | |
56 | need to manually direct binaries to the respective runtime libraries. | |
57 | ||
58 | ``` | |
59 | # Optionally locate the respective runtime DSO | |
60 | $ ldd build/systemd | grep libclang_rt.asan | |
61 | libclang_rt.asan-x86_64.so => not found | |
62 | libclang_rt.asan-x86_64.so => not found | |
63 | $ find /usr/lib* /usr/local/lib* -type f -name libclang_rt.asan-x86_64.so 2>/dev/null | |
64 | /usr/lib64/clang/7.0.1/lib/libclang_rt.asan-x86_64.so | |
65 | ||
66 | # Set the LD_LIBRARY_PATH accordingly | |
67 | export LD_LIBRARY_PATH=/usr/lib64/clang/7.0.1/lib/ | |
68 | ||
69 | # If the path is correct, the "not found" message should change to an actual path | |
70 | $ ldd build/systemd | grep libclang_rt.asan | |
71 | libclang_rt.asan-x86_64.so => /usr/lib64/clang/7.0.1/lib/libclang_rt.asan-x86_64.so (0x00007fa9752fc000) | |
72 | ``` | |
73 | ||
74 | This should help binaries to correctly find necessary sanitizer DSOs. | |
75 | ||
76 | Also, to make the reports useful, `llvm-symbolizer` tool is required (usually | |
77 | part of the `llvm` package). | |
78 | ||
79 | ## Background notes | |
80 | The reason why you need to force dynamic linking in case of `clang` is that some | |
81 | applications make use of `libsystemd`, which is compiled with sanitizers as well. | |
82 | However, if a *standard* (uninstrumented) application loads an instrumented library, | |
83 | it will immediately fail due to unresolved symbols. To fix/workaround this, you | |
84 | need to pre-load the ASan DSO using `LD_PRELOAD=/path/to/asan/dso`, which will | |
85 | make things work as expected in most cases. This will, obviously, not work with | |
86 | statically linked sanitizer libraries. | |
87 | ||
88 | These shenanigans are performed automatically when running the integration test | |
89 | suite (i.e. `test/TEST-??-*`) and are located in `test/test-functions` (mainly, | |
90 | but not only, in the `create_asan_wrapper` function). |