]>
Commit | Line | Data |
---|---|---|
e862bcc5 QY |
1 | .. _fuzzing: |
2 | ||
3 | Fuzzing | |
4 | ======= | |
5 | ||
6 | This page describes the fuzzing targets and supported fuzzers available in FRR | |
7 | and how to use them. Familiarity with fuzzing techniques and tools is assumed. | |
8 | ||
9 | Overview | |
10 | -------- | |
11 | ||
12 | It is well known that networked applications tend to be difficult to fuzz on | |
13 | their network-facing attack surfaces. Approaches involving actual network | |
14 | transmission tend to be slow and are subject to intermediate devices and | |
15 | networking stacks which tend to drop fuzzed packets, especially if the fuzzing | |
16 | surface covers IP itself. Some time was spent on fuzzing FRR this way with some | |
17 | mediocre results but attention quickly turned towards skipping the actual | |
18 | networking and instead adding fuzzing targets directly in the packet processing | |
19 | code for use by more traditional in- and out-of-process fuzzers. Results from | |
20 | this approach have been very fruitful. | |
21 | ||
22 | The patches to add fuzzing targets are kept in a separate git branch. Typically | |
23 | it is better to keep them in the main branch so they are kept up to date and do | |
24 | not need to be constantly synchronized with the main codebase. Unfortunately, | |
25 | changes to FRR to support fuzzing necessarily extend far beyond the | |
26 | entrypoints. Checksums must be disarmed, interactions with the kernel must be | |
27 | skipped, sockets and files must be avoided, desired under/overflows must be | |
28 | marked, etc. There are the usual ``LD_PRELOAD`` libraries to emulate these | |
29 | things (preeny et al) but FRR is a very kernel-reliant program and these | |
30 | libraries tend to create annoying problems when used with FRR for whatever | |
31 | reason. Keeping this code in the main codebase is cluttering, difficult to work | |
32 | with / around, and runs the risk of accidentally introducing bugs even if | |
33 | ``#ifdef``'d out. Consequently it's in a separate branch that is rebased on | |
34 | ``master`` from time to time. | |
35 | ||
36 | ||
37 | Code | |
38 | ---- | |
39 | ||
40 | The git branch with fuzzing targets is located here: | |
41 | ||
42 | https://github.com/FRRouting/frr/tree/fuzz | |
43 | ||
44 | To build libFuzzer targets, pass ``--enable-libfuzzer`` to ``configure``. | |
45 | To build AFL targets, compile with ``afl-clang`` as usual. | |
46 | ||
47 | Fuzzing with sanitizers is strongly recommended, especially ASAN, which you can | |
48 | enable by passing ``--enable-address-sanitizer`` to ``configure``. | |
49 | ||
50 | Suggested UBSAN flags: ``-fsanitize-recover=unsigned-integer-overflow,implicit-conversion -fsanitize=unsigned-integer-overflow,implicit-conversion,nullability-arg,nullability-assign,nullability-return`` | |
51 | Recommended cflags: ``-Wno-all -g3 -O3 -funroll-loops`` | |
52 | ||
53 | Design | |
54 | ------ | |
55 | ||
56 | All fuzzing targets have support for libFuzzer and AFL. This is done by writing | |
57 | the target as a libFuzzer entrypoint (``LLVMFuzzerTestOneInput()``) and calling | |
58 | it from the AFL entrypoint in ``main()``. New targets should use this rule. | |
59 | ||
60 | When adding AFL entrypoints, it's a good idea to use AFL persistent mode for | |
61 | better performance. Grep ``bgpd/bgp_main.c`` for ``__AFL_INIT()`` for an | |
62 | example of how to do this in FRR. Typically it involves moving all internal | |
63 | daemon setup into a setup function. Then this setup function is called exactly | |
64 | once for the lifetime of the process. In ``LLVMFuzzerTestOneInput()`` this | |
65 | means you need to call it at the start of the function protected by a static | |
66 | boolean that is set to true, since that function is your entrypoint. You also | |
67 | need to call it prior to ``__AFL_INIT()`` in ``main()`` because ``main()`` is | |
68 | your entrypoint in the AFL case. | |
69 | ||
70 | Adding support to daemons | |
71 | ^^^^^^^^^^^^^^^^^^^^^^^^^ | |
72 | ||
73 | This section describes how to add entrypoints to daemons that do not have any | |
74 | yet. | |
75 | ||
76 | Because libFuzzer has its own ``main()`` function, when adding fuzzing support | |
77 | to a daemon that doesn't have any targets already, ``main()`` needs to be | |
78 | ``#ifdef``'d out like so: | |
79 | ||
80 | .. code:: c | |
81 | ||
82 | #ifndef FUZZING_LIBFUZZER | |
83 | ||
84 | int main(int argc, char **argv) | |
85 | { | |
86 | ... | |
87 | } | |
88 | ||
89 | #endif /* FUZZING_LIBFUZZER */ | |
90 | ||
91 | ||
92 | The ``FUZZING_LIBFUZZER`` macro is set by ``--enable-libfuzzer``. | |
93 | ||
94 | Because libFuzzer can only be linked into daemons that have | |
95 | ``LLVMFuzzerTestOneInput()`` implemented, we can't pass ``-fsanitize=fuzzer`` | |
96 | to all daemons in ``AM_CFLAGS``. It needs to go into a variable specific to | |
97 | each daemon. Since it can be thought of as a kind of sanitizer, for daemons | |
98 | that have libFuzzer support there are now individual flags variables for those | |
99 | daemons named ``DAEMON_SAN_FLAGS`` (e.g. ``BGPD_SAN_FLAGS``, | |
100 | ``ZEBRA_SAN_FLAGS``). This variable has the contents of the generic | |
101 | ``SAN_FLAGS`` plus any fuzzing-related flags. It is used in daemons' | |
102 | ``subdir.am`` in place of ``SAN_FLAGS``. Daemons that don't support libFuzzer | |
103 | still use ``SAN_FLAGS``. If you want to add fuzzing support to a daemon you | |
104 | need to do this flag variable conversion; look at ``configure.ac`` for | |
105 | examples, it is fairly straightforward. Remember to update ``subdir.am`` to use | |
106 | the new variable. | |
107 | ||
108 | Do note that when fuzzing is enabled, ``SAN_FLAGS`` gains | |
109 | ``-fsanitize=fuzzer-no-link``; the result is that all daemons are instrumented | |
110 | for fuzzing but only the ones with ``LLVMFuzzerTestOneInput()`` actually get | |
111 | linked with libFuzzer. | |
112 | ||
113 | ||
114 | Targets | |
115 | ------- | |
116 | ||
117 | A given daemon can have lots of different paths that are interesting to fuzz. | |
118 | There's not really a great way to handle this, most fuzzers assume the program | |
119 | has one entrypoint. The approach taken in FRR for multiple entrypoints is to | |
120 | control which path is taken within ``LLVMFuzzerTestOneInput()`` using | |
121 | ``#ifdef`` and passing whatever controlling macro definition you want. Take a | |
122 | look at that function for the daemon you're interested in fuzzing, pick the | |
123 | target, add ``#define MY_TARGET 1`` somewhere before the ``#ifdef`` switch, | |
124 | recompile. | |
125 | ||
126 | .. list-table:: Fuzzing Targets | |
127 | ||
128 | * - Daemon | |
129 | - Target | |
130 | - Fuzzers | |
131 | * - bgpd | |
132 | - packet parser | |
133 | - libfuzzer, afl | |
134 | * - ospfd | |
135 | - packet parser | |
136 | - libfuzzer, afl | |
137 | * - pimd | |
138 | - packet parser | |
139 | - libfuzzer, afl | |
140 | * - vrrpd | |
141 | - packet parser | |
142 | - libfuzzer, afl | |
143 | * - vrrpd | |
144 | - zapi parser | |
145 | - libfuzzer, afl | |
146 | * - zebra | |
147 | - netlink | |
148 | - libfuzzer, afl | |
149 | * - zebra | |
150 | - zserv / zapi | |
151 | - libfuzzer, afl | |
152 | ||
153 | ||
154 | Fuzzer Notes | |
155 | ------------ | |
156 | ||
157 | Some interesting seed corpuses for various daemons are available `here | |
158 | <https://github.com/qlyoung/frr-fuzz/tree/master/samples>`_. | |
159 | ||
160 | For libFuzzer, you need to pass ``-rss_limit_mb=0`` if you are fuzzing with | |
161 | ASAN enabled, as you should. | |
162 | ||
163 | For AFL, afl++ is strongly recommended; afl proper isn't really maintained | |
164 | anymore. |