Coverage Report

Created: 2026-06-30 07:16

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/freeradius-server/src/fuzzer/common.c
Line
Count
Source
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: 4ebec1b64138d9666671808c30937bcb5ab1f6ad $
19
 *
20
 * @file src/fuzzer/common.c
21
 * @brief Common initialization for fuzzers
22
 *
23
 * @copyright 2026 Network RADIUS SAS (legal@networkradius.com)
24
 */
25
RCSID("$Id: 4ebec1b64138d9666671808c30937bcb5ab1f6ad $")
26
27
#include <freeradius-devel/fuzzer/common.h>
28
29
TALLOC_CTX    *autofree = NULL;
30
31
fr_dict_t   *dict = NULL;
32
fr_dict_attr_t const  *root_da = NULL;
33
34
fr_dict_protocol_t  *dl_proto = NULL;
35
36
37
static void exitHandler(void)
38
66
{
39
66
  if (dl_proto && dl_proto->free) dl_proto->free();
40
41
66
  fr_dict_free(&dict, __FILE__);
42
43
66
  talloc_free(autofree);
44
45
  /*
46
   *  Ensure our atexit handlers run before any other
47
   *  atexit handlers registered by third party libraries.
48
   */
49
66
  fr_atexit_global_trigger_all();
50
66
}
51
52
/**  Perform all bootstrapping for the fuzzer.
53
 *
54
 */
