diff options
author | Douglas Bagnall <douglas.bagnall@catalyst.net.nz> | 2019-10-31 16:28:28 +1300 |
---|---|---|
committer | Andrew Bartlett <abartlet@samba.org> | 2019-12-10 07:50:28 +0000 |
commit | 7b265830ad6796dbbe721f7abfd62a19c2185b65 (patch) | |
tree | 86dea325c0e73ef5765b6768a1a8bfade35602d2 /lib/fuzzing | |
parent | ef5d79e24ba8aec226419e594de0cf91c24d7fc4 (diff) | |
download | samba-7b265830ad6796dbbe721f7abfd62a19c2185b65.tar.gz |
lib/fuzzing: add fuzz_ndr_X
This NDR fuzzer links with each "interface" in the IDL files to
create avsingle binary. This tries to matches what the fuzzing
engines desire.
It started as a copy of ndrdump but very little of that remains
in place.
The fancy build rules try to avoid needing a lof of boilerplate
in the wscript_build files and ensure new fuzzers are generated
and run when new IDL is added automatically.
Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
Signed-off-by: Andrew Bartlett <abartlet@samba.org>
Pair-programmed-by: Andrew Bartlett <abartlet@samba.org>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
Diffstat (limited to 'lib/fuzzing')
-rw-r--r-- | lib/fuzzing/fuzz_ndr_X.c | 291 | ||||
-rw-r--r-- | lib/fuzzing/wscript_build | 80 |
2 files changed, 371 insertions, 0 deletions
diff --git a/lib/fuzzing/fuzz_ndr_X.c b/lib/fuzzing/fuzz_ndr_X.c new file mode 100644 index 00000000000..8c9e5721739 --- /dev/null +++ b/lib/fuzzing/fuzz_ndr_X.c @@ -0,0 +1,291 @@ +/* + Unix SMB/CIFS implementation. + SMB torture tester + 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; + + 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; + + 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; + } + ndr_pull->flags |= LIBNDR_FLAG_REF_ALLOC; + + 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; +} diff --git a/lib/fuzzing/wscript_build b/lib/fuzzing/wscript_build index 386145c43b2..25cdcd323bf 100644 --- a/lib/fuzzing/wscript_build +++ b/lib/fuzzing/wscript_build @@ -1,5 +1,7 @@ #!/usr/bin/env python +from waflib import Build + bld.SAMBA_SUBSYSTEM('fuzzing', source='fuzzing.c', deps='talloc', @@ -40,3 +42,81 @@ bld.SAMBA_BINARY('fuzz_ldb_parse_tree', source='fuzz_ldb_parse_tree.c', deps='fuzzing ldb', fuzzer=True) + +def SAMBA_NDR_FUZZ(bld, interface): + name = "fuzz_ndr_%s" % (interface.lower()) + fuzz_dir = os.path.join(bld.env.srcdir, 'lib/fuzzing') + fuzz_reldir = os.path.relpath(fuzz_dir, bld.path.abspath()) + fuzz_src = os.path.join(fuzz_reldir, 'fuzz_ndr_X.c') + fuzz_named_src = os.path.join(fuzz_reldir, + 'fuzz_ndr_%s.c' % interface.lower()) + + # Work around an issue that WAF is invoked from up to 3 different + # directories so doesn't create a unique name for the multiple .o + # files like it would if called from just one place. + bld.SAMBA_GENERATOR(fuzz_named_src, + source=fuzz_src, + target=fuzz_named_src, + rule='cp ${SRC} ${TGT}') + + bld.SAMBA_BINARY(name, source=fuzz_named_src, + cflags = "-D FUZZ_PIPE_TABLE=ndr_table_%s" % interface, + deps = "ndr-table NDR_DCERPC", + install=False, + fuzzer=True) + +Build.BuildContext.SAMBA_NDR_FUZZ = SAMBA_NDR_FUZZ + +# fuzz_ndr_X is generated from the list if IDL fed to PIDL +# however there are exceptions to the normal pattern +bld.SAMBA_NDR_FUZZ('IOXIDResolver') # oxidresolver.idl +bld.SAMBA_NDR_FUZZ('IRemoteActivation') # remact.idl +bld.SAMBA_NDR_FUZZ('iremotewinspool') # winspool.idl +bld.SAMBA_NDR_FUZZ('FileServerVssAgent') # fsvrp.idl +bld.SAMBA_NDR_FUZZ('lsarpc') # lsa.idl +bld.SAMBA_NDR_FUZZ('netdfs') # dfs.idl +bld.SAMBA_NDR_FUZZ('nfs4acl_interface') # nfs4acl.idl +bld.SAMBA_NDR_FUZZ('ObjectRpcBaseTypes') # orpc.idl +bld.SAMBA_NDR_FUZZ('rpcecho') # echo.idl + +# quota.idl +bld.SAMBA_NDR_FUZZ('file_quota') +bld.SAMBA_NDR_FUZZ('smb2_query_quota') +bld.SAMBA_NDR_FUZZ('smb1_nt_transact_query_quota') + +# ioctl.idl +bld.SAMBA_NDR_FUZZ('copychunk') +bld.SAMBA_NDR_FUZZ('compression') +bld.SAMBA_NDR_FUZZ('netinterface') +bld.SAMBA_NDR_FUZZ('sparse') +bld.SAMBA_NDR_FUZZ('resiliency') +bld.SAMBA_NDR_FUZZ('trim') + +# Skipped: dsbackup (all todo) + +# WMI tables +bld.SAMBA_NDR_FUZZ('IWbemClassObject') +bld.SAMBA_NDR_FUZZ('IWbemServices') +bld.SAMBA_NDR_FUZZ('IEnumWbemClassObject') +bld.SAMBA_NDR_FUZZ('IWbemContext') +bld.SAMBA_NDR_FUZZ('IWbemLevel1Login') +bld.SAMBA_NDR_FUZZ('IWbemWCOSmartEnum') +bld.SAMBA_NDR_FUZZ('IWbemFetchSmartEnum') +bld.SAMBA_NDR_FUZZ('IWbemCallResult') +bld.SAMBA_NDR_FUZZ('IWbemObjectSink') + +# DCOM tables +bld.SAMBA_NDR_FUZZ('dcom_Unknown') +bld.SAMBA_NDR_FUZZ('IUnknown') +bld.SAMBA_NDR_FUZZ('IClassFactory') +bld.SAMBA_NDR_FUZZ('IRemUnknown') +bld.SAMBA_NDR_FUZZ('IClassActivator') +bld.SAMBA_NDR_FUZZ('ISCMLocalActivator') +bld.SAMBA_NDR_FUZZ('IMachineLocalActivator') +bld.SAMBA_NDR_FUZZ('ILocalObjectExporter') +bld.SAMBA_NDR_FUZZ('ISystemActivator') +bld.SAMBA_NDR_FUZZ('IRemUnknown2') +bld.SAMBA_NDR_FUZZ('IDispatch') +bld.SAMBA_NDR_FUZZ('IMarshal') +bld.SAMBA_NDR_FUZZ('ICoffeeMachine') +bld.SAMBA_NDR_FUZZ('IStream') |