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/fuzzer_bfd.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: a94361a62c0da22ceb13848e4bb081e5d4923ce2 $
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: a94361a62c0da22ceb13848e4bb081e5d4923ce2 $")
26
27
#include <freeradius-devel/fuzzer/common.h>
28
29
/*
30
 *  Run from the source directory via:
31
 *
32
 *  ./build/make/jlibtool --mode=execute ./build/bin/local/fuzzer_radius -D share/dictionary /path/to/corpus/directory/
33
 */
34
35
/*
36
 *  @todo - re-enable this later.
37
 */
38
static bool     do_encode = false;
39
40
extern fr_test_point_proto_decode_t bfd_tp_decode_proto;
41
extern fr_test_point_proto_encode_t bfd_tp_encode_proto;
42
43
int LLVMFuzzerInitialize(int *argc, char ***argv);
44
int LLVMFuzzerTestOneInput(const uint8_t *buf, size_t len);
45
46
int LLVMFuzzerInitialize(int *argc, char ***argv)
47
40
{
48
40
  if (fuzzer_common_init(argc, argv, true) < 0) fr_exit_now(EXIT_FAILURE);
49
50
40
  return 1;
51
40
}
52
53
static uint8_t encoded_data[65536];
54
55
int LLVMFuzzerTestOneInput(const uint8_t *buf, size_t len)
56
23.8k
{
57
23.8k
  TALLOC_CTX *   ctx = talloc_init_const("fuzzer");
58
23.8k
  ssize_t slen;
59
23.8k
  fr_pair_list_t vps;
60
23.8k
  void *decode_ctx = NULL;
61
23.8k
  void *encode_ctx = NULL;
62
23.8k
  fr_test_point_proto_decode_t *tp_decode = &bfd_tp_decode_proto;
63
23.8k
  fr_test_point_proto_encode_t *tp_encode = &bfd_tp_encode_proto;
64
65
23.8k
  fr_pair_list_init(&vps);
66
23.8k
  if (!dict) LLVMFuzzerInitialize(NULL, NULL);
67
68
23.8k
  if (tp_decode->test_ctx && (tp_decode->test_ctx(&decode_ctx, NULL, dict, root_da) < 0)) {
69
0
    fr_perror("fuzzer: Failed initializing test point decode_ctx");
70
0
    fr_exit_now(EXIT_FAILURE);
71
0
  }
72
73
23.8k
  if (do_encode) {
74
0
    if (tp_encode->test_ctx && (tp_encode->test_ctx(&encode_ctx, NULL, dict, root_da) < 0)) {
75
0
      fr_perror("fuzzer: Failed initializing test point encode_ctx");
76
0
      fr_exit_now(EXIT_FAILURE);
77
0
    }
78
0
  }
79
80
23.8k
  if (fr_debug_lvl > 3) {
81
0
    FR_PROTO_TRACE("Fuzzer bfd input");
82
83
0
    FR_PROTO_HEX_DUMP(buf, len, "");
84
0
  }
85
86
  /*
87
   *  Decode the input, and print the resulting data if we
88
   *  decoded it successfully.
89
   *
90
   *  If we have successfully decoded the data, then encode
91
   *  it again, too.
92
   */
93
23.8k
  if (tp_decode->func(ctx, &vps, buf, len, decode_ctx) < 0) goto cleanup;
94
95
17.3k
  PAIR_LIST_VERIFY_WITH_CTX(ctx, &vps);
96
97
17.3k
  if (fr_debug_lvl > 3) fr_pair_list_debug(stderr, &vps);
98
99
17.3k
  if (!do_encode) goto cleanup;
100
101
0
  slen = tp_encode->func(ctx, &vps, encoded_data, sizeof(encoded_data), encode_ctx);
102
0
  if (!slen) goto cleanup;
103
104
0
  if (slen < 0) {
105
0
#if 1
106
    /*
107
     *  We would like to fail on encode, but right now some protocols will decode packets that
108
     *  they cannot later encode.
109
     *
110
     *  In addition, the decoder "canonicalizes" the value-pairs, by merging the same
111
     *  attributes into one output pair list.  But the encoders don't always split the pair list when encoding.
112
     */
113
0
    goto cleanup;
114
#else
115
    fr_debug_lvl = 4;
116
    FR_PROTO_TRACE("Input data for bfd");
117
    FR_PROTO_HEX_DUMP(buf, len, "");
118
119
    fr_pair_list_debug(stderr, &vps);
120
    fr_perror("fuzzer_bfd: Failed encoding data");
121
    fr_exit_now(EXIT_FAILURE);
122
#endif
123
0
  }
124
125
  /*
126
   *  Round-trip: if the encoder produced a packet, decode it again into a fresh pair list. The
127
   *  result is discarded - the point is that the encoder's output must be something the decoder
128
   *  accepts without crashing.
129
   *
130
   *  We do this by reinitializing the ctx and decode_ctx.
131
   */
132
0
  talloc_free(decode_ctx);
133
0
  talloc_free(ctx);
134
0
  ctx = talloc_init_const("fuzzer-roundtrip");
135
0
  fr_pair_list_init(&vps);
136
137
0
  if (tp_decode->test_ctx && (tp_decode->test_ctx(&decode_ctx, NULL, dict, root_da) < 0)) {
138
0
    fr_perror("fuzzer_bfd: Failed re-initializing test point decode_ctx");
139
0
    fr_exit_now(EXIT_FAILURE);
140
0
  }
141
142
0
  (void) tp_decode->func(ctx, &vps, encoded_data, (size_t) slen, decode_ctx);
143
144
23.8k
cleanup:
145
23.8k
  talloc_free(decode_ctx);
146
23.8k
  talloc_free(encode_ctx);
147
23.8k
  talloc_free(ctx);
148
149
  /*
150
   *  Clear error messages from the run.  Clearing these
151
   *  keeps malloc/free balanced, which helps to avoid the
152
   *  fuzzers leak heuristics from firing.
153
   */
154
23.8k
  fr_strerror_clear();
155
156
23.8k
  return 0;
157
0
}