diff options
Diffstat (limited to 'lib/fuzzing/fuzz_ndr_X.c')
-rw-r--r-- | lib/fuzzing/fuzz_ndr_X.c | 337 |
1 files changed, 337 insertions, 0 deletions
diff --git a/lib/fuzzing/fuzz_ndr_X.c b/lib/fuzzing/fuzz_ndr_X.c new file mode 100644 index 0000000..a3fb984 --- /dev/null +++ b/lib/fuzzing/fuzz_ndr_X.c @@ -0,0 +1,337 @@ +/* + Unix SMB/CIFS implementation. + Fuzzer for pidl-generated NDR pipes. + Copyright (C) Andrew Tridgell 2003 + Copyright (C) Jelmer Vernooij 2006 + Copyright (C) Andrew Bartlett 2019 + Copyright (C) Catalyst.NET Ltd 2019 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "system/filesys.h" +#include "system/locale.h" +#include "librpc/ndr/libndr.h" +#include "librpc/gen_ndr/ndr_dcerpc.h" +#include "util/byteorder.h" +#include "fuzzing/fuzzing.h" + +extern const struct ndr_interface_table FUZZ_PIPE_TABLE; + +#define FLAG_NDR64 4 + +enum { + TYPE_STRUCT = 0, + TYPE_IN, + TYPE_OUT +}; + +/* + * header design (little endian): + * + * struct { + * uint16_t flags; + * uint16_t function_or_struct_no; + * }; + */ + +/* + * We want an even number here to ensure 4-byte alignment later + * not just for efficieny but because the fuzzers are known to guess + * that numbers will be 4-byte aligned + */ +#define HEADER_SIZE 4 + +#define INVALID_FLAGS (~(FLAG_NDR64 | 3)) + +static const struct ndr_interface_call *find_function( + const struct ndr_interface_table *p, + unsigned int function_no) +{ + if (function_no >= p->num_calls) { + return NULL; + } + return &p->calls[function_no]; +} + +/* + * Get a public structure by number and return it as if it were + * a function. + */ +static const struct ndr_interface_call *find_struct( + const struct ndr_interface_table *p, + unsigned int struct_no, + struct ndr_interface_call *out_buffer) +{ + const struct ndr_interface_public_struct *s = NULL; + + if (struct_no >= p->num_public_structs) { + return NULL; + } + + s = &p->public_structs[struct_no]; + + *out_buffer = (struct ndr_interface_call) { + .name = s->name, + .struct_size = s->struct_size, + .ndr_pull = s->ndr_pull, + .ndr_push = s->ndr_push, + .ndr_print = s->ndr_print + }; + return out_buffer; +} + + +static NTSTATUS pull_chunks(struct ndr_pull *ndr_pull, + const struct ndr_interface_call_pipes *pipes) +{ + enum ndr_err_code ndr_err; + uint32_t i; + + for (i=0; i < pipes->num_pipes; i++) { + while (true) { + void *saved_mem_ctx; + uint32_t *count; + void *c; + + c = talloc_zero_size(ndr_pull, pipes->pipes[i].chunk_struct_size); + if (c == NULL) { + return NT_STATUS_NO_MEMORY; + } + /* + * Note: the first struct member is always + * 'uint32_t count;' + */ + count = (uint32_t *)c; + + saved_mem_ctx = ndr_pull->current_mem_ctx; + ndr_pull->current_mem_ctx = c; + ndr_err = pipes->pipes[i].ndr_pull(ndr_pull, NDR_SCALARS, c); + ndr_pull->current_mem_ctx = saved_mem_ctx; + + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + talloc_free(c); + return ndr_map_error2ntstatus(ndr_err); + } + if (*count == 0) { + talloc_free(c); + break; + } + talloc_free(c); + } + } + + return NT_STATUS_OK; +} + +static void ndr_print_nothing(struct ndr_print *ndr, const char *format, ...) +{ + /* + * This is here so that we walk the tree but don't output anything. + * This helps find buggy ndr_print routines + */ + + /* + * TODO: consider calling snprinf() to find strings without NULL + * terminators (for example) + */ +} + + +int LLVMFuzzerTestOneInput(uint8_t *data, size_t size) { + uint8_t type; + int pull_push_print_flags; + uint16_t fuzz_packet_flags, function; + TALLOC_CTX *mem_ctx = NULL; + uint32_t ndr_flags = 0; + struct ndr_push *ndr_push; + enum ndr_err_code ndr_err; + struct ndr_interface_call f_buffer; + const struct ndr_interface_call *f = NULL; + NTSTATUS status; + +/* + * This allows us to build binaries to fuzz just one target function + * + * In this mode the input becomes the 'stub data', there is no prefix. + * + * There is no NDR64 support in this mode at this time. + */ +#if defined(FUZZ_TYPE) && defined(FUZZ_FUNCTION) +#undef HEADER_SIZE +#define HEADER_SIZE 0 + fuzz_packet_flags = 0; + type = FUZZ_TYPE; + function = FUZZ_FUNCTION; +#else + if (size < HEADER_SIZE) { + /* + * the first few bytes decide what is being fuzzed -- + * if they aren't all there we do nothing. + */ + return 0; + } + + fuzz_packet_flags = SVAL(data, 0); + if (fuzz_packet_flags & INVALID_FLAGS) { + return 0; + } + + function = SVAL(data, 2); + + type = fuzz_packet_flags & 3; + +#ifdef FUZZ_TYPE + /* + * Fuzz targets should have as small an interface as possible. + * This allows us to create 3 binaries for most pipes, + * TYPE_IN, TYPE_OUT and TYPE_STRUCT + * + * We keep the header format, and just exit early if it does + * not match. + */ + if (type != FUZZ_TYPE) { + return 0; + } +#endif +#endif + + switch (type) { + case TYPE_STRUCT: + pull_push_print_flags = NDR_SCALARS|NDR_BUFFERS; + f = find_struct(&FUZZ_PIPE_TABLE, function, &f_buffer); + break; + case TYPE_IN: + pull_push_print_flags = NDR_IN; + f = find_function(&FUZZ_PIPE_TABLE, function); + break; + case TYPE_OUT: + pull_push_print_flags = NDR_OUT; + f = find_function(&FUZZ_PIPE_TABLE, function); + break; + default: + return 0; + } + + if (f == NULL) { + return 0; + } + if (fuzz_packet_flags & FLAG_NDR64) { + ndr_flags |= LIBNDR_FLAG_NDR64; + } + + mem_ctx = talloc_init("ndrfuzz"); + + { + /* + * f->struct_size is well-controlled, it is essentially + * defined in the IDL + */ + uint8_t st[f->struct_size]; + + DATA_BLOB blob = data_blob_const(data + HEADER_SIZE, + size - HEADER_SIZE); + struct ndr_pull *ndr_pull = ndr_pull_init_blob(&blob, + mem_ctx); + + if (ndr_pull == NULL) { + perror("ndr_pull_init_blob"); + TALLOC_FREE(mem_ctx); + return 0; + } + + /* + * We must initialise the buffer (even if we would + * prefer not to for the sake of eg valgrind) as + * otherwise the special handler for 'out pointer with + * [size_is()] refers to in value with [ref]' fails to + * trigger + */ + memset(st, '\0', sizeof(st)); + + ndr_pull->flags |= LIBNDR_FLAG_REF_ALLOC; + ndr_pull->global_max_recursion = 128; + + if (type == TYPE_OUT) { + status = pull_chunks(ndr_pull, + &f->out_pipes); + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(mem_ctx); + return 0; + } + } + + ndr_err = f->ndr_pull(ndr_pull, + pull_push_print_flags, + st); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + TALLOC_FREE(mem_ctx); + return 0; + } + + if (type == TYPE_IN) { + status = pull_chunks(ndr_pull, + &f->in_pipes); + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(mem_ctx); + return 0; + } + } + + ndr_push = ndr_push_init_ctx(mem_ctx); + if (ndr_push == NULL) { + TALLOC_FREE(mem_ctx); + return 0; + } + + ndr_push->flags |= ndr_flags; + + /* + * Now push what was pulled, just in case we generated an + * invalid structure in memory, this should notice + */ + ndr_err = f->ndr_push(ndr_push, + pull_push_print_flags, + st); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + TALLOC_FREE(mem_ctx); + return 0; + } + + { + struct ndr_print *ndr_print = talloc_zero(mem_ctx, struct ndr_print); + ndr_print->print = ndr_print_nothing; + ndr_print->depth = 1; + + /* + * Finally print (to nowhere) the structure, this may also + * notice invalid memory + */ + f->ndr_print(ndr_print, + f->name, + pull_push_print_flags, + st); + } + } + TALLOC_FREE(mem_ctx); + + return 0; +} + + +int LLVMFuzzerInitialize(int *argc, char ***argv) +{ + return 0; +} |