The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
fuzzer.c
Go to the documentation of this file.
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/**
18 * $Id: 0e7dd248d3c2fc91564b93f91c440cc3581a31b3 $
19 *
20 * @file src/bin/fuzzer.c
21 * @brief Functions to fuzz protocol decoding
22 *
23 * @copyright 2019 Network RADIUS SAS (legal@networkradius.com)
24 */
25RCSID("$Id: 0e7dd248d3c2fc91564b93f91c440cc3581a31b3 $")
26
27#include <freeradius-devel/util/dl.h>
28#include <freeradius-devel/util/conf.h>
29#include <freeradius-devel/util/dict.h>
30#include <freeradius-devel/util/proto.h>
31#include <freeradius-devel/util/syserror.h>
32#include <freeradius-devel/io/test_point.h>
33
34/*
35 * Run from the source directory via:
36 *
37 * ./build/make/jlibtool --mode=execute ./build/bin/local/fuzzer_radius -D share/dictionary /path/to/corpus/directory/
38 */
39
40static bool init = false;
41static dl_t *dl = NULL;
44static TALLOC_CTX *autofree = NULL;
45static bool do_encode = false;
46
47static fr_dict_t *dict = NULL;
48static fr_dict_attr_t const *root_da = NULL;
49
52
53int LLVMFuzzerInitialize(int *argc, char ***argv);
54int LLVMFuzzerTestOneInput(const uint8_t *buf, size_t len);
55
56static void exitHandler(void)
57{
59
60 fr_dict_free(&dict, __FILE__);
61
62 if (dl && dl->handle) {
63 dlclose(dl->handle);
64 dl->handle = NULL;
65 }
67
69
70 /*
71 * Ensure our atexit handlers run before any other
72 * atexit handlers registered by third party libraries.
73 */
75}
76
77static inline
78fr_dict_protocol_t *fuzzer_dict_init(void *dl_handle, char const *proto)
79{
80 char buffer[256];
81 fr_dict_protocol_t *our_dl_proto;
82
83 snprintf(buffer, sizeof(buffer), "libfreeradius_%s_dict_protocol", proto);
84
85 our_dl_proto = dlsym(dl_handle, buffer);
86 if (our_dl_proto && our_dl_proto->init && (our_dl_proto->init() < 0)) {
87 fr_perror("fuzzer: Failed initializing library %s", buffer);
88 fr_exit_now(EXIT_FAILURE);
89 }
90
91 return our_dl_proto;
92}
93
94int LLVMFuzzerInitialize(int *argc, char ***argv)
95{
96 char const *lib_dir = getenv("FR_LIBRARY_PATH");
97 char const *proto = getenv("FR_LIBRARY_FUZZ_PROTOCOL");
98 char const *dict_dir = getenv("FR_DICTIONARY_DIR");
99 char const *debug_lvl_str = getenv("FR_DEBUG_LVL");
100 char const *panic_action = getenv("PANIC_ACTION");
101 char const *root_attr = getenv("FR_FUZZER_ROOT_ATTR");
102 char const *p;
103#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
104 char *dict_dir_to_free = NULL;
105 char *lib_dir_to_free = NULL;
106#endif
107
108 if (!argc || !argv || !*argv) return -1; /* shut up clang scan */
109
110 if (debug_lvl_str) {
111 fr_debug_lvl = atoi(debug_lvl_str);
112
114 }
115
116 /*
117 * Setup atexit handlers to free any thread local
118 * memory on exit
119 */
121
122 /*
123 * Initialise the talloc fault handlers.
124 */
126
127 /*
128 * Initialise the error stack _before_ we run any
129 * tests so there's no chance of the memory
130 * appearing as a leak the first time an error
131 * is generated.
132 */
133 fr_strerror_const("fuzz"); /* allocate the pools */
134 fr_strerror_clear(); /* clears the message, leaves the pools */
135
136 /*
137 * Setup our own internal atexit handler
138 */
139 if (atexit(exitHandler)) {
140 fr_perror("fuzzer: Failed to register exit handler: %s", fr_syserror(errno));
141 fr_exit_now(EXIT_FAILURE);
142 }
143
144 /*
145 * Get the name from the binary name of fuzzer_foo
146 */
147 if (!proto) {
148 proto = strrchr((*argv)[0], '_');
149 if (proto) proto++;
150 }
151
152 /*
153 * Look for -D dir
154 *
155 * If found, nuke it from the argument list.
156 */
157 if (!dict_dir) {
158 int i, j;
159
160 for (i = 0; i < *argc - 1; i++) {
161 p = (*argv)[i];
162
163 if ((p[0] == '-') && (p[1] == 'D')) {
164 dict_dir = (*argv)[i + 1];
165
166 for (j = i + 2; j < *argc; i++, j++) {
167 (*argv)[i] = (*argv)[j];
168 }
169
170 *argc -= 2;
171 break;
172 }
173 }
174 }
175
176#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
177 /*
178 * oss-fuzz puts the dictionaries, etc. into subdirectories named after the location of the
179 * binary. So we find the directory of the binary, and append "/dict" or "/lib" to find
180 * dictionaries and libraries.
181 */
182 p = strrchr((*argv)[0], '/');
183 if (p) {
184 if (!dict_dir) {
185 dict_dir = dict_dir_to_free = talloc_asprintf(NULL, "%.*s/dict", (int) (p - (*argv)[0]), (*argv)[0]);
186 if (!dict_dir_to_free) fr_exit_now(EXIT_FAILURE);
187 }
188
189 if (!lib_dir) {
190 lib_dir = lib_dir_to_free = talloc_asprintf(NULL, "%.*s/lib", (int) (p - (*argv)[0]), (*argv)[0]);
191 if (!lib_dir_to_free) fr_exit_now(EXIT_FAILURE);
192 }
193 }
194#endif
195
196 if (!dict_dir) dict_dir = DICTDIR;
197 if (!lib_dir) lib_dir = LIBDIR;
198
199 /*
200 * Set the global search path for all dynamic libraries we load.
201 */
202 if (dl_search_global_path_set(lib_dir) < 0) {
203 fr_perror("fuzzer: Failed setting library path");
204 fr_exit_now(EXIT_FAILURE);
205 }
206
207 /*
208 * When jobs=N is specified the fuzzer spawns worker processes via
209 * a shell. We have removed any -D dictdir argument that were
210 * supplied, so we pass it to our children via the environment.
211 */
212 if (setenv("FR_DICTIONARY_DIR", dict_dir, 1)) {
213 fprintf(stderr, "Failed to set FR_DICTIONARY_DIR env variable\n");
214 fr_exit_now(EXIT_FAILURE);
215 }
216
217 if (!fr_dict_global_ctx_init(NULL, true, dict_dir)) {
218 fr_perror("dict_global");
219 fr_exit_now(EXIT_FAILURE);
220 }
221
223 fr_perror("fuzzer: Failed initializing internal dictionary");
224 fr_exit_now(EXIT_FAILURE);
225 }
226
227 if (!proto) {
228 fr_perror("Failed to find protocol for fuzzer");
229 fr_exit_now(EXIT_FAILURE);
230 }
231
232 if (root_attr) {
233 root_da = fr_dict_attr_by_name(NULL, fr_dict_root(dict), root_attr);
234 if (!root_da) {
235 fr_perror("Failed to find root attribute '%s'", root_attr);
236 fr_exit_now(EXIT_FAILURE);
237 }
238 }
239
240 /*
241 * Disable hostname lookups, so we don't produce spurious DNS
242 * queries, and there's no chance of spurious failures if
243 * it takes a long time to get a response.
244 */
246
247 /*
248 * Search in our symbol space first. We may have been dynamically
249 * or statically linked to the library we're fuzzing...
250 */
251 dl_proto = fuzzer_dict_init(RTLD_DEFAULT, proto);
252
254 if (fr_fault_setup(autofree, panic_action, (*argv)[0]) < 0) {
255 fr_perror("Failed initializing fault handler");
256 fr_exit_now(EXIT_FAILURE);
257 }
258
259 init = true;
260
261#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
262 talloc_free(dict_dir_to_free);
263 talloc_free(lib_dir_to_free);
264#endif
265
266 return 1;
267}
268
269static uint8_t encoded_data[65536];
270
271int LLVMFuzzerTestOneInput(const uint8_t *buf, size_t len)
272{
273 TALLOC_CTX * ctx = talloc_init_const("fuzzer");
274 fr_pair_list_t vps;
275 void *decode_ctx = NULL;
276 void *encode_ctx = NULL;
279
280 fr_pair_list_init(&vps);
281 if (!init) LLVMFuzzerInitialize(NULL, NULL);
282
283 if (tp_decode->test_ctx && (tp_decode->test_ctx(&decode_ctx, NULL, dict, root_da) < 0)) {
284 fr_perror("fuzzer: Failed initializing test point decode_ctx");
285 fr_exit_now(EXIT_FAILURE);
286 }
287
288 if (tp_encode->test_ctx && (tp_encode->test_ctx(&encode_ctx, NULL, dict, root_da) < 0)) {
289 fr_perror("fuzzer: Failed initializing test point encode_ctx");
290 fr_exit_now(EXIT_FAILURE);
291 }
292
293 if (fr_debug_lvl > 3) {
294 FR_PROTO_TRACE("Fuzzer input");
295
296 FR_PROTO_HEX_DUMP(buf, len, "");
297 }
298
299 /*
300 * Decode the input, and print the resulting data if we
301 * decoded it successfully.
302 *
303 * If we have successfully decoded the data, then encode
304 * it again, too.
305 */
306 if (tp_decode->func(ctx, &vps, buf, len, decode_ctx) > 0) {
307 PAIR_LIST_VERIFY_WITH_CTX(ctx, &vps);
308
309 if (fr_debug_lvl > 3) fr_pair_list_debug(stderr, &vps);
310
311 if (do_encode) (void) tp_encode->func(ctx, &vps, encoded_data, sizeof(encoded_data), encode_ctx);
312 }
313
314 talloc_free(decode_ctx);
316 talloc_free(ctx);
317
318 /*
319 * Clear error messages from the run. Clearing these
320 * keeps malloc/free balanced, which helps to avoid the
321 * fuzzers leak heuristics from firing.
322 */
324
325 return 0;
326}
static int const char char buffer[256]
Definition acutest.h:576
int fr_atexit_global_setup(void)
Setup the atexit handler, should be called at the start of a program's execution.
Definition atexit.c:179
int fr_atexit_global_trigger_all(void)
Cause all global free triggers to fire.
Definition atexit.c:305
#define RCSID(id)
Definition build.h:512
static char panic_action[512]
The command to execute when panicking.
Definition debug.c:66
int fr_fault_setup(TALLOC_CTX *ctx, char const *cmd, char const *program)
Registers signal handlers to execute panic_action on fatal signal.
Definition debug.c:1070
void fr_talloc_fault_setup(void)
Register talloc fault handlers.
Definition debug.c:1051
#define fr_exit_now(_x)
Exit without calling atexit() handlers, producing a log message in debug builds.
Definition debug.h:226
fr_dict_protocol_free_t free
free the library
Definition dict.h:485
fr_dict_attr_t const * fr_dict_attr_by_name(fr_dict_attr_err_t *err, fr_dict_attr_t const *parent, char const *attr))
Locate a fr_dict_attr_t by its name.
Definition dict_util.c:3522
int fr_dict_internal_afrom_file(fr_dict_t **out, char const *dict_subdir, char const *dependent))
(Re-)Initialize the special internal dictionary
fr_dict_attr_t const * fr_dict_root(fr_dict_t const *dict)
Return the root attribute of a dictionary.
Definition dict_util.c:2659
fr_dict_protocol_init_t init
initialize the library
Definition dict.h:484
int fr_dict_free(fr_dict_t **dict, char const *dependent)
Decrement the reference count on a previously loaded dictionary.
Definition dict_util.c:4323
fr_dict_gctx_t * fr_dict_global_ctx_init(TALLOC_CTX *ctx, bool free_at_exit, char const *dict_dir))
Initialise the global protocol hashes.
Definition dict_util.c:4707
Protocol-specific callbacks in libfreeradius-PROTOCOL.
Definition dict.h:455
int dl_search_global_path_set(char const *lib_dir)
Set the global library path.
Definition dl.c:783
A dynamic loader.
Definition dl.c:81
void * handle
Handle returned by dlopen.
Definition dl.h:61
Module handle.
Definition dl.h:57
static dl_loader_t * dl_loader
Definition fuzzer.c:42
static TALLOC_CTX * autofree
Definition fuzzer.c:44
fr_test_point_proto_decode_t XX_PROTOCOL_XX_tp_decode_proto
static dl_t * dl
Definition fuzzer.c:41
static bool do_encode
Definition fuzzer.c:45
static fr_dict_t * dict
Definition fuzzer.c:47
static fr_dict_protocol_t * dl_proto
Definition fuzzer.c:43
static bool init
Definition fuzzer.c:40
static fr_dict_protocol_t * fuzzer_dict_init(void *dl_handle, char const *proto)
Definition fuzzer.c:78
int LLVMFuzzerInitialize(int *argc, char ***argv)
Definition fuzzer.c:94
fr_test_point_proto_encode_t XX_PROTOCOL_XX_tp_encode_proto
static void exitHandler(void)
Definition fuzzer.c:56
static uint8_t encoded_data[65536]
Definition fuzzer.c:269
static fr_dict_attr_t const * root_da
Definition fuzzer.c:48
int LLVMFuzzerTestOneInput(const uint8_t *buf, size_t len)
Definition fuzzer.c:271
talloc_free(hp)
bool fr_hostname_lookups
hostname -> IP lookups?
Definition inet.c:52
bool fr_reverse_lookups
IP -> hostname lookups?
Definition inet.c:51
int fr_debug_lvl
Definition log.c:41
unsigned char uint8_t
void fr_pair_list_init(fr_pair_list_t *list)
Initialise a pair list header.
Definition pair.c:46
static fr_internal_encode_ctx_t encode_ctx
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
Definition snprintf.c:689
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
Definition syserror.c:243
static TALLOC_CTX * talloc_init_const(char const *name)
Allocate a top level chunk with a constant name.
Definition talloc.h:127
#define talloc_asprintf
Definition talloc.h:151
#define talloc_autofree_context
The original function is deprecated, so replace it with our version.
Definition talloc.h:55
fr_tp_proto_decode_t func
Decoder for proto layer.
Definition test_point.h:69
fr_tp_proto_encode_t func
Encoder for proto layer.
Definition test_point.h:77
fr_test_point_ctx_alloc_t test_ctx
Allocate a test ctx for the encoder.
Definition test_point.h:76
fr_test_point_ctx_alloc_t test_ctx
Allocate a test ctx for the encoder.
Definition test_point.h:68
Entry point for protocol decoders.
Definition test_point.h:67
Entry point for protocol encoders.
Definition test_point.h:75
int fr_time_start(void)
Initialize the local time.
Definition time.c:150
#define FR_DICTIONARY_INTERNAL_DIR
Definition conf.h:8
void fr_pair_list_debug(FILE *fp, fr_pair_list_t const *list)
Dumps a list to the default logging destination - Useful for calling from debuggers.
Definition pair_print.c:448
#define PAIR_LIST_VERIFY_WITH_CTX(_c, _x)
Definition pair.h:208
#define FR_PROTO_HEX_DUMP(_data, _data_len, _fmt,...)
Definition proto.h:42
#define FR_PROTO_TRACE(_fmt,...)
Definition proto.h:41
void fr_perror(char const *fmt,...)
Print the current error to stderr with a prefix.
Definition strerror.c:737
void fr_strerror_clear(void)
Clears all pending messages from the talloc pools.
Definition strerror.c:581
#define fr_strerror_const(_msg)
Definition strerror.h:223