/* * Generate and munge a 64bit number. */ u64 rand64(void) { unsigned long r = 0; if (RAND_BOOL()) { /* 32-bit ranges. */ r = rand32(); } else { /* 33:64-bit ranges. */ switch (rand() % 5) { case 0: r = rand_single_bit(64); break; case 1: r = randbits(64); break; case 2: r = RAND_64(); break; case 3: r = rept_byte(); break; /* Sometimes pick a not-so-random number. */ case 4: return get_interesting_value(); } /* limit the size */ switch (rand() % 4) { case 0: r &= 0x000000ffffffffffULL; break; case 1: r &= 0x0000ffffffffffffULL; break; case 2: r &= 0x00ffffffffffffffULL; break; default: /* no limiting. */ break; } } /* Sometimes invert the generated number. */ if (ONE_IN(25)) r = ~r; /* increase distribution in MSB */ if (ONE_IN(10)) { unsigned int i; unsigned int rounds; rounds = rand() % 4; for (i = 0; i < rounds; i++) r |= (1UL << ((__WORDSIZE - 1) - (rand() % 8))); } /* Sometimes flip sign */ if (ONE_IN(25)) r = ~r + 1; return r; }
/* * Generate, and munge a 32bit number. */ unsigned int rand32(void) { unsigned long r = 0; switch (rnd() % 7) { case 0: r = RAND_BYTE(); break; case 1: r = rand16(); break; case 2: r = rand_single_bit(32); break; case 3: r = randbits(32); break; case 4: r = rnd(); break; case 5: r = rept_byte(); break; case 6: return get_interesting_value(); } /* Sometimes deduct it from INT_MAX */ if (ONE_IN(25)) r = INT_MAX - r; /* Sometimes flip sign */ if (ONE_IN(25)) r = ~r + 1; /* we might get lucky if something is counting ints/longs etc. */ if (ONE_IN(4)) { int _div = 1 << RAND_RANGE(1, 4); /* 2,4,8 or 16 */ r /= _div; } /* limit the size */ switch (rnd() % 5) { case 0: r &= 0xff; break; case 1: r &= 0xffff; break; case 2: r &= PAGE_MASK; break; case 3: r &= 0xffffff; break; case 4: // do nothing break; } return r; }
static void generic_sanitise_ioctl(struct syscallrecord *rec) { if (ONE_IN(50)) ioctl_mangle_cmd(rec); ioctl_mangle_arg(rec); }
static void sanitise_linkat(struct syscallrecord *rec) { /* .. If oldpath is relative and olddirfd is the special value AT_FDCWD, then oldpath is * interpreted relative to the current working directory of the calling process */ if (ONE_IN(100)) rec->a1 = AT_FDCWD; }
static void sanitise_ioctl(struct syscallrecord *rec) { const struct ioctl_group *grp; if (ONE_IN(100)) grp = get_random_ioctl_group(); else grp = find_ioctl_group(rec->a1); if (grp) { ioctl_mangle_arg(rec); grp->sanitise(grp, rec); if (ONE_IN(100)) ioctl_mangle_cmd(rec); } else generic_sanitise_ioctl(rec); }
/* * Generate, and munge a 16bit number. */ unsigned short rand16(void) { unsigned short r = 0, r2; switch (rnd() % 5) { case 0: r = RAND_BYTE(); break; case 1: r = rand_single_bit(16); break; case 2: r = randbits(16); break; case 3: r = rnd(); break; case 4: r2 = rnd() & 0xff; r = r2 | r2 << 8; break; } /* Sometimes flip sign */ if (ONE_IN(25)) r = ~r + 1; if (ONE_IN(4)) { int _div = 1 << RAND_RANGE(1, 4); /* 2,4,8 or 16 */ r /= _div; } /* limit the size */ switch (rnd() % 4) { case 0: r &= 0xff; break; case 1: r &= 0xfff; break; case 2: r &= PAGE_MASK; break; case 3: // do nothing break; } return r; }
static void gen_random_ipv6_address(struct in6_addr *v6) { const char *p; /* 90% of the time, just do localhost */ if (!(ONE_IN(10))) { inet_pton(AF_INET6, "::1", v6); return; } if (RAND_BOOL()) { /* v4 in v6 somehow. */ in_addr_t v4; const struct addrtext v4_in_v6_addresses[] = { { "::" }, /* deprecated ipv4 style ::v4 */ { "::ffff:0:0" }, /* v4 in v6 ::ffff:0:0/96 */ { "::ffff:0:0:0" }, /* stateless IP/ICMP translation (SIIT) ::ffff:0:0:0/96 */ { "2002::" }, /* 2002::/16 "6to4" */ }; p = RAND_ELEMENT(v4_in_v6_addresses, name); inet_pton(AF_INET6, p, v6); v4 = random_ipv4_address(); v6->s6_addr32[3] = htonl(v4); } else { /* actual v6 addresses. */ const struct addrtext v6_addresses[] = { { "::" }, /* ::/128 unspecified */ { "fe80::" }, /* fe80::/10 link-local */ { "fc00::" }, /* fc00::/7 unique local address (ULA) */ { "64:ff9b::" }, /* 64:ff9b::/96 "Well known" prefix */ { "0100::" }, /* 0100::/64 remotely triggered blackhole */ }; p = RAND_ELEMENT(v6_addresses, name); inet_pton(AF_INET6, p, v6); } }
/* * SYSCALL_DEFINE3(sendmsg, int, fd, struct msghdr __user *, msg, unsigned, flags) */ static void sanitise_sendmsg(struct syscallrecord *rec) { struct socketinfo *si = (struct socketinfo *) rec->a1; struct msghdr *msg; struct sockaddr *sa = NULL; socklen_t salen = 0; if (si == NULL) // handle --disable-fds=sockets goto skip_si; rec->a1 = fd_from_socketinfo((struct socketinfo *) rec->a1); generate_sockaddr((struct sockaddr **) &sa, (socklen_t *) &salen, si->triplet.family); skip_si: msg = zmalloc(sizeof(struct msghdr)); msg->msg_name = sa; msg->msg_namelen = salen; if (RAND_BOOL()) { unsigned int num_entries; num_entries = RAND_RANGE(1, 3); msg->msg_iov = alloc_iovec(num_entries); msg->msg_iovlen = num_entries; } if (RAND_BOOL()) { msg->msg_controllen = rand32() % 20480; // /proc/sys/net/core/optmem_max msg->msg_control = get_address(); } else { msg->msg_controllen = 0; } if (ONE_IN(100)) msg->msg_flags = rand32(); else msg->msg_flags = 0; rec->a2 = (unsigned long) msg; }
static bool choose_syscall_table(void) { bool do32 = FALSE; if (biarch == FALSE) { active_syscalls = shm->active_syscalls; } else { /* First, check that we have syscalls enabled in either table. */ if (validate_syscall_table_64() == FALSE) { use_64bit = FALSE; /* If no 64bit syscalls enabled, force 32bit. */ do32 = TRUE; } if (validate_syscall_table_32() == FALSE) use_32bit = FALSE; /* If both tables enabled, pick randomly. */ if ((use_64bit == TRUE) && (use_32bit == TRUE)) { /* 10% possibility of a 32bit syscall */ if (ONE_IN(10)) do32 = TRUE; } if (do32 == FALSE) { syscalls = syscalls_64bit; active_syscalls = shm->active_syscalls64; max_nr_syscalls = max_nr_64bit_syscalls; } else { syscalls = syscalls_32bit; active_syscalls = shm->active_syscalls32; max_nr_syscalls = max_nr_32bit_syscalls; } } return do32; }
static void socket_destructor(struct object *obj) { struct socketinfo *si = &obj->sockinfo; struct linger ling = { .l_onoff = FALSE, .l_linger = 0 }; int fd; //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 (si->triplet.family == PF_BLUETOOTH) return; /* Grab an fd, and nuke it before someone else uses it. */ fd = si->fd; si->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", si->triplet.family, si->triplet.type, si->triplet.protocol, strerror(errno)); } static void socket_dump(struct object *obj, bool global) { struct socketinfo *si = &obj->sockinfo; struct msg_objcreatedsocket objmsg; output(2, "socket fd:%d domain:%u (%s) type:0x%u protocol:%u\n", si->fd, si->triplet.family, get_domain_name(si->triplet.family), si->triplet.type, si->triplet.protocol); init_msgobjhdr(&objmsg.hdr, OBJ_CREATED_SOCKET, global, obj); objmsg.si.fd = si->fd; objmsg.si.triplet.family = si->triplet.family; objmsg.si.triplet.type = si->triplet.type; objmsg.si.triplet.protocol = si->triplet.protocol; sendudp((char *) &objmsg, sizeof(objmsg)); } static int open_sockets(void) { struct objhead *head; int bytesread = -1; int ret; head = get_objhead(OBJ_GLOBAL, OBJ_FD_SOCKET); head->destroy = &socket_destructor; head->dump = &socket_dump; cachefile = open(cachefilename, O_RDONLY); if (cachefile < 0) { output(1, "Couldn't find socket cachefile. Regenerating.\n"); ret = generate_sockets(); output(1, "created %d sockets\n", nr_sockets); return ret; } lock_cachefile(F_RDLCK); while (bytesread != 0) { unsigned int domain, type, protocol; unsigned int buffer[3]; int fd; bytesread = read(cachefile, buffer, sizeof(int) * 3); if (bytesread == 0) { if (nr_sockets == 0) goto regenerate; break; } domain = buffer[0]; type = buffer[1]; protocol = buffer[2]; if (domain >= TRINITY_PF_MAX) { output(1, "cachefile contained invalid domain %u\n", domain); goto regenerate; } if ((do_specific_domain == TRUE && domain != specific_domain) || (no_domains[domain] == TRUE)) { output(1, "ignoring socket cachefile due to specific " "protocol request (or protocol disabled), " "and stale data in cachefile.\n"); regenerate: unlock_cachefile(); /* drop the reader lock. */ close(cachefile); unlink(cachefilename); 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; } } output(1, "%d sockets created based on info from socket cachefile.\n", nr_sockets); unlock_cachefile(); close(cachefile); return TRUE; } struct socketinfo * get_rand_socketinfo(void) { struct object *obj; /* When using victim files, sockets can be 0. */ if (objects_empty(OBJ_FD_SOCKET) == TRUE) return NULL; obj = get_random_object(OBJ_FD_SOCKET, OBJ_GLOBAL); return &obj->sockinfo; } static int get_rand_socket_fd(void) { struct socketinfo *sockinfo; sockinfo = get_rand_socketinfo(); if (sockinfo == NULL) return -1; return sockinfo->fd; } int fd_from_socketinfo(struct socketinfo *si) { if (si != NULL) { if (!(ONE_IN(1000))) return si->fd; } return get_random_fd(); } static const struct fd_provider socket_fd_provider = { .name = "sockets", .enabled = TRUE, .open = &open_sockets, .get = &get_rand_socket_fd, }; REG_FD_PROV(socket_fd_provider);