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);
}
(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);
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;
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.
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");
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
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))
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);
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;
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 {
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;
}
*/
#define MAX_VALUE 0x7fffffff
+/* If the build mode is for fuzzing this removes randomness from the output.
+ * This helps fuzz engines from having state increase due to the randomness. */
+#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
+struct ub_randstate {
+ unsigned int dummy;
+};
+
+struct ub_randstate* ub_initstate(struct ub_randstate* ATTR_UNUSED(from))
+{
+ struct ub_randstate* s = (struct ub_randstate*)calloc(1, sizeof(*s));
+ if(!s) {
+ log_err("malloc failure in random init");
+ return NULL;
+ }
+ return s;
+}
+
+long int ub_random(struct ub_randstate* state)
+{
+ state->dummy++;
+ return (long int)(state->dummy & MAX_VALUE);
+}
+
+long int
+ub_random_max(struct ub_randstate* state, long int x)
+{
+ state->dummy++;
+ return ((long int)state->dummy % x);
+}
+#else /* !FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION */
+
#if defined(HAVE_SSL) || defined(HAVE_LIBBSD)
struct ub_randstate*
ub_initstate(struct ub_randstate* ATTR_UNUSED(from))
}
#endif /* HAVE_NSS or HAVE_NETTLE and !HAVE_LIBBSD */
+#endif /* FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION */
+
void
ub_randfree(struct ub_randstate* s)
{