mirror of
https://github.com/NLnetLabs/unbound.git
synced 2026-08-17 21:25:50 +02:00
- Fix #1288: [FR] Improve fuzzing of unbound by adapting the netbound
program.
This commit is contained in:
+12
-4
@@ -900,8 +900,10 @@ run_scenario(struct replay_runtime* runtime)
|
||||
runtime->now->evt_type == repevt_front_reply) {
|
||||
answer_check_it(runtime);
|
||||
advance_moment(runtime);
|
||||
} else if(pending_matches_range(runtime, &entry, &pending)) {
|
||||
answer_callback_from_entry(runtime, entry, pending);
|
||||
} else if(runtime->now && pending_matches_range(runtime,
|
||||
&entry, &pending)) {
|
||||
if(entry)
|
||||
answer_callback_from_entry(runtime, entry, pending);
|
||||
} else {
|
||||
do_moment_and_advance(runtime);
|
||||
}
|
||||
@@ -1274,7 +1276,7 @@ struct serviced_query* outnet_serviced_query(struct outside_network* outnet,
|
||||
(flags&~(BIT_RD|BIT_CD))?" MORE":"", (dnssec)?" DO":"");
|
||||
|
||||
/* create packet with EDNS */
|
||||
pend->buffer = sldns_buffer_new(512);
|
||||
pend->buffer = sldns_buffer_new(4096);
|
||||
log_assert(pend->buffer);
|
||||
sldns_buffer_write_u16(pend->buffer, 0); /* id */
|
||||
sldns_buffer_write_u16(pend->buffer, flags);
|
||||
@@ -1334,7 +1336,13 @@ struct serviced_query* outnet_serviced_query(struct outside_network* outnet,
|
||||
edns.opt_list_in = NULL;
|
||||
edns.opt_list_out = per_upstream_opt_list;
|
||||
edns.opt_list_inplace_cb_out = NULL;
|
||||
attach_edns_record(pend->buffer, &edns);
|
||||
if(sldns_buffer_capacity(pend->buffer) >=
|
||||
sldns_buffer_limit(pend->buffer)
|
||||
+calc_edns_field_size(&edns)) {
|
||||
attach_edns_record(pend->buffer, &edns);
|
||||
} else {
|
||||
verbose(VERB_ALGO, "edns field too large to fit");
|
||||
}
|
||||
}
|
||||
memcpy(&pend->addr, addr, addrlen);
|
||||
pend->addrlen = addrlen;
|
||||
|
||||
+3
-1
@@ -795,7 +795,7 @@ macro_expand(rbtree_type* store, struct replay_runtime* runtime, char** text)
|
||||
char buf[10240];
|
||||
char* at = *text;
|
||||
size_t len = macro_length(at);
|
||||
int dofunc = 0;
|
||||
int tries = 0, dofunc = 0;
|
||||
char* arithstart = NULL;
|
||||
if(len >= sizeof(buf))
|
||||
return NULL; /* too long */
|
||||
@@ -834,6 +834,8 @@ macro_expand(rbtree_type* store, struct replay_runtime* runtime, char** text)
|
||||
/* actual macro text expansion */
|
||||
while(*at) {
|
||||
size_t remain = sizeof(buf)-strlen(buf);
|
||||
if(tries++ > 10000)
|
||||
return NULL; /* looks like got into an infinite loop, bail out */
|
||||
if(strncmp(at, "${", 2) == 0) {
|
||||
at = do_macro_recursion(store, runtime, at, remain);
|
||||
} else if(*at == '$') {
|
||||
|
||||
+125
-15
@@ -339,6 +339,35 @@ static void remove_configfile(void)
|
||||
cfgfiles = NULL;
|
||||
}
|
||||
|
||||
/** perform the playback on the playback_file with the args. */
|
||||
static int
|
||||
perform_playback(char* playback_file, int pass_argc, char** pass_argv)
|
||||
{
|
||||
struct replay_scenario* scen = NULL;
|
||||
int c, res;
|
||||
|
||||
/* setup test environment */
|
||||
scen = setup_playback(playback_file, &pass_argc, pass_argv);
|
||||
/* init fake event backend */
|
||||
fake_event_init(scen);
|
||||
|
||||
pass_argv[pass_argc] = NULL;
|
||||
echo_cmdline(pass_argc, pass_argv);
|
||||
|
||||
/* run the normal daemon */
|
||||
res = daemon_main(pass_argc, pass_argv);
|
||||
|
||||
fake_event_cleanup();
|
||||
for(c=1; c<pass_argc; c++)
|
||||
free(pass_argv[c]);
|
||||
return res;
|
||||
}
|
||||
|
||||
/* For fuzzing the main routine is replaced with
|
||||
* LLVMFuzzerTestOneInput. */
|
||||
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
|
||||
#define main dummy_main
|
||||
#endif
|
||||
/**
|
||||
* Main fake event test program. Setup, teardown and report errors.
|
||||
* @param argc: arg count.
|
||||
@@ -354,7 +383,6 @@ main(int argc, char* argv[])
|
||||
char* playback_file = NULL;
|
||||
int init_optind = optind;
|
||||
char* init_optarg = optarg;
|
||||
struct replay_scenario* scen = NULL;
|
||||
|
||||
/* we do not want the test to depend on the timezone */
|
||||
(void)putenv("TZ=UTC");
|
||||
@@ -462,24 +490,11 @@ main(int argc, char* argv[])
|
||||
if(atexit(&remove_configfile) != 0)
|
||||
fatal_exit("atexit() failed: %s", strerror(errno));
|
||||
|
||||
/* setup test environment */
|
||||
scen = setup_playback(playback_file, &pass_argc, pass_argv);
|
||||
/* init fake event backend */
|
||||
fake_event_init(scen);
|
||||
|
||||
pass_argv[pass_argc] = NULL;
|
||||
echo_cmdline(pass_argc, pass_argv);
|
||||
|
||||
/* reset getopt processing */
|
||||
optind = init_optind;
|
||||
optarg = init_optarg;
|
||||
|
||||
/* run the normal daemon */
|
||||
res = daemon_main(pass_argc, pass_argv);
|
||||
|
||||
fake_event_cleanup();
|
||||
for(c=1; c<pass_argc; c++)
|
||||
free(pass_argv[c]);
|
||||
res = perform_playback(playback_file, pass_argc, pass_argv);
|
||||
if(res == 0) {
|
||||
log_info("Testbound Exit Success\n");
|
||||
/* remove configfile from here, the atexit() is for when
|
||||
@@ -499,6 +514,101 @@ main(int argc, char* argv[])
|
||||
return res;
|
||||
}
|
||||
|
||||
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
|
||||
static int delete_file(const char *pathname) {
|
||||
int ret = unlink(pathname);
|
||||
free((void *)pathname);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static char *buf_to_file(const uint8_t *buf, size_t size) {
|
||||
int fd;
|
||||
size_t pos;
|
||||
char *pathname = strdup("/tmp/fuzz-XXXXXX");
|
||||
if (pathname == NULL)
|
||||
return NULL;
|
||||
|
||||
fd = mkstemp(pathname);
|
||||
if (fd == -1) {
|
||||
log_err("mkstemp of file %s failed: %s", pathname, strerror(errno));
|
||||
free(pathname);
|
||||
return NULL;
|
||||
}
|
||||
pos = 0;
|
||||
while (pos < size) {
|
||||
int nbytes = write(fd, &buf[pos], size - pos);
|
||||
if (nbytes <= 0) {
|
||||
if (nbytes == -1 && errno == EINTR)
|
||||
continue;
|
||||
log_err("write to file %s failed: %s", pathname, strerror(errno));
|
||||
goto err;
|
||||
}
|
||||
pos += nbytes;
|
||||
}
|
||||
|
||||
if (close(fd) == -1) {
|
||||
log_err("close of file %s failed: %s", pathname, strerror(errno));
|
||||
goto err;
|
||||
}
|
||||
|
||||
return pathname;
|
||||
err:
|
||||
delete_file(pathname);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* based on main() above, but with: hard-coded passed args, file created from fuzz input */
|
||||
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size)
|
||||
{
|
||||
int c, res;
|
||||
int pass_argc = 0;
|
||||
char* pass_argv[MAXARG];
|
||||
char* playback_file = NULL;
|
||||
|
||||
/* we do not want the test to depend on the timezone */
|
||||
(void)putenv("TZ=UTC");
|
||||
memset(pass_argv, 0, sizeof(pass_argv));
|
||||
#ifdef HAVE_SYSTEMD
|
||||
/* we do not want the test to use systemd daemon startup notification*/
|
||||
(void)unsetenv("NOTIFY_SOCKET");
|
||||
#endif /* HAVE_SYSTEMD */
|
||||
|
||||
checklock_start();
|
||||
log_init(NULL, 0, NULL);
|
||||
/* determine commandline options for the daemon */
|
||||
pass_argc = 1;
|
||||
pass_argv[0] = "unbound";
|
||||
add_opts("-d", &pass_argc, pass_argv);
|
||||
|
||||
playback_file = buf_to_file(Data, Size);
|
||||
if (playback_file) {
|
||||
log_info("Start of %s testbound program.", PACKAGE_STRING);
|
||||
|
||||
res = perform_playback(playback_file, pass_argc, pass_argv);
|
||||
if(res == 0) {
|
||||
log_info("Testbound Exit Success\n");
|
||||
/* remove configfile from here, the atexit() is for when
|
||||
* there is a crash to remove the tmpdir file.
|
||||
* This one removes the file while alloc and log locks are
|
||||
* still valid, and can be logged (for memory calculation),
|
||||
* it leaves the ptr NULL so the atexit does nothing. */
|
||||
remove_configfile();
|
||||
#ifdef HAVE_PTHREAD
|
||||
/* dlopen frees its thread state (dlopen of gost engine) */
|
||||
pthread_exit(NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
delete_file(playback_file);
|
||||
}
|
||||
|
||||
if(log_get_lock()) {
|
||||
lock_basic_destroy((lock_basic_type*)log_get_lock());
|
||||
}
|
||||
return res;
|
||||
}
|
||||
#endif /* FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION */
|
||||
|
||||
/* fake remote control */
|
||||
struct listen_port* daemon_remote_open_ports(struct config_file*
|
||||
ATTR_UNUSED(cfg))
|
||||
|
||||
+19
-2
@@ -923,10 +923,14 @@ pkt_snip_edns_option(uint8_t* pkt, size_t len, sldns_edns_option code,
|
||||
if(!pkt_find_edns_opt(&opt_position, &remaining)) return 0;
|
||||
if(remaining < 8) return -1; /* malformed */
|
||||
rdlen = sldns_read_uint16(opt_position+6);
|
||||
if(remaining < ((size_t)rdlen)+8)
|
||||
return -1; /* malformed */
|
||||
rdata = opt_position + 8;
|
||||
while(rdlen > 0) {
|
||||
if(rdlen < 4) return -1; /* malformed */
|
||||
optlen = sldns_read_uint16(rdata+2);
|
||||
if((size_t)rdlen < 4+((size_t)optlen))
|
||||
return -1; /* malformed */
|
||||
if(sldns_read_uint16(rdata) == code) {
|
||||
/* save data to buf for caller inspection */
|
||||
memmove(buf, rdata+4, optlen);
|
||||
@@ -1134,8 +1138,9 @@ static void lowercase_dname(uint8_t** p, size_t* remain)
|
||||
while(**p != 0) {
|
||||
/* compressed? */
|
||||
if((**p & 0xc0) == 0xc0) {
|
||||
*p += 2;
|
||||
*remain -= 2;
|
||||
llen = *remain < 2 ? (unsigned int)*remain : 2;
|
||||
*p += llen;
|
||||
*remain -= llen;
|
||||
return;
|
||||
}
|
||||
llen = (unsigned int)**p;
|
||||
@@ -1178,6 +1183,12 @@ static void lowercase_rdata(uint8_t** p, size_t* remain,
|
||||
uint8_t len;
|
||||
if(rdataremain == 0) return;
|
||||
len = **p;
|
||||
if(rdataremain < ((size_t)len)+1) {
|
||||
/* malformed LDNS_RDF_TYPE_STR, skip remainder */
|
||||
*p += rdataremain;
|
||||
*remain -= rdatalen;
|
||||
return;
|
||||
}
|
||||
*p += len+1;
|
||||
rdataremain -= len+1;
|
||||
} else {
|
||||
@@ -1207,6 +1218,12 @@ static void lowercase_rdata(uint8_t** p, size_t* remain,
|
||||
break;
|
||||
default: error("bad rdf type in lowercase %d", (int)f);
|
||||
}
|
||||
if (rdataremain < (size_t)len) {
|
||||
/* malformed RDF, skip remainder */
|
||||
*p += rdataremain;
|
||||
*remain -= rdatalen;
|
||||
return;
|
||||
}
|
||||
*p += len;
|
||||
rdataremain -= len;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user