From f78642861b4dbe396a67d5e2a750e22f83f3edd5 Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Sun, 15 Oct 2017 11:15:27 +0200 Subject: pkg/csource: support akaros --- pkg/csource/csource.go | 276 +++++++++++++++++++++++++++---------------------- 1 file changed, 154 insertions(+), 122 deletions(-) (limited to 'pkg/csource/csource.go') 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 { -- cgit mrf-deployment