Coverage Report

Created: 2024-08-28 06:17

/src/freeradius-server/src/bin/fuzzer_bfd.c
Line
Count
Source (jump to first uncovered line)
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: 0baa119684f866010d15ae1a0c487a5d6f263c2f $
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
 */
25
RCSID("$Id: 0baa119684f866010d15ae1a0c487a5d6f263c2f $")
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/atexit.h>
31
#include <freeradius-devel/util/syserror.h>
32
#include <freeradius-devel/util/strerror.h>
33
#include <freeradius-devel/io/test_point.h>
34
35
/*
36
 *  Run from the source directory via:
37
 *
38
 *  ./build/make/jlibtool --mode=execute ./build/bin/local/fuzzer_radius -D share/dictionary /path/to/corpus/directory/
39
 */
40
41
static bool     init = false;
42
static dl_t     *dl = NULL;
43
static dl_loader_t    *dl_loader;
44
static fr_dict_protocol_t *dl_proto;
45
46
static fr_dict_t    *dict = NULL;
47
48
extern fr_test_point_proto_decode_t bfd_tp_decode_proto;
49
50
int LLVMFuzzerInitialize(int *argc, char ***argv);
51
int LLVMFuzzerTestOneInput(const uint8_t *buf, size_t len);
52
53
static void exitHandler(void)
54
5
{
55
5
  if (dl_proto && dl_proto->free) dl_proto->free();
56
57
5
  fr_dict_free(&dict, __FILE__);
58
59
5
  if (dl && dl->handle) {
60
0
    dlclose(dl->handle);
61
0
    dl->handle = NULL;
62
0
  }
63
5
  talloc_free(dl_loader);
64
65
  /*
66
   *  Ensure our atexit handlers run before any other
67
   *  atexit handlers registered by third party libraries.
68
   */
69
5
  fr_atexit_global_trigger_all();
70
5
}
71
72
static inline
73
fr_dict_protocol_t *fuzzer_dict_init(void *dl_handle, char const *proto)
74
2
{
75
2
  char      buffer[256];
76
2
  fr_dict_protocol_t  *our_dl_proto;
77
78
2
  snprintf(buffer, sizeof(buffer), "libfreeradius_%s_dict_protocol", proto);
79
80
2
  our_dl_proto = dlsym(dl_handle, buffer);
81
2
  if (our_dl_proto && our_dl_proto->init() && (our_dl_proto->init() < 0)) {
82
0
    fr_perror("fuzzer: Failed initializing library %s", buffer);
83
0
    fr_exit_now(EXIT_FAILURE);
84
0
  }
85
86
2
  return our_dl_proto;
87
2
}
88
89
int LLVMFuzzerInitialize(int *argc, char ***argv)
90
38
{
91
38
  char const    *lib_dir    = getenv("FR_LIBRARY_PATH");
92
38
  char const    *proto      = getenv("FR_LIBRARY_FUZZ_PROTOCOL");
93
38
  char const    *dict_dir = getenv("FR_DICTIONARY_DIR");
94
38
  char const    *debug_lvl_str  = getenv("FR_DEBUG_LVL");
95
38
  char const    *p;
96
38
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
97
38
  char      *dict_dir_to_free = NULL;
98
38
  char      *lib_dir_to_free = NULL;
99
38
#endif
100
101
38
  if (!argc || !argv || !*argv) return -1; /* shut up clang scan */
102
103
38
  if (debug_lvl_str) fr_debug_lvl = atoi(debug_lvl_str);
104
105
  /*
106
   *  Setup atexit handlers to free any thread local
107
   *  memory on exit
108
   */
109
38
  fr_atexit_global_setup();
110
111
  /*
112
   *  Initialise the talloc fault handlers.
113
   */
114
38
  fr_talloc_fault_setup();
115
116
  /*
117
   *  Initialise the error stack _before_ we run any
118
   *  tests so there's no chance of the memory
119
   *  appearing as a leak the first time an error
120
   *  is generated.
121
   */
122
38
  fr_strerror_const("fuzz"); /* allocate the pools */
123
38
  fr_strerror_clear(); /* clears the message, leaves the pools */
124
125
  /*
126
   *  Setup our own internal atexit handler
127
   */
128
38
  if (atexit(exitHandler)) {
129
0
    fr_perror("fuzzer: Failed to register exit handler: %s", fr_syserror(errno));
130
0
    fr_exit_now(EXIT_FAILURE);
131
0
  }
132
133
  /*
134
   *  Get the name from the binary name of fuzzer_foo
135
   */
136
38
  if (!proto) {
137
38
    proto = strrchr((*argv)[0], '_');
138
38
    if (proto) proto++;
139
38
  }
140
141
  /*
142
   *  Look for -D dir
143
   *
144
   *  If found, nuke it from the argument list.
145
   */
146
38
  if (!dict_dir) {
147
18
    int i, j;
148
149
90
    for (i = 0; i < *argc - 1; i++) {
150
72
      p = (*argv)[i];
151
152
72
      if ((p[0] == '-') && (p[1] == 'D')) {
153
0
        dict_dir = (*argv)[i + 1];
154
155
0
        for (j = i + 2; j < *argc; i++, j++) {
156
0
          (*argv)[i] = (*argv)[j];
157
0
        }
158
159
0
        *argc -= 2;
160
0
        break;
161
0
      }
162
72
    }
163
18
  }
164
165
38
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
166
  /*
167
   *  oss-fuzz puts the dictionaries, etc. into subdirectories named after the location of the
168
   *  binary.  So we find the directory of the binary, and append "/dict" or "/lib" to find
169
   *  dictionaries and libraries.
170
   */
171
38
  p = strrchr((*argv)[0], '/');
172
38
  if (p) {
173
38
    if (!dict_dir) {
174
18
      dict_dir = dict_dir_to_free = talloc_asprintf(NULL, "%.*s/dict", (int) (p - (*argv)[0]), (*argv)[0]);
175
18
      if (!dict_dir_to_free) fr_exit_now(EXIT_FAILURE);
176
18
    }
177
178
38
    if (!lib_dir) {
179
38
      lib_dir = lib_dir_to_free = talloc_asprintf(NULL, "%.*s/lib", (int) (p - (*argv)[0]), (*argv)[0]);
180
38
      if (!lib_dir_to_free) fr_exit_now(EXIT_FAILURE);
181
38
    }
182
38
  }
183
38
#endif
184
185
38
  if (!dict_dir) dict_dir = DICTDIR;
186
38
  if (!lib_dir) lib_dir = LIBDIR;
187
188
  /*
189
   *  Set the global search path for all dynamic libraries we load.
190
   */
191
38
  if (dl_search_global_path_set(lib_dir) < 0) {
192
0
    fr_perror("fuzzer: Failed setting library path");
193
0
    fr_exit_now(EXIT_FAILURE);
194
0
  }
195
196
  /*
197
   *  When jobs=N is specified the fuzzer spawns worker processes via
198
   *  a shell. We have removed any -D dictdir argument that were
199
   *  supplied, so we pass it to our children via the environment.
200
   */
201
38
  if (setenv("FR_DICTIONARY_DIR", dict_dir, 1)) {
202
0
    fprintf(stderr, "Failed to set FR_DICTIONARY_DIR env variable\n");
203
0
    fr_exit_now(EXIT_FAILURE);
204
0
  }
205
206
38
  if (!fr_dict_global_ctx_init(NULL, true, dict_dir)) {
207
0
    fr_perror("dict_global");
208
0
    fr_exit_now(EXIT_FAILURE);
209
0
  }
210
211
38
  if (fr_dict_internal_afrom_file(&dict, FR_DICTIONARY_INTERNAL_DIR, __FILE__) < 0) {
212
0
    fr_perror("fuzzer: Failed initializing internal dictionary");
213
0
    fr_exit_now(EXIT_FAILURE);
214
0
  }
215
216
38
  if (!proto) {
217
0
    fr_perror("Failed to find protocol for fuzzer");
218
0
    fr_exit_now(EXIT_FAILURE);
219
0
  }
220
221
  /*
222
   *  Disable hostname lookups, so we don't produce spurious DNS
223
   *  queries, and there's no chance of spurious failures if
224
   *  it takes a long time to get a response.
225
   */
226
38
  fr_hostname_lookups = fr_reverse_lookups = false;
227
228
  /*
229
   *  Search in our symbol space first.  We may have been dynamically
230
   *  or statically linked to the library we're fuzzing...
231
   */
232
38
  dl_proto = fuzzer_dict_init(RTLD_DEFAULT, proto);
233
234
38
  init = true;
235
236
38
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
237
38
  talloc_free(dict_dir_to_free);
238
38
  talloc_free(lib_dir_to_free);
239
38
#endif
240
241
38
  return 1;
242
38
}
243
244
int LLVMFuzzerTestOneInput(const uint8_t *buf, size_t len)
245
17.7k
{
246
17.7k
  TALLOC_CTX *   ctx = talloc_init_const("fuzzer");
247
17.7k
  fr_pair_list_t vps;
248
17.7k
  void *decode_ctx = NULL;
249
17.7k
  fr_test_point_proto_decode_t *tp = &bfd_tp_decode_proto;
250
251
17.7k
  fr_pair_list_init(&vps);
252
17.7k
  if (!init) LLVMFuzzerInitialize(NULL, NULL);
253
254
17.7k
  if (tp->test_ctx && (tp->test_ctx(&decode_ctx, NULL) < 0)) {
255
0
    fr_perror("fuzzer: Failed initializing test point decode_ctx");
256
0
    fr_exit_now(EXIT_FAILURE);
257
0
  }
258
259
17.7k
  tp->func(ctx, &vps, buf, len, decode_ctx);
260
17.7k
  if (fr_debug_lvl > 3) fr_pair_list_debug(&vps);
261
262
17.7k
  talloc_free(decode_ctx);
263
17.7k
  talloc_free(ctx);
264
265
  /*
266
   *  Clear error messages from the run.  Clearing these
267
   *  keeps malloc/free balanced, which helps to avoid the
268
   *  fuzzers leak heuristics from firing.
269
   */
270
17.7k
  fr_strerror_clear();
271
272
17.7k
  return 0;
273
17.7k
}