Пример #1
0
// Exec starts executor binary to execute program p and returns information about the execution:
// output: process output
// strace: strace output if env is created with FlagStrace
// cov: per-call coverage, len(cov) == len(p.Calls)
// failed: true if executor has detected a kernel bug
// hanged: program hanged and was killed
// err0: failed to start process, or executor has detected a logical error
func (env *Env) Exec(p *prog.Prog) (output, strace []byte, cov [][]uint32, failed, hanged bool, err0 error) {
	if p != nil {
		// Copy-in serialized program.
		progData := p.SerializeForExec()
		if len(progData) > len(env.In) {
			panic("program is too long")
		}
		copy(env.In, progData)
	}
	if env.flags&FlagCover != 0 {
		// Zero out the first word (ncmd), so that we don't have garbage there
		// if executor crashes before writing non-garbage there.
		for i := 0; i < 4; i++ {
			env.Out[i] = 0
		}
	}

	atomic.AddUint64(&env.StatExecs, 1)
	if env.cmd == nil {
		atomic.AddUint64(&env.StatRestarts, 1)
		env.cmd, err0 = makeCommand(env.bin, env.timeout, env.flags, env.inFile, env.outFile)
		if err0 != nil {
			return
		}
	}
	output, strace, failed, hanged, err0 = env.cmd.exec()
	if err0 != nil {
		env.cmd.close()
		env.cmd = nil
		return
	}

	if env.flags&FlagCover == 0 || p == nil {
		return
	}
	// Read out coverage information.
	r := bytes.NewReader(env.Out)
	var ncmd uint32
	if err := binary.Read(r, binary.LittleEndian, &ncmd); err != nil {
		err0 = fmt.Errorf("failed to read output coverage: %v", err)
		return
	}
	cov = make([][]uint32, len(p.Calls))
	for i := uint32(0); i < ncmd; i++ {
		var callIndex, callNum, coverSize, pc uint32
		if err := binary.Read(r, binary.LittleEndian, &callIndex); err != nil {
			err0 = fmt.Errorf("failed to read output coverage: %v", err)
			return
		}
		if err := binary.Read(r, binary.LittleEndian, &callNum); err != nil {
			err0 = fmt.Errorf("failed to read output coverage: %v", err)
			return
		}
		if err := binary.Read(r, binary.LittleEndian, &coverSize); err != nil {
			err0 = fmt.Errorf("failed to read output coverage: %v", err)
			return
		}
		if int(callIndex) > len(cov) {
			err0 = fmt.Errorf("failed to read output coverage: expect index %v, got %v", i, callIndex)
			return
		}
		if cov[callIndex] != nil {
			err0 = fmt.Errorf("failed to read output coverage: double coverage for call %v", callIndex)
			return
		}
		c := p.Calls[callIndex]
		if num := c.Meta.ID; uint32(num) != callNum {
			err0 = fmt.Errorf("failed to read output coverage: call %v: expect syscall %v, got %v, executed %v", callIndex, num, callNum, ncmd)
			return
		}
		cov1 := make([]uint32, coverSize)
		for j := uint32(0); j < coverSize; j++ {
			if err := binary.Read(r, binary.LittleEndian, &pc); err != nil {
				err0 = fmt.Errorf("failed to read output coverage: expect index %v, got %v", i, callIndex)
				return
			}
			cov1[j] = pc
		}
		cov[callIndex] = cov1
	}
	return
}
Пример #2
0
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
		if handled[name] {
			continue
		}
		handled[name] = true
		fmt.Fprintf(w, "#ifndef SYS_%v\n", name)
		fmt.Fprintf(w, "#define SYS_%v %v\n", name, c.Meta.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()
}
Пример #3
0
func Write(p *prog.Prog, opts Options) ([]byte, error) {
	exec := p.SerializeForExec(0)
	w := new(bytes.Buffer)

	fmt.Fprint(w, "// autogenerated by syzkaller (http://github.com/google/syzkaller)\n\n")

	handled := make(map[string]int)
	for _, c := range p.Calls {
		handled[c.Meta.CallName] = c.Meta.NR
	}
	for name, nr := range handled {
		fmt.Fprintf(w, "#ifndef __NR_%v\n", name)
		fmt.Fprintf(w, "#define __NR_%v %v\n", name, nr)
		fmt.Fprintf(w, "#endif\n")
	}
	fmt.Fprintf(w, "\n")

	enableTun := "false"
	if _, ok := handled["syz_emit_ethernet"]; ok {
		enableTun = "true"
	}

	hdr, err := preprocessCommonHeader(opts, handled)
	if err != nil {
		return nil, err
	}
	fmt.Fprint(w, hdr)
	fmt.Fprint(w, "\n")

	calls, nvar := generateCalls(exec)
	fmt.Fprintf(w, "long r[%v];\n", nvar)

	if !opts.Repeat {
		generateTestFunc(w, opts, calls, "loop")

		fmt.Fprint(w, "int main()\n{\n")
		fmt.Fprintf(w, "\tsetup_main_process(0, %v);\n", enableTun)
		fmt.Fprintf(w, "\tint pid = do_sandbox_%v();\n", opts.Sandbox)
		fmt.Fprint(w, "\tint status = 0;\n")
		fmt.Fprint(w, "\twhile (waitpid(pid, &status, __WALL) != pid) {}\n")
		fmt.Fprint(w, "\treturn 0;\n}\n")
	} else {
		generateTestFunc(w, opts, calls, "test")
		if opts.Procs <= 1 {
			fmt.Fprint(w, "int main()\n{\n")
			fmt.Fprintf(w, "\tsetup_main_process(0, %v);\n", enableTun)
			fmt.Fprintf(w, "\tint pid = do_sandbox_%v();\n", opts.Sandbox)
			fmt.Fprint(w, "\tint status = 0;\n")
			fmt.Fprint(w, "\twhile (waitpid(pid, &status, __WALL) != pid) {}\n")
			fmt.Fprint(w, "\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")
			fmt.Fprintf(w, "\t\t\tsetup_main_process(i, %v);\n", enableTun)
			fmt.Fprintf(w, "\t\t\tdo_sandbox_%v();\n", opts.Sandbox)
			fmt.Fprint(w, "\t\t}\n")
			fmt.Fprint(w, "\t}\n")
			fmt.Fprint(w, "\tsleep(1000000);\n")
			fmt.Fprint(w, "\treturn 0;\n}\n")
		}
	}
	// Remove duplicate new lines.
	out := w.Bytes()
	for {
		out1 := bytes.Replace(out, []byte{'\n', '\n', '\n'}, []byte{'\n', '\n'}, -1)
		if len(out) == len(out1) {
			break
		}
		out = out1
	}
	return out, nil
}
Пример #4
0
func Write(p *prog.Prog, opts Options) []byte {
	exec := p.SerializeForExec()
	w := new(bytes.Buffer)

	fmt.Fprint(w, "// autogenerated by syzkaller (http://github.com/google/syzkaller)\n\n")

	handled := make(map[string]int)
	for _, c := range p.Calls {
		handled[c.Meta.CallName] = c.Meta.NR
	}
	for _, c := range sys.Calls {
		if strings.HasPrefix(c.CallName, "syz_") {
			handled[c.CallName] = c.NR
		}
	}
	for name, nr := range handled {
		fmt.Fprintf(w, "#ifndef __NR_%v\n", name)
		fmt.Fprintf(w, "#define __NR_%v %v\n", name, nr)
		fmt.Fprintf(w, "#endif\n")
	}
	fmt.Fprintf(w, "\n")

	fmt.Fprint(w, commonHeader)
	fmt.Fprint(w, "\n")

	calls, nvar := generateCalls(exec)
	fmt.Fprintf(w, "long r[%v];\n", nvar)

	if !opts.Threaded && !opts.Collide {
		fmt.Fprint(w, `
int main()
{
	install_segv_handler();
	memset(r, -1, sizeof(r));
`)
		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", 2*len(calls))
		fmt.Fprintf(w, "\n")
		fmt.Fprintf(w, "install_segv_handler();\n")
		fmt.Fprintf(w, "\tmemset(r, -1, sizeof(r));\n")
		fmt.Fprintf(w, "\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(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[%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")
		}
		fmt.Fprintf(w, "\tusleep(100000);\n")
		fmt.Fprintf(w, "\treturn 0;\n}\n")
	}
	return w.Bytes()
}
Пример #5
0
// Exec starts executor binary to execute program p and returns information about the execution:
// output: process output
// cov: per-call coverage, len(cov) == len(p.Calls)
// failed: true if executor has detected a kernel bug
// hanged: program hanged and was killed
// err0: failed to start process, or executor has detected a logical error
func (env *Env) Exec(p *prog.Prog) (output []byte, cov [][]uint32, errnos []int, failed, hanged bool, err0 error) {
	if p != nil {
		// Copy-in serialized program.
		progData := p.SerializeForExec(env.pid)
		if len(progData) > len(env.In) {
			err0 = fmt.Errorf("executor %v: program is too long: %v/%v", env.pid, len(progData), len(env.In))
			return
		}
		copy(env.In, progData)
	}
	if env.flags&FlagCover != 0 {
		// Zero out the first word (ncmd), so that we don't have garbage there
		// if executor crashes before writing non-garbage there.
		for i := 0; i < 4; i++ {
			env.Out[i] = 0
		}
	}

	atomic.AddUint64(&env.StatExecs, 1)
	if env.cmd == nil {
		atomic.AddUint64(&env.StatRestarts, 1)
		env.cmd, err0 = makeCommand(env.pid, env.bin, env.timeout, env.flags, env.inFile, env.outFile)
		if err0 != nil {
			return
		}
	}
	var restart bool
	output, failed, hanged, restart, err0 = env.cmd.exec()
	if err0 != nil || restart {
		env.cmd.close()
		env.cmd = nil
		return
	}

	if env.flags&FlagCover == 0 || p == nil {
		return
	}
	// Read out coverage information.
	r := bytes.NewReader(env.Out)
	var ncmd uint32
	if err := binary.Read(r, binary.LittleEndian, &ncmd); err != nil {
		err0 = fmt.Errorf("executor %v: failed to read output coverage: %v", env.pid, err)
		return
	}
	cov = make([][]uint32, len(p.Calls))
	errnos = make([]int, len(p.Calls))
	for i := range errnos {
		errnos[i] = -1 // not executed
	}
	dumpCov := func() string {
		buf := new(bytes.Buffer)
		for i, c := range cov {
			str := "nil"
			if c != nil {
				str = fmt.Sprint(len(c))
			}
			fmt.Fprintf(buf, "%v:%v|", i, str)
		}
		return buf.String()
	}
	for i := uint32(0); i < ncmd; i++ {
		var callIndex, callNum, errno, coverSize, pc uint32
		if err := binary.Read(r, binary.LittleEndian, &callIndex); err != nil {
			err0 = fmt.Errorf("executor %v: failed to read output coverage: %v", env.pid, err)
			return
		}
		if err := binary.Read(r, binary.LittleEndian, &callNum); err != nil {
			err0 = fmt.Errorf("executor %v: failed to read output coverage: %v", env.pid, err)
			return
		}
		if err := binary.Read(r, binary.LittleEndian, &errno); err != nil {
			err0 = fmt.Errorf("executor %v: failed to read output errno: %v", env.pid, err)
			return
		}
		if err := binary.Read(r, binary.LittleEndian, &coverSize); err != nil {
			err0 = fmt.Errorf("executor %v: failed to read output coverage: %v", env.pid, err)
			return
		}
		if int(callIndex) > len(cov) {
			err0 = fmt.Errorf("executor %v: failed to read output coverage: record %v, call %v, total calls %v (cov: %v)",
				env.pid, i, callIndex, len(cov), dumpCov())
			return
		}
		if cov[callIndex] != nil {
			err0 = fmt.Errorf("executor %v: failed to read output coverage: double coverage for call %v (cov: %v)",
				env.pid, callIndex, dumpCov())
			return
		}
		c := p.Calls[callIndex]
		if num := c.Meta.ID; uint32(num) != callNum {
			err0 = fmt.Errorf("executor %v: failed to read output coverage: call %v: expect syscall %v, got %v, executed %v (cov: %v)",
				env.pid, callIndex, num, callNum, ncmd, dumpCov())
			return
		}
		cov1 := make([]uint32, coverSize)
		for j := uint32(0); j < coverSize; j++ {
			if err := binary.Read(r, binary.LittleEndian, &pc); err != nil {
				err0 = fmt.Errorf("executor %v: failed to read output coverage: record %v, call %v, coversize=%v err=%v", env.pid, i, callIndex, coverSize, err)
				return
			}
			cov1[j] = pc
		}
		cov[callIndex] = cov1
		errnos[callIndex] = int(errno)
	}
	return
}