/* Generate children*/ static void fork_children(void) { while (shm->running_childs < max_children) { int childno; int pid = 0; if (shm->spawn_no_more == TRUE) return; /* a new child means a new seed, or the new child * will do the same syscalls as the one in the child it's replacing. * (special case startup, or we reseed unnecessarily) */ if (shm->ready == TRUE) reseed(); /* Find a space for it in the pid map */ childno = find_childno(EMPTY_PIDSLOT); if (childno == CHILD_NOT_FOUND) { outputerr("## Pid map was full!\n"); dump_childnos(); exit_main_fail(); } fflush(stdout); pid = fork(); if (pid == 0) { /* Child process. */ init_child(childno); child_process(); debugf("child %d %d exiting.\n", childno, getpid()); close_logfile(&this_child->logfile); _exit(EXIT_SUCCESS); } else { if (pid == -1) { /* We failed, wait for a child to exit before retrying. */ if (shm->running_childs > 0) return; output(0, "couldn't create child! (%s)\n", strerror(errno)); panic(EXIT_FORK_FAILURE); exit_main_fail(); } } shm->children[childno]->pid = pid; shm->running_childs++; debugf("Created child %d (pid:%d) [total:%d/%d]\n", childno, pid, shm->running_childs, max_children); if (shm->exit_reason != STILL_RUNNING) return; } shm->ready = TRUE; debugf("created enough children\n"); }
int main(int argc, char* argv[]) { int ret = EXIT_SUCCESS; int childstatus; pid_t pid; const char taskname[13]="trinity-main"; outputstd("Trinity " VERSION " Dave Jones <*****@*****.**>\n"); progname = argv[0]; initpid = getpid(); page_size = getpagesize(); num_online_cpus = sysconf(_SC_NPROCESSORS_ONLN); max_children = num_online_cpus; /* possibly overridden in params. */ if (init_random() == FALSE) exit(EXIT_FAILURE); set_seed(0); select_syscall_tables(); create_shm(); /* We do this before the parse_args because --fds will need to * operate on it when implemented. */ setup_fd_providers(); parse_args(argc, argv); init_uids(); change_tmp_dir(); init_logging(); init_shm(); kernel_taint_initial = check_tainted(); if (kernel_taint_initial != 0) output(0, "Kernel was tainted on startup. Will ignore flags that are already set.\n"); if (munge_tables() == FALSE) { ret = EXIT_FAILURE; goto out; } if (show_syscall_list == TRUE) { dump_syscall_tables(); goto out; } init_syscalls(); if (show_ioctl_list == TRUE) { dump_ioctls(); goto out; } do_uid0_check(); if (do_specific_domain == TRUE) find_specific_domain(specific_domain_optarg); setup_initial_mappings(); parse_devices(); pids_init(); setup_main_signals(); /* check if we ctrl'c or something went wrong during init. */ if (shm->exit_reason != STILL_RUNNING) goto cleanup_fds; init_watchdog(); /* do an extra fork so that the watchdog and the children don't share a common parent */ fflush(stdout); pid = fork(); if (pid == 0) { shm->mainpid = getpid(); setup_main_signals(); no_bind_to_cpu = RAND_BOOL(); output(0, "Main thread is alive.\n"); prctl(PR_SET_NAME, (unsigned long) &taskname); set_seed(0); if (open_fds() == FALSE) { if (shm->exit_reason != STILL_RUNNING) panic(EXIT_FD_INIT_FAILURE); // FIXME: Later, push this down to multiple EXIT's. exit_main_fail(); } if (dropprivs == TRUE) //FIXME: Push down into child processes later. drop_privs(); main_loop(); shm->mainpid = 0; _exit(EXIT_SUCCESS); } /* wait for main loop process to exit. */ (void)waitpid(pid, &childstatus, 0); /* wait for watchdog to exit. */ waitpid(watchdog_pid, &childstatus, 0); output(0, "Ran %ld syscalls. Successes: %ld Failures: %ld\n", shm->stats.total_syscalls_done - 1, shm->stats.successes, shm->stats.failures); cleanup_fds: close_sockets(); destroy_initial_mappings(); shutdown_logging(); ret = set_exit_code(shm->exit_reason); out: exit(ret); }
/* * level defines whether it gets displayed to the screen with printf. * (it always logs). * 0 = everything, even all the registers * 1 = Watchdog prints syscall count * 2 = Just the reseed values * */ void output(unsigned char level, const char *fmt, ...) { va_list args; int n; FILE *handle; pid_t pid; char outputbuf[BUFSIZE]; char *prefix = NULL; char watchdog_prefix[]="[watchdog]"; char init_prefix[]="[init]"; char main_prefix[]="[main]"; char child_prefix[32]; if (logging == FALSE && level >= quiet_level) return; /* prefix preparation */ pid = getpid(); if (pid == watchdog_pid) prefix = watchdog_prefix; if (pid == initpid) prefix = init_prefix; if (pid == shm->mainpid) prefix = main_prefix; if (prefix == NULL) { unsigned int childno; childno = find_childno(pid); snprintf(child_prefix, sizeof(child_prefix), "[child%u:%u]", childno, pid); prefix = child_prefix; shm->children[childno]->logdirty = TRUE; } /* formatting output */ va_start(args, fmt); n = vsnprintf(outputbuf, sizeof(outputbuf), fmt, args); va_end(args); if (n < 0) { outputerr("## Something went wrong in output() [%d]\n", n); if (getpid() == shm->mainpid) exit_main_fail(); else exit(EXIT_FAILURE); } /* stdout output if needed */ if (quiet_level >= level) { printf("%s %s", prefix, outputbuf); (void)fflush(stdout); } /* go on with file logs only if enabled */ if (logging == FALSE) return; handle = find_logfile_handle(); if (!handle) return; strip_ansi(outputbuf); fprintf(handle, "%s %s", prefix, outputbuf); (void)fflush(handle); }
static void lock_cachefile(int cachefile, int type) { struct flock fl = { .l_len = 0, .l_start = 0, .l_whence = SEEK_SET, }; fl.l_pid = getpid(); fl.l_type = type; if (verbose) output(2, "waiting on lock for cachefile\n"); if (fcntl(cachefile, F_SETLKW, &fl) == -1) { perror("fcntl F_SETLKW"); exit_main_fail(); } if (verbose) output(2, "took lock for cachefile\n"); } static void unlock_cachefile(int cachefile) { struct flock fl = { .l_len = 0, .l_start = 0, .l_whence = SEEK_SET, }; fl.l_pid = getpid(); fl.l_type = F_UNLCK; if (fcntl(cachefile, F_SETLK, &fl) == -1) { perror("fcntl F_UNLCK F_SETLK "); exit_main_fail(); } if (verbose) output(2, "dropped lock for cachefile\n"); } static unsigned int valid_proto(unsigned int family) { const char *famstr; famstr = get_proto_name(family); /* Not used for creating sockets. */ if (strncmp(famstr, "PF_UNSPEC", 9) == 0) return FALSE; if (strncmp(famstr, "PF_BRIDGE", 9) == 0) return FALSE; if (strncmp(famstr, "PF_SECURITY", 11) == 0) return FALSE; /* Not actually implemented (or now removed). */ if (strncmp(famstr, "PF_NETBEUI", 10) == 0) return FALSE; if (strncmp(famstr, "PF_ASH", 6) == 0) return FALSE; if (strncmp(famstr, "PF_ECONET", 9) == 0) return FALSE; if (strncmp(famstr, "PF_SNA", 6) == 0) return FALSE; if (strncmp(famstr, "PF_WANPIPE", 10) == 0) return FALSE; /* Needs root. */ if (orig_uid != 0) { if (strncmp(famstr, "PF_KEY", 6) == 0) return FALSE; if (strncmp(famstr, "PF_PACKET", 9) == 0) return FALSE; if (strncmp(famstr, "PF_LLC", 6) == 0) return FALSE; } return TRUE; } static int generate_sockets(void) { int fd, n, ret = FALSE; int cachefile; unsigned int nr_to_create = NR_SOCKET_FDS; unsigned int buffer[3]; cachefile = creat(cachefilename, S_IWUSR|S_IRUSR); if (cachefile == -1) outputerr("Couldn't open cachefile for writing! (%s)\n", strerror(errno)); else lock_cachefile(cachefile, F_WRLCK); /* * Don't loop forever if all protos all are disabled. */ if (!do_specific_proto) { for (n = 0; n < (int)ARRAY_SIZE(no_protos); n++) { if (!no_protos[n]) break; } if (n >= (int)ARRAY_SIZE(no_protos)) nr_to_create = 0; } while (nr_to_create > 0) { struct socket_triplet st; for (st.family = 0; st.family < TRINITY_PF_MAX; st.family++) { /* check for ctrl-c again. */ if (shm->exit_reason != STILL_RUNNING) goto out_unlock; if (do_specific_proto == TRUE) { st.family = specific_proto; //FIXME: If we've passed -P and we're spinning here without making progress // then we should abort after a few hundred loops. } if (get_proto_name(st.family) == NULL) continue; if (valid_proto(st.family) == FALSE) { if (do_specific_proto == TRUE) { outputerr("Can't do protocol %s\n", get_proto_name(st.family)); goto out_unlock; } else { continue; } } BUG_ON(st.family >= ARRAY_SIZE(no_protos)); if (no_protos[st.family]) continue; if (sanitise_socket_triplet(&st) == -1) rand_proto_type(&st); fd = open_socket(st.family, st.type, st.protocol); if (fd > -1) { nr_to_create--; if (cachefile != -1) { buffer[0] = st.family; buffer[1] = st.type; buffer[2] = st.protocol; n = write(cachefile, &buffer, sizeof(int) * 3); if (n == -1) { outputerr("something went wrong writing the cachefile!\n"); goto out_unlock; } } if (nr_to_create == 0) goto done; } else { //outputerr("Couldn't open family:%d (%s)\n", st.family, get_proto_name(st.family)); } } } done: ret = TRUE; output(1, "created %d sockets\n", nr_sockets); out_unlock: if (cachefile != -1) { unlock_cachefile(cachefile); close(cachefile); } return ret; } void close_sockets(void) { unsigned int i; int fd; struct linger ling = { .l_onoff = FALSE, .l_linger = 0 }; for (i = 0; i < nr_sockets; i++) { //FIXME: This is a workaround for a weird bug where we hang forevre // waiting for bluetooth sockets when we setsockopt. // Hopefully at some point we can remove this when someone figures out what's going on. if (shm->sockets[i].triplet.family == PF_BLUETOOTH) continue; /* Grab an fd, and nuke it before someone else uses it. */ fd = shm->sockets[i].fd; shm->sockets[i].fd = 0; /* disable linger */ (void) setsockopt(fd, SOL_SOCKET, SO_LINGER, &ling, sizeof(struct linger)); (void) shutdown(fd, SHUT_RDWR); if (close(fd) != 0) output(1, "failed to close socket [%d:%d:%d].(%s)\n", shm->sockets[i].triplet.family, shm->sockets[i].triplet.type, shm->sockets[i].triplet.protocol, strerror(errno)); } nr_sockets = 0; } static int open_sockets(void) { int cachefile; unsigned int domain, type, protocol; unsigned int buffer[3]; int bytesread = -1; int fd; int ret; /* If we're doing victim files we probably don't care about sockets. */ //FIXME: Is this really true ? We might want to sendfile for eg if (victim_path != NULL) return TRUE; cachefile = open(cachefilename, O_RDONLY); if (cachefile < 0) { output(1, "Couldn't find socket cachefile. Regenerating.\n"); ret = generate_sockets(); return ret; } lock_cachefile(cachefile, F_RDLCK); while (bytesread != 0) { bytesread = read(cachefile, buffer, sizeof(int) * 3); if (bytesread == 0) break; domain = buffer[0]; type = buffer[1]; protocol = buffer[2]; if ((do_specific_proto == TRUE && domain != specific_proto) || (domain < ARRAY_SIZE(no_protos) && no_protos[domain] == TRUE)) { output(1, "ignoring socket cachefile due to specific " "protocol request (or protocol disabled), " "and stale data in cachefile.\n"); regenerate: unlock_cachefile(cachefile); /* drop the reader lock. */ close(cachefile); unlink(cachefilename); close_sockets(); ret = generate_sockets(); return ret; } fd = open_socket(domain, type, protocol); if (fd < 0) { output(1, "Cachefile is stale. Need to regenerate.\n"); goto regenerate; } /* check for ctrl-c */ if (shm->exit_reason != STILL_RUNNING) { close(cachefile); return FALSE; } } if (nr_sockets < NR_SOCKET_FDS) { output(1, "Insufficient sockets in cachefile (%d). Regenerating.\n", nr_sockets); goto regenerate; } output(1, "%d sockets created based on info from socket cachefile.\n", nr_sockets); unlock_cachefile(cachefile); close(cachefile); return TRUE; }