s3: smbd: Cleanup. smb2_file_rename_information() can never have a @GMT path in the...
[Samba.git] / lib / fuzzing / fuzz_ndr_X.c
bloba3fb984451f12759ee3c02a75d51f37bc7904312
1 /*
2 Unix SMB/CIFS implementation.
3 Fuzzer for pidl-generated NDR pipes.
4 Copyright (C) Andrew Tridgell 2003
5 Copyright (C) Jelmer Vernooij 2006
6 Copyright (C) Andrew Bartlett 2019
7 Copyright (C) Catalyst.NET Ltd 2019
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 3 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include "includes.h"
24 #include "system/filesys.h"
25 #include "system/locale.h"
26 #include "librpc/ndr/libndr.h"
27 #include "librpc/gen_ndr/ndr_dcerpc.h"
28 #include "util/byteorder.h"
29 #include "fuzzing/fuzzing.h"
31 extern const struct ndr_interface_table FUZZ_PIPE_TABLE;
33 #define FLAG_NDR64 4
35 enum {
36 TYPE_STRUCT = 0,
37 TYPE_IN,
38 TYPE_OUT
42 * header design (little endian):
44 * struct {
45 * uint16_t flags;
46 * uint16_t function_or_struct_no;
47 * };
51 * We want an even number here to ensure 4-byte alignment later
52 * not just for efficieny but because the fuzzers are known to guess
53 * that numbers will be 4-byte aligned
55 #define HEADER_SIZE 4
57 #define INVALID_FLAGS (~(FLAG_NDR64 | 3))
59 static const struct ndr_interface_call *find_function(
60 const struct ndr_interface_table *p,
61 unsigned int function_no)
63 if (function_no >= p->num_calls) {
64 return NULL;
66 return &p->calls[function_no];
70 * Get a public structure by number and return it as if it were
71 * a function.
73 static const struct ndr_interface_call *find_struct(
74 const struct ndr_interface_table *p,
75 unsigned int struct_no,
76 struct ndr_interface_call *out_buffer)
78 const struct ndr_interface_public_struct *s = NULL;
80 if (struct_no >= p->num_public_structs) {
81 return NULL;
84 s = &p->public_structs[struct_no];
86 *out_buffer = (struct ndr_interface_call) {
87 .name = s->name,
88 .struct_size = s->struct_size,
89 .ndr_pull = s->ndr_pull,
90 .ndr_push = s->ndr_push,
91 .ndr_print = s->ndr_print
93 return out_buffer;
97 static NTSTATUS pull_chunks(struct ndr_pull *ndr_pull,
98 const struct ndr_interface_call_pipes *pipes)
100 enum ndr_err_code ndr_err;
101 uint32_t i;
103 for (i=0; i < pipes->num_pipes; i++) {
104 while (true) {
105 void *saved_mem_ctx;
106 uint32_t *count;
107 void *c;
109 c = talloc_zero_size(ndr_pull, pipes->pipes[i].chunk_struct_size);
110 if (c == NULL) {
111 return NT_STATUS_NO_MEMORY;
114 * Note: the first struct member is always
115 * 'uint32_t count;'
117 count = (uint32_t *)c;
119 saved_mem_ctx = ndr_pull->current_mem_ctx;
120 ndr_pull->current_mem_ctx = c;
121 ndr_err = pipes->pipes[i].ndr_pull(ndr_pull, NDR_SCALARS, c);
122 ndr_pull->current_mem_ctx = saved_mem_ctx;
124 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
125 talloc_free(c);
126 return ndr_map_error2ntstatus(ndr_err);
128 if (*count == 0) {
129 talloc_free(c);
130 break;
132 talloc_free(c);
136 return NT_STATUS_OK;
139 static void ndr_print_nothing(struct ndr_print *ndr, const char *format, ...)
142 * This is here so that we walk the tree but don't output anything.
143 * This helps find buggy ndr_print routines
147 * TODO: consider calling snprinf() to find strings without NULL
148 * terminators (for example)
153 int LLVMFuzzerTestOneInput(uint8_t *data, size_t size) {
154 uint8_t type;
155 int pull_push_print_flags;
156 uint16_t fuzz_packet_flags, function;
157 TALLOC_CTX *mem_ctx = NULL;
158 uint32_t ndr_flags = 0;
159 struct ndr_push *ndr_push;
160 enum ndr_err_code ndr_err;
161 struct ndr_interface_call f_buffer;
162 const struct ndr_interface_call *f = NULL;
163 NTSTATUS status;
166 * This allows us to build binaries to fuzz just one target function
168 * In this mode the input becomes the 'stub data', there is no prefix.
170 * There is no NDR64 support in this mode at this time.
172 #if defined(FUZZ_TYPE) && defined(FUZZ_FUNCTION)
173 #undef HEADER_SIZE
174 #define HEADER_SIZE 0
175 fuzz_packet_flags = 0;
176 type = FUZZ_TYPE;
177 function = FUZZ_FUNCTION;
178 #else
179 if (size < HEADER_SIZE) {
181 * the first few bytes decide what is being fuzzed --
182 * if they aren't all there we do nothing.
184 return 0;
187 fuzz_packet_flags = SVAL(data, 0);
188 if (fuzz_packet_flags & INVALID_FLAGS) {
189 return 0;
192 function = SVAL(data, 2);
194 type = fuzz_packet_flags & 3;
196 #ifdef FUZZ_TYPE
198 * Fuzz targets should have as small an interface as possible.
199 * This allows us to create 3 binaries for most pipes,
200 * TYPE_IN, TYPE_OUT and TYPE_STRUCT
202 * We keep the header format, and just exit early if it does
203 * not match.
205 if (type != FUZZ_TYPE) {
206 return 0;
208 #endif
209 #endif
211 switch (type) {
212 case TYPE_STRUCT:
213 pull_push_print_flags = NDR_SCALARS|NDR_BUFFERS;
214 f = find_struct(&FUZZ_PIPE_TABLE, function, &f_buffer);
215 break;
216 case TYPE_IN:
217 pull_push_print_flags = NDR_IN;
218 f = find_function(&FUZZ_PIPE_TABLE, function);
219 break;
220 case TYPE_OUT:
221 pull_push_print_flags = NDR_OUT;
222 f = find_function(&FUZZ_PIPE_TABLE, function);
223 break;
224 default:
225 return 0;
228 if (f == NULL) {
229 return 0;
231 if (fuzz_packet_flags & FLAG_NDR64) {
232 ndr_flags |= LIBNDR_FLAG_NDR64;
235 mem_ctx = talloc_init("ndrfuzz");
239 * f->struct_size is well-controlled, it is essentially
240 * defined in the IDL
242 uint8_t st[f->struct_size];
244 DATA_BLOB blob = data_blob_const(data + HEADER_SIZE,
245 size - HEADER_SIZE);
246 struct ndr_pull *ndr_pull = ndr_pull_init_blob(&blob,
247 mem_ctx);
249 if (ndr_pull == NULL) {
250 perror("ndr_pull_init_blob");
251 TALLOC_FREE(mem_ctx);
252 return 0;
256 * We must initialise the buffer (even if we would
257 * prefer not to for the sake of eg valgrind) as
258 * otherwise the special handler for 'out pointer with
259 * [size_is()] refers to in value with [ref]' fails to
260 * trigger
262 memset(st, '\0', sizeof(st));
264 ndr_pull->flags |= LIBNDR_FLAG_REF_ALLOC;
265 ndr_pull->global_max_recursion = 128;
267 if (type == TYPE_OUT) {
268 status = pull_chunks(ndr_pull,
269 &f->out_pipes);
270 if (!NT_STATUS_IS_OK(status)) {
271 TALLOC_FREE(mem_ctx);
272 return 0;
276 ndr_err = f->ndr_pull(ndr_pull,
277 pull_push_print_flags,
278 st);
279 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
280 TALLOC_FREE(mem_ctx);
281 return 0;
284 if (type == TYPE_IN) {
285 status = pull_chunks(ndr_pull,
286 &f->in_pipes);
287 if (!NT_STATUS_IS_OK(status)) {
288 TALLOC_FREE(mem_ctx);
289 return 0;
293 ndr_push = ndr_push_init_ctx(mem_ctx);
294 if (ndr_push == NULL) {
295 TALLOC_FREE(mem_ctx);
296 return 0;
299 ndr_push->flags |= ndr_flags;
302 * Now push what was pulled, just in case we generated an
303 * invalid structure in memory, this should notice
305 ndr_err = f->ndr_push(ndr_push,
306 pull_push_print_flags,
307 st);
308 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
309 TALLOC_FREE(mem_ctx);
310 return 0;
314 struct ndr_print *ndr_print = talloc_zero(mem_ctx, struct ndr_print);
315 ndr_print->print = ndr_print_nothing;
316 ndr_print->depth = 1;
319 * Finally print (to nowhere) the structure, this may also
320 * notice invalid memory
322 f->ndr_print(ndr_print,
323 f->name,
324 pull_push_print_flags,
325 st);
328 TALLOC_FREE(mem_ctx);
330 return 0;
334 int LLVMFuzzerInitialize(int *argc, char ***argv)
336 return 0;