diff options
Diffstat (limited to 'executor/executor.h')
| -rw-r--r-- | executor/executor.h | 967 |
1 files changed, 0 insertions, 967 deletions
diff --git a/executor/executor.h b/executor/executor.h deleted file mode 100644 index 6741a4baf..000000000 --- a/executor/executor.h +++ /dev/null @@ -1,967 +0,0 @@ -// Copyright 2017 syzkaller project authors. All rights reserved. -// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. - -#include <algorithm> -#include <errno.h> -#include <signal.h> -#include <stdarg.h> -#include <stddef.h> -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> - -#ifndef GIT_REVISION -#define GIT_REVISION "unknown" -#endif - -#ifndef GOOS -#define GOOS "unknown" -#endif - -const int kMaxInput = 2 << 20; -const int kMaxOutput = 16 << 20; -const int kCoverSize = 256 << 10; -const int kMaxArgs = 9; -const int kMaxThreads = 16; -const int kMaxCommands = 1000; - -const uint64 instr_eof = -1; -const uint64 instr_copyin = -2; -const uint64 instr_copyout = -3; - -const uint64 arg_const = 0; -const uint64 arg_result = 1; -const uint64 arg_data = 2; -const uint64 arg_csum = 3; - -const uint64 binary_format_native = 0; -const uint64 binary_format_bigendian = 1; -const uint64 binary_format_strdec = 2; -const uint64 binary_format_strhex = 3; -const uint64 binary_format_stroct = 4; - -const uint64 no_copyout = -1; - -enum sandbox_type { - sandbox_none, - sandbox_setuid, - sandbox_namespace, -}; - -bool flag_cover; -bool flag_sandbox_privs; -sandbox_type flag_sandbox; -bool flag_enable_tun; -bool flag_enable_net_dev; -bool flag_enable_fault_injection; - -bool flag_collect_cover; -bool flag_dedup_cover; -bool flag_threaded; -bool flag_collide; - -// If true, then executor should write the comparisons data to fuzzer. -bool flag_collect_comps; - -// Inject fault into flag_fault_nth-th operation in flag_fault_call-th syscall. -bool flag_inject_fault; -int flag_fault_call; -int flag_fault_nth; - -unsigned long long procid; - -int running; -uint32 completed; -bool collide; -bool is_kernel_64_bit = true; - -ALIGNED(64 << 10) -char input_data[kMaxInput]; - -// Checksum kinds. -const uint64 arg_csum_inet = 0; - -// Checksum chunk kinds. -const uint64 arg_csum_chunk_data = 0; -const uint64 arg_csum_chunk_const = 1; - -struct thread_t { - bool created; - int id; - osthread_t th; - char* cover_data; - char* cover_end; - uint64* cover_size_ptr; - - event_t ready; - event_t done; - uint64* copyout_pos; - uint64 copyout_index; - bool colliding; - bool handled; - int call_index; - int call_num; - int num_args; - long args[kMaxArgs]; - long res; - uint32 reserrno; - uint32 cover_size; - bool fault_injected; - int cover_fd; -}; - -thread_t threads[kMaxThreads]; - -struct res_t { - bool executed; - uint64 val; -}; - -res_t results[kMaxCommands]; - -const uint64 kInMagic = 0xbadc0ffeebadface; -const uint32 kOutMagic = 0xbadf00d; - -struct handshake_req { - uint64 magic; - uint64 flags; // env flags - uint64 pid; -}; - -struct handshake_reply { - uint32 magic; -}; - -struct execute_req { - uint64 magic; - uint64 env_flags; - uint64 exec_flags; - uint64 pid; - uint64 fault_call; - uint64 fault_nth; - uint64 prog_size; -}; - -struct execute_reply { - uint32 magic; - uint32 done; - uint32 status; -}; - -struct call_reply { - execute_reply header; - uint32 call_index; - uint32 call_num; - uint32 reserrno; - uint32 fault_injected; - uint32 signal_size; - uint32 cover_size; - uint32 comps_size; - // signal/cover/comps follow -}; - -enum { - KCOV_CMP_CONST = 1, - KCOV_CMP_SIZE1 = 0, - KCOV_CMP_SIZE2 = 2, - KCOV_CMP_SIZE4 = 4, - KCOV_CMP_SIZE8 = 6, - KCOV_CMP_SIZE_MASK = 6, -}; - -struct kcov_comparison_t { - // Note: comparisons are always 64-bits regardless of kernel bitness. - uint64 type; - uint64 arg1; - uint64 arg2; - uint64 pc; - - bool ignore() const; - void write(); - bool operator==(const struct kcov_comparison_t& other) const; - bool operator<(const struct kcov_comparison_t& other) const; -}; - -long execute_syscall(const call_t* c, long a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8); -thread_t* schedule_call(int call_index, int call_num, bool colliding, uint64 copyout_index, uint64 num_args, uint64* args, uint64* pos); -void handle_completion(thread_t* th); -void execute_call(thread_t* th); -void thread_create(thread_t* th, int id); -void* worker_thread(void* arg); -uint32* write_output(uint32 v); -void write_completed(uint32 completed); -uint64 read_input(uint64** input_posp, bool peek = false); -uint64 read_arg(uint64** input_posp); -uint64 read_const_arg(uint64** input_posp, uint64* size_p, uint64* bf, uint64* bf_off_p, uint64* bf_len_p); -uint64 read_result(uint64** input_posp); -void copyin(char* addr, uint64 val, uint64 size, uint64 bf, uint64 bf_off, uint64 bf_len); -bool copyout(char* addr, uint64 size, uint64* res); -void cover_open(); -void cover_enable(thread_t* th); -void cover_reset(thread_t* th); -uint32 cover_read_size(thread_t* th); -bool cover_check(uint32 pc); -bool cover_check(uint64 pc); -static uint32 hash(uint32 a); -static bool dedup(uint32 sig); -void setup_control_pipes(); -void receive_handshake(); -void receive_execute(); - -void main_init() -{ - setup_control_pipes(); - if (SYZ_EXECUTOR_USES_FORK_SERVER) - receive_handshake(); - else - receive_execute(); - if (flag_cover) - cover_open(); -} - -void setup_control_pipes() -{ - if (dup2(0, kInPipeFd) < 0) - fail("dup2(0, kInPipeFd) failed"); - if (dup2(1, kOutPipeFd) < 0) - fail("dup2(1, kOutPipeFd) failed"); - if (dup2(2, 1) < 0) - fail("dup2(2, 1) failed"); - // We used to close(0), but now we dup stderr to stdin to keep fd numbers - // stable across executor and C programs generated by pkg/csource. - if (dup2(2, 0) < 0) - fail("dup2(2, 0) failed"); -} - -void parse_env_flags(uint64 flags) -{ - flag_debug = flags & (1 << 0); - flag_cover = flags & (1 << 1); - flag_sandbox = sandbox_none; - if (flags & (1 << 2)) - flag_sandbox = sandbox_setuid; - else if (flags & (1 << 3)) - flag_sandbox = sandbox_namespace; - flag_enable_tun = flags & (1 << 4); - flag_enable_net_dev = flags & (1 << 5); - flag_enable_fault_injection = flags & (1 << 6); -} - -void receive_handshake() -{ - handshake_req req = {}; - int n = read(kInPipeFd, &req, sizeof(req)); - if (n != sizeof(req)) - fail("handshake read failed: %d", n); - if (req.magic != kInMagic) - fail("bad handshake magic 0x%llx", req.magic); - parse_env_flags(req.flags); - procid = req.pid; -} - -void reply_handshake() -{ - handshake_reply reply = {}; - reply.magic = kOutMagic; - if (write(kOutPipeFd, &reply, sizeof(reply)) != sizeof(reply)) - fail("control pipe write failed"); -} - -void receive_execute() -{ - execute_req req; - if (read(kInPipeFd, &req, sizeof(req)) != (ssize_t)sizeof(req)) - fail("control pipe read failed"); - if (req.magic != kInMagic) - fail("bad execute request magic 0x%llx", req.magic); - if (req.prog_size > kMaxInput) - fail("bad execute prog size 0x%llx", req.prog_size); - parse_env_flags(req.env_flags); - procid = req.pid; - flag_collect_cover = req.exec_flags & (1 << 0); - flag_dedup_cover = req.exec_flags & (1 << 1); - flag_inject_fault = req.exec_flags & (1 << 2); - flag_collect_comps = req.exec_flags & (1 << 3); - flag_threaded = req.exec_flags & (1 << 4); - flag_collide = req.exec_flags & (1 << 5); - flag_fault_call = req.fault_call; - flag_fault_nth = req.fault_nth; - if (!flag_threaded) - flag_collide = false; - debug("exec opts: pid=%llu threaded=%d collide=%d cover=%d comps=%d dedup=%d fault=%d/%d/%d prog=%llu\n", - procid, flag_threaded, flag_collide, flag_collect_cover, flag_collect_comps, - flag_dedup_cover, flag_inject_fault, flag_fault_call, flag_fault_nth, - req.prog_size); - if (SYZ_EXECUTOR_USES_SHMEM) { - if (req.prog_size) - fail("need_prog: no program"); - return; - } - if (req.prog_size == 0) - fail("need_prog: no program"); - uint64 pos = 0; - for (;;) { - ssize_t rv = read(kInPipeFd, input_data + pos, sizeof(input_data) - pos); - if (rv < 0) - fail("read failed"); - pos += rv; - if (rv == 0 || pos >= req.prog_size) - break; - } - if (pos != req.prog_size) - fail("bad input size %lld, want %lld", pos, req.prog_size); -} - -void reply_execute(int status) -{ - execute_reply reply = {}; - reply.magic = kOutMagic; - reply.done = true; - reply.status = status; - if (write(kOutPipeFd, &reply, sizeof(reply)) != sizeof(reply)) - fail("control pipe write failed"); -} - -// execute_one executes program stored in input_data. -void execute_one() -{ - // Duplicate global collide variable on stack. - // Fuzzer once come up with ioctl(fd, FIONREAD, 0x920000), - // where 0x920000 was exactly collide address, so every iteration reset collide to 0. - bool colliding = false; - write_output(0); // Number of executed syscalls (updated later). - uint64 start = current_time_ms(); - -retry: - uint64* input_pos = (uint64*)input_data; - - if (flag_cover && !colliding && !flag_threaded) - cover_enable(&threads[0]); - - int call_index = 0; - for (;;) { - uint64 call_num = read_input(&input_pos); - if (call_num == instr_eof) - break; - if (call_num == instr_copyin) { - char* addr = (char*)read_input(&input_pos); - uint64 typ = read_input(&input_pos); - switch (typ) { - case arg_const: { - uint64 size, bf, bf_off, bf_len; - uint64 arg = read_const_arg(&input_pos, &size, &bf, &bf_off, &bf_len); - copyin(addr, arg, size, bf, bf_off, bf_len); - break; - } - case arg_result: { - uint64 meta = read_input(&input_pos); - uint64 size = meta & 0xff; - uint64 bf = meta >> 8; - uint64 val = read_result(&input_pos); - copyin(addr, val, size, bf, 0, 0); - break; - } - case arg_data: { - uint64 size = read_input(&input_pos); - NONFAILING(memcpy(addr, input_pos, size)); - // Read out the data. - for (uint64 i = 0; i < (size + 7) / 8; i++) - read_input(&input_pos); - break; - } - case arg_csum: { - debug("checksum found at %p\n", addr); - uint64 size = read_input(&input_pos); - char* csum_addr = addr; - uint64 csum_kind = read_input(&input_pos); - switch (csum_kind) { - case arg_csum_inet: { - if (size != 2) - fail("inet checksum must be 2 bytes, not %llu", size); - debug("calculating checksum for %p\n", csum_addr); - struct csum_inet csum; - csum_inet_init(&csum); - uint64 chunks_num = read_input(&input_pos); - uint64 chunk; - for (chunk = 0; chunk < chunks_num; chunk++) { - uint64 chunk_kind = read_input(&input_pos); - uint64 chunk_value = read_input(&input_pos); - uint64 chunk_size = read_input(&input_pos); - switch (chunk_kind) { - case arg_csum_chunk_data: - debug("#%lld: data chunk, addr: %llx, size: %llu\n", chunk, chunk_value, chunk_size); - NONFAILING(csum_inet_update(&csum, (const uint8*)chunk_value, chunk_size)); - break; - case arg_csum_chunk_const: - if (chunk_size != 2 && chunk_size != 4 && chunk_size != 8) { - fail("bad checksum const chunk size %lld\n", chunk_size); - } - // Here we assume that const values come to us big endian. - debug("#%lld: const chunk, value: %llx, size: %llu\n", chunk, chunk_value, chunk_size); - csum_inet_update(&csum, (const uint8*)&chunk_value, chunk_size); - break; - default: - fail("bad checksum chunk kind %llu", chunk_kind); - } - } - uint16 csum_value = csum_inet_digest(&csum); - debug("writing inet checksum %hx to %p\n", csum_value, csum_addr); - copyin(csum_addr, csum_value, 2, binary_format_native, 0, 0); - break; - } - default: - fail("bad checksum kind %llu", csum_kind); - } - break; - } - default: - fail("bad argument type %llu", typ); - } - continue; - } - if (call_num == instr_copyout) { - read_input(&input_pos); // index - read_input(&input_pos); // addr - read_input(&input_pos); // size - // The copyout will happen when/if the call completes. - continue; - } - - // Normal syscall. - if (call_num >= SYZ_SYSCALL_COUNT) - fail("invalid command number %llu", call_num); - uint64 copyout_index = read_input(&input_pos); - uint64 num_args = read_input(&input_pos); - if (num_args > kMaxArgs) - fail("command has bad number of arguments %llu", num_args); - uint64 args[kMaxArgs] = {}; - for (uint64 i = 0; i < num_args; i++) - args[i] = read_arg(&input_pos); - for (uint64 i = num_args; i < 6; i++) - args[i] = 0; - thread_t* th = schedule_call(call_index++, call_num, colliding, copyout_index, num_args, args, input_pos); - - if (colliding && (call_index % 2) == 0) { - // Don't wait for every other call. - // We already have results from the previous execution. - } else if (flag_threaded) { - // Wait for call completion. - // Note: sys knows about this 25ms timeout when it generates - // timespec/timeval values. - const uint64 timeout_ms = flag_debug ? 3000 : 25; - if (event_timedwait(&th->done, timeout_ms)) - handle_completion(th); - // Check if any of previous calls have completed. - // Give them some additional time, because they could have been - // just unblocked by the current call. - if (running < 0) - fail("running = %d", running); - if (running > 0) { - bool last = read_input(&input_pos, true) == instr_eof; - uint64 wait = last ? 100 : 2; - uint64 wait_start = current_time_ms(); - uint64 wait_end = wait_start + wait; - if (!colliding && wait_end < start + 800) - wait_end = start + 800; - while (running > 0 && current_time_ms() <= wait_end) { - sleep_ms(1); - for (int i = 0; i < kMaxThreads; i++) { - th = &threads[i]; - if (!th->handled && event_isset(&th->done)) - handle_completion(th); - } - } - } - } else { - // Execute directly. - if (th != &threads[0]) - fail("using non-main thread in non-thread mode"); - execute_call(th); - handle_completion(th); - } - } - - if (flag_collide && !flag_inject_fault && !colliding && !collide) { - debug("enabling collider\n"); - collide = colliding = true; - goto retry; - } -} - -thread_t* schedule_call(int call_index, int call_num, bool colliding, uint64 copyout_index, uint64 num_args, uint64* args, uint64* pos) -{ - // Find a spare thread to execute the call. - int i; - for (i = 0; i < kMaxThreads; i++) { - thread_t* th = &threads[i]; - if (!th->created) - thread_create(th, i); - if (event_isset(&th->done)) { - if (!th->handled) - handle_completion(th); - break; - } - } - if (i == kMaxThreads) - exitf("out of threads"); - thread_t* th = &threads[i]; - debug("scheduling call %d [%s] on thread %d\n", call_index, syscalls[call_num].name, th->id); - if (event_isset(&th->ready) || !event_isset(&th->done) || !th->handled) - fail("bad thread state in schedule: ready=%d done=%d handled=%d", - event_isset(&th->ready), event_isset(&th->done), th->handled); - th->colliding = colliding; - th->copyout_pos = pos; - th->copyout_index = copyout_index; - event_reset(&th->done); - th->handled = false; - th->call_index = call_index; - th->call_num = call_num; - th->num_args = num_args; - for (int i = 0; i < kMaxArgs; i++) - th->args[i] = args[i]; - event_set(&th->ready); - running++; - return th; -} - -template <typename cover_t> -void write_coverage_signal(thread_t* th, uint32* signal_count_pos, uint32* cover_count_pos) -{ - // Write out feedback signals. - // Currently it is code edges computed as xor of two subsequent basic block PCs. - cover_t* cover_data = ((cover_t*)th->cover_data) + 1; - uint32 nsig = 0; - cover_t prev = 0; - for (uint32 i = 0; i < th->cover_size; i++) { - cover_t pc = cover_data[i]; - if (!cover_check(pc)) { - debug("got bad pc: 0x%llx\n", (uint64)pc); - doexit(0); - } - cover_t sig = pc ^ prev; - prev = hash(pc); - if (dedup(sig)) - continue; - write_output(sig); - nsig++; - } - // Write out number of signals. - *signal_count_pos = nsig; - - if (!flag_collect_cover) - return; - // Write out real coverage (basic block PCs). - uint32 cover_size = th->cover_size; - if (flag_dedup_cover) { - cover_t* end = cover_data + cover_size; - std::sort(cover_data, end); - cover_size = std::unique(cover_data, end) - cover_data; - } - // Truncate PCs to uint32 assuming that they fit into 32-bits. - // True for x86_64 and arm64 without KASLR. - for (uint32 i = 0; i < cover_size; i++) - write_output(cover_data[i]); - *cover_count_pos = cover_size; -} - -void handle_completion(thread_t* th) -{ - debug("completion of call %d [%s] on thread %d\n", th->call_index, syscalls[th->call_num].name, th->id); - if (event_isset(&th->ready) || !event_isset(&th->done) || th->handled) - fail("bad thread state in completion: ready=%d done=%d handled=%d", - event_isset(&th->ready), event_isset(&th->done), th->handled); - if (th->res != (long)-1) { - if (th->copyout_index != no_copyout) { - if (th->copyout_index >= kMaxCommands) - fail("result idx %lld overflows kMaxCommands", th->copyout_index); - results[th->copyout_index].executed = true; - results[th->copyout_index].val = th->res; - } - for (bool done = false; !done;) { - uint64 instr = read_input(&th->copyout_pos); - switch (instr) { - case instr_copyout: { - uint64 index = read_input(&th->copyout_pos); - if (index >= kMaxCommands) - fail("result idx %lld overflows kMaxCommands", index); - char* addr = (char*)read_input(&th->copyout_pos); - uint64 size = read_input(&th->copyout_pos); - uint64 val = 0; - if (copyout(addr, size, &val)) { - results[index].executed = true; - results[index].val = val; - } - debug("copyout 0x%llx from %p\n", val, addr); - break; - } - default: - done = true; - break; - } - } - } - if (!collide && !th->colliding) { - uint32 reserrno = th->res != -1 ? 0 : th->reserrno; - if (SYZ_EXECUTOR_USES_SHMEM) { - write_output(th->call_index); - write_output(th->call_num); - write_output(reserrno); - write_output(th->fault_injected); - uint32* signal_count_pos = write_output(0); // filled in later - uint32* cover_count_pos = write_output(0); // filled in later - uint32* comps_count_pos = write_output(0); // filled in later - - if (flag_collect_comps) { - // Collect only the comparisons - uint32 ncomps = th->cover_size; - kcov_comparison_t* start = (kcov_comparison_t*)(th->cover_data + sizeof(uint64)); - kcov_comparison_t* end = start + ncomps; - if ((char*)end > th->cover_end) - fail("too many comparisons %u", ncomps); - std::sort(start, end); - ncomps = std::unique(start, end) - start; - uint32 comps_size = 0; - for (uint32 i = 0; i < ncomps; ++i) { - if (start[i].ignore()) - continue; - comps_size++; - start[i].write(); - } - // Write out number of comparisons. - *comps_count_pos = comps_size; - } else if (flag_cover) { - if (is_kernel_64_bit) - write_coverage_signal<uint64>(th, signal_count_pos, cover_count_pos); - else - write_coverage_signal<uint32>(th, signal_count_pos, cover_count_pos); - } - debug("out #%u: index=%u num=%u errno=%d sig=%u cover=%u comps=%u\n", - completed, th->call_index, th->call_num, reserrno, - *signal_count_pos, *cover_count_pos, *comps_count_pos); - completed++; - write_completed(completed); - } else { - call_reply reply; - reply.header.magic = kOutMagic; - reply.header.done = 0; - reply.header.status = 0; - reply.call_index = th->call_index; - reply.call_num = th->call_num; - reply.reserrno = reserrno; - reply.fault_injected = th->fault_injected; - reply.signal_size = 0; - reply.cover_size = 0; - reply.comps_size = 0; - if (write(kOutPipeFd, &reply, sizeof(reply)) != sizeof(reply)) - fail("control pipe call write failed"); - debug("out: index=%u num=%u errno=%d\n", th->call_index, th->call_num, reserrno); - } - } - th->handled = true; - running--; -} - -void thread_create(thread_t* th, int id) -{ - th->created = true; - th->id = id; - th->handled = true; - event_init(&th->ready); - event_init(&th->done); - event_set(&th->done); - if (flag_threaded) - thread_start(&th->th, worker_thread, th); -} - -void* worker_thread(void* arg) -{ - thread_t* th = (thread_t*)arg; - - if (flag_cover) - cover_enable(th); - for (;;) { - event_wait(&th->ready); - execute_call(th); - } - return 0; -} - -void execute_call(thread_t* th) -{ - event_reset(&th->ready); - const call_t* call = &syscalls[th->call_num]; - debug("#%d: %s(", th->id, call->name); - for (int i = 0; i < th->num_args; i++) { - if (i != 0) - debug(", "); - debug("0x%lx", th->args[i]); - } - debug(")\n"); - - int fail_fd = -1; - if (flag_inject_fault && th->call_index == flag_fault_call) { - if (collide) - fail("both collide and fault injection are enabled"); - debug("injecting fault into %d-th operation\n", flag_fault_nth); - fail_fd = inject_fault(flag_fault_nth); - } - - if (flag_cover) - cover_reset(th); - errno = 0; - th->res = execute_syscall(call, th->args[0], th->args[1], th->args[2], - th->args[3], th->args[4], th->args[5], - th->args[6], th->args[7], th->args[8]); - th->reserrno = errno; - if (th->res == -1 && th->reserrno == 0) - th->reserrno = EINVAL; // our syz syscalls may misbehave - if (flag_cover) - th->cover_size = cover_read_size(th); - th->fault_injected = false; - - if (flag_inject_fault && th->call_index == flag_fault_call) { - th->fault_injected = fault_injected(fail_fd); - debug("fault injected: %d\n", th->fault_injected); - } - - if (th->res == -1) - debug("#%d: %s = errno(%d)\n", th->id, call->name, th->reserrno); - else - debug("#%d: %s = 0x%lx\n", th->id, call->name, th->res); - event_set(&th->done); -} - -static uint32 hash(uint32 a) -{ - a = (a ^ 61) ^ (a >> 16); - a = a + (a << 3); - a = a ^ (a >> 4); - a = a * 0x27d4eb2d; - a = a ^ (a >> 15); - return a; -} - -const uint32 dedup_table_size = 8 << 10; -uint32 dedup_table[dedup_table_size]; - -// Poorman's best-effort hashmap-based deduplication. -// The hashmap is global which means that we deduplicate across different calls. -// This is OK because we are interested only in new signals. -static bool dedup(uint32 sig) -{ - for (uint32 i = 0; i < 4; i++) { - uint32 pos = (sig + i) % dedup_table_size; - if (dedup_table[pos] == sig) - return true; - if (dedup_table[pos] == 0) { - dedup_table[pos] = sig; - return false; - } - } - dedup_table[sig % dedup_table_size] = sig; - return false; -} - -void copyin(char* addr, uint64 val, uint64 size, uint64 bf, uint64 bf_off, uint64 bf_len) -{ - if (bf != binary_format_native && (bf_off != 0 || bf_len != 0)) - fail("bitmask for string format %llu/%llu", bf_off, bf_len); - switch (bf) { - case binary_format_native: - NONFAILING(switch (size) { - case 1: - STORE_BY_BITMASK(uint8, addr, val, bf_off, bf_len); - break; - case 2: - STORE_BY_BITMASK(uint16, addr, val, bf_off, bf_len); - break; - case 4: - STORE_BY_BITMASK(uint32, addr, val, bf_off, bf_len); - break; - case 8: - STORE_BY_BITMASK(uint64, addr, val, bf_off, bf_len); - break; - default: - fail("copyin: bad argument size %llu", size); - }); - break; - case binary_format_strdec: - if (size != 20) - fail("bad strdec size %llu", size); - NONFAILING(sprintf((char*)addr, "%020llu", val)); - break; - case binary_format_strhex: - if (size != 18) - fail("bad strhex size %llu", size); - NONFAILING(sprintf((char*)addr, "0x%016llx", val)); - break; - case binary_format_stroct: - if (size != 23) - fail("bad stroct size %llu", size); - NONFAILING(sprintf((char*)addr, "%023llo", val)); - break; - default: - fail("unknown binary format %llu", bf); - } -} - -bool copyout(char* addr, uint64 size, uint64* res) -{ - bool ok = false; - NONFAILING( - switch (size) { - case 1: - *res = *(uint8*)addr; - break; - case 2: - *res = *(uint16*)addr; - break; - case 4: - *res = *(uint32*)addr; - break; - case 8: - *res = *(uint64*)addr; - break; - default: - fail("copyout: bad argument size %llu", size); - } __atomic_store_n(&ok, true, __ATOMIC_RELEASE);); - return ok; -} - -uint64 read_arg(uint64** input_posp) -{ - uint64 typ = read_input(input_posp); - switch (typ) { - case arg_const: { - uint64 size, bf, bf_off, bf_len; - uint64 val = read_const_arg(input_posp, &size, &bf, &bf_off, &bf_len); - if (bf != binary_format_native) - fail("bad argument binary format %llu", bf); - if (bf_off != 0 || bf_len != 0) - fail("bad argument bitfield %llu/%llu", bf_off, bf_len); - return val; - } - case arg_result: { - uint64 meta = read_input(input_posp); - uint64 bf = meta >> 8; - if (bf != binary_format_native) - fail("bad result argument format %llu", bf); - return read_result(input_posp); - } - default: - fail("bad argument type %llu", typ); - } -} - -uint64 read_const_arg(uint64** input_posp, uint64* size_p, uint64* bf_p, uint64* bf_off_p, uint64* bf_len_p) -{ - uint64 meta = read_input(input_posp); - uint64 val = read_input(input_posp); - *size_p = meta & 0xff; - uint64 bf = (meta >> 8) & 0xff; - *bf_off_p = (meta >> 16) & 0xff; - *bf_len_p = (meta >> 24) & 0xff; - uint64 pid_stride = meta >> 32; - val += pid_stride * procid; - if (bf == binary_format_bigendian) { - bf = binary_format_native; - switch (*size_p) { - case 2: - val = htobe16(val); - break; - case 4: - val = htobe32(val); - break; - case 8: - val = htobe64(val); - break; - default: - fail("bad big-endian int size %llu", *size_p); - } - } - *bf_p = bf; - return val; -} - -uint64 read_result(uint64** input_posp) -{ - uint64 idx = read_input(input_posp); - uint64 op_div = read_input(input_posp); - uint64 op_add = read_input(input_posp); - uint64 arg = read_input(input_posp); - if (idx >= kMaxCommands) - fail("command refers to bad result %lld", idx); - if (results[idx].executed) { - arg = results[idx].val; - if (op_div != 0) - arg = arg / op_div; - arg += op_add; - } - return arg; -} - -uint64 read_input(uint64** input_posp, bool peek) -{ - uint64* input_pos = *input_posp; - if ((char*)input_pos >= input_data + kMaxInput) - fail("input command overflows input %p: [%p:%p)", input_pos, input_data, input_data + kMaxInput); - if (!peek) - *input_posp = input_pos + 1; - return *input_pos; -} - -void kcov_comparison_t::write() -{ - // Write order: type arg1 arg2 pc. - write_output((uint32)type); - - // KCOV converts all arguments of size x first to uintx_t and then to - // uint64. We want to properly extend signed values, e.g we want - // int8 c = 0xfe to be represented as 0xfffffffffffffffe. - // Note that uint8 c = 0xfe will be represented the same way. - // This is ok because during hints processing we will anyways try - // the value 0x00000000000000fe. - switch (type & KCOV_CMP_SIZE_MASK) { - case KCOV_CMP_SIZE1: - arg1 = (uint64)(long long)(signed char)arg1; - arg2 = (uint64)(long long)(signed char)arg2; - break; - case KCOV_CMP_SIZE2: - arg1 = (uint64)(long long)(short)arg1; - arg2 = (uint64)(long long)(short)arg2; - break; - case KCOV_CMP_SIZE4: - arg1 = (uint64)(long long)(int)arg1; - arg2 = (uint64)(long long)(int)arg2; - break; - } - bool is_size_8 = (type & KCOV_CMP_SIZE_MASK) == KCOV_CMP_SIZE8; - if (!is_size_8) { - write_output((uint32)arg1); - write_output((uint32)arg2); - return; - } - // If we have 64 bits arguments then write them in Little-endian. - write_output((uint32)(arg1 & 0xFFFFFFFF)); - write_output((uint32)(arg1 >> 32)); - write_output((uint32)(arg2 & 0xFFFFFFFF)); - write_output((uint32)(arg2 >> 32)); -} - -bool kcov_comparison_t::operator==(const struct kcov_comparison_t& other) const -{ - // We don't check for PC equality now, because it is not used. - return type == other.type && arg1 == other.arg1 && arg2 == other.arg2; -} - -bool kcov_comparison_t::operator<(const struct kcov_comparison_t& other) const -{ - if (type != other.type) - return type < other.type; - if (arg1 != other.arg1) - return arg1 < other.arg1; - // We don't check for PC equality now, because it is not used. - return arg2 < other.arg2; -} |
