]> git.proxmox.com Git - mirror_frr.git/blob - doc/developer/fuzzing.rst
Merge pull request #13430 from opensourcerouting/feature/rip_allow-ecmp_limit
[mirror_frr.git] / doc / developer / fuzzing.rst
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.