diff options
| -rw-r--r-- | pkg/compiler/consts.go | 11 | ||||
| -rw-r--r-- | pkg/csource/akaros_common.go | 381 | ||||
| -rw-r--r-- | pkg/csource/csource.go | 276 | ||||
| -rw-r--r-- | pkg/csource/gen.go | 12 | ||||
| -rw-r--r-- | pkg/csource/linux_common.go (renamed from pkg/csource/common.go) | 2 | ||||
| -rw-r--r-- | sys/syz-sysgen/sysgen.go | 3 | ||||
| -rw-r--r-- | sys/targets/targets.go | 39 |
7 files changed, 583 insertions, 141 deletions
diff --git a/pkg/compiler/consts.go b/pkg/compiler/consts.go index f4337cab1..61198c96a 100644 --- a/pkg/compiler/consts.go +++ b/pkg/compiler/consts.go @@ -42,8 +42,6 @@ func ExtractConsts(desc *ast.Description, target *targets.Target, eh0 ast.ErrorH includeMap := make(map[string]bool) incdirMap := make(map[string]bool) constMap := make(map[string]bool) - syscallNumbers := targets.OSList[target.OS].SyscallNumbers - syscallPrefix := targets.OSList[target.OS].SyscallPrefix ast.Walk(desc, func(n1 ast.Node) { switch n := n1.(type) { @@ -76,8 +74,8 @@ func ExtractConsts(desc *ast.Description, target *targets.Target, eh0 ast.ErrorH info.Defines[name] = v constMap[name] = true case *ast.Call: - if syscallNumbers && !strings.HasPrefix(n.CallName, "syz_") { - constMap[syscallPrefix+n.CallName] = true + if target.SyscallNumbers && !strings.HasPrefix(n.CallName, "syz_") { + constMap[target.SyscallPrefix+n.CallName] = true } case *ast.Type: if c := typeConstIdentifier(n); c != nil { @@ -119,7 +117,6 @@ func (comp *compiler) assignSyscallNumbers(consts map[string]uint64) { } var top []ast.Node - syscallPrefix := targets.OSList[comp.target.OS].SyscallPrefix for _, decl := range comp.desc.Nodes { switch decl.(type) { case *ast.Call: @@ -129,12 +126,12 @@ func (comp *compiler) assignSyscallNumbers(consts map[string]uint64) { top = append(top, decl) continue } - if !targets.OSList[comp.target.OS].SyscallNumbers { + if !comp.target.SyscallNumbers { top = append(top, decl) continue } // Lookup in consts. - str := syscallPrefix + c.CallName + str := comp.target.SyscallPrefix + c.CallName nr, ok := consts[str] top = append(top, decl) if ok { diff --git a/pkg/csource/akaros_common.go b/pkg/csource/akaros_common.go new file mode 100644 index 000000000..a02f0834c --- /dev/null +++ b/pkg/csource/akaros_common.go @@ -0,0 +1,381 @@ +// AUTOGENERATED FROM executor/common_akaros.h +package csource + +var commonHeaderAkaros = ` + + +#include <unistd.h> +#if defined(SYZ_EXECUTOR) || defined(SYZ_THREADED) || defined(SYZ_COLLIDE) +#include <pthread.h> +#include <stdlib.h> +#endif +#if defined(SYZ_EXECUTOR) || (defined(SYZ_REPEAT) && defined(SYZ_WAIT_REPEAT)) +#include <errno.h> +#include <signal.h> +#include <stdarg.h> +#include <stdio.h> +#include <sys/time.h> +#include <sys/wait.h> +#include <time.h> +#endif +#if defined(SYZ_EXECUTOR) || defined(SYZ_HANDLE_SEGV) +#include <setjmp.h> +#include <signal.h> +#endif +#if defined(SYZ_EXECUTOR) || defined(SYZ_USE_TMP_DIR) +#include <stdlib.h> +#include <sys/stat.h> +#endif +#if defined(SYZ_EXECUTOR) || (defined(SYZ_REPEAT) && defined(SYZ_WAIT_REPEAT) && defined(SYZ_USE_TMP_DIR)) +#include <dirent.h> +#endif + +#define doexit exit +#define NORETURN __attribute__((noreturn)) + + + +#include <stdint.h> +#include <string.h> +#if defined(SYZ_EXECUTOR) || defined(SYZ_USE_TMP_DIR) +#include <errno.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/stat.h> +#endif +#if defined(SYZ_EXECUTOR) || defined(SYZ_HANDLE_SEGV) +#include <setjmp.h> +#include <signal.h> +#include <string.h> +#endif +#if defined(SYZ_EXECUTOR) || defined(SYZ_DEBUG) +#include <stdarg.h> +#include <stdio.h> +#endif + +#if defined(SYZ_EXECUTOR) +#ifndef SYSCALLAPI +#define SYSCALLAPI +#endif + +typedef long(SYSCALLAPI* syscall_t)(long, long, long, long, long, long, long, long, long); + +struct call_t { + const char* name; + int sys_nr; + syscall_t call; +}; + +extern call_t syscalls[]; +extern unsigned syscall_count; +#endif + +#if defined(SYZ_EXECUTOR) || (defined(SYZ_REPEAT) && defined(SYZ_WAIT_REPEAT)) || \ + defined(SYZ_USE_TMP_DIR) || defined(SYZ_TUN_ENABLE) || defined(SYZ_SANDBOX_NAMESPACE) || \ + defined(SYZ_SANDBOX_SETUID) || defined(SYZ_FAULT_INJECTION) || defined(__NR_syz_kvm_setup_cpu) +const int kFailStatus = 67; +const int kRetryStatus = 69; +#endif + +#if defined(SYZ_EXECUTOR) +const int kErrorStatus = 68; +#endif + +#if defined(SYZ_EXECUTOR) || (defined(SYZ_REPEAT) && defined(SYZ_WAIT_REPEAT)) || \ + defined(SYZ_USE_TMP_DIR) || defined(SYZ_TUN_ENABLE) || defined(SYZ_SANDBOX_NAMESPACE) || \ + defined(SYZ_SANDBOX_SETUID) || defined(SYZ_FAULT_INJECTION) || defined(__NR_syz_kvm_setup_cpu) +NORETURN static void fail(const char* msg, ...) +{ + int e = errno; + fflush(stdout); + va_list args; + va_start(args, msg); + vfprintf(stderr, msg, args); + va_end(args); + fprintf(stderr, " (errno %d)\n", e); + doexit((e == ENOMEM || e == EAGAIN) ? kRetryStatus : kFailStatus); +} +#endif + +#if defined(SYZ_EXECUTOR) +NORETURN static void error(const char* msg, ...) +{ + fflush(stdout); + va_list args; + va_start(args, msg); + vfprintf(stderr, msg, args); + va_end(args); + fprintf(stderr, "\n"); + doexit(kErrorStatus); +} +#endif + +#if defined(SYZ_EXECUTOR) || (defined(SYZ_REPEAT) && defined(SYZ_WAIT_REPEAT)) +NORETURN static void exitf(const char* msg, ...) +{ + int e = errno; + fflush(stdout); + va_list args; + va_start(args, msg); + vfprintf(stderr, msg, args); + va_end(args); + fprintf(stderr, " (errno %d)\n", e); + doexit(kRetryStatus); +} +#endif + +#if defined(SYZ_EXECUTOR) || defined(SYZ_DEBUG) +static int flag_debug; + +static void debug(const char* msg, ...) +{ + if (!flag_debug) + return; + va_list args; + va_start(args, msg); + vfprintf(stderr, msg, args); + va_end(args); + fflush(stderr); +} +#endif + +#if defined(SYZ_EXECUTOR) || defined(SYZ_USE_BITMASKS) +#define BITMASK_LEN(type, bf_len) (type)((1ull << (bf_len)) - 1) + +#define BITMASK_LEN_OFF(type, bf_off, bf_len) (type)(BITMASK_LEN(type, (bf_len)) << (bf_off)) + +#define STORE_BY_BITMASK(type, addr, val, bf_off, bf_len) \ + if ((bf_off) == 0 && (bf_len) == 0) { \ + *(type*)(addr) = (type)(val); \ + } else { \ + type new_val = *(type*)(addr); \ + new_val &= ~BITMASK_LEN_OFF(type, (bf_off), (bf_len)); \ + new_val |= ((type)(val)&BITMASK_LEN(type, (bf_len))) << (bf_off); \ + *(type*)(addr) = new_val; \ + } +#endif + +#if defined(SYZ_EXECUTOR) || defined(SYZ_USE_CHECKSUMS) +struct csum_inet { + uint32_t acc; +}; + +static void csum_inet_init(struct csum_inet* csum) +{ + csum->acc = 0; +} + +static void csum_inet_update(struct csum_inet* csum, const uint8_t* data, size_t length) +{ + if (length == 0) + return; + + size_t i; + for (i = 0; i < length - 1; i += 2) + csum->acc += *(uint16_t*)&data[i]; + + if (length & 1) + csum->acc += (uint16_t)data[length - 1]; + + while (csum->acc > 0xffff) + csum->acc = (csum->acc & 0xffff) + (csum->acc >> 16); +} + +static uint16_t csum_inet_digest(struct csum_inet* csum) +{ + return ~csum->acc; +} +#endif + +#if defined(SYZ_EXECUTOR) || defined(SYZ_HANDLE_SEGV) +static __thread int skip_segv; +static __thread jmp_buf segv_env; + +static void segv_handler(int sig, siginfo_t* info, void* ctx) +{ + uintptr_t addr = (uintptr_t)info->si_addr; + const uintptr_t prog_start = 1 << 20; + const uintptr_t prog_end = 100 << 20; + if (__atomic_load_n(&skip_segv, __ATOMIC_RELAXED) && (addr < prog_start || addr > prog_end)) { + debug("SIGSEGV on %p, skipping\n", addr); + siglongjmp(segv_env, 1); + } + debug("SIGSEGV on %p, exiting\n", addr); + doexit(sig); + for (;;) { + } +} + +static void install_segv_handler() +{ + struct sigaction sa; + + memset(&sa, 0, sizeof(sa)); + sa.sa_sigaction = segv_handler; + sa.sa_flags = SA_NODEFER | SA_SIGINFO; + sigemptyset(&sa.sa_mask); + sigaction(SIGSEGV, &sa, NULL); + sigaction(SIGBUS, &sa, NULL); +} + +#define NONFAILING(...) \ + { \ + __atomic_fetch_add(&skip_segv, 1, __ATOMIC_SEQ_CST); \ + if (_setjmp(segv_env) == 0) { \ + __VA_ARGS__; \ + } \ + __atomic_fetch_sub(&skip_segv, 1, __ATOMIC_SEQ_CST); \ + } +#endif + +#if defined(SYZ_EXECUTOR) || (defined(SYZ_REPEAT) && defined(SYZ_WAIT_REPEAT)) +static uint64_t current_time_ms() +{ + struct timespec ts; + + if (clock_gettime(CLOCK_MONOTONIC, &ts)) + fail("clock_gettime failed"); + return (uint64_t)ts.tv_sec * 1000 + (uint64_t)ts.tv_nsec / 1000000; +} +#endif + +#if defined(SYZ_EXECUTOR) || defined(SYZ_USE_TMP_DIR) +static void use_temporary_dir() +{ + char tmpdir_template[] = "./syzkaller.XXXXXX"; + char* tmpdir = mkdtemp(tmpdir_template); + if (!tmpdir) + fail("failed to mkdtemp"); + if (chmod(tmpdir, 0777)) + fail("failed to chmod"); + if (chdir(tmpdir)) + fail("failed to chdir"); +} +#endif + +#if defined(SYZ_EXECUTOR) +static void sleep_ms(uint64_t ms) +{ + usleep(ms * 1000); +} +#endif + +#if defined(SYZ_EXECUTOR) || defined(SYZ_FAULT_INJECTION) +static int inject_fault(int nth) +{ + return 0; +} + +static int fault_injected(int fail_fd) +{ + return 0; +} +#endif + +#if defined(SYZ_EXECUTOR) || (defined(SYZ_REPEAT) && defined(SYZ_WAIT_REPEAT) && defined(SYZ_USE_TMP_DIR)) +static void remove_dir(const char* dir) +{ + DIR* dp; + struct dirent* ep; + int iter = 0; +retry: + dp = opendir(dir); + if (dp == NULL) + return; + while ((ep = readdir(dp))) { + if (strcmp(ep->d_name, ".") == 0 || strcmp(ep->d_name, "..") == 0) + continue; + char filename[FILENAME_MAX]; + snprintf(filename, sizeof(filename), "%s/%s", dir, ep->d_name); + struct stat st; + if (lstat(filename, &st)) + return; + if (S_ISDIR(st.st_mode)) { + remove_dir(filename); + continue; + } + int i; + for (i = 0;; i++) { + if (unlink(filename) == 0) + break; + if (errno == EROFS) + break; + if (errno != EBUSY || i > 100) + return; + } + } + closedir(dp); + int i; + for (i = 0;; i++) { + if (rmdir(dir) == 0) + break; + if (i < 100) { + if (errno == EROFS) + break; + if (errno == ENOTEMPTY) { + if (iter < 100) { + iter++; + goto retry; + } + } + } + return; + } +} +#endif + +#if defined(SYZ_REPEAT) +static void test(); + +#if defined(SYZ_WAIT_REPEAT) +void loop() +{ + int iter; + for (iter = 0;; iter++) { +#ifdef SYZ_USE_TMP_DIR + char cwdbuf[256]; + sprintf(cwdbuf, "./%d", iter); + if (mkdir(cwdbuf, 0777)) + fail("failed to mkdir"); +#endif + int pid = fork(); + if (pid < 0) + fail("clone failed"); + if (pid == 0) { +#ifdef SYZ_USE_TMP_DIR + if (chdir(cwdbuf)) + fail("failed to chdir"); +#endif + test(); + doexit(0); + } + int status = 0; + uint64_t start = current_time_ms(); + for (;;) { + int res = waitpid(-1, &status, WNOHANG); + if (res == pid) + break; + usleep(1000); + if (current_time_ms() - start > 5 * 1000) { + kill(pid, SIGKILL); + while (waitpid(-1, &status, 0) != pid) { + } + break; + } + } +#ifdef SYZ_USE_TMP_DIR + remove_dir(cwdbuf); +#endif + } +} +#else +void loop() +{ + while (1) { + test(); + } +} +#endif +#endif +` diff --git a/pkg/csource/csource.go b/pkg/csource/csource.go index b0d44a94f..1bc1ec5ba 100644 --- a/pkg/csource/csource.go +++ b/pkg/csource/csource.go @@ -1,17 +1,13 @@ // Copyright 2015 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. -// I heard you like shell... -//go:generate bash -c "echo -e '// AUTOGENERATED FROM executor/common_linux.h\npackage csource\nvar commonHeader = `' > common.go; cat ../../executor/common_linux.h | sed -e '/#include \"common.h\"/ {' -e 'r ../../executor/common.h' -e 'd' -e '}' - | sed -e '/#include \"common_kvm_amd64.h\"/ {' -e 'r ../../executor/common_kvm_amd64.h' -e 'd' -e '}' - | sed -e '/#include \"common_kvm_arm64.h\"/ {' -e 'r ../../executor/common_kvm_arm64.h' -e 'd' -e '}' - | sed -e '/#include \"kvm.h\"/ {' -e 'r ../../executor/kvm.h' -e 'd' -e '}' - | sed -e '/#include \"kvm.S.h\"/ {' -e 'r ../../executor/kvm.S.h' -e 'd' -e '}' - | egrep -v '^[ ]*//' | sed '/^[ ]*\\/\\/.*/d' | sed 's#[ ]*//.*##g' >> common.go; echo '`' >> common.go" -//go:generate go fmt common.go - +// Package csource generates [almost] equivalent C programs from syzkaller programs. package csource import ( "bytes" "errors" "fmt" - "io" "io/ioutil" "os" "os/exec" @@ -71,122 +67,119 @@ func Write(p *prog.Prog, opts Options) ([]byte, error) { if err := opts.Check(); err != nil { return nil, fmt.Errorf("csource: invalid opts: %v", err) } - exec := make([]byte, prog.ExecBufferSize) - if _, err := p.SerializeForExec(exec, 0); err != nil { - return nil, fmt.Errorf("failed to serialize program: %v", err) + commonHeader := "" + switch p.Target.OS { + case "linux": + commonHeader = commonHeaderLinux + case "akaros": + commonHeader = commonHeaderAkaros + default: + return nil, fmt.Errorf("unsupported OS: %v", p.Target.OS) + } + ctx := &context{ + p: p, + opts: opts, + target: p.Target, + sysTarget: targets.List[p.Target.OS][p.Target.Arch], + w: new(bytes.Buffer), + calls: make(map[string]uint64), } - w := new(bytes.Buffer) - - fmt.Fprint(w, "// autogenerated by syzkaller (http://github.com/google/syzkaller)\n\n") - - handled := make(map[string]uint64) for _, c := range p.Calls { - handled[c.Meta.CallName] = c.Meta.NR + ctx.calls[c.Meta.CallName] = c.Meta.NR } - hdr, err := preprocessCommonHeader(p.Target, opts, handled, prog.RequiresBitmasks(p), prog.RequiresChecksums(p)) + + ctx.print("// autogenerated by syzkaller (http://github.com/google/syzkaller)\n\n") + + hdr, err := ctx.preprocessCommonHeader(commonHeader) if err != nil { return nil, err } - fmt.Fprint(w, hdr) - fmt.Fprint(w, "\n") + ctx.print(hdr) + ctx.print("\n") - for name, nr := range handled { - if strings.HasPrefix(name, "syz_") { - continue - } - if p.Target.OS == "linux" && p.Target.Arch == "amd64" && nr < 313 { - // Only generate defines for new syscalls (added after commit 8a1ab3155c2ac on 2012-10-04). - continue - } - fmt.Fprintf(w, "#ifndef __NR_%v\n", name) - fmt.Fprintf(w, "#define __NR_%v %v\n", name, nr) - fmt.Fprintf(w, "#endif\n") - } - if p.Target.OS == "linux" && p.Target.PtrSize == 4 { - // This is a dirty hack. - // On 32-bit linux mmap translated to old_mmap syscall which has a different signature. - // mmap2 has the right signature. executor translates mmap to mmap2, do the same here. - fmt.Fprintf(w, "#undef __NR_mmap\n") - fmt.Fprintf(w, "#define __NR_mmap __NR_mmap2\n") - } - fmt.Fprintf(w, "\n") + ctx.generateSyscallDefines() - calls, nvar := generateCalls(p.Target, exec, opts) - fmt.Fprintf(w, "long r[%v];\n", nvar) + exec := make([]byte, prog.ExecBufferSize) + progSize, err := ctx.p.SerializeForExec(exec, 0) + if err != nil { + return nil, fmt.Errorf("failed to serialize program: %v", err) + } + calls, nvar := ctx.generateCalls(exec[:progSize]) + ctx.printf("long r[%v];\n", nvar) if !opts.Repeat { - generateTestFunc(w, opts, calls, "loop") + ctx.generateTestFunc(calls, "loop") - fmt.Fprint(w, "int main()\n{\n") + ctx.print("int main()\n{\n") if opts.HandleSegv { - fmt.Fprintf(w, "\tinstall_segv_handler();\n") + ctx.printf("\tinstall_segv_handler();\n") } if opts.UseTmpDir { - fmt.Fprintf(w, "\tuse_temporary_dir();\n") + ctx.printf("\tuse_temporary_dir();\n") } if opts.Sandbox != "" { - fmt.Fprintf(w, "\tint pid = do_sandbox_%v(0, %v);\n", opts.Sandbox, opts.EnableTun) - fmt.Fprint(w, "\tint status = 0;\n") - fmt.Fprint(w, "\twhile (waitpid(pid, &status, __WALL) != pid) {}\n") + ctx.printf("\tint pid = do_sandbox_%v(0, %v);\n", opts.Sandbox, opts.EnableTun) + ctx.print("\tint status = 0;\n") + ctx.print("\twhile (waitpid(pid, &status, __WALL) != pid) {}\n") } else { if opts.EnableTun { - fmt.Fprintf(w, "\tsetup_tun(0, %v);\n", opts.EnableTun) + ctx.printf("\tsetup_tun(0, %v);\n", opts.EnableTun) } - fmt.Fprint(w, "\tloop();\n") + ctx.print("\tloop();\n") } - fmt.Fprint(w, "\treturn 0;\n}\n") + ctx.print("\treturn 0;\n}\n") } else { - generateTestFunc(w, opts, calls, "test") + ctx.generateTestFunc(calls, "test") if opts.Procs <= 1 { - fmt.Fprint(w, "int main()\n{\n") + ctx.print("int main()\n{\n") if opts.HandleSegv { - fmt.Fprintf(w, "\tinstall_segv_handler();\n") + ctx.printf("\tinstall_segv_handler();\n") } if opts.UseTmpDir { - fmt.Fprintf(w, "\tuse_temporary_dir();\n") + ctx.printf("\tuse_temporary_dir();\n") } if opts.Sandbox != "" { - fmt.Fprintf(w, "\tint pid = do_sandbox_%v(0, %v);\n", opts.Sandbox, opts.EnableTun) - fmt.Fprint(w, "\tint status = 0;\n") - fmt.Fprint(w, "\twhile (waitpid(pid, &status, __WALL) != pid) {}\n") + ctx.printf("\tint pid = do_sandbox_%v(0, %v);\n", opts.Sandbox, opts.EnableTun) + ctx.print("\tint status = 0;\n") + ctx.print("\twhile (waitpid(pid, &status, __WALL) != pid) {}\n") } else { if opts.EnableTun { - fmt.Fprintf(w, "\tsetup_tun(0, %v);\n", opts.EnableTun) + ctx.printf("\tsetup_tun(0, %v);\n", opts.EnableTun) } - fmt.Fprint(w, "\tloop();\n") + ctx.print("\tloop();\n") } - fmt.Fprint(w, "\treturn 0;\n}\n") + ctx.print("\treturn 0;\n}\n") } else { - fmt.Fprint(w, "int main()\n{\n") - fmt.Fprint(w, "\tint i;") - fmt.Fprintf(w, "\tfor (i = 0; i < %v; i++) {\n", opts.Procs) - fmt.Fprint(w, "\t\tif (fork() == 0) {\n") + ctx.print("int main()\n{\n") + ctx.print("\tint i;") + ctx.printf("\tfor (i = 0; i < %v; i++) {\n", opts.Procs) + ctx.print("\t\tif (fork() == 0) {\n") if opts.HandleSegv { - fmt.Fprintf(w, "\t\t\tinstall_segv_handler();\n") + ctx.printf("\t\t\tinstall_segv_handler();\n") } if opts.UseTmpDir { - fmt.Fprintf(w, "\t\t\tuse_temporary_dir();\n") + ctx.printf("\t\t\tuse_temporary_dir();\n") } if opts.Sandbox != "" { - fmt.Fprintf(w, "\t\t\tint pid = do_sandbox_%v(i, %v);\n", opts.Sandbox, opts.EnableTun) - fmt.Fprint(w, "\t\t\tint status = 0;\n") - fmt.Fprint(w, "\t\t\twhile (waitpid(pid, &status, __WALL) != pid) {}\n") + ctx.printf("\t\t\tint pid = do_sandbox_%v(i, %v);\n", opts.Sandbox, opts.EnableTun) + ctx.print("\t\t\tint status = 0;\n") + ctx.print("\t\t\twhile (waitpid(pid, &status, __WALL) != pid) {}\n") } else { if opts.EnableTun { - fmt.Fprintf(w, "\t\t\tsetup_tun(i, %v);\n", opts.EnableTun) + ctx.printf("\t\t\tsetup_tun(i, %v);\n", opts.EnableTun) } - fmt.Fprint(w, "\t\t\tloop();\n") + ctx.print("\t\t\tloop();\n") } - fmt.Fprint(w, "\t\t\treturn 0;\n") - fmt.Fprint(w, "\t\t}\n") - fmt.Fprint(w, "\t}\n") - fmt.Fprint(w, "\tsleep(1000000);\n") - fmt.Fprint(w, "\treturn 0;\n}\n") + ctx.print("\t\t\treturn 0;\n") + ctx.print("\t\t}\n") + ctx.print("\t}\n") + ctx.print("\tsleep(1000000);\n") + ctx.print("\treturn 0;\n}\n") } } // Remove NONFAILING and debug calls. - out0 := w.String() + out0 := ctx.w.String() if !opts.HandleSegv { re := regexp.MustCompile(`\t*NONFAILING\((.*)\);\n`) out0 = re.ReplaceAllString(out0, "$1;\n") @@ -211,64 +204,102 @@ func Write(p *prog.Prog, opts Options) ([]byte, error) { return out1, nil } -func generateTestFunc(w io.Writer, opts Options, calls []string, name string) { +type context struct { + p *prog.Prog + opts Options + target *prog.Target + sysTarget *targets.Target + w *bytes.Buffer + calls map[string]uint64 // CallName -> NR +} + +func (ctx *context) print(str string) { + ctx.w.WriteString(str) +} + +func (ctx *context) printf(str string, args ...interface{}) { + ctx.print(fmt.Sprintf(str, args...)) +} + +func (ctx *context) generateTestFunc(calls []string, name string) { + opts := ctx.opts if !opts.Threaded && !opts.Collide { - fmt.Fprintf(w, "void %v()\n{\n", name) + ctx.printf("void %v()\n{\n", name) if opts.Debug { // Use debug to avoid: error: ‘debug’ defined but not used. - fmt.Fprintf(w, "\tdebug(\"%v\\n\");\n", name) + ctx.printf("\tdebug(\"%v\\n\");\n", name) } if opts.Repro { - fmt.Fprintf(w, "\tsyscall(SYS_write, 1, \"executing program\\n\", strlen(\"executing program\\n\"));\n") + ctx.printf("\tsyscall(SYS_write, 1, \"executing program\\n\", strlen(\"executing program\\n\"));\n") } - fmt.Fprintf(w, "\tmemset(r, -1, sizeof(r));\n") + ctx.printf("\tmemset(r, -1, sizeof(r));\n") for _, c := range calls { - fmt.Fprintf(w, "%s", c) + ctx.printf("%s", c) } - fmt.Fprintf(w, "}\n\n") + ctx.printf("}\n\n") } else { - fmt.Fprintf(w, "void *thr(void *arg)\n{\n") - fmt.Fprintf(w, "\tswitch ((long)arg) {\n") + ctx.printf("void *thr(void *arg)\n{\n") + ctx.printf("\tswitch ((long)arg) {\n") for i, c := range calls { - fmt.Fprintf(w, "\tcase %v:\n", i) - fmt.Fprintf(w, "%s", strings.Replace(c, "\t", "\t\t", -1)) - fmt.Fprintf(w, "\t\tbreak;\n") + ctx.printf("\tcase %v:\n", i) + ctx.printf("%s", strings.Replace(c, "\t", "\t\t", -1)) + ctx.printf("\t\tbreak;\n") } - fmt.Fprintf(w, "\t}\n") - fmt.Fprintf(w, "\treturn 0;\n}\n\n") + ctx.printf("\t}\n") + ctx.printf("\treturn 0;\n}\n\n") - fmt.Fprintf(w, "void %v()\n{\n", name) - fmt.Fprintf(w, "\tlong i;\n") - fmt.Fprintf(w, "\tpthread_t th[%v];\n", 2*len(calls)) - fmt.Fprintf(w, "\n") + ctx.printf("void %v()\n{\n", name) + ctx.printf("\tlong i;\n") + ctx.printf("\tpthread_t th[%v];\n", 2*len(calls)) + ctx.printf("\n") if opts.Debug { // Use debug to avoid: error: ‘debug’ defined but not used. - fmt.Fprintf(w, "\tdebug(\"%v\\n\");\n", name) + ctx.printf("\tdebug(\"%v\\n\");\n", name) } if opts.Repro { - fmt.Fprintf(w, "\tsyscall(SYS_write, 1, \"executing program\\n\", strlen(\"executing program\\n\"));\n") + ctx.printf("\tsyscall(SYS_write, 1, \"executing program\\n\", strlen(\"executing program\\n\"));\n") } - fmt.Fprintf(w, "\tmemset(r, -1, sizeof(r));\n") + ctx.printf("\tmemset(r, -1, sizeof(r));\n") if opts.Collide { - fmt.Fprintf(w, "\tsrand(getpid());\n") + ctx.printf("\tsrand(getpid());\n") } - fmt.Fprintf(w, "\tfor (i = 0; i < %v; i++) {\n", len(calls)) - fmt.Fprintf(w, "\t\tpthread_create(&th[i], 0, thr, (void*)i);\n") - fmt.Fprintf(w, "\t\tusleep(rand()%%10000);\n") - fmt.Fprintf(w, "\t}\n") + ctx.printf("\tfor (i = 0; i < %v; i++) {\n", len(calls)) + ctx.printf("\t\tpthread_create(&th[i], 0, thr, (void*)i);\n") + ctx.printf("\t\tusleep(rand()%%10000);\n") + ctx.printf("\t}\n") if opts.Collide { - fmt.Fprintf(w, "\tfor (i = 0; i < %v; i++) {\n", len(calls)) - fmt.Fprintf(w, "\t\tpthread_create(&th[%v+i], 0, thr, (void*)i);\n", len(calls)) - fmt.Fprintf(w, "\t\tif (rand()%%2)\n") - fmt.Fprintf(w, "\t\t\tusleep(rand()%%10000);\n") - fmt.Fprintf(w, "\t}\n") + ctx.printf("\tfor (i = 0; i < %v; i++) {\n", len(calls)) + ctx.printf("\t\tpthread_create(&th[%v+i], 0, thr, (void*)i);\n", len(calls)) + ctx.printf("\t\tif (rand()%%2)\n") + ctx.printf("\t\t\tusleep(rand()%%10000);\n") + ctx.printf("\t}\n") + } + ctx.printf("\tusleep(rand()%%100000);\n") + ctx.printf("}\n\n") + } +} + +func (ctx *context) generateSyscallDefines() { + prefix := ctx.sysTarget.SyscallPrefix + for name, nr := range ctx.calls { + if strings.HasPrefix(name, "syz_") || !ctx.sysTarget.NeedSyscallDefine(nr) { + continue } - fmt.Fprintf(w, "\tusleep(rand()%%100000);\n") - fmt.Fprintf(w, "}\n\n") + ctx.printf("#ifndef %v%v\n", prefix, name) + ctx.printf("#define %v%v %v\n", prefix, name, nr) + ctx.printf("#endif\n") + } + if ctx.target.OS == "linux" && ctx.target.PtrSize == 4 { + // This is a dirty hack. + // On 32-bit linux mmap translated to old_mmap syscall which has a different signature. + // mmap2 has the right signature. executor translates mmap to mmap2, do the same here. + ctx.printf("#undef __NR_mmap\n") + ctx.printf("#define __NR_mmap __NR_mmap2\n") } + ctx.printf("\n") } -func generateCalls(target *prog.Target, exec []byte, opts Options) ([]string, int) { +func (ctx *context) generateCalls(exec []byte) ([]string, int) { read := func() uint64 { if len(exec) < 8 { panic("exec program overflow") @@ -372,23 +403,25 @@ loop: default: // Normal syscall. newCall() - if opts.Fault && opts.FaultCall == len(calls) { + if ctx.opts.Fault && ctx.opts.FaultCall == len(calls) { fmt.Fprintf(w, "\twrite_file(\"/sys/kernel/debug/failslab/ignore-gfp-wait\", \"N\");\n") fmt.Fprintf(w, "\twrite_file(\"/sys/kernel/debug/fail_futex/ignore-private\", \"N\");\n") - fmt.Fprintf(w, "\tinject_fault(%v);\n", opts.FaultNth) + fmt.Fprintf(w, "\tinject_fault(%v);\n", ctx.opts.FaultNth) } - meta := target.Syscalls[instr] + meta := ctx.target.Syscalls[instr] emitCall := true if meta.CallName == "syz_test" { emitCall = false } - if !opts.EnableTun && (meta.CallName == "syz_emit_ethernet" || meta.CallName == "syz_extract_tcp_res") { + if !ctx.opts.EnableTun && (meta.CallName == "syz_emit_ethernet" || + meta.CallName == "syz_extract_tcp_res") { emitCall = false } native := !strings.HasPrefix(meta.CallName, "syz_") if emitCall { if native { - fmt.Fprintf(w, "\tr[%v] = syscall(__NR_%v", n, meta.CallName) + fmt.Fprintf(w, "\tr[%v] = syscall(%v%v", + n, ctx.sysTarget.SyscallPrefix, meta.CallName) } else { fmt.Fprintf(w, "\tr[%v] = %v(", n, meta.CallName) } @@ -430,14 +463,15 @@ loop: return calls, n } -func preprocessCommonHeader(target *prog.Target, opts Options, handled map[string]uint64, useBitmasks, useChecksums bool) (string, error) { +func (ctx *context) preprocessCommonHeader(commonHeader string) (string, error) { var defines []string - if useBitmasks { + if prog.RequiresBitmasks(ctx.p) { defines = append(defines, "SYZ_USE_BITMASKS") } - if useChecksums { + if prog.RequiresChecksums(ctx.p) { defines = append(defines, "SYZ_USE_CHECKSUMS") } + opts := ctx.opts switch opts.Sandbox { case "": // No sandbox, do nothing. @@ -477,12 +511,10 @@ func preprocessCommonHeader(target *prog.Target, opts Options, handled map[strin if opts.Debug { defines = append(defines, "SYZ_DEBUG") } - for name, _ := range handled { + for name, _ := range ctx.calls { defines = append(defines, "__NR_"+name) } - - sysTarget := targets.List[target.OS][target.Arch] - defines = append(defines, sysTarget.CArch...) + defines = append(defines, ctx.sysTarget.CArch...) cmd := exec.Command("cpp", "-nostdinc", "-undef", "-fdirectives-only", "-dDI", "-E", "-P", "-") for _, def := range defines { diff --git a/pkg/csource/gen.go b/pkg/csource/gen.go new file mode 100644 index 000000000..103a6215e --- /dev/null +++ b/pkg/csource/gen.go @@ -0,0 +1,12 @@ +// 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. + +// I heard you like shell... + +//go:generate bash -c "echo -e '// AUTOGENERATED FROM executor/common_linux.h\npackage csource\nvar commonHeaderLinux = `' > linux_common.go; cat ../../executor/common_linux.h | sed -e '/#include \"common.h\"/ {' -e 'r ../../executor/common.h' -e 'd' -e '}' - | sed -e '/#include \"common_kvm_amd64.h\"/ {' -e 'r ../../executor/common_kvm_amd64.h' -e 'd' -e '}' - | sed -e '/#include \"common_kvm_arm64.h\"/ {' -e 'r ../../executor/common_kvm_arm64.h' -e 'd' -e '}' - | sed -e '/#include \"kvm.h\"/ {' -e 'r ../../executor/kvm.h' -e 'd' -e '}' - | sed -e '/#include \"kvm.S.h\"/ {' -e 'r ../../executor/kvm.S.h' -e 'd' -e '}' - | egrep -v '^[ ]*//' | sed '/^[ ]*\\/\\/.*/d' | sed 's#[ ]*//.*##g' >> linux_common.go; echo '`' >> linux_common.go" +//go:generate go fmt linux_common.go + +//go:generate bash -c "echo -e '// AUTOGENERATED FROM executor/common_akaros.h\npackage csource\nvar commonHeaderAkaros = `' > akaros_common.go; cat ../../executor/common_akaros.h | sed -e '/#include \"common.h\"/ {' -e 'r ../../executor/common.h' -e 'd' -e '}' - | egrep -v '^[ ]*//' | sed '/^[ ]*\\/\\/.*/d' | sed 's#[ ]*//.*##g' >> akaros_common.go; echo '`' >> akaros_common.go" +//go:generate go fmt akaros_common.go + +package csource diff --git a/pkg/csource/common.go b/pkg/csource/linux_common.go index f11b5360c..b86424ff9 100644 --- a/pkg/csource/common.go +++ b/pkg/csource/linux_common.go @@ -1,7 +1,7 @@ // AUTOGENERATED FROM executor/common_linux.h package csource -var commonHeader = ` +var commonHeaderLinux = ` #ifndef _GNU_SOURCE diff --git a/sys/syz-sysgen/sysgen.go b/sys/syz-sysgen/sysgen.go index e34055f1c..c0d0cd21b 100644 --- a/sys/syz-sysgen/sysgen.go +++ b/sys/syz-sysgen/sysgen.go @@ -186,7 +186,6 @@ func generateExecutorSyscalls(target *targets.Target, syscalls []*prog.Syscall, CARCH: target.CArch, } fake := make(map[string]uint64) - syscallNumbers := targets.OSList[target.OS].SyscallNumbers for _, c := range syscalls { syz := strings.HasPrefix(c.CallName, "syz_") if syz { @@ -196,7 +195,7 @@ func generateExecutorSyscalls(target *targets.Target, syscalls []*prog.Syscall, Name: c.Name, CallName: c.CallName, NR: int32(c.NR), - NeedCall: syz || !syscallNumbers, + NeedCall: syz || !target.SyscallNumbers, }) } for name, nr := range fake { diff --git a/sys/targets/targets.go b/sys/targets/targets.go index c0abdf86b..042637306 100644 --- a/sys/targets/targets.go +++ b/sys/targets/targets.go @@ -4,6 +4,7 @@ package targets type Target struct { + os OS string Arch string PtrSize uint64 @@ -14,6 +15,15 @@ type Target struct { KernelArch string KernelHeaderArch string KernelCrossCompile string + // NeedSyscallDefine is used by csource package to decide when to emit __NR_* defines. + NeedSyscallDefine func(nr uint64) bool +} + +type os struct { + // Does the OS use syscall numbers (e.g. Linux) or has interface based on functions (e.g. fuchsia). + SyscallNumbers bool + // E.g. "__NR_" or "SYS_". + SyscallPrefix string } var List = map[string]map[string]*Target{ @@ -26,6 +36,11 @@ var List = map[string]map[string]*Target{ CCompilerPrefix: "x86_64-linux-gnu-", KernelArch: "x86_64", KernelHeaderArch: "x86", + NeedSyscallDefine: func(nr uint64) bool { + // Only generate defines for new syscalls + // (added after commit 8a1ab3155c2ac on 2012-10-04). + return nr >= 313 + }, }, "386": { PtrSize: 4, @@ -87,19 +102,14 @@ var List = map[string]map[string]*Target{ }, "akaros": map[string]*Target{ "amd64": { - PtrSize: 8, - CArch: []string{"__x86_64__"}, + PtrSize: 8, + CArch: []string{"__x86_64__"}, + NeedSyscallDefine: dontNeedSyscallDefine, }, }, } -type OS struct { - // Does the OS use syscall numbers (e.g. Linux) or has interface based on functions (e.g. fuchsia). - SyscallNumbers bool - SyscallPrefix string -} - -var OSList = map[string]*OS{ +var oses = map[string]os{ "linux": { SyscallNumbers: true, SyscallPrefix: "__NR_", @@ -123,8 +133,19 @@ var OSList = map[string]*OS{ func init() { for OS, archs := range List { for arch, target := range archs { + target.os = oses[OS] target.OS = OS target.Arch = arch + if target.NeedSyscallDefine == nil { + target.NeedSyscallDefine = needSyscallDefine + } } } } + +func needSyscallDefine(nr uint64) bool { + return true +} +func dontNeedSyscallDefine(nr uint64) bool { + return false +} |
