aboutsummaryrefslogtreecommitdiffstats
path: root/pkg/csource/csource.go
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2017-10-15 11:15:27 +0200
committerDmitry Vyukov <dvyukov@google.com>2017-10-16 14:21:54 +0200
commitf78642861b4dbe396a67d5e2a750e22f83f3edd5 (patch)
treede2808e3e9d47aa1687b259e8ab48e34731532bc /pkg/csource/csource.go
parentd158fb9d3b6e03882c60a51854f149a8d2a637a0 (diff)
pkg/csource: support akaros
Diffstat (limited to 'pkg/csource/csource.go')
-rw-r--r--pkg/csource/csource.go276
1 files changed, 154 insertions, 122 deletions
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 {