The FreeRADIUS server $Id: f3670dba8951ca10eb4948feb3dc3db9423a334f $
Loading...
Searching...
No Matches
fuzzer_xlat.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
13/**
14 * @file src/fuzzer/fuzzer_xlat.c
15 * @brief Fuzz the xlat tokenize -> resolve -> eval pipeline.
16 *
17 * Drives the three public xlat tokenisers (xlat_tokenize,
18 * xlat_tokenize_expression, xlat_tokenize_condition) and, on success,
19 * follows through xlat_resolve() and xlat_aeval_compiled() against a
20 * synthetic request built from the test dictionary. This exercises:
21 *
22 * src/lib/unlang/xlat_tokenize.c
23 * src/lib/unlang/xlat_expr.c
24 * src/lib/unlang/xlat_eval.c
25 * src/lib/unlang/xlat_builtin.c
26 *
27 * All of which are at 0% coverage under the existing protocol-decoder
28 * fuzzers despite being on the network-attacker-reachable path: xlat
29 * expansions interpolate attribute values that originate from RADIUS,
30 * DHCP, DNS etc. packets at request time.
31 *
32 * Input layout:
33 * byte[0] - low 2 bits select the tokeniser variant
34 * byte[1..] - the xlat expression text (not NUL-terminated)
35 */
36RCSID("$Id: fd06ebec54f8385f3879b36b5fb9d4830988f2cb $")
37
38#include <freeradius-devel/fuzzer/common.h>
39#include <freeradius-devel/server/base.h>
40#include <freeradius-devel/server/request.h>
41#include <freeradius-devel/server/tmpl.h>
42#include <freeradius-devel/unlang/base.h>
43#include <freeradius-devel/unlang/xlat.h>
44
45int LLVMFuzzerInitialize(int *argc, char ***argv);
46int LLVMFuzzerTestOneInput(const uint8_t *buf, size_t len);
47
48int LLVMFuzzerInitialize(int *argc, char ***argv)
49{
50 if (dict) return 0;
51
52 if (fuzzer_common_init(argc, argv, false) < 0) fr_exit_now(EXIT_FAILURE);
53
55 error:
56 fr_perror("fuzzer_xlat");
57 fr_exit_now(EXIT_FAILURE);
58 }
59
60 if (tmpl_global_init() < 0) goto error;
61
62 if (request_global_init() < 0) goto error;
63
64 /*
65 * Bootstraps xlat_func tree, registers builtins, prepares
66 * the unlang interpreter. Required before xlat_tokenize().
67 */
68 if (unlang_global_init() < 0) goto error;
69
70 return 0;
71}
72
73/*
74 * Poison gutters either side of the fmt buffer so ASan flags any
75 * path inside xlat_tokenize* / xlat_resolve that walks past the
76 * declared sbuff bounds. __asan_poison_memory_region rounds to
77 * 8-byte granules, so reads more than ~7 bytes past either end will
78 * be reported; tight over-reads may slip through but the pattern
79 * mirrors fuzzer_value.c and src/bin/unit_test_attribute.c.
80 */
81#define POISON_START 64
82#define POISON_END 64
83
84int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
85{
86 TALLOC_CTX *ctx;
87 xlat_exp_head_t *head = NULL;
88 fr_sbuff_t sbuff;
89 tmpl_rules_t t_rules;
90 uint8_t mode;
91 uint8_t *raw_fmt = NULL;
92 char *fmt = NULL;
93 size_t fmt_len;
94 fr_slen_t slen;
95
96 if (!dict) return 0;
97 if (size < 2) return 0;
98 if (size > 4096) return 0; /* keep iterations fast */
99
100 mode = data[0] & 0x03;
101 fmt_len = size - 1;
102
103 ctx = talloc_init_const("fuzzer_xlat");
104 if (!ctx) return 0;
105
106 /*
107 * Tokenisers consume an sbuff with an explicit length, so we
108 * deliberately do NOT NUL-terminate. Any code path that does
109 * strlen/strchr/memchr on the underlying buffer (rather than
110 * respecting the sbuff end) will read into the poisoned gutter
111 * and trip ASan, which is the bug we want to surface.
112 */
113 raw_fmt = talloc_array(ctx, uint8_t, POISON_START + fmt_len + POISON_END);
114 if (!raw_fmt) goto done;
115 fmt = (char *)(raw_fmt + POISON_START);
116 if (fmt_len) memcpy(fmt, data + 1, fmt_len);
119
120 /*
121 * Tighten tmpl_rules: refuse unresolved attribute references
122 * at tokenize-time so we never reach eval with a half-resolved
123 * tree (which trips the per-node tmpl_needs_resolving assert
124 * in xlat_frame_eval, see xlat_eval.c:1475).
125 */
126 t_rules = (tmpl_rules_t) {
128 .dict_def = dict,
129 .list_def = request_attr_request,
130 .allow_unresolved = false,
131 .allow_unknown = false,
132 .allow_wildcard = true,
133 },
134 };
135
136 sbuff = FR_SBUFF_IN(fmt, fmt_len);
137
138 switch (mode) {
139 case 0:
140 slen = xlat_tokenize(ctx, &head, &sbuff, NULL, &t_rules);
141 break;
142 case 1:
143 slen = xlat_tokenize_expression(ctx, &head, &sbuff, NULL, &t_rules);
144 break;
145 case 2:
146 slen = xlat_tokenize_condition(ctx, &head, &sbuff, NULL, &t_rules);
147 break;
148 default:
149 /*
150 * Argv-style takes an arg parser table; pass NULL
151 * args - the tokeniser tolerates this by treating
152 * every arg as STRING/required.
153 */
154 slen = xlat_tokenize_argv(ctx, &head, &sbuff, NULL, NULL, &t_rules, false);
155 break;
156 }
157
158 if (slen <= 0 || !head) goto done;
159
160 /*
161 * Resolve unknown function / attribute references. The
162 * resolve pass is itself a meaningful target - it walks
163 * the whole AST. Tolerant of failure: many fuzzer inputs
164 * will leave dangling references that won't resolve, but
165 * the walk still exercises code.
166 */
167 {
168 xlat_res_rules_t const xr_rules = {
170 .allow_unresolved = false,
171 };
172 (void) xlat_resolve(head, &xr_rules);
173 }
174
175 /*
176 * xlat_print round-trip - exercises the unparse path
177 * (xlat_tokenize.c) and validates the tree is well-formed
178 * enough to be serialised back to text.
179 *
180 * We deliberately do NOT call xlat_aeval_compiled / xlat_eval
181 * here: eval requires invariants that are normally established
182 * by xlat_compile() / xlat_purify() (e.g. quote != T_BARE_WORD,
183 * all per-node tmpls fully resolved). Fuzzer-generated trees
184 * skip those passes and trip dev-asserts in xlat_frame_eval.
185 * Tokenize + resolve already cover the bulk of the previously
186 * 0%-covered xlat code.
187 */
188 {
189 fr_sbuff_t print_sb;
190 char print_buf[1024];
191 print_sb = FR_SBUFF_OUT(print_buf, sizeof(print_buf));
192 (void) xlat_print(&print_sb, head, NULL);
193 }
194
195done:
196 /*
197 * Unpoison before talloc_free walks the chunk headers.
198 */
199 if (raw_fmt) {
201 }
202
203 talloc_free(ctx);
205 return 0;
206}
static int const char * fmt
Definition acutest.h:573
#define RCSID(id)
Definition build.h:512
fr_dict_t * dict
Definition common.c:31
int fuzzer_common_init(int *argc, char ***argv, bool load_proto)
Perform all bootstrapping for the fuzzer.
Definition common.c:55
#define fr_exit_now(_x)
Exit without calling atexit() handlers, producing a log message in debug builds.
Definition debug.h:226
#define POISON_START
Definition fuzzer_xlat.c:81
#define POISON_END
Definition fuzzer_xlat.c:82
int LLVMFuzzerInitialize(int *argc, char ***argv)
Definition fuzzer_xlat.c:48
int LLVMFuzzerTestOneInput(const uint8_t *buf, size_t len)
Definition fuzzer_xlat.c:84
talloc_free(hp)
int unlang_global_init(void)
Definition base.c:158
#define ASAN_POISON_MEMORY_REGION(_start, _size)
Definition lsan.h:62
#define ASAN_UNPOISON_MEMORY_REGION(_start, _size)
Definition lsan.h:63
unsigned char uint8_t
ssize_t fr_slen_t
static bool done
Definition radclient.c:80
fr_dict_attr_t const * request_attr_request
Definition request.c:43
int request_global_init(void)
Definition request.c:596
#define FR_SBUFF_IN(_start, _len_or_end)
#define FR_SBUFF_OUT(_start, _len_or_end)
int tmpl_global_init(void)
Definition tmpl_eval.c:1383
fr_dict_t const * dict_def
Alternative default dictionary to use if vpt->rules->dict_def is NULL.
Definition tmpl.h:369
tmpl_attr_rules_t attr
Rules/data for parsing attribute references.
Definition tmpl.h:339
struct tmpl_res_rules_s tmpl_res_rules_t
Definition tmpl.h:237
struct tmpl_rules_s tmpl_rules_t
Definition tmpl.h:233
struct tmpl_attr_rules_s tmpl_attr_rules_t
Definition tmpl.h:234
Optional arguments passed to vp_tmpl functions.
Definition tmpl.h:336
fr_dict_t const * dict_def
Default dictionary to use with unqualified attribute references.
Definition tmpl.h:273
static TALLOC_CTX * talloc_init_const(char const *name)
Allocate a top level chunk with a constant name.
Definition talloc.h:127
tmpl_res_rules_t const * tr_rules
tmpl resolution rules.
Definition xlat.h:165
fr_slen_t xlat_tokenize_condition(TALLOC_CTX *ctx, xlat_exp_head_t **head, fr_sbuff_t *in, fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules))
Definition xlat_expr.c:3191
fr_slen_t xlat_print(fr_sbuff_t *in, xlat_exp_head_t const *node, fr_sbuff_escape_rules_t const *e_rules)
Reconstitute an xlat expression from its constituent nodes.
fr_slen_t xlat_tokenize(TALLOC_CTX *ctx, xlat_exp_head_t **head, fr_sbuff_t *in, fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules)
Tokenize an xlat expansion.
static fr_slen_t head
Definition xlat.h:420
int xlat_resolve(xlat_exp_head_t *head, xlat_res_rules_t const *xr_rules)
Walk over an xlat tree recursively, resolving any unresolved functions or references.
fr_slen_t xlat_tokenize_argv(TALLOC_CTX *ctx, xlat_exp_head_t **head, fr_sbuff_t *in, xlat_arg_parser_t const *xlat_args, fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules, bool spaces))
Tokenize an xlat expansion into a series of XLAT_TYPE_CHILD arguments.
fr_slen_t xlat_tokenize_expression(TALLOC_CTX *ctx, xlat_exp_head_t **head, fr_sbuff_t *in, fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules))
Definition xlat_expr.c:3163
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
int fr_check_lib_magic(uint64_t magic)
Check if the application linking to the library has the correct magic number.
Definition version.c:40
#define RADIUSD_MAGIC_NUMBER
Definition version.h:81
static fr_slen_t data
Definition value.h:1340