- Fix #1288: [FR] Improve fuzzing of unbound by adapting the netbound

program.
This commit is contained in:
W.C.A. Wijngaards
2025-05-21 12:41:54 +02:00
parent 342a0f48e3
commit ff7dfd52a2
6 changed files with 196 additions and 22 deletions
+125 -15
View File
@@ -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))