diff options
Diffstat (limited to '')
28 files changed, 2212 insertions, 0 deletions
diff --git a/lib/fuzzing/README.md b/lib/fuzzing/README.md new file mode 100644 index 0000000..d3e34bd --- /dev/null +++ b/lib/fuzzing/README.md @@ -0,0 +1,87 @@ +# Fuzzing Samba + +See also https://wiki.samba.org/index.php/Fuzzing + +Fuzzing supplies valid, invalid, unexpected or random data as input to a piece +of code. Instrumentation, usually compiler-implemented, is used to monitor for +exceptions such as crashes, assertions or memory corruption. + +See [Wikipedia article on fuzzing](https://en.wikipedia.org/wiki/Fuzzing) for +more information. + +# Honggfuzz + +## Configure with fuzzing + +Example command line to build binaries for use with +[honggfuzz](https://github.com/google/honggfuzz/): + +```sh +./configure -C --without-gettext --enable-debug --enable-developer \ + --address-sanitizer --enable-libfuzzer --abi-check-disable \ + CC=.../honggfuzz/hfuzz_cc/hfuzz-clang \ + LINK_CC=.../honggfuzz/hfuzz_cc/hfuzz-clang +``` + + +## Fuzzing tiniparser + +Example for fuzzing `tiniparser` using `honggfuzz` (see `--help` for more +options): + +```sh +make bin/fuzz_tiniparser && \ +.../honggfuzz/honggfuzz --sanitizers --timeout 3 --max_file_size 256 \ + --rlimit_rss 100 -f .../tiniparser-corpus -- bin/fuzz_tiniparser +``` + +# AFL (american fuzzy lop) + +## Configure with fuzzing + +Example command line to build binaries for use with +[afl](http://lcamtuf.coredump.cx/afl/) + +```sh +./configure -C --without-gettext --enable-debug --enable-developer \ + --enable-afl-fuzzer --abi-check-disable \ + CC=afl-gcc +``` + +## Fuzzing tiniparser + +Example for fuzzing `tiniparser` using `afl-fuzz` (see `--help` for more +options): + +```sh +make bin/fuzz_tiniparser build && \ +afl-fuzz -m 200 -i inputdir -o outputdir -- bin/fuzz_tiniparser +``` + +# oss-fuzz + +Samba can be fuzzed by Google's oss-fuzz system. Assuming you have an +oss-fuzz checkout from https://github.com/google/oss-fuzz with Samba's +metadata in projects/samba, the following guides will help: + +## Testing locally + +https://google.github.io/oss-fuzz/getting-started/new-project-guide/#testing-locally + +## Debugging oss-fuzz + +See https://google.github.io/oss-fuzz/advanced-topics/debugging/ + +## Samba-specific hints + +A typical debugging workflow is: + +oss-fuzz$ python infra/helper.py shell samba +git fetch $REMOTE $BRANCH +git checkout FETCH_HEAD +lib/fuzzing/oss-fuzz/build_image.sh +compile + +This will pull in any new Samba deps and build Samba's fuzzers. + +# vim: set sw=8 sts=8 ts=8 tw=79 : diff --git a/lib/fuzzing/afl-fuzz-main.c b/lib/fuzzing/afl-fuzz-main.c new file mode 100644 index 0000000..e0a1d26 --- /dev/null +++ b/lib/fuzzing/afl-fuzz-main.c @@ -0,0 +1,66 @@ +/* + Unix SMB/CIFS implementation. + + Fuzz driver (AFL style) + + Copyright (C) Andrew Bartlett 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 "lib/util/samba_util.h" +#include "fuzzing.h" + +int main(int argc, char *argv[]) { + int ret; + size_t size = 0; + int i; + + ret = LLVMFuzzerInitialize(&argc, &argv); + if (ret != 0) { + printf("LLVMFuzzerInitialize returned %d\n", ret); + return ret; + } + + +#ifdef __AFL_LOOP + while (__AFL_LOOP(1000)) +#else + for (i = 1; i < argc; i++) { + uint8_t *buf = (uint8_t *)file_load(argv[i], + &size, + 0, + NULL); + ret = LLVMFuzzerTestOneInput(buf, size); + TALLOC_FREE(buf); + if (ret != 0) { + printf("LLVMFuzzerTestOneInput returned %d on argument %d\n", + ret, i); + return ret; + } + } + if (i == 1) +#endif + { + uint8_t *buf = (uint8_t *)fd_load(0, &size, 0, NULL); + if (buf == NULL) { + exit(1); + } + + ret = LLVMFuzzerTestOneInput(buf, size); + TALLOC_FREE(buf); + } + return ret; +} diff --git a/lib/fuzzing/decode_ndr_X_crash b/lib/fuzzing/decode_ndr_X_crash new file mode 100755 index 0000000..63c3cd7 --- /dev/null +++ b/lib/fuzzing/decode_ndr_X_crash @@ -0,0 +1,137 @@ +#!/usr/bin/env python3 +# +# Interpret a file that crashes an fuzz_ndr_X binary. +# +# Copyright (C) Catalyst IT Ltd. 2019 + + +import sys +import os +from base64 import b64encode +import struct +import argparse +import re + +TYPE_MASK = 3 +TYPES = ['struct', 'in', 'out'] + +FLAGS = [ + (4, 'ndr64', '--ndr64'), +] + + +def print_if_verbose(*args, **kwargs): + if verbose: + print(*args, **kwargs) + + +def process_one_file(f): + print_if_verbose(f.name) + print_if_verbose('-' * len(f.name)) + + b = f.read() + flags, function = struct.unpack('<HH', b[:4]) + if opnum is not None and opnum != function: + return + + t = TYPES[flags & TYPE_MASK] + if ndr_type and ndr_type != t: + return + + payload = b[4:] + data64 = b64encode(payload).decode('utf-8') + + cmd = ['bin/ndrdump', + pipe, + str(function), + t, + '--base64-input', + '--input', data64, + ] + + for flag, name, option in FLAGS: + if flags & flag: + print_if_verbose("flag: %s" % name) + cmd.append(option) + + print_if_verbose("length: %d\n" % len(payload)) + print(' '.join(cmd)) + print_if_verbose() + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('-p', '--pipe', default='$PIPE', + help='pipe name (for output command line)') + parser.add_argument('-t', '--type', default=None, choices=TYPES, + help='restrict to this type') + parser.add_argument('-o', '--opnum', default=None, type=int, + help='restrict to this function/struct number') + parser.add_argument('FILES', nargs='*', default=(), + help="read from these files") + parser.add_argument('-k', '--ignore-errors', action='store_true', + help='do not stop on errors') + parser.add_argument('-v', '--verbose', action='store_true', + help='say more') + parser.add_argument('-H', '--honggfuzz-file', + help="extract crashes from this honggfuzz report") + parser.add_argument('-f', '--crash-filter', + help="only print crashes matching this rexexp") + + args = parser.parse_args() + + global pipe, opnum, ndr_type, verbose + pipe = args.pipe + opnum = args.opnum + ndr_type = args.type + verbose = args.verbose + + if not args.FILES and not args.honggfuzz_file: + parser.print_usage() + sys.exit(1) + + for fn in args.FILES: + if args.crash_filter is not None: + if not re.search(args.crash_filter, fn): + print_if_verbose(f"skipping {fn}") + continue + try: + if fn == '-': + process_one_file(sys.stdin) + else: + with open(fn, 'rb') as f: + process_one_file(f) + except Exception: + print_if_verbose("Error processing %s\n" % fn) + if args.ignore_errors: + continue + raise + + if args.honggfuzz_file: + print_if_verbose(f"looking at {args.honggfuzz_file}") + with open(args.honggfuzz_file) as f: + pipe = None + crash = None + for line in f: + m = re.match(r'^\s*fuzzTarget\s*:\s*bin/fuzz_ndr_(\w+)\s*$', line) + if m: + pipe = m.group(1).split('_TYPE_', 1)[0] + print_if_verbose(f"found pipe {pipe}") + m = re.match(r'^FUZZ_FNAME: (\S+)$', line) + if m: + crash = m.group(1) + if args.crash_filter is not None: + if not re.search(args.crash_filter, crash): + print_if_verbose(f"skipping {crash}") + pipe = None + crash = None + continue + print_if_verbose(f"found crash {crash}") + if pipe is not None and crash is not None: + with open(crash, 'rb') as f: + process_one_file(f) + pipe = None + crash = None + + +main() diff --git a/lib/fuzzing/fuzz_cli_credentials_parse_string.c b/lib/fuzzing/fuzz_cli_credentials_parse_string.c new file mode 100644 index 0000000..b71ef35 --- /dev/null +++ b/lib/fuzzing/fuzz_cli_credentials_parse_string.c @@ -0,0 +1,63 @@ +/* + Fuzz cli_credentials_parse_string + Copyright (C) Catalyst IT 2020 + + 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 "auth/credentials/credentials.h" +#include "fuzzing/fuzzing.h" + +#define MAX_LENGTH (1024 * 10) +char buf[MAX_LENGTH + 1]; + +const enum credentials_obtained obtained = CRED_UNINITIALISED; + + +int LLVMFuzzerTestOneInput(uint8_t *input, size_t len) +{ + TALLOC_CTX *mem_ctx = NULL; + struct cli_credentials *credentials = NULL; + bool anon; + const char *username; + const char *domain; + + if (len > MAX_LENGTH) { + return 0; + } + + memcpy(buf, input, len); + buf[len] = '\0'; + + mem_ctx = talloc_new(NULL); + credentials = cli_credentials_init(mem_ctx); + + cli_credentials_parse_string(credentials, buf, obtained); + + anon = cli_credentials_is_anonymous(credentials); + + cli_credentials_get_ntlm_username_domain(credentials, + mem_ctx, + &username, + &domain); + + talloc_free(mem_ctx); + return 0; +} + + +int LLVMFuzzerInitialize(int *argc, char ***argv) +{ + return 0; +} diff --git a/lib/fuzzing/fuzz_dcerpc_parse_binding.c b/lib/fuzzing/fuzz_dcerpc_parse_binding.c new file mode 100644 index 0000000..6eb3c15 --- /dev/null +++ b/lib/fuzzing/fuzz_dcerpc_parse_binding.c @@ -0,0 +1,76 @@ +/* + Fuzz dcerpc_parse_binding + Copyright (C) Catalyst IT 2020 + + 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 "librpc/gen_ndr/ndr_epmapper.h" +#include "librpc/rpc/dcerpc.h" +#include "fuzzing/fuzzing.h" + +#define MAX_LENGTH (1024 * 10) +char buf[MAX_LENGTH + 1]; + + +int LLVMFuzzerTestOneInput(uint8_t *input, size_t len) +{ + TALLOC_CTX *mem_ctx = NULL; + struct dcerpc_binding *binding = NULL; + struct dcerpc_binding *dup = NULL; + struct epm_tower tower; + NTSTATUS status; + struct GUID guid; + + if (len > MAX_LENGTH) { + return 0; + } + + memcpy(buf, input, len); + buf[len] = '\0'; + + mem_ctx = talloc_new(NULL); + status = dcerpc_parse_binding(mem_ctx, buf, &binding); + + if (! NT_STATUS_IS_OK(status)) { + talloc_free(mem_ctx); + return 0; + } + + /* If the string parses, we try manipulating it a bit */ + + dcerpc_binding_string(mem_ctx, binding); + dcerpc_binding_get_abstract_syntax(binding); + dup = dcerpc_binding_dup(mem_ctx, binding); + + status = dcerpc_binding_build_tower(mem_ctx, + binding, + &tower); + if (NT_STATUS_IS_OK(status)) { + status = dcerpc_binding_from_tower(mem_ctx, + &tower, + &dup); + } + + guid = dcerpc_binding_get_object(binding); + + talloc_free(mem_ctx); + return 0; +} + + +int LLVMFuzzerInitialize(int *argc, char ***argv) +{ + return 0; +} diff --git a/lib/fuzzing/fuzz_ldap_decode.c b/lib/fuzzing/fuzz_ldap_decode.c new file mode 100644 index 0000000..e3bcf7b --- /dev/null +++ b/lib/fuzzing/fuzz_ldap_decode.c @@ -0,0 +1,66 @@ +/* + Fuzzing for ldap_decode. + Copyright (C) Michael Hanselmann 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 "fuzzing/fuzzing.h" +#include "lib/util/asn1.h" +#include "libcli/ldap/ldap_message.h" +#include "libcli/ldap/ldap_proto.h" + +int LLVMFuzzerInitialize(int *argc, char ***argv) +{ + return 0; +} + +int LLVMFuzzerTestOneInput(uint8_t *buf, size_t len) +{ + TALLOC_CTX *mem_ctx = talloc_init(__FUNCTION__); + struct asn1_data *asn1; + struct ldap_message *ldap_msg; + struct ldap_request_limits limits = { + /* + * The default size is currently 256000 bytes + */ + .max_search_size = 256000 + }; + NTSTATUS status; + + /* + * Need to limit the max parse tree depth to 250 to prevent + * ASAN detecting stack overflows. + */ + asn1 = asn1_init(mem_ctx, 250); + if (!asn1) { + goto out; + } + + asn1_load_nocopy(asn1, buf, len); + + ldap_msg = talloc(mem_ctx, struct ldap_message); + if (!ldap_msg) { + goto out; + } + + status = ldap_decode( + asn1, &limits, samba_ldap_control_handlers(), ldap_msg); + +out: + talloc_free(mem_ctx); + + return 0; +} diff --git a/lib/fuzzing/fuzz_ldb_dn_explode.c b/lib/fuzzing/fuzz_ldb_dn_explode.c new file mode 100644 index 0000000..e024212 --- /dev/null +++ b/lib/fuzzing/fuzz_ldb_dn_explode.c @@ -0,0 +1,53 @@ +/* + Fuzzing ldb_dn_explode + Copyright (C) Catalyst IT 2020 + + 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 "fuzzing/fuzzing.h" +#include "ldb.h" + + +#define MAX_LENGTH (2 * 1024 * 1024 - 1) +char buf[MAX_LENGTH + 1] = {0}; + +int LLVMFuzzerTestOneInput(uint8_t *input, size_t len) +{ + struct ldb_dn *dn = NULL; + struct ldb_context *ldb = ldb_init(NULL, NULL); + if (ldb == NULL) { + return 0; + } + /* + * We copy the buffer in order to NUL-terminate, because running off + * the end of the string would be an uninteresting crash. + */ + if (len > MAX_LENGTH) { + len = MAX_LENGTH; + } + memcpy(buf, input, len); + buf[len] = 0; + + dn = ldb_dn_new(ldb, ldb, buf); + ldb_dn_validate(dn); + TALLOC_FREE(ldb); + return 0; +} + + +int LLVMFuzzerInitialize(int *argc, char ***argv) +{ + return 0; +} diff --git a/lib/fuzzing/fuzz_ldb_ldif_read.c b/lib/fuzzing/fuzz_ldb_ldif_read.c new file mode 100644 index 0000000..f44e1ea --- /dev/null +++ b/lib/fuzzing/fuzz_ldb_ldif_read.c @@ -0,0 +1,56 @@ +/* + Fuzzing ldb_ldif_read_string + Copyright (C) Catalyst IT 2020 + + 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 "fuzzing/fuzzing.h" +#include "ldb_private.h" + + +#define MAX_LENGTH (2 * 1024 * 1024 - 1) +char buf[MAX_LENGTH + 1] = {0}; + +int LLVMFuzzerTestOneInput(uint8_t *input, size_t len) +{ + struct ldb_ldif *ldif = NULL; + const char *s = NULL; + struct ldb_context *ldb = ldb_init(NULL, NULL); + if (ldb == NULL) { + return 0; + } + + if (len > MAX_LENGTH) { + len = MAX_LENGTH; + } + memcpy(buf, input, len); + buf[len] = 0; + s = buf; + + ldif = ldb_ldif_read_string(ldb, &s); + + if(ldif != NULL) { + ldb_ldif_write_string(ldb, ldb, ldif); + ldb_ldif_write_redacted_trace_string(ldb, ldb, ldif); + } + TALLOC_FREE(ldb); + return 0; +} + + +int LLVMFuzzerInitialize(int *argc, char ***argv) +{ + return 0; +} diff --git a/lib/fuzzing/fuzz_ldb_parse_binary_decode.c b/lib/fuzzing/fuzz_ldb_parse_binary_decode.c new file mode 100644 index 0000000..feb26e2 --- /dev/null +++ b/lib/fuzzing/fuzz_ldb_parse_binary_decode.c @@ -0,0 +1,55 @@ +/* + Fuzzing ldb_binary_decode and ldb_binary_encode_string + Copyright (C) Catalyst IT 2020 + + 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 "fuzzing/fuzzing.h" +#include "ldb_private.h" + + +#define MAX_LENGTH (2 * 1024 * 1024 - 1) +char buf[MAX_LENGTH + 1] = {0}; + +static char * possibly_truncate(uint8_t *input, size_t len) +{ + if (len > MAX_LENGTH) { + len = MAX_LENGTH; + } + memcpy(buf, input, len); + buf[len] = 0; + return buf; +} + + +int LLVMFuzzerTestOneInput(uint8_t *input, size_t len) +{ + TALLOC_CTX *mem_ctx = talloc_init(__FUNCTION__); + struct ldb_val val = {0}; + const char *s = possibly_truncate(input, len); + + /* we treat the same string to encoding and decoding, not + * round-tripping. */ + val = ldb_binary_decode(mem_ctx, s); + ldb_binary_encode_string(mem_ctx, s); + TALLOC_FREE(mem_ctx); + return 0; +} + + +int LLVMFuzzerInitialize(int *argc, char ***argv) +{ + return 0; +} diff --git a/lib/fuzzing/fuzz_ldb_parse_control.c b/lib/fuzzing/fuzz_ldb_parse_control.c new file mode 100644 index 0000000..c78222c --- /dev/null +++ b/lib/fuzzing/fuzz_ldb_parse_control.c @@ -0,0 +1,55 @@ +/* + Fuzzing ldb_parse_control_from_string + Copyright (C) Catalyst IT 2020 + + 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 "fuzzing/fuzzing.h" +#include "ldb_private.h" + + +#define MAX_LENGTH (2 * 1024 * 1024 - 1) +char buf[MAX_LENGTH + 1] = {0}; + +int LLVMFuzzerTestOneInput(uint8_t *input, size_t len) +{ + struct ldb_control *control = NULL; + struct ldb_context *ldb = ldb_init(NULL, NULL); + if (ldb == NULL) { + return 0; + } + /* + * We copy the buffer in order to NUL-terminate, because running off + * the end of the string would be an uninteresting crash. + */ + if (len > MAX_LENGTH) { + len = MAX_LENGTH; + } + memcpy(buf, input, len); + buf[len] = 0; + + control = ldb_parse_control_from_string(ldb, ldb, buf); + if (control != NULL) { + ldb_control_to_string(ldb, control); + } + TALLOC_FREE(ldb); + return 0; +} + + +int LLVMFuzzerInitialize(int *argc, char ***argv) +{ + return 0; +} diff --git a/lib/fuzzing/fuzz_ldb_parse_tree.c b/lib/fuzzing/fuzz_ldb_parse_tree.c new file mode 100644 index 0000000..e22dd77 --- /dev/null +++ b/lib/fuzzing/fuzz_ldb_parse_tree.c @@ -0,0 +1,53 @@ +/* + Fuzzing for ldb_parse_tree + Copyright (C) Michael Hanselmann 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 "fuzzing/fuzzing.h" +#include "ldb.h" +#include "ldb_module.h" + +int LLVMFuzzerInitialize(int *argc, char ***argv) +{ + return 0; +} + +int LLVMFuzzerTestOneInput(uint8_t *buf, size_t len) +{ + TALLOC_CTX *mem_ctx = talloc_init(__FUNCTION__); + struct ldb_parse_tree *tree; + char *filter; + + if (len < 1) { + goto out; + } + + filter = talloc_strndup(mem_ctx, (const char*)buf, len); + + if (filter == NULL) { + goto out; + } + + tree = ldb_parse_tree(mem_ctx, filter); + + (void)ldb_filter_from_tree(mem_ctx, tree); + +out: + talloc_free(mem_ctx); + + return 0; +} diff --git a/lib/fuzzing/fuzz_lzxpress.c b/lib/fuzzing/fuzz_lzxpress.c new file mode 100644 index 0000000..61ce9e6 --- /dev/null +++ b/lib/fuzzing/fuzz_lzxpress.c @@ -0,0 +1,35 @@ +/* + Fuzzing for lzxpress_decompress + Copyright (C) Michael Hanselmann 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 "fuzzing/fuzzing.h" +#include "lzxpress.h" + +int LLVMFuzzerInitialize(int *argc, char ***argv) +{ + return 0; +} + +int LLVMFuzzerTestOneInput(uint8_t *buf, size_t len) +{ + static uint8_t output[1024 * 1024] = {0}; + + lzxpress_decompress(buf, len, output, sizeof(output)); + + return 0; +} diff --git a/lib/fuzzing/fuzz_lzxpress_compress.c b/lib/fuzzing/fuzz_lzxpress_compress.c new file mode 100644 index 0000000..39e909d --- /dev/null +++ b/lib/fuzzing/fuzz_lzxpress_compress.c @@ -0,0 +1,35 @@ +/* + Fuzzing for lzxpress_decompress + Copyright (C) Michael Hanselmann 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 "fuzzing/fuzzing.h" +#include "lzxpress.h" + +int LLVMFuzzerInitialize(int *argc, char ***argv) +{ + return 0; +} + +int LLVMFuzzerTestOneInput(uint8_t *buf, size_t len) +{ + static uint8_t output[1024 * 1024] = {0}; + + lzxpress_compress(buf, len, output, sizeof(output)); + + return 0; +} diff --git a/lib/fuzzing/fuzz_lzxpress_round_trip.c b/lib/fuzzing/fuzz_lzxpress_round_trip.c new file mode 100644 index 0000000..a6173bb --- /dev/null +++ b/lib/fuzzing/fuzz_lzxpress_round_trip.c @@ -0,0 +1,53 @@ +/* + Fuzzing for lzxpress_decompress + Copyright (C) Michael Hanselmann 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 "fuzzing/fuzzing.h" +#include "lzxpress.h" + +int LLVMFuzzerInitialize(int *argc, char ***argv) +{ + return 0; +} + +int LLVMFuzzerTestOneInput(uint8_t *buf, size_t len) +{ + static uint8_t compressed[1024 * 1024] = {0}; + static uint8_t decompressed[1024 * 1024] = {0}; + ssize_t compressed_size; + ssize_t decompressed_size; + + if (len > sizeof(decompressed)) { + return 0; + } + + compressed_size = lzxpress_compress(buf, len, + compressed, sizeof(compressed)); + + decompressed_size = lzxpress_decompress(compressed, compressed_size, + decompressed, sizeof(decompressed)); + + if (decompressed_size != len) { + abort(); + } + if (memcmp(buf, decompressed, len) != 0) { + abort(); + } + + return 0; +} 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; +} diff --git a/lib/fuzzing/fuzz_nmblib_parse_packet.c b/lib/fuzzing/fuzz_nmblib_parse_packet.c new file mode 100644 index 0000000..85dd823 --- /dev/null +++ b/lib/fuzzing/fuzz_nmblib_parse_packet.c @@ -0,0 +1,62 @@ +/* + Fuzz NMB parse_packet + Copyright (C) Catalyst IT 2020 + + 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 "../../source3/include/includes.h" +#include "libsmb/libsmb.h" +#include "libsmb/nmblib.h" +#include "fuzzing/fuzzing.h" + +#define PORT 138 +#define MAX_LENGTH (1024 * 1024) +char buf[MAX_LENGTH + 1]; + + +int LLVMFuzzerTestOneInput(uint8_t *input, size_t len) +{ + struct packet_struct *p = NULL; + struct in_addr ip = { + 0x0100007f /* 127.0.0.1 */ + }; + + p = parse_packet((char *)input, + len, + NMB_PACKET, + ip, + PORT); + /* + * We expect NULL (parse failure) most of the time. + * + * When it is not NULL we want to ensure the parsed packet is + * reasonably sound. + */ + + if (p != NULL) { + struct nmb_packet *nmb = &p->packet.nmb; + pull_ascii_nstring(buf, MAX_LENGTH, + nmb->question.question_name.name); + build_packet(buf, MAX_LENGTH, p); + free_packet(p); + } + return 0; +} + + +int LLVMFuzzerInitialize(int *argc, char ***argv) +{ + return 0; +} diff --git a/lib/fuzzing/fuzz_oLschema2ldif.c b/lib/fuzzing/fuzz_oLschema2ldif.c new file mode 100644 index 0000000..873e8f1 --- /dev/null +++ b/lib/fuzzing/fuzz_oLschema2ldif.c @@ -0,0 +1,71 @@ +/* + Fuzzing for oLschema2ldif + Copyright (C) Michael Hanselmann 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 "fuzzing.h" +#include "utils/oLschema2ldif/lib.h" + +static FILE *devnull; + +int LLVMFuzzerInitialize(int *argc, char ***argv) +{ + devnull = fopen("/dev/null", "w"); + + return 0; +} + +int LLVMFuzzerTestOneInput(uint8_t *buf, size_t len) +{ + TALLOC_CTX *mem_ctx; + struct conv_options opt; + + if (len == 0) { + /* + * Otherwise fmemopen() will return null and set errno + * to EINVAL + */ + return 0; + } + + mem_ctx = talloc_init(__FUNCTION__); + if (mem_ctx == NULL) { + return 0; + } + + opt.in = fmemopen(buf, len, "r"); + opt.out = devnull; + opt.ldb_ctx = ldb_init(mem_ctx, NULL); + if (opt.ldb_ctx == NULL || opt.in == NULL) { + talloc_free(mem_ctx); + return 0; + } + + opt.basedn = ldb_dn_new(mem_ctx, opt.ldb_ctx, ""); + if (opt.basedn == NULL) { + talloc_free(mem_ctx); + return 0; + } + + process_file(mem_ctx, &opt); + + fclose(opt.in); + + talloc_free(mem_ctx); + + return 0; +} diff --git a/lib/fuzzing/fuzz_parse_lpq_entry.c b/lib/fuzzing/fuzz_parse_lpq_entry.c new file mode 100644 index 0000000..720cc9b --- /dev/null +++ b/lib/fuzzing/fuzz_parse_lpq_entry.c @@ -0,0 +1,65 @@ +/* + Fuzzing parse_lpq_entry + Copyright (C) Douglas Bagnall <dbagnall@samba.org> 2021 + + 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 "../../source3/include/includes.h" +#include "printing.h" +#include "fuzzing/fuzzing.h" + + +int LLVMFuzzerInitialize(int *argc, char ***argv) +{ + return 0; +} + +#define MAX_LENGTH (1024 * 1024) +char line[MAX_LENGTH + 1]; + +int LLVMFuzzerTestOneInput(uint8_t *input, size_t len) +{ + enum printing_types printing_type; + print_queue_struct pq_buf = {0}; + print_status_struct status = {0}; + bool first; + unsigned x; + TALLOC_CTX *frame = NULL; + + if (len < 1 || len > MAX_LENGTH) { + return 0; + } + + x = input[0]; + input++; + len--; + + /* There are 14 types, default goes to bsd */ + printing_type = x & 15; + first = (x & 16) ? true : false; + + memcpy(line, input, len); + line[len] = '\0'; + + /* parse_lpq_bsd requires a stackframe */ + frame = talloc_stackframe(); + + parse_lpq_entry(printing_type, + line, + &pq_buf, /* out */ + &status, /* out */ + first); + talloc_free(frame); + return 0; +} diff --git a/lib/fuzzing/fuzz_reg_parse.c b/lib/fuzzing/fuzz_reg_parse.c new file mode 100644 index 0000000..a061cd6 --- /dev/null +++ b/lib/fuzzing/fuzz_reg_parse.c @@ -0,0 +1,46 @@ +/* + * Fuzzing for reg_parse + * Copyright (C) Michael Hanselmann 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 "fuzzing/fuzzing.h" +#include "lib/util/fault.h" +#include "registry.h" +#include "registry/reg_parse.h" + +static FILE *fp; + +int LLVMFuzzerInitialize(int *argc, char ***argv) +{ + fp = tmpfile(); + + return 0; +} + +int LLVMFuzzerTestOneInput(uint8_t *buf, size_t len) +{ + const reg_parse_callback cb = {0}; + + rewind(fp); + (void)fwrite(buf, len, 1, fp); + (void)fflush(fp); + rewind(fp); + + (void)reg_parse_fd(fileno(fp), &cb, ""); + + return 0; +} diff --git a/lib/fuzzing/fuzz_regfio.c b/lib/fuzzing/fuzz_regfio.c new file mode 100644 index 0000000..c4ced88 --- /dev/null +++ b/lib/fuzzing/fuzz_regfio.c @@ -0,0 +1,68 @@ +/* + * Unix SMB/CIFS implementation. + * Windows NT registry I/O library + * Copyright (C) Michael Hanselmann 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 "fuzzing/fuzzing.h" +#include "system/filesys.h" +#include "lib/util/fault.h" +#include "registry/reg_objects.h" +#include "registry/regfio.h" + +static FILE *fp; +static char filename[128]; + +int LLVMFuzzerInitialize(int *argc, char ***argv) +{ + fp = tmpfile(); + + (void)snprintf(filename, sizeof(filename), "/proc/self/fd/%d", fileno(fp)); + + return 0; +} + +int LLVMFuzzerTestOneInput(uint8_t *buf, size_t len) +{ + REGF_FILE* regfile; + REGF_NK_REC *nk, *subkey; + + rewind(fp); + (void)fwrite(buf, len, 1, fp); + (void)fflush(fp); + + regfile = regfio_open(filename, O_RDONLY, 0600); + if (!regfile) { + goto out; + } + + regfile->ignore_checksums = true; + + nk = regfio_rootkey(regfile); + if (nk != NULL) { + nk->subkey_index = 0; + while ((subkey = regfio_fetch_subkey(regfile, nk))) { + } + } + +out: + if (regfile != NULL) { + regfio_close(regfile); + } + + return 0; +} diff --git a/lib/fuzzing/fuzz_tiniparser.c b/lib/fuzzing/fuzz_tiniparser.c new file mode 100644 index 0000000..6908f18 --- /dev/null +++ b/lib/fuzzing/fuzz_tiniparser.c @@ -0,0 +1,51 @@ +/* + Fuzzing for trivial smb.conf parsing code. + Copyright (C) Michael Hanselmann 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 "fuzzing.h" +#include "lib/util/tiniparser.h" + +int LLVMFuzzerInitialize(int *argc, char ***argv) +{ + return 0; +} + +int LLVMFuzzerTestOneInput(uint8_t *buf, size_t len) +{ + FILE *fp = NULL; + struct tiniparser_dictionary *d = NULL; + + if (len == 0) { + /* + * Otherwise fmemopen() will return null and set errno + * to EINVAL + */ + return 0; + } + + fp = fmemopen(buf, len, "r"); + + d = tiniparser_load_stream(fp); + if (d != NULL) { + tiniparser_freedict(d); + } + + fclose(fp); + + return 0; +} diff --git a/lib/fuzzing/fuzzing.c b/lib/fuzzing/fuzzing.c new file mode 100644 index 0000000..f0d7fb4 --- /dev/null +++ b/lib/fuzzing/fuzzing.c @@ -0,0 +1,21 @@ +/* + Unix SMB/CIFS implementation. + Fuzzing utility functions + Copyright (C) Michael Hanselmann 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 "fuzzing/fuzzing.h" diff --git a/lib/fuzzing/fuzzing.h b/lib/fuzzing/fuzzing.h new file mode 100644 index 0000000..67e49c3 --- /dev/null +++ b/lib/fuzzing/fuzzing.h @@ -0,0 +1,30 @@ +/* + Unix SMB/CIFS implementation. + Fuzzing utility functions + Copyright (C) Michael Hanselmann 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/>. +*/ + +#ifndef _SAMBA_FUZZING_H +#define _SAMBA_FUZZING_H + +#include <stddef.h> +#include <stdint.h> + +/* Prototypes for fuzzing interface */ +int LLVMFuzzerInitialize(int *argc, char ***argv); +int LLVMFuzzerTestOneInput(uint8_t * buf, size_t len); + +#endif /* _SAMBA_FUZZING_H */ diff --git a/lib/fuzzing/oss-fuzz/build_image.sh b/lib/fuzzing/oss-fuzz/build_image.sh new file mode 100755 index 0000000..62a626b --- /dev/null +++ b/lib/fuzzing/oss-fuzz/build_image.sh @@ -0,0 +1,7 @@ +#!/bin/sh -e + +DIST=ubuntu2004 +SCRIPT_DIR=$(dirname $0) + +$SCRIPT_DIR/../../../bootstrap/generated-dists/$DIST/bootstrap.sh +$SCRIPT_DIR/../../../bootstrap/generated-dists/$DIST/locale.sh diff --git a/lib/fuzzing/oss-fuzz/build_samba.sh b/lib/fuzzing/oss-fuzz/build_samba.sh new file mode 100755 index 0000000..dc5387d --- /dev/null +++ b/lib/fuzzing/oss-fuzz/build_samba.sh @@ -0,0 +1,25 @@ +#!/bin/sh +# +# This is not a general-purpose build script, but instead one specific +# to the Google oss-fuzz compile environment. +# +# https://google.github.io/oss-fuzz/getting-started/new-project-guide/#Requirements +# +# https://github.com/google/oss-fuzz/blob/master/infra/base-images/base-builder/README.md#provided-environment-variables +# +# This file is run by +# https://github.com/google/oss-fuzz/blob/master/projects/samba/build.sh +# which does nothing else. +# +# Additional arguments are passed to configure, to allow this to be +# tested in autobuild.py +# + +# Ensure we give good trace info, fail right away and fail with unset +# variables +set -e +set -x +set -u + +$(dirname $0)/do_build.sh $@ +$(dirname $0)/check_build.sh $OUT diff --git a/lib/fuzzing/oss-fuzz/check_build.sh b/lib/fuzzing/oss-fuzz/check_build.sh new file mode 100755 index 0000000..6523bf3 --- /dev/null +++ b/lib/fuzzing/oss-fuzz/check_build.sh @@ -0,0 +1,48 @@ +#!/bin/sh -eux +# +# A very simple check script to confirm we still provide binaries +# that look like the targets oss-fuzz wants. +# +# A much stronger check is availble in oss-fuzz via +# infra/helper.py check_build samba +# + +# oss-fuzz provides an OUT variable, so for clarity this script +# uses the same. See build_samba.sh +OUT=$1 + +# build_samba.sh will have put a non-zero number of fuzzers here. If +# there are none, this will fail as it becomes literally fuzz_* + +seeds_found=no + +for bin in $OUT/fuzz_*; do + # we only want to look at the elf files, not the zips + if [ ${bin%_seed_corpus.zip} != $bin ]; then + continue + fi + # Confirm that the chrpath was reset to lib/ in the same directory + # as the binary. RPATH (not RUNPATH) is critical, otherwise + # libraries used by libraries won't be found on the oss-fuzz + # target host. + chrpath -l $bin | grep 'RPATH=$ORIGIN/lib' + + # Confirm that we link to at least some libraries in this + # directory (shows that the libraries were found and copied). + ldd $bin | grep "$OUT/lib" + num_libs=$(ldd $bin | grep -v ld-linux | grep -v linux-vdso | grep -v "$OUT/lib" | wc -l) + + if [ 0$num_libs -ne 0 ]; then + echo "some libraries not linked to $ORIGIN/lib, oss-fuzz will fail!" + exit 1 + fi + + if [ -f ${bin}_seed_corpus.zip ]; then + seeds_found=yes + fi +done + +if [ $seeds_found = no ]; then + echo "no seed zip files were found!" + exit 1 +fi diff --git a/lib/fuzzing/oss-fuzz/do_build.sh b/lib/fuzzing/oss-fuzz/do_build.sh new file mode 100755 index 0000000..3b2fdd0 --- /dev/null +++ b/lib/fuzzing/oss-fuzz/do_build.sh @@ -0,0 +1,294 @@ +#!/bin/sh +# +# This is not a general-purpose build script, but instead one specific +# to the Google oss-fuzz compile environment. +# +# https://google.github.io/oss-fuzz/getting-started/new-project-guide/#Requirements +# +# https://github.com/google/oss-fuzz/blob/master/infra/base-images/base-builder/README.md#provided-environment-variables +# +# This file is run by build_samba.sh, which is run by +# https://github.com/google/oss-fuzz/blob/master/projects/samba/build.sh +# which does nothing else. +# +# We have to push to oss-fuzz CFLAGS into the waf ADDITIONAL_CFLAGS +# as otherwise waf's configure fails linking the first test binary +# +# CFLAGS are supplied by the caller, eg the oss-fuzz compile command +# +# Additional arguments are passed to configure, to allow this to be +# tested in autobuild.py +# + +# Ensure we give good trace info, fail right away and fail with unset +# variables +set -e +set -x +set -u + +# It is critical that this script, just as the rest of Samba's GitLab +# CI docker has LANG set to en_US.utf8 (oss-fuzz fails to set this) +if [ -f /etc/default/locale ]; then + . /etc/default/locale +elif [ -f /etc/locale.conf ]; then + . /etc/locale.conf +fi +export LANG +export LC_ALL + +ADDITIONAL_CFLAGS="$CFLAGS" +export ADDITIONAL_CFLAGS +CFLAGS="" +export CFLAGS +LD="$CXX" +export LD + +# Use the system Python, not the OSS-Fuzz provided statically linked +# and instrumented Python, because we can't statically link. + +PYTHON=/usr/bin/python3 +export PYTHON + +# $SANITIZER is provided by the oss-fuzz "compile" command +# +# We need to add the waf configure option as otherwise when we also +# get (eg) -fsanitize=address via the CFLAGS we will fail to link +# correctly + +case "$SANITIZER" in +address) + SANITIZER_ARG='--address-sanitizer' + ;; +undefined) + SANITIZER_ARG='--undefined-sanitizer' + ;; +coverage) + # Thankfully clang operating as ld has no objection to the + # cc style options, so we can just set ADDITIONAL_LDFLAGS + # to ensure the coverage build is done, despite waf splitting + # the compile and link phases. + ADDITIONAL_LDFLAGS="${ADDITIONAL_LDFLAGS:-} $COVERAGE_FLAGS" + export ADDITIONAL_LDFLAGS + + SANITIZER_ARG='' + ;; +esac + +# $LIB_FUZZING_ENGINE is provided by the oss-fuzz "compile" command +# + +# --disable-new-dtags linker flag creates fuzzer binaries with RPATH +# header instead of RUNPATH header. Modern linkers use RUNPATH by +# default. +./configure -C --without-gettext --enable-debug --enable-developer \ + --enable-libfuzzer \ + $SANITIZER_ARG \ + --disable-warnings-as-errors \ + --abi-check-disable \ + "--fuzz-target-ldflags=-Wl,--disable-new-dtags $LIB_FUZZING_ENGINE" \ + --nonshared-binary=ALL \ + "$@" \ + LINK_CC="$CXX" + +make -j + +# Make a directory for the system shared libraries to be copied into +mkdir -p $OUT/lib + +# oss-fuzz would prefer for all the binaries put into $OUT to be +# statically linked. +# +# We can't static link to all the system libs with waf, so copy the +# libraries we need to $OUT/lib and set the rpath to point there. +# This is similar to how firefox handles this. +# +# NOTE on RPATH vs RUNPATH: +# +# RUNPATH appears to be the more modern version, and so modern ld.bfd +# and ld.gold only set RUNPATH. RUNPATH makes sense on most systems, +# but not for our hack. +# +# If we use RUNPATH, we can get an error like this: +# Step #6: Error occured while running fuzz_nmblib_parse_packet: +# Step #6: /workspace/out/coverage/fuzz_nmblib_parse_packet: error while loading shared libraries: libavahi-common.so.3: cannot open shared object file: No such file or directory +# +# This is because the full contents of $OUT are copied to yet another +# host, which otherwise does not have much of linux at all. oss-fuzz +# prefers a static binary because that will 'just work', but we can't +# do that, so we need to use linker tricks. +# +# If the linker used RUNPATH (eg ld.bfd on Ubuntu 18.04 and later, ld.gold): +# * bin=fuzz_nmblib_parse_packet +# * OUT=/tmp/3/b12207/prefix/samba-fuzz +# * chrpath -r '$ORIGIN/lib' $OUT/$bin' +# * ldd $OUT/$bin +# linux-vdso.so.1 (0x00007ffd4b7a5000) +# libasan.so.5 => /tmp/3/b12207/prefix/samba-fuzz/lib/libasan.so.5 (0x00007ff25bdd0000) +# libldap_r-2.4.so.2 => /tmp/3/b12207/prefix/samba-fuzz/lib/libldap_r-2.4.so.2 (0x00007ff25bd7a000) +# liblber-2.4.so.2 => /tmp/3/b12207/prefix/samba-fuzz/lib/liblber-2.4.so.2 (0x00007ff25bd69000) +# libunwind-x86_64.so.8 => /tmp/3/b12207/prefix/samba-fuzz/lib/libunwind-x86_64.so.8 (0x00007ff25bd47000) +# libunwind.so.8 => /tmp/3/b12207/prefix/samba-fuzz/lib/libunwind.so.8 (0x00007ff25bd2a000) +# libgnutls.so.30 => /tmp/3/b12207/prefix/samba-fuzz/lib/libgnutls.so.30 (0x00007ff25bb54000) +# libdl.so.2 => /tmp/3/b12207/prefix/samba-fuzz/lib/libdl.so.2 (0x00007ff25bb4c000) +# libz.so.1 => /tmp/3/b12207/prefix/samba-fuzz/lib/libz.so.1 (0x00007ff25bb30000) +# libjansson.so.4 => /tmp/3/b12207/prefix/samba-fuzz/lib/libjansson.so.4 (0x00007ff25bb21000) +# libresolv.so.2 => /tmp/3/b12207/prefix/samba-fuzz/lib/libresolv.so.2 (0x00007ff25bb05000) +# libsystemd.so.0 => /tmp/3/b12207/prefix/samba-fuzz/lib/libsystemd.so.0 (0x00007ff25ba58000) +# libpthread.so.0 => /tmp/3/b12207/prefix/samba-fuzz/lib/libpthread.so.0 (0x00007ff25ba35000) +# libicuuc.so.66 => /tmp/3/b12207/prefix/samba-fuzz/lib/libicuuc.so.66 (0x00007ff25b84d000) +# libicui18n.so.66 => /tmp/3/b12207/prefix/samba-fuzz/lib/libicui18n.so.66 (0x00007ff25b54e000) +# libcap.so.2 => /tmp/3/b12207/prefix/samba-fuzz/lib/libcap.so.2 (0x00007ff25b545000) +# libbsd.so.0 => /tmp/3/b12207/prefix/samba-fuzz/lib/libbsd.so.0 (0x00007ff25b52b000) +# libnsl.so.1 => /tmp/3/b12207/prefix/samba-fuzz/lib/libnsl.so.1 (0x00007ff25b50e000) +# libc.so.6 => /tmp/3/b12207/prefix/samba-fuzz/lib/libc.so.6 (0x00007ff25b31c000) +# librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007ff25b2f2000) +# libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007ff25b1a3000) +# libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007ff25b188000) +# libsasl2.so.2 => /usr/lib/x86_64-linux-gnu/libsasl2.so.2 (0x00007ff25b16b000) +# libgssapi.so.3 => /usr/lib/x86_64-linux-gnu/libgssapi.so.3 (0x00007ff25b126000) +# liblzma.so.5 => /lib/x86_64-linux-gnu/liblzma.so.5 (0x00007ff25b0fd000) +# /lib64/ld-linux-x86-64.so.2 (0x00007ff25ea0c000) +# libp11-kit.so.0 => /usr/lib/x86_64-linux-gnu/libp11-kit.so.0 (0x00007ff25afc5000) +# libidn2.so.0 => /usr/lib/x86_64-linux-gnu/libidn2.so.0 (0x00007ff25afa4000) +# libunistring.so.2 => /usr/lib/x86_64-linux-gnu/libunistring.so.2 (0x00007ff25ae22000) +# libtasn1.so.6 => /usr/lib/x86_64-linux-gnu/libtasn1.so.6 (0x00007ff25ae0c000) +# libnettle.so.7 => /usr/lib/x86_64-linux-gnu/libnettle.so.7 (0x00007ff25add2000) +# libhogweed.so.5 => /usr/lib/x86_64-linux-gnu/libhogweed.so.5 (0x00007ff25ad9a000) +# libgmp.so.10 => /usr/lib/x86_64-linux-gnu/libgmp.so.10 (0x00007ff25ad14000) +# liblz4.so.1 => /usr/lib/x86_64-linux-gnu/liblz4.so.1 (0x00007ff25acf3000) +# libgcrypt.so.20 => /usr/lib/x86_64-linux-gnu/libgcrypt.so.20 (0x00007ff25abd5000) +# libicudata.so.66 => /usr/lib/x86_64-linux-gnu/libicudata.so.66 (0x00007ff259114000) +# libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007ff258f33000) +# libheimntlm.so.0 => /usr/lib/x86_64-linux-gnu/libheimntlm.so.0 (0x00007ff258f25000) +# libkrb5.so.26 => /usr/lib/x86_64-linux-gnu/libkrb5.so.26 (0x00007ff258e92000) +# libasn1.so.8 => /usr/lib/x86_64-linux-gnu/libasn1.so.8 (0x00007ff258deb000) +# libcom_err.so.2 => /lib/x86_64-linux-gnu/libcom_err.so.2 (0x00007ff258de4000) +# libhcrypto.so.4 => /usr/lib/x86_64-linux-gnu/libhcrypto.so.4 (0x00007ff258dac000) +# libroken.so.18 => /usr/lib/x86_64-linux-gnu/libroken.so.18 (0x00007ff258d93000) +# libffi.so.7 => /usr/lib/x86_64-linux-gnu/libffi.so.7 (0x00007ff258d85000) +# libgpg-error.so.0 => /lib/x86_64-linux-gnu/libgpg-error.so.0 (0x00007ff258d62000) +# libwind.so.0 => /usr/lib/x86_64-linux-gnu/libwind.so.0 (0x00007ff258d38000) +# libheimbase.so.1 => /usr/lib/x86_64-linux-gnu/libheimbase.so.1 (0x00007ff258d26000) +# libhx509.so.5 => /usr/lib/x86_64-linux-gnu/libhx509.so.5 (0x00007ff258cd8000) +# libsqlite3.so.0 => /usr/lib/x86_64-linux-gnu/libsqlite3.so.0 (0x00007ff258bad000) +# libcrypt.so.1 => /lib/x86_64-linux-gnu/libcrypt.so.1 (0x00007ff258b72000) +# +# Note how all the dependencies of libc and gnutls are not forced to +# $OUT/lib (via the magic $ORIGIN variable, meaning the directory of +# the binary). These will not be found on the target system! +# +# If the linker used RPATH however +# * bin=fuzz_nmblib_parse_packet +# * OUT=/tmp/3/b22/prefix/samba-fuzz +# * chrpath -r '$ORIGIN/lib' $OUT/$bin' +# * ldd $OUT/$bin +# linux-vdso.so.1 => (0x00007ffef85c7000) +# libasan.so.2 => /tmp/3/b22/prefix/samba-fuzz/lib/libasan.so.2 (0x00007f3668b4f000) +# libldap_r-2.4.so.2 => /tmp/3/b22/prefix/samba-fuzz/lib/libldap_r-2.4.so.2 (0x00007f36688fe000) +# liblber-2.4.so.2 => /tmp/3/b22/prefix/samba-fuzz/lib/liblber-2.4.so.2 (0x00007f36686ef000) +# libunwind-x86_64.so.8 => /tmp/3/b22/prefix/samba-fuzz/lib/libunwind-x86_64.so.8 (0x00007f36684d0000) +# libunwind.so.8 => /tmp/3/b22/prefix/samba-fuzz/lib/libunwind.so.8 (0x00007f36682b5000) +# libgnutls.so.30 => /tmp/3/b22/prefix/samba-fuzz/lib/libgnutls.so.30 (0x00007f3667f85000) +# libdl.so.2 => /tmp/3/b22/prefix/samba-fuzz/lib/libdl.so.2 (0x00007f3667d81000) +# libz.so.1 => /tmp/3/b22/prefix/samba-fuzz/lib/libz.so.1 (0x00007f3667b67000) +# libjansson.so.4 => /tmp/3/b22/prefix/samba-fuzz/lib/libjansson.so.4 (0x00007f366795a000) +# libresolv.so.2 => /tmp/3/b22/prefix/samba-fuzz/lib/libresolv.so.2 (0x00007f366773f000) +# libsystemd.so.0 => /tmp/3/b22/prefix/samba-fuzz/lib/libsystemd.so.0 (0x00007f366be2f000) +# libpthread.so.0 => /tmp/3/b22/prefix/samba-fuzz/lib/libpthread.so.0 (0x00007f3667522000) +# libicuuc.so.55 => /tmp/3/b22/prefix/samba-fuzz/lib/libicuuc.so.55 (0x00007f366718e000) +# libicui18n.so.55 => /tmp/3/b22/prefix/samba-fuzz/lib/libicui18n.so.55 (0x00007f3666d2c000) +# libcap.so.2 => /tmp/3/b22/prefix/samba-fuzz/lib/libcap.so.2 (0x00007f3666b26000) +# libbsd.so.0 => /tmp/3/b22/prefix/samba-fuzz/lib/libbsd.so.0 (0x00007f3666911000) +# libnsl.so.1 => /tmp/3/b22/prefix/samba-fuzz/lib/libnsl.so.1 (0x00007f36666f8000) +# libc.so.6 => /tmp/3/b22/prefix/samba-fuzz/lib/libc.so.6 (0x00007f366632e000) +# libm.so.6 => /tmp/3/b22/prefix/samba-fuzz/lib/libm.so.6 (0x00007f3666025000) +# libgcc_s.so.1 => /tmp/3/b22/prefix/samba-fuzz/lib/libgcc_s.so.1 (0x00007f3665e0f000) +# libsasl2.so.2 => /tmp/3/b22/prefix/samba-fuzz/lib/libsasl2.so.2 (0x00007f3665bf4000) +# libgssapi.so.3 => /tmp/3/b22/prefix/samba-fuzz/lib/libgssapi.so.3 (0x00007f36659b3000) +# liblzma.so.5 => /tmp/3/b22/prefix/samba-fuzz/lib/liblzma.so.5 (0x00007f3665791000) +# /lib64/ld-linux-x86-64.so.2 (0x00007f366bc93000) +# libp11-kit.so.0 => /tmp/3/b22/prefix/samba-fuzz/lib/libp11-kit.so.0 (0x00007f366552d000) +# libidn.so.11 => /tmp/3/b22/prefix/samba-fuzz/lib/libidn.so.11 (0x00007f36652fa000) +# libtasn1.so.6 => /tmp/3/b22/prefix/samba-fuzz/lib/libtasn1.so.6 (0x00007f36650e7000) +# libnettle.so.6 => /tmp/3/b22/prefix/samba-fuzz/lib/libnettle.so.6 (0x00007f3664eb1000) +# libhogweed.so.4 => /tmp/3/b22/prefix/samba-fuzz/lib/libhogweed.so.4 (0x00007f3664c7e000) +# libgmp.so.10 => /tmp/3/b22/prefix/samba-fuzz/lib/libgmp.so.10 (0x00007f36649fe000) +# libselinux.so.1 => /tmp/3/b22/prefix/samba-fuzz/lib/libselinux.so.1 (0x00007f36647dc000) +# librt.so.1 => /tmp/3/b22/prefix/samba-fuzz/lib/librt.so.1 (0x00007f36645d4000) +# libgcrypt.so.20 => /tmp/3/b22/prefix/samba-fuzz/lib/libgcrypt.so.20 (0x00007f36642f3000) +# libicudata.so.55 => /tmp/3/b22/prefix/samba-fuzz/lib/libicudata.so.55 (0x00007f366283c000) +# libstdc++.so.6 => /tmp/3/b22/prefix/samba-fuzz/lib/libstdc++.so.6 (0x00007f36624ba000) +# libheimntlm.so.0 => /tmp/3/b22/prefix/samba-fuzz/lib/libheimntlm.so.0 (0x00007f36622b1000) +# libkrb5.so.26 => /tmp/3/b22/prefix/samba-fuzz/lib/libkrb5.so.26 (0x00007f3662027000) +# libasn1.so.8 => /tmp/3/b22/prefix/samba-fuzz/lib/libasn1.so.8 (0x00007f3661d85000) +# libcom_err.so.2 => /tmp/3/b22/prefix/samba-fuzz/lib/libcom_err.so.2 (0x00007f3661b81000) +# libhcrypto.so.4 => /tmp/3/b22/prefix/samba-fuzz/lib/libhcrypto.so.4 (0x00007f366194e000) +# libroken.so.18 => /tmp/3/b22/prefix/samba-fuzz/lib/libroken.so.18 (0x00007f3661738000) +# libffi.so.6 => /tmp/3/b22/prefix/samba-fuzz/lib/libffi.so.6 (0x00007f3661530000) +# libpcre.so.3 => /tmp/3/b22/prefix/samba-fuzz/lib/libpcre.so.3 (0x00007f36612c0000) +# libgpg-error.so.0 => /tmp/3/b22/prefix/samba-fuzz/lib/libgpg-error.so.0 (0x00007f36610ac000) +# libwind.so.0 => /tmp/3/b22/prefix/samba-fuzz/lib/libwind.so.0 (0x00007f3660e83000) +# libheimbase.so.1 => /tmp/3/b22/prefix/samba-fuzz/lib/libheimbase.so.1 (0x00007f3660c74000) +# libhx509.so.5 => /tmp/3/b22/prefix/samba-fuzz/lib/libhx509.so.5 (0x00007f3660a29000) +# libsqlite3.so.0 => /tmp/3/b22/prefix/samba-fuzz/lib/libsqlite3.so.0 (0x00007f3660754000) +# libcrypt.so.1 => /tmp/3/b22/prefix/samba-fuzz/lib/libcrypt.so.1 (0x00007f366051c000) +# +# See how the runtime linker seems to honour the RPATH for +# dependencies of dependencies in this case. This helps us us lot. + +for x in bin/fuzz_*; do + # Copy any system libraries needed by this fuzzer to $OUT/lib. + + # We run ldd on $x, the fuzz_binary in bin/ which has not yet had + # the RPATH altered. This is clearer for debugging in local + # development builds as $OUT is not cleaned between runs. + # + # Otherwise trying to re-run this can see cp can fail with: + # cp: '/out/lib/libgcc_s.so.1' and '/out/lib/libgcc_s.so.1' are the same file + # which is really confusing! + + # The cut for ( and ' ' removes the special case references to: + # linux-vdso.so.1 => (0x00007ffe8f2b2000) + # /lib64/ld-linux-x86-64.so.2 (0x00007fc63ea6f000) + + ldd $x | cut -f 2 -d '>' | cut -f 1 -d \( | cut -f 2 -d ' ' | xargs -i cp \{\} $OUT/lib/ + + cp $x $OUT/ + bin=$(basename $x) + + # This means the copied libraries are found on the runner. + # + # The binaries should we built with RPATH, not RUNPATH, to allow + # libraries used by libraries to be found. This command retains the + # RPATH/RUNPATH header and only changes the path. We later verify this + # in the check_build.sh script. + chrpath -r '$ORIGIN/lib' $OUT/$bin + + # Truncate the original binary to save space + echo -n >$x + +done + +# Strip RUNPATH: or RPATH: entries from shared libraries copied over to $OUT/lib. +# When those libraries get loaded and have further dependencies, a RUNPATH: header +# will cause the dynamic linker to search in the runpath, and not in $OUT/lib, +# and there's no way it will be found in the fuzzing env. +# +# So how is the indirect depedency found in $OUT/lib? Well, suppose the fuzzer binary +# links library A which links library B. During linking, both A and B as listed in the +# executable file's runtime dependencies (This was pioneered in Fedora 13 in 2010, but +# is common behavior now). So we have the fuzzer binary with RPATH set to $OUT/lib, and +# a dependency on library B, and it will therefor find library B in $OUT/lib. On the +# hand, if we keep the RUNPATH in library A, and load A first, it will try loading +# library B as a dependency of A from the wrong place. +chrpath -d $OUT/lib/* + +# Grap the seeds dictionary from github and put the seed zips in place +# beside their executables. + +wget https://gitlab.com/samba-team/samba-fuzz-seeds/-/jobs/artifacts/master/download?job=zips \ + -O seeds.zip + +# We might not have unzip, but we do have python +$PYTHON -mzipfile -e seeds.zip $OUT +rm -f seeds.zip diff --git a/lib/fuzzing/wscript_build b/lib/fuzzing/wscript_build new file mode 100644 index 0000000..9c559b3 --- /dev/null +++ b/lib/fuzzing/wscript_build @@ -0,0 +1,197 @@ +#!/usr/bin/env python + +from waflib import Build + +bld.SAMBA_SUBSYSTEM('fuzzing', + source='fuzzing.c', + deps='talloc') + +bld.SAMBA_SUBSYSTEM('afl-fuzz-main', + source='afl-fuzz-main.c', + deps='samba-util', + enabled=bld.env.enable_afl_fuzzer + ) + +bld.SAMBA_BINARY('fuzz_tiniparser', + source='fuzz_tiniparser.c', + deps='fuzzing tiniparser talloc afl-fuzz-main', + fuzzer=True) + +bld.SAMBA_BINARY('fuzz_parse_lpq_entry', + source='fuzz_parse_lpq_entry.c', + deps='fuzzing afl-fuzz-main smbd_base PRINTING', + fuzzer=True) + +bld.SAMBA_BINARY('fuzz_oLschema2ldif', + source='fuzz_oLschema2ldif.c', + deps='fuzzing oLschema2ldif-lib afl-fuzz-main', + fuzzer=True) + +bld.SAMBA_BINARY('fuzz_reg_parse', + source='fuzz_reg_parse.c', + deps='fuzzing samba3-util smbconf REGFIO afl-fuzz-main', + fuzzer=True) + +bld.SAMBA_BINARY('fuzz_nmblib_parse_packet', + source='fuzz_nmblib_parse_packet.c', + deps='fuzzing libsmb afl-fuzz-main', + fuzzer=True) + +bld.SAMBA_BINARY('fuzz_regfio', + source='fuzz_regfio.c', + deps='fuzzing samba3-util smbconf REGFIO afl-fuzz-main', + fuzzer=True) + +bld.SAMBA_BINARY('fuzz_lzxpress', + source='fuzz_lzxpress.c', + deps='fuzzing LZXPRESS afl-fuzz-main', + fuzzer=True) + +bld.SAMBA_BINARY('fuzz_lzxpress_compress', + source='fuzz_lzxpress_compress.c', + deps='fuzzing LZXPRESS afl-fuzz-main', + fuzzer=True) + +bld.SAMBA_BINARY('fuzz_lzxpress_round_trip', + source='fuzz_lzxpress_round_trip.c', + deps='fuzzing LZXPRESS afl-fuzz-main', + fuzzer=True) + +bld.SAMBA_BINARY('fuzz_ldap_decode', + source='fuzz_ldap_decode.c', + deps='fuzzing cli-ldap afl-fuzz-main', + fuzzer=True) + +bld.SAMBA_BINARY('fuzz_ldb_parse_control', + source='fuzz_ldb_parse_control.c', + deps='fuzzing ldb afl-fuzz-main', + fuzzer=True) + +bld.SAMBA_BINARY('fuzz_ldb_dn_explode', + source='fuzz_ldb_dn_explode.c', + deps='fuzzing ldb afl-fuzz-main', + fuzzer=True) + +bld.SAMBA_BINARY('fuzz_ldb_ldif_read', + source='fuzz_ldb_ldif_read.c', + deps='fuzzing ldb afl-fuzz-main', + fuzzer=True) + +bld.SAMBA_BINARY('fuzz_ldb_parse_binary_decode', + source='fuzz_ldb_parse_binary_decode.c', + deps='fuzzing ldb afl-fuzz-main', + fuzzer=True) + +bld.SAMBA_BINARY('fuzz_ldb_parse_tree', + source='fuzz_ldb_parse_tree.c', + deps='fuzzing ldb afl-fuzz-main', + fuzzer=True) + +bld.SAMBA_BINARY('fuzz_dcerpc_parse_binding', + source='fuzz_dcerpc_parse_binding.c', + deps='fuzzing dcerpc afl-fuzz-main', + fuzzer=True) + +bld.SAMBA_BINARY('fuzz_cli_credentials_parse_string', + source='fuzz_cli_credentials_parse_string.c', + deps='fuzzing samba-credentials afl-fuzz-main', + fuzzer=True) + +# The fuzz_type and fuzz_function parameters make the built +# fuzzer take the same input as ndrdump and so the same that +# could be sent to the client or server as the stub data. + +def SAMBA_NDR_FUZZ(bld, interface, auto_deps=False, + fuzz_type=None, fuzz_function=None): + 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') + + cflags = "-D FUZZ_PIPE_TABLE=ndr_table_%s" % interface + if fuzz_type: + name += "_%s" % (fuzz_type) + cflags += " -D FUZZ_TYPE=%s " % (fuzz_type) + if fuzz_type and fuzz_function: + name += "_%d" % (fuzz_function) + cflags += " -D FUZZ_FUNCTION=%d" % (fuzz_function) + + fuzz_named_src = os.path.join(fuzz_reldir, + '%s.c' % (name)) + # 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}') + + if auto_deps: + deps = "afl-fuzz-main talloc ndr NDR_%s" % interface.upper() + else: + deps = "afl-fuzz-main ndr-table NDR_DCERPC" + + bld.SAMBA_BINARY(name, source=fuzz_named_src, + cflags = cflags, + deps = deps, + 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('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') + +# 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') + +# Specific struct or function on the interface + +bld.SAMBA_NDR_FUZZ('spoolss', + auto_deps=True, + fuzz_type="TYPE_IN", + fuzz_function=65) |