mirror of
https://github.com/samba-team/samba.git
synced 2024-12-23 17:34:34 +03:00
0cb833b32c
Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abartlet@samba.org>
338 lines
7.5 KiB
C
338 lines
7.5 KiB
C
/*
|
|
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;
|
|
}
|