fuzz:fuzz_ndr_X: don't skip printing on push error
[gd/samba-autobuild/.git] / lib / fuzzing / fuzz_ndr_X.c
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
8
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.
13
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.
18
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/>.
21 */
22
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"
30
31 extern const struct ndr_interface_table FUZZ_PIPE_TABLE;
32
33 #define FLAG_NDR64 4
34
35 enum {
36         TYPE_STRUCT = 0,
37         TYPE_IN,
38         TYPE_OUT
39 };
40
41 /*
42  * header design (little endian):
43  *
44  * struct {
45  *   uint16_t flags;
46  *   uint16_t function_or_struct_no;
47  * };
48  */
49
50 /*
51  * We want an even number here to ensure 4-byte alignment later
52  * not just for efficiency but because the fuzzers are known to guess
53  * that numbers will be 4-byte aligned
54  */
55 #define HEADER_SIZE 4
56
57 #define INVALID_FLAGS (~(FLAG_NDR64 | 3))
58
59 static const struct ndr_interface_call *find_function(
60         const struct ndr_interface_table *p,
61         unsigned int function_no)
62 {
63         if (function_no >= p->num_calls) {
64                 return NULL;
65         }
66         return &p->calls[function_no];
67 }
68
69 /*
70  * Get a public structure by number and return it as if it were
71  * a function.
72  */
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)
77 {
78         const struct ndr_interface_public_struct *s = NULL;
79
80         if (struct_no >= p->num_public_structs) {
81                 return NULL;
82         }
83
84         s = &p->public_structs[struct_no];
85
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
92         };
93         return out_buffer;
94 }
95
96
97 static NTSTATUS pull_chunks(struct ndr_pull *ndr_pull,
98                             const struct ndr_interface_call_pipes *pipes)
99 {
100         enum ndr_err_code ndr_err;
101         uint32_t i;
102
103         for (i=0; i < pipes->num_pipes; i++) {
104                 while (true) {
105                         void *saved_mem_ctx;
106                         uint32_t *count;
107                         void *c;
108
109                         c = talloc_zero_size(ndr_pull, pipes->pipes[i].chunk_struct_size);
110                         if (c == NULL) {
111                                 return NT_STATUS_NO_MEMORY;
112                         }
113                         /*
114                          * Note: the first struct member is always
115                          * 'uint32_t count;'
116                          */
117                         count = (uint32_t *)c;
118
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;
123
124                         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
125                                 talloc_free(c);
126                                 return ndr_map_error2ntstatus(ndr_err);
127                         }
128                         if (*count == 0) {
129                                 talloc_free(c);
130                                 break;
131                         }
132                         talloc_free(c);
133                 }
134         }
135
136         return NT_STATUS_OK;
137 }
138
139 static void ndr_print_and_forget(struct ndr_print *ndr, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
140
141 static char print_buffer[1000000];
142
143 static void ndr_print_and_forget(struct ndr_print *ndr, const char *format, ...)
144 {
145         /*
146          * This is here so that we walk the tree but don't output anything.
147          * This helps find buggy ndr_print routines.
148          *
149          * We call snprinf() to find e.g. strings without NULL terminators.
150          */
151         va_list list;
152
153         va_start(list, format);
154         vsnprintf(print_buffer, sizeof(print_buffer), format, list);
155         va_end(list);
156 }
157
158
159 int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
160         uint8_t type;
161         ndr_flags_type pull_push_print_flags;
162         uint16_t fuzz_packet_flags, function;
163         TALLOC_CTX *mem_ctx = NULL;
164         libndr_flags ndr_flags = 0;
165         struct ndr_push *ndr_push;
166         enum ndr_err_code ndr_err;
167         struct ndr_interface_call f_buffer;
168         const struct ndr_interface_call *f = NULL;
169         NTSTATUS status;
170
171 /*
172  * This allows us to build binaries to fuzz just one target function
173  *
174  * In this mode the input becomes the 'stub data', there is no prefix.
175  *
176  * There is no NDR64 support in this mode at this time.
177  */
178 #if defined(FUZZ_TYPE) && defined(FUZZ_FUNCTION)
179 #undef HEADER_SIZE
180 #define HEADER_SIZE 0
181         fuzz_packet_flags = 0;
182         type = FUZZ_TYPE;
183         function = FUZZ_FUNCTION;
184 #else
185         if (size < HEADER_SIZE) {
186                 /*
187                  * the first few bytes decide what is being fuzzed --
188                  * if they aren't all there we do nothing.
189                  */
190                 return 0;
191         }
192
193         fuzz_packet_flags = SVAL(data, 0);
194         if (fuzz_packet_flags & INVALID_FLAGS) {
195                 return 0;
196         }
197
198         function = SVAL(data, 2);
199
200         type = fuzz_packet_flags & 3;
201
202 #ifdef FUZZ_TYPE
203         /*
204          * Fuzz targets should have as small an interface as possible.
205          * This allows us to create 3 binaries for most pipes,
206          * TYPE_IN, TYPE_OUT and TYPE_STRUCT
207          *
208          * We keep the header format, and just exit early if it does
209          * not match.
210          */
211         if (type != FUZZ_TYPE) {
212                 return 0;
213         }
214 #endif
215 #endif
216
217         switch (type) {
218         case TYPE_STRUCT:
219                 pull_push_print_flags = NDR_SCALARS|NDR_BUFFERS;
220                 f = find_struct(&FUZZ_PIPE_TABLE, function, &f_buffer);
221                 break;
222         case TYPE_IN:
223                 pull_push_print_flags = NDR_IN;
224                 f = find_function(&FUZZ_PIPE_TABLE, function);
225                 break;
226         case TYPE_OUT:
227                 pull_push_print_flags = NDR_OUT;
228                 f = find_function(&FUZZ_PIPE_TABLE, function);
229                 break;
230         default:
231                 return 0;
232         }
233
234         if (f == NULL) {
235                 return 0;
236         }
237         if (fuzz_packet_flags & FLAG_NDR64) {
238                 ndr_flags |= LIBNDR_FLAG_NDR64;
239         }
240
241         mem_ctx = talloc_init("ndrfuzz");
242
243         {
244                 /*
245                  * f->struct_size is well-controlled, it is essentially
246                  * defined in the IDL
247                  */
248                 uint8_t st[f->struct_size];
249
250                 DATA_BLOB blob = data_blob_const(data + HEADER_SIZE,
251                                                  size - HEADER_SIZE);
252                 struct ndr_pull *ndr_pull = ndr_pull_init_blob(&blob,
253                                                                mem_ctx);
254
255                 if (ndr_pull == NULL) {
256                         perror("ndr_pull_init_blob");
257                         TALLOC_FREE(mem_ctx);
258                         return 0;
259                 }
260
261                 /*
262                  * We must initialise the buffer (even if we would
263                  * prefer not to for the sake of eg valgrind) as
264                  * otherwise the special handler for 'out pointer with
265                  * [size_is()] refers to in value with [ref]' fails to
266                  * trigger
267                  */
268                 memset(st, '\0', sizeof(st));
269
270                 ndr_pull->flags |= LIBNDR_FLAG_REF_ALLOC;
271                 ndr_pull->global_max_recursion = 128;
272
273                 if (type == TYPE_OUT) {
274                         status = pull_chunks(ndr_pull,
275                                              &f->out_pipes);
276                         if (!NT_STATUS_IS_OK(status)) {
277                                 TALLOC_FREE(mem_ctx);
278                                 return 0;
279                         }
280                 }
281
282                 ndr_err = f->ndr_pull(ndr_pull,
283                                       pull_push_print_flags,
284                                       st);
285                 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
286                         TALLOC_FREE(mem_ctx);
287                         return 0;
288                 }
289
290                 if (type == TYPE_IN) {
291                         status = pull_chunks(ndr_pull,
292                                              &f->in_pipes);
293                         if (!NT_STATUS_IS_OK(status)) {
294                                 TALLOC_FREE(mem_ctx);
295                                 return 0;
296                         }
297                 }
298
299                 ndr_push = ndr_push_init_ctx(mem_ctx);
300                 if (ndr_push == NULL) {
301                         TALLOC_FREE(mem_ctx);
302                         return 0;
303                 }
304
305                 ndr_push->flags |= ndr_flags;
306
307                 /*
308                  * Now push what was pulled, just in case we generated an
309                  * invalid structure in memory, this should notice
310                  */
311                 ndr_err = f->ndr_push(ndr_push,
312                                       pull_push_print_flags,
313                                       st);
314                 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
315                         /*
316                          * Note we aren't bailing here.
317                          *
318                          * It is good that ndr_push returned an error, because
319                          * the structure is no doubt rubbish. But that doesn't
320                          * mean we don't also want to see if ndr_print can
321                          * handle it.
322                          */
323                 }
324
325                 {
326                         struct ndr_print *ndr_print = talloc_zero(mem_ctx, struct ndr_print);
327                         ndr_print->print = ndr_print_and_forget;
328                         ndr_print->depth = 1;
329
330                         /*
331                          * Finally print (to nowhere) the structure, this may also
332                          * notice invalid memory
333                          */
334                         f->ndr_print(ndr_print,
335                                      f->name,
336                                      pull_push_print_flags,
337                                      st);
338                 }
339         }
340         TALLOC_FREE(mem_ctx);
341
342         return 0;
343 }
344
345
346 int LLVMFuzzerInitialize(int *argc, char ***argv)
347 {
348         return 0;
349 }