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/>.
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
;
42 * header design (little endian):
46 * uint16_t function_or_struct_no;
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
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
) {
66 return &p
->calls
[function_no
];
70 * Get a public structure by number and return it as if it were
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
) {
84 s
= &p
->public_structs
[struct_no
];
86 *out_buffer
= (struct ndr_interface_call
) {
88 .struct_size
= s
->struct_size
,
89 .ndr_pull
= s
->ndr_pull
,
90 .ndr_push
= s
->ndr_push
,
91 .ndr_print
= s
->ndr_print
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
;
103 for (i
=0; i
< pipes
->num_pipes
; i
++) {
109 c
= talloc_zero_size(ndr_pull
, pipes
->pipes
[i
].chunk_struct_size
);
111 return NT_STATUS_NO_MEMORY
;
114 * Note: the first struct member is always
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
)) {
126 return ndr_map_error2ntstatus(ndr_err
);
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
) {
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
;
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)
174 #define HEADER_SIZE 0
175 fuzz_packet_flags
= 0;
177 function
= FUZZ_FUNCTION
;
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.
187 fuzz_packet_flags
= SVAL(data
, 0);
188 if (fuzz_packet_flags
& INVALID_FLAGS
) {
192 function
= SVAL(data
, 2);
194 type
= fuzz_packet_flags
& 3;
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
205 if (type
!= FUZZ_TYPE
) {
213 pull_push_print_flags
= NDR_SCALARS
|NDR_BUFFERS
;
214 f
= find_struct(&FUZZ_PIPE_TABLE
, function
, &f_buffer
);
217 pull_push_print_flags
= NDR_IN
;
218 f
= find_function(&FUZZ_PIPE_TABLE
, function
);
221 pull_push_print_flags
= NDR_OUT
;
222 f
= find_function(&FUZZ_PIPE_TABLE
, function
);
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
242 uint8_t st
[f
->struct_size
];
244 DATA_BLOB blob
= data_blob_const(data
+ HEADER_SIZE
,
246 struct ndr_pull
*ndr_pull
= ndr_pull_init_blob(&blob
,
249 if (ndr_pull
== NULL
) {
250 perror("ndr_pull_init_blob");
251 TALLOC_FREE(mem_ctx
);
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
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
,
270 if (!NT_STATUS_IS_OK(status
)) {
271 TALLOC_FREE(mem_ctx
);
276 ndr_err
= f
->ndr_pull(ndr_pull
,
277 pull_push_print_flags
,
279 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
280 TALLOC_FREE(mem_ctx
);
284 if (type
== TYPE_IN
) {
285 status
= pull_chunks(ndr_pull
,
287 if (!NT_STATUS_IS_OK(status
)) {
288 TALLOC_FREE(mem_ctx
);
293 ndr_push
= ndr_push_init_ctx(mem_ctx
);
294 if (ndr_push
== NULL
) {
295 TALLOC_FREE(mem_ctx
);
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
,
308 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
309 TALLOC_FREE(mem_ctx
);
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
,
324 pull_push_print_flags
,
328 TALLOC_FREE(mem_ctx
);
334 int LLVMFuzzerInitialize(int *argc
, char ***argv
)