55
int fuzzer_common_init(int *argc, char ***argv, bool load_proto)
56
66
{
57
66
  char const    *lib_dir    = getenv("FR_LIBRARY_PATH");
58
66
  char const    *dict_dir = getenv("FR_DICTIONARY_DIR");
59
66
  char const    *debug_lvl_str  = getenv("FR_DEBUG_LVL");
60
66
  char const    *panic_action = getenv("PANIC_ACTION");
61
62
66
  char const    *proto      = getenv("FR_LIBRARY_FUZZ_PROTOCOL");
63
66
  char const    *root_attr  = getenv("FR_FUZZER_ROOT_ATTR");
64
65
66
  char      *p, buffer[256];
66
67
66
  if (!argc || !argv || !*argv) return -1; /* shut up clang scan */
68
69
66
  fr_time_start();
70
71
66
  if (debug_lvl_str) {
72
0
    fr_debug_lvl = atoi(debug_lvl_str);
73
0
  }
74
75
66
  autofree = talloc_autofree_context();
76
66
  if (fr_fault_setup(autofree, panic_action, (*argv)[0]) < 0) {
77
0
    fr_perror("fuzzer: Failed to register fault handler: %s", fr_syserror(errno));
78
0
    return -1;
79
0
  }
80
81
  /*
82
   *  Setup atexit handlers to free any thread local
83
   *  memory on exit
84
   */
85
66
  fr_atexit_global_setup();
86
87
  /*
88
   *  Initialise the talloc fault handlers.
89
   */
90
66
  fr_talloc_fault_setup();
91
92
  /*
93
   *  Initialise the error stack _before_ we run any
94
   *  tests so there's no chance of the memory
95
   *  appearing as a leak the first time an error
96
   *  is generated.
97
   */
98
66
  fr_strerror_const("fuzz"); /* allocate the pools */
99
66
  fr_strerror_clear(); /* clears the message, leaves the pools */
100
101
  /*
102
   *  Setup our own internal atexit handler
103
   */
104
66
  if (atexit(exitHandler)) {
105
0
    fr_perror("fuzzer: Failed to register exit handler: %s", fr_syserror(errno));
106
0
    return -1;
107
0
  }
108
109
  /*
110
   *  Disable hostname lookups, so we don't produce spurious DNS
111
   *  queries, and there's no chance of spurious failures if
112
   *  it takes a long time to get a response.
113
   */
114
66
  fr_hostname_lookups = fr_reverse_lookups = false;
115
116
  /*
117
   *  Look for -D dir
118
   *
119
   *  If found, nuke it from the argument list.
120
   */
121
66
  if (!dict_dir) {
122
28
    int i, j;
123
124
140
    for (i = 0; i < *argc - 1; i++) {
125
112
      p = (*argv)[i];
126
127
112
      if ((p[0] == '-') && (p[1] == 'D')) {
128
0
        dict_dir = (*argv)[i + 1];
129
130
0
        for (j = i + 2; j < *argc; i++, j++) {
131
0
          (*argv)[i] = (*argv)[j];
132
0
        }
133
134
0
        *argc -= 2;
135
0
        break;
136
0
      }
137
112
    }
138
28
  }
139
140
66
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
141
  /*
142
   *  oss-fuzz puts the dictionaries, etc. into subdirectories named after the location of the
143
   *  binary.  So we find the directory of the binary, and append "/dict" or "/lib" to find
144
   *  dictionaries and libraries.
145
   */
146
66
  p = strrchr((*argv)[0], '/');
147
66
  if (p) {
148
66
    if (!dict_dir) {
149
28
      dict_dir = talloc_asprintf(autofree, "%.*s/dict", (int) (p - (*argv)[0]), (*argv)[0]);
150
28
      if (!dict_dir) return -1;
151
28
    }
152
153
66
    if (!lib_dir) {
154
66
      lib_dir = talloc_asprintf(autofree, "%.*s/lib", (int) (p - (*argv)[0]), (*argv)[0]);
155
66
      if (!lib_dir) return -1;
156
66
    }
157
66
  }
158
66
#endif
159
160
66
  if (!dict_dir) dict_dir = DICTDIR;
161
66
  if (!lib_dir) lib_dir = LIBDIR;
162
163
  /*
164
   *  Set the global search path for all dynamic libraries we load.
165
   */
166
66
  if (dl_search_global_path_set(lib_dir) < 0) {
167
0
    fr_perror("fuzzer: Failed setting library path");
168
0
    return -1;
169
0
  }
170
171
  /*
172
   *  When jobs=N is specified the fuzzer spawns worker processes via
173
   *  a shell. We have removed any -D dictdir argument that were
174
   *  supplied, so we pass it to our children via the environment.
175
   */
176
66
  if (setenv("FR_DICTIONARY_DIR", dict_dir, 1)) {
177
0
    fprintf(stderr, "Failed to set FR_DICTIONARY_DIR env variable\n");
178
0
    return -1;
179
0
  }
180
181
66
  if (!fr_dict_global_ctx_init(NULL, true, dict_dir)) {
182
0
    fr_perror("fuzzer: Failed initializing global dictionary context");
183
0
    return -1;
184
0
  }
185
186
66
  if (fr_dict_internal_afrom_file(&dict, FR_DICTIONARY_INTERNAL_DIR, __FILE__) < 0) {
187
0
    fr_perror("fuzzer: Failed initializing internal dictionary");
188
0
    return -1;
189
0
  }
190
191
66
  if (!load_proto) return 0;
192
193
  /*
194
   *  Get the name from the binary name of fuzzer_foo
195
   */
196
62
  if (!proto) {
197
22
    proto = strrchr((*argv)[0], '_');
198
22
    if (proto) proto++;
199
22
  }
200
201
62
  if (!proto) {
202
0
    fr_perror("Failed to find protocol for fuzzer");
203
0
    return -1;
204
0
  }
205
206
62
  if (root_attr) {
207
0
    root_da = fr_dict_attr_by_name(NULL, fr_dict_root(dict), root_attr);
208
0
    if (!root_da) {
209
0
      fr_perror("Failed to find root attribute '%s'", root_attr);
210
0
      return -1;
211
0
    }
212
0
  }
213
214
  /*
215
   *  Search in our symbol space first.  We may have been dynamically
216
   *  or statically linked to the library we're fuzzing...
217
   */
218
62
  snprintf(buffer, sizeof(buffer), "libfreeradius_%s_dict_protocol", proto);
219
220
62
  dl_proto = dlsym(RTLD_DEFAULT, buffer);
221
62
  if (!dl_proto) return 0;
222
223
55
  if (dl_proto->init && (dl_proto->init() < 0)) {
224
0
    fr_perror("fuzzer: Failed initializing library %s", buffer);
225
0
    return -1;
226
0
  }
227
228
55
  return 0;
229
55
}