diff options
| author | Dmitry Vyukov <dvyukov@google.com> | 2015-12-23 13:38:31 +0100 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2015-12-23 13:38:31 +0100 |
| commit | e253cbc79fc20b65ce9dd965c6fe3adddac817ca (patch) | |
| tree | 3a64c5a18d8b0a020c3cee24abbfe32fc468fc86 /csource | |
| parent | 071ad4e91f95f115236a639e934181c7e596f337 (diff) | |
csource: new package
Move C source generation into a separate package.
Prog is too bloated already.
Diffstat (limited to 'csource')
| -rw-r--r-- | csource/csource.go | 233 | ||||
| -rw-r--r-- | csource/csource_test.go | 53 |
2 files changed, 286 insertions, 0 deletions
diff --git a/csource/csource.go b/csource/csource.go new file mode 100644 index 000000000..51e817884 --- /dev/null +++ b/csource/csource.go @@ -0,0 +1,233 @@ +// 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. + +package csource + +import ( + "bytes" + "fmt" + "io/ioutil" + "strings" + "os" + "os/exec" + "unsafe" + + "github.com/google/syzkaller/prog" + "github.com/google/syzkaller/sys" +) + +type Options struct { + Threaded bool + Collide bool +} + +func Write(p *prog.Prog, opts Options) []byte { + exec := p.SerializeForExec() + w := new(bytes.Buffer) + + fmt.Fprintf(w, `// autogenerated by syzkaller (http://github.com/google/syzkaller) +#include <unistd.h> +#include <sys/syscall.h> +#include <string.h> +#include <stdint.h> +#include <pthread.h> + +`) + + handled := make(map[string]bool) + for _, c := range p.Calls { + name := c.Meta.CallName + nr, ok := prog.NewSyscalls[name] + if !ok || handled[name] { + continue + } + handled[name] = true + fmt.Fprintf(w, "#ifndef SYS_%v\n", name) + fmt.Fprintf(w, "#define SYS_%v %v\n", name, nr) + fmt.Fprintf(w, "#endif\n") + } + fmt.Fprintf(w, "\n") + + calls,nvar := generateCalls(exec) + fmt.Fprintf(w, "long r[%v];\n\n", nvar) + + if !opts.Threaded && !opts.Collide { + fmt.Fprintf(w, "int main()\n{\n") + fmt.Fprintf(w, "\tmemset(r, -1, sizeof(r));\n") + for _, c := range calls { + fmt.Fprintf(w, "%s", c) + } + fmt.Fprintf(w, "\treturn 0;\n}\n") + } else { + fmt.Fprintf(w, "void *thr(void *arg)\n{\n") + fmt.Fprintf(w, "\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") + } + fmt.Fprintf(w, "\t}\n") + fmt.Fprintf(w, "\treturn 0;\n}\n\n") + + fmt.Fprintf(w, "int main()\n{\n") + fmt.Fprintf(w, "\tlong i;\n") + fmt.Fprintf(w, "\tpthread_t th[%v];\n", len(calls)) + fmt.Fprintf(w, "\n") + fmt.Fprintf(w, "\tmemset(r, -1, sizeof(r));\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(10000);\n") + fmt.Fprintf(w, "\t}\n") + if opts.Collide { + 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\tif (i%%2==0)\n") + fmt.Fprintf(w, "\t\t\tusleep(10000);\n") + fmt.Fprintf(w, "\t}\n") + } + fmt.Fprintf(w, "\tusleep(100000);\n") + fmt.Fprintf(w, "\treturn 0;\n}\n") + } + return w.Bytes() +} + +func generateCalls(exec []byte) ([]string, int) { + read := func() uintptr { + if len(exec) < 8 { + panic("exec program overflow") + } + v := *(*uint64)(unsafe.Pointer(&exec[0])) + exec = exec[8:] + return uintptr(v) + } + resultRef := func() string { + arg := read() + res := fmt.Sprintf("r[%v]", arg) + if opDiv := read(); opDiv != 0 { + res = fmt.Sprintf("%v/%v", res, opDiv) + } + if opAdd := read(); opAdd != 0 { + res = fmt.Sprintf("%v+%v", res, opAdd) + } + return res + } + lastCall := 0 + seenCall := false + var calls []string + w := new(bytes.Buffer) + newCall := func() { + if seenCall { + seenCall = false + calls = append(calls, w.String()) + w = new(bytes.Buffer) + } + } + n := 0 +loop: + for ;; n++ { + switch instr := read(); instr { + case prog.ExecInstrEOF: + break loop + case prog.ExecInstrCopyin: + newCall() + addr := read() + typ := read() + size := read() + switch typ { + case prog.ExecArgConst: + arg := read() + fmt.Fprintf(w, "\t*(uint%v_t*)0x%x = (uint%v_t)0x%x;\n", size*8, addr, size*8, arg) + case prog.ExecArgResult: + fmt.Fprintf(w, "\t*(uint%v_t*)0x%x = %v;\n", size*8, addr, resultRef()) + case prog.ExecArgData: + data := exec[:size] + exec = exec[(size+7)/8*8:] + var esc []byte + for _, v := range data { + hex := func(v byte) byte { + if v < 10 { + return '0' + v + } + return 'a' + v - 10 + } + esc = append(esc, '\\', 'x', hex(v>>4), hex(v<<4>>4)) + } + fmt.Fprintf(w, "\tmemcpy((void*)0x%x, \"%s\", %v);\n", addr, esc, size) + default: + panic("bad argument type") + } + case prog.ExecInstrCopyout: + addr := read() + size := read() + fmt.Fprintf(w, "\tif (r[%v] != -1)\n", lastCall) + fmt.Fprintf(w, "\t\tr[%v] = *(uint%v_t*)0x%x;\n", n, size*8, addr) + case prog.ExecInstrSetPad: + newCall() + read() // addr + read() // size + case prog.ExecInstrCheckPad: + read() // addr + read() // size + default: + // Normal syscall. + newCall() + meta := sys.Calls[instr] + fmt.Fprintf(w, "\tr[%v] = syscall(SYS_%v", n, meta.CallName) + nargs := read() + for i := uintptr(0); i < nargs; i++ { + typ := read() + size := read() + _ = size + switch typ { + case prog.ExecArgConst: + fmt.Fprintf(w, ", 0x%xul", read()) + case prog.ExecArgResult: + fmt.Fprintf(w, ", %v", resultRef()) + default: + panic("unknown arg type") + } + } + for i := nargs; i < 6; i++ { + fmt.Fprintf(w, ", 0") + } + fmt.Fprintf(w, ");\n") + lastCall = n + seenCall = true + } + } + newCall() + return calls, n +} + + +// WriteTempFile writes data to a temp file and returns its name. +func WriteTempFile(data []byte) (string, error) { + f, err := ioutil.TempFile("", "syz-prog") + if err != nil { + return "", fmt.Errorf("failed to create a temp file: %v", err) + } + if _, err := f.Write(data); err != nil { + f.Close() + os.Remove(f.Name()) + return "", fmt.Errorf("failed to write temp file: %v", err) + } + f.Close() + return f.Name(), nil +} + +// Build builds a C/C++ program from source file src +// and returns name of the resulting binary. +func Build(src string) (string, error) { + bin, err := ioutil.TempFile("", "syzkaller") + if err != nil { + return "", fmt.Errorf("failed to create temp file: %v", err) + } + bin.Close() + out, err := exec.Command("gcc", "-x", "c++", "-std=gnu++11", src, "-o", bin.Name(), "-lpthread", "-static", "-O1", "-g").CombinedOutput() + if err != nil { + os.Remove(bin.Name()) + data, _ := ioutil.ReadFile(src) + return "", fmt.Errorf("failed to build program::\n%s\n%s", data, out) + } + return bin.Name(), nil +} diff --git a/csource/csource_test.go b/csource/csource_test.go new file mode 100644 index 000000000..b2fbda6dc --- /dev/null +++ b/csource/csource_test.go @@ -0,0 +1,53 @@ +// 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. + +package csource + +import ( + "math/rand" + "os" + "testing" + "time" + + "github.com/google/syzkaller/prog" +) + +func initTest(t *testing.T) (rand.Source, int) { + iters := 1000 + if testing.Short() { + iters = 10 + } + seed := int64(time.Now().UnixNano()) + rs := rand.NewSource(seed) + t.Logf("seed=%v", seed) + return rs, iters +} + +func Test(t *testing.T) { + rs, iters := initTest(t) + options := []Options{ + Options{}, + Options{Threaded: true}, + Options{Threaded: true, Collide: true}, + } + for i := 0; i < iters; i++ { + p := prog.Generate(rs, 10, nil) + for _, opts := range options { + testOne(t, p, opts) + } + } +} + +func testOne(t *testing.T, p *prog.Prog, opts Options) { + src := Write(p, opts) + srcf, err := WriteTempFile(src) + if err != nil { + t.Fatalf("%v", err) + } + defer os.Remove(srcf) + bin, err := Build(srcf) + if err != nil { + t.Fatalf("%v", err) + } + defer os.Remove(bin) +} |
