lib/fuzzer: Allow building a fuzz binary for just one interface
[samba.git] / lib / fuzzing / fuzz_ndr_X.c
1 /*
2    Unix SMB/CIFS implementation.
3    SMB torture tester
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 efficieny 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_nothing(struct ndr_print *ndr, const char *format, ...)
140 {
141         /*
142          * This is here so that we walk the tree but don't output anything.
143          * This helps find buggy ndr_print routines
144          */
145
146         /*
147          * TODO: consider calling snprinf() to find strings without NULL
148          * terminators (for example)
149          */
150 }
151
152
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;
164
165 /*
166  * This allows us to build binaries to fuzz just one target function
167  *
168  * In this mode the input becomes the 'stub data', there is no prefix.
169  *
170  * There is no NDR64 support in this mode at this time.
171  */
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) {
180                 /*
181                  * the first few bytes decide what is being fuzzed --
182                  * if they aren't all there we do nothing.
183                  */
184                 return 0;
185         }
186
187         fuzz_packet_flags = SVAL(data, 0);
188         if (fuzz_packet_flags & INVALID_FLAGS) {
189                 return 0;
190         }
191
192         function = SVAL(data, 2);
193
194         type = fuzz_packet_flags & 3;
195 #endif
196
197         switch (type) {
198         case TYPE_STRUCT:
199                 pull_push_print_flags = NDR_SCALARS|NDR_BUFFERS;
200                 f = find_struct(&FUZZ_PIPE_TABLE, function, &f_buffer);
201                 break;
202         case TYPE_IN:
203                 pull_push_print_flags = NDR_IN;
204                 f = find_function(&FUZZ_PIPE_TABLE, function);
205                 break;
206         case TYPE_OUT:
207                 pull_push_print_flags = NDR_OUT;
208                 f = find_function(&FUZZ_PIPE_TABLE, function);
209                 break;
210         default:
211                 return 0;
212         }
213
214         if (f == NULL) {
215                 return 0;
216         }
217         if (fuzz_packet_flags & FLAG_NDR64) {
218                 ndr_flags |= LIBNDR_FLAG_NDR64;
219         }
220
221         mem_ctx = talloc_init("ndrfuzz");
222
223         {
224                 /*
225                  * f->struct_size is well-controlled, it is essentially
226                  * defined in the IDL
227                  */
228                 uint8_t st[f->struct_size];
229
230                 DATA_BLOB blob = data_blob_const(data + HEADER_SIZE,
231                                                  size - HEADER_SIZE);
232                 struct ndr_pull *ndr_pull = ndr_pull_init_blob(&blob,
233                                                                mem_ctx);
234
235                 if (ndr_pull == NULL) {
236                         perror("ndr_pull_init_blob");
237                         TALLOC_FREE(mem_ctx);
238                         return 0;
239                 }
240                 ndr_pull->flags |= LIBNDR_FLAG_REF_ALLOC;
241
242                 if (type == TYPE_OUT) {
243                         status = pull_chunks(ndr_pull,
244                                              &f->out_pipes);
245                         if (!NT_STATUS_IS_OK(status)) {
246                                 TALLOC_FREE(mem_ctx);
247                                 return 0;
248                         }
249                 }
250
251                 ndr_err = f->ndr_pull(ndr_pull,
252                                       pull_push_print_flags,
253                                       st);
254                 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
255                         TALLOC_FREE(mem_ctx);
256                         return 0;
257                 }
258
259                 if (type == TYPE_IN) {
260                         status = pull_chunks(ndr_pull,
261                                              &f->in_pipes);
262                         if (!NT_STATUS_IS_OK(status)) {
263                                 TALLOC_FREE(mem_ctx);
264                                 return 0;
265                         }
266                 }
267
268                 ndr_push = ndr_push_init_ctx(mem_ctx);
269                 if (ndr_push == NULL) {
270                         TALLOC_FREE(mem_ctx);
271                         return 0;
272                 }
273
274                 ndr_push->flags |= ndr_flags;
275
276                 /*
277                  * Now push what was pulled, just in case we generated an
278                  * invalid structure in memory, this should notice
279                  */
280                 ndr_err = f->ndr_push(ndr_push,
281                                       pull_push_print_flags,
282                                       st);
283                 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
284                         TALLOC_FREE(mem_ctx);
285                         return 0;
286                 }
287
288                 {
289                         struct ndr_print *ndr_print = talloc_zero(mem_ctx, struct ndr_print);
290                         ndr_print->print = ndr_print_nothing;
291                         ndr_print->depth = 1;
292
293                         /*
294                          * Finally print (to nowhere) the structure, this may also
295                          * notice invalid memory
296                          */
297                         f->ndr_print(ndr_print,
298                                      f->name,
299                                      pull_push_print_flags,
300                                      st);
301                 }
302         }
303         TALLOC_FREE(mem_ctx);
304
305         return 0;
306 }