brintos

brintos / llvm-project-archived public Read only

0
0
Text · 21.5 KiB · 23720e5 Raw
610 lines · plain
1=================2SanitizerCoverage3=================4 5.. contents::6   :local:7 8Introduction9============10 11LLVM has a simple code coverage instrumentation built in (SanitizerCoverage).12It inserts calls to user-defined functions on function-, basic-block-, and edge- levels.13Default implementations of those callbacks are provided and implement14simple coverage reporting and visualization,15however if you need *just* coverage visualization you may want to use16:doc:`SourceBasedCodeCoverage <SourceBasedCodeCoverage>` instead.17 18Tracing PCs with guards19=======================20 21With ``-fsanitize-coverage=trace-pc-guard`` the compiler will insert the following code22on every edge:23 24.. code-block:: none25 26   __sanitizer_cov_trace_pc_guard(&guard_variable)27 28Every edge will have its own `guard_variable` (uint32_t).29 30The compiler will also insert calls to a module constructor:31 32.. code-block:: c++33 34   // The guards are [start, stop).35   // This function will be called at least once per DSO and may be called36   // more than once with the same values of start/stop.37   __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop);38 39With an additional ``...=trace-pc,indirect-calls`` flag40``__sanitizer_cov_trace_pc_indirect(void *callee)`` will be inserted on every indirect call.41 42The functions `__sanitizer_cov_trace_pc_*` should be defined by the user.43 44Example:45 46.. code-block:: c++47 48  // trace-pc-guard-cb.cc49  #include <stdint.h>50  #include <stdio.h>51  #include <sanitizer/coverage_interface.h>52 53  // This callback is inserted by the compiler as a module constructor54  // into every DSO. 'start' and 'stop' correspond to the55  // beginning and end of the section with the guards for the entire56  // binary (executable or DSO). The callback will be called at least57  // once per DSO and may be called multiple times with the same parameters.58  extern "C" void __sanitizer_cov_trace_pc_guard_init(uint32_t *start,59                                                      uint32_t *stop) {60    static uint64_t N;  // Counter for the guards.61    if (start == stop || *start) return;  // Initialize only once.62    printf("INIT: %p %p\n", start, stop);63    for (uint32_t *x = start; x < stop; x++)64      *x = ++N;  // Guards should start from 1.65  }66 67  // This callback is inserted by the compiler on every edge in the68  // control flow (some optimizations apply).69  // Typically, the compiler will emit the code like this:70  //    if(*guard)71  //      __sanitizer_cov_trace_pc_guard(guard);72  // But for large functions it will emit a simple call:73  //    __sanitizer_cov_trace_pc_guard(guard);74  extern "C" void __sanitizer_cov_trace_pc_guard(uint32_t *guard) {75    if (!*guard) return;  // Duplicate the guard check.76    // If you set *guard to 0 this code will not be called again for this edge.77    // Now you can get the PC and do whatever you want:78    //   store it somewhere or symbolize it and print right away.79    // The values of `*guard` are as you set them in80    // __sanitizer_cov_trace_pc_guard_init and so you can make them consecutive81    // and use them to dereference an array or a bit vector.82    void *PC = __builtin_return_address(0);83    char PcDescr[1024];84    // This function is a part of the sanitizer run-time.85    // To use it, link with AddressSanitizer or other sanitizer.86    __sanitizer_symbolize_pc(PC, "%p %F %L", PcDescr, sizeof(PcDescr));87    printf("guard: %p %x PC %s\n", guard, *guard, PcDescr);88  }89 90.. code-block:: c++91 92  // trace-pc-guard-example.cc93  void foo() { }94  int main(int argc, char **argv) {95    if (argc > 1) foo();96  }97 98.. code-block:: console99 100  clang++ -g  -fsanitize-coverage=trace-pc-guard trace-pc-guard-example.cc -c101  clang++ trace-pc-guard-cb.cc trace-pc-guard-example.o -fsanitize=address102  ASAN_OPTIONS=strip_path_prefix=`pwd`/ ./a.out103 104.. code-block:: console105 106  INIT: 0x71bcd0 0x71bce0107  guard: 0x71bcd4 2 PC 0x4ecd5b in main trace-pc-guard-example.cc:2108  guard: 0x71bcd8 3 PC 0x4ecd9e in main trace-pc-guard-example.cc:3:7109 110.. code-block:: console111 112  ASAN_OPTIONS=strip_path_prefix=`pwd`/ ./a.out with-foo113 114 115.. code-block:: console116 117  INIT: 0x71bcd0 0x71bce0118  guard: 0x71bcd4 2 PC 0x4ecd5b in main trace-pc-guard-example.cc:3119  guard: 0x71bcdc 4 PC 0x4ecdc7 in main trace-pc-guard-example.cc:4:17120  guard: 0x71bcd0 1 PC 0x4ecd20 in foo() trace-pc-guard-example.cc:2:14121 122Inline 8bit-counters123====================124 125**Experimental, may change or disappear in future**126 127With ``-fsanitize-coverage=inline-8bit-counters`` the compiler will insert128inline counter increments on every edge.129This is similar to ``-fsanitize-coverage=trace-pc-guard`` but instead of a130callback the instrumentation simply increments a counter.131 132Users need to implement a single function to capture the counters at startup.133 134.. code-block:: c++135 136  extern "C"137  void __sanitizer_cov_8bit_counters_init(char *start, char *end) {138    // [start,end) is the array of 8-bit counters created for the current DSO.139    // Capture this array in order to read/modify the counters.140  }141 142 143Inline bool-flag144================145 146**Experimental, may change or disappear in future**147 148With ``-fsanitize-coverage=inline-bool-flag`` the compiler will insert149setting an inline boolean to true on every edge.150This is similar to ``-fsanitize-coverage=inline-8bit-counter`` but instead of151an increment of a counter, it just sets a boolean to true.152 153Users need to implement a single function to capture the flags at startup.154 155.. code-block:: c++156 157  extern "C"158  void __sanitizer_cov_bool_flag_init(bool *start, bool *end) {159    // [start,end) is the array of boolean flags created for the current DSO.160    // Capture this array in order to read/modify the flags.161  }162 163 164PC-Table165========166 167**Experimental, may change or disappear in future**168 169**Note:** this instrumentation might be incompatible with dead code stripping170(``-Wl,-gc-sections``) for linkers other than LLD, thus resulting in a171significant binary size overhead. For more information, see172`Bug 34636 <https://bugs.llvm.org/show_bug.cgi?id=34636>`_.173 174With ``-fsanitize-coverage=pc-table`` the compiler will create a table of175instrumented PCs. Requires either ``-fsanitize-coverage=inline-8bit-counters``,176or ``-fsanitize-coverage=inline-bool-flag``, or ``-fsanitize-coverage=trace-pc-guard``.177 178Users need to implement a single function to capture the PC table at startup:179 180.. code-block:: c++181 182  extern "C"183  void __sanitizer_cov_pcs_init(const uintptr_t *pcs_beg,184                                const uintptr_t *pcs_end) {185    // [pcs_beg,pcs_end) is the array of ptr-sized integers representing186    // pairs [PC,PCFlags] for every instrumented block in the current DSO.187    // Capture this array in order to read the PCs and their Flags.188    // The number of PCs and PCFlags for a given DSO is the same as the number189    // of 8-bit counters (-fsanitize-coverage=inline-8bit-counters), or190    // boolean flags (-fsanitize-coverage=inline=bool-flags), or trace_pc_guard191    // callbacks (-fsanitize-coverage=trace-pc-guard).192    // A PCFlags describes the basic block:193    //  * bit0: 1 if the block is the function entry block, 0 otherwise.194  }195 196 197Tracing PCs198===========199 200With ``-fsanitize-coverage=trace-pc`` the compiler will insert201``__sanitizer_cov_trace_pc()`` on every edge.202With an additional ``...=trace-pc,indirect-calls`` flag203``__sanitizer_cov_trace_pc_indirect(void *callee)`` will be inserted on every indirect call.204These callbacks are not implemented in the Sanitizer run-time and should be defined205by the user.206This mechanism is used for fuzzing the Linux kernel207(https://github.com/google/syzkaller).208 209Instrumentation points210======================211Sanitizer Coverage offers different levels of instrumentation.212 213* ``edge`` (default): edges are instrumented (see below).214* ``bb``: basic blocks are instrumented.215* ``func``: only the entry block of every function will be instrumented.216 217Use these flags together with ``trace-pc-guard`` or ``trace-pc``,218like this: ``-fsanitize-coverage=func,trace-pc-guard``.219 220When ``edge`` or ``bb`` is used, some of the edges/blocks may still be left221uninstrumented (pruned) if such instrumentation is considered redundant.222Use ``no-prune`` (e.g. ``-fsanitize-coverage=bb,no-prune,trace-pc-guard``)223to disable pruning. This could be useful for better coverage visualization.224 225 226Edge coverage227-------------228 229Consider this code:230 231.. code-block:: c++232 233    void foo(int *a) {234      if (a)235        *a = 0;236    }237 238It contains 3 basic blocks, let's name them A, B, C:239 240.. code-block:: none241 242    A243    |\244    | \245    |  B246    | /247    |/248    C249 250If blocks A, B, and C are all covered we know for certain that the edges A=>B251and B=>C were executed, but we still don't know if the edge A=>C was executed.252Such edges of control flow graph are called253`critical <https://en.wikipedia.org/wiki/Control_flow_graph#Special_edges>`_.254The edge-level coverage simply splits all critical edges by introducing new255dummy blocks and then instruments those blocks:256 257.. code-block:: none258 259    A260    |\261    | \262    D  B263    | /264    |/265    C266 267Tracing data flow268=================269 270Support for data-flow-guided fuzzing.271With ``-fsanitize-coverage=trace-cmp`` the compiler will insert extra instrumentation272around comparison instructions and switch statements.273Similarly, with ``-fsanitize-coverage=trace-div`` the compiler will instrument274integer division instructions (to capture the right argument of division)275and with  ``-fsanitize-coverage=trace-gep`` --276the `LLVM GEP instructions <https://llvm.org/docs/GetElementPtr.html>`_277(to capture array indices).278Similarly, with ``-fsanitize-coverage=trace-loads`` and ``-fsanitize-coverage=trace-stores``279the compiler will instrument loads and stores, respectively.280 281Currently, these flags do not work by themselves - they require one282of ``-fsanitize-coverage={trace-pc,inline-8bit-counters,inline-bool}``283flags to work.284 285Unless ``no-prune`` option is provided, some of the comparison instructions286will not be instrumented.287 288.. code-block:: c++289 290  // Called before a comparison instruction.291  // Arg1 and Arg2 are arguments of the comparison.292  void __sanitizer_cov_trace_cmp1(uint8_t Arg1, uint8_t Arg2);293  void __sanitizer_cov_trace_cmp2(uint16_t Arg1, uint16_t Arg2);294  void __sanitizer_cov_trace_cmp4(uint32_t Arg1, uint32_t Arg2);295  void __sanitizer_cov_trace_cmp8(uint64_t Arg1, uint64_t Arg2);296 297  // Called before a comparison instruction if exactly one of the arguments is constant.298  // Arg1 and Arg2 are arguments of the comparison, Arg1 is a compile-time constant.299  // These callbacks are emitted by -fsanitize-coverage=trace-cmp since 2017-08-11300  void __sanitizer_cov_trace_const_cmp1(uint8_t Arg1, uint8_t Arg2);301  void __sanitizer_cov_trace_const_cmp2(uint16_t Arg1, uint16_t Arg2);302  void __sanitizer_cov_trace_const_cmp4(uint32_t Arg1, uint32_t Arg2);303  void __sanitizer_cov_trace_const_cmp8(uint64_t Arg1, uint64_t Arg2);304 305  // Called before a switch statement.306  // Val is the switch operand.307  // Cases[0] is the number of case constants.308  // Cases[1] is the size of Val in bits.309  // Cases[2:] are the case constants.310  void __sanitizer_cov_trace_switch(uint64_t Val, uint64_t *Cases);311 312  // Called before a division statement.313  // Val is the second argument of division.314  void __sanitizer_cov_trace_div4(uint32_t Val);315  void __sanitizer_cov_trace_div8(uint64_t Val);316 317  // Called before a GetElementPtr (GEP) instruction318  // for every non-constant array index.319  void __sanitizer_cov_trace_gep(uintptr_t Idx);320 321  // Called before a load of appropriate size. Addr is the address of the load.322  void __sanitizer_cov_load1(uint8_t *addr);323  void __sanitizer_cov_load2(uint16_t *addr);324  void __sanitizer_cov_load4(uint32_t *addr);325  void __sanitizer_cov_load8(uint64_t *addr);326  void __sanitizer_cov_load16(__int128 *addr);327  // Called before a store of appropriate size. Addr is the address of the store.328  void __sanitizer_cov_store1(uint8_t *addr);329  void __sanitizer_cov_store2(uint16_t *addr);330  void __sanitizer_cov_store4(uint32_t *addr);331  void __sanitizer_cov_store8(uint64_t *addr);332  void __sanitizer_cov_store16(__int128 *addr);333 334 335Tracing control flow336====================337 338With ``-fsanitize-coverage=control-flow`` the compiler will create a table to collect339control flow for each function. More specifically, for each basic block in the function,340two lists are populated. One list for successors of the basic block and another list for341non-intrinsic called functions.342 343**TODO:** in the current implementation, indirect calls are not tracked344and are only marked with special value (-1) in the list.345 346Each table row consists of the basic block address347followed by ``null``-ended lists of successors and callees.348The table is encoded in a special section named ``sancov_cfs``349 350Example:351 352.. code-block:: c++353 354  int foo (int x) {355    if (x > 0)356      bar(x);357    else358      x = 0;359    return x;360  }361 362The code above contains 4 basic blocks, let's name them A, B, C, D:363 364.. code-block:: none365 366    A367    |\368    | \369    B  C370    | /371    |/372    D373 374The collected control flow table is as follows:375``A, B, C, null, null, B, D, null, @bar, null, C, D, null, null, D, null, null.``376 377Users need to implement a single function to capture the CF table at startup:378 379.. code-block:: c++380 381  extern "C"382  void __sanitizer_cov_cfs_init(const uintptr_t *cfs_beg,383                                const uintptr_t *cfs_end) {384    // [cfs_beg,cfs_end) is the array of ptr-sized integers representing385    // the collected control flow.386  }387 388Tracing Stack Depth389===================390 391With ``-fsanitize-coverage=stack-depth`` the compiler will track how much392stack space has been used for a function call chain. Leaf functions are393not included in this tracing.394 395The maximum depth of a function call graph is stored in the thread-local396``__sancov_lowest_stack`` variable. Instrumentation is inserted in every397non-leaf function to check the frame pointer against this variable,398and if it is lower, store the current frame pointer. This effectively399inserts the following:400 401.. code-block:: c++402 403  extern thread_local uintptr_t __sancov_lowest_stack;404 405  uintptr_t stack = (uintptr_t)__builtin_frame_address(0);406  if (stack < __sancov_lowest_stack)407    __sancov_lowest_stack = stack;408 409If ``-fsanitize-coverage-stack-depth-callback-min=N`` (where410``N > 0``) is also used, the tracking is delegated to a callback,411``__sanitizer_cov_stack_depth``, instead of adding instrumentation to412update ``__sancov_lowest_stack``. The ``N`` of the argument is used413to determine which functions to instrument. Only functions estimated414to be using ``N`` bytes or more of stack space will be instrumented to415call the tracing callback. In the case of a dynamically sized stack,416the callback is unconditionally added.417 418The callback takes no arguments and is responsible for determining419the stack usage and doing any needed comparisons and storage. A roughly420equivalent implementation of ``__sancov_lowest_stack`` using the callback421would look like this:422 423.. code-block:: c++424 425  void __sanitizer_cov_stack_depth(void) {426    uintptr_t stack = (uintptr_t)__builtin_frame_address(0);427 428    if (stack < __sancov_lowest_stack)429      __sancov_lowest_stack = stack;430  }431 432Gated Trace Callbacks433=====================434 435Gate the invocation of the tracing callbacks with436``-sanitizer-coverage-gated-trace-callbacks``.437 438When this option is enabled, the instrumentation will not call into the439runtime-provided callbacks for tracing, thus only incurring in a trivial440branch without going through a function call.441 442It is up to the runtime to toggle the value of the global variable in order to443enable tracing.444 445This option is only supported for trace-pc-guard and trace-cmp.446 447Disabling instrumentation with ``__attribute__((no_sanitize("coverage")))``448===========================================================================449 450It is possible to disable coverage instrumentation for select functions via the451function attribute ``__attribute__((no_sanitize("coverage")))``. Because this452attribute may not be supported by other compilers, it is recommended to use it453together with ``__has_feature(coverage_sanitizer)``.454 455Disabling instrumentation without source modification456=====================================================457 458It is sometimes useful to tell SanitizerCoverage to instrument only a subset of the459functions in your target without modifying source files.460With ``-fsanitize-coverage-allowlist=allowlist.txt``461and ``-fsanitize-coverage-ignorelist=blocklist.txt``,462you can specify such a subset through the combination of an allowlist and a blocklist.463 464SanitizerCoverage will only instrument functions that satisfy two conditions.465First, the function should belong to a source file with a path that is both allowlisted466and not blocklisted.467Second, the function should have a mangled name that is both allowlisted and not blocklisted.468 469The allowlist and blocklist format is similar to that of the sanitizer blocklist format.470The default allowlist will match every source file and every function.471The default blocklist will match no source file and no function.472 473A common use case is to have the allowlist list folders or source files for which you want474instrumentation and allow all function names, while the blocklist will opt out some specific475files or functions that the allowlist loosely allowed.476 477Here is an example allowlist:478 479.. code-block:: none480 481  # Enable instrumentation for a whole folder482  src:bar/*483  # Enable instrumentation for a specific source file484  src:foo/a.cpp485  # Enable instrumentation for all functions in those files486  fun:*487 488And an example blocklist:489 490.. code-block:: none491 492  # Disable instrumentation for a specific source file that the allowlist allowed493  src:bar/b.cpp494  # Disable instrumentation for a specific function that the allowlist allowed495  fun:*myFunc*496 497The use of ``*`` wildcards above is required because function names are matched after mangling.498Without the wildcards, one would have to write the whole mangled name.499 500Be careful that the paths of source files are matched exactly as they are provided on the clang501command line.502For example, the allowlist above would include file ``bar/b.cpp`` if the path was provided503exactly like this, but would it would fail to include it with other ways to refer to the same504file such as ``./bar/b.cpp``, or ``bar\b.cpp`` on Windows.505So, please make sure to always double check that your lists are correctly applied.506 507Default implementation508======================509 510The sanitizer run-time (AddressSanitizer, MemorySanitizer, etc) provide a511default implementations of some of the coverage callbacks.512You may use this implementation to dump the coverage on disk at the process513exit.514 515Example:516 517.. code-block:: console518 519    % cat -n cov.cc520         1  #include <stdio.h>521         2  __attribute__((noinline))522         3  void foo() { printf("foo\n"); }523         4524         5  int main(int argc, char **argv) {525         6    if (argc == 2)526         7      foo();527         8    printf("main\n");528         9  }529    % clang++ -g cov.cc -fsanitize=address -fsanitize-coverage=trace-pc-guard530    % ASAN_OPTIONS=coverage=1 ./a.out; wc -c *.sancov531    main532    SanitizerCoverage: ./a.out.7312.sancov 2 PCs written533    24 a.out.7312.sancov534    % ASAN_OPTIONS=coverage=1 ./a.out foo ; wc -c *.sancov535    foo536    main537    SanitizerCoverage: ./a.out.7316.sancov 3 PCs written538    24 a.out.7312.sancov539    32 a.out.7316.sancov540 541Every time you run an executable instrumented with SanitizerCoverage542one ``*.sancov`` file is created during the process shutdown.543If the executable is dynamically linked against instrumented DSOs,544one ``*.sancov`` file will be also created for every DSO.545 546Sancov data format547------------------548 549The format of ``*.sancov`` files is very simple: the first 8 bytes is the magic,550one of ``0xC0BFFFFFFFFFFF64`` and ``0xC0BFFFFFFFFFFF32``. The last byte of the551magic defines the size of the following offsets. The rest of the data is the552offsets in the corresponding binary/DSO that were executed during the run.553 554Sancov Tool555-----------556 557A simple ``sancov`` tool is provided to process coverage files.558The tool is part of LLVM project and is currently supported only on Linux.559It can handle symbolization tasks autonomously without any extra support560from the environment. You need to pass .sancov files (named561``<module_name>.<pid>.sancov`` and paths to all corresponding binary elf files.562Sancov matches these files using module names and binaries file names.563 564.. code-block:: console565 566    USAGE: sancov [options] <action> (<binary file>|<.sancov file>)...567 568    Action (required)569      -print                    - Print coverage addresses570      -covered-functions        - Print all covered functions.571      -not-covered-functions    - Print all not covered functions.572      -symbolize                - Symbolizes the report.573 574    Options575      -blocklist=<string>         - Blocklist file (sanitizer blocklist format).576      -demangle                   - Print demangled function name.577      -strip_path_prefix=<string> - Strip this prefix from file paths in reports578 579 580Coverage Reports581----------------582 583**Experimental**584 585``.sancov`` files do not contain enough information to generate a source-level586coverage report. The missing information is contained587in debug info of the binary. Thus the ``.sancov`` has to be symbolized588to produce a ``.symcov`` file first:589 590.. code-block:: console591 592    sancov -symbolize my_program.123.sancov my_program > my_program.123.symcov593 594The ``.symcov`` file can be browsed overlaid over the source code by595running ``tools/sancov/coverage-report-server.py`` script that will start596an HTTP server.597 598Output directory599----------------600 601By default, .sancov files are created in the current working directory.602This can be changed with ``ASAN_OPTIONS=coverage_dir=/path``:603 604.. code-block:: console605 606    % ASAN_OPTIONS="coverage=1:coverage_dir=/tmp/cov" ./a.out foo607    % ls -l /tmp/cov/*sancov608    -rw-r----- 1 kcc eng 4 Nov 27 12:21 a.out.22673.sancov609    -rw-r----- 1 kcc eng 8 Nov 27 12:21 a.out.22679.sancov610