aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2017-09-20 21:18:36 +0200
committerDmitry Vyukov <dvyukov@google.com>2017-09-20 21:19:29 +0200
commit8cb7d3dcfcbe11a6d5682743816409d1c8e8f6a0 (patch)
tree75e6dd62ae61ce7986cf4e0e04e9954586033635
parentd606e60dfe3d50499812f7d740dae6e727fa9f76 (diff)
all: initial support for fuchsia
Nothing works, but builds. Update #191
-rw-r--r--executor/executor_fuchsia.cc3
-rw-r--r--executor/executor_linux.cc2
-rw-r--r--executor/syscalls_fuchsia.h26
-rw-r--r--executor/syscalls_linux.h (renamed from executor/syscalls.h)10
-rw-r--r--executor/test.go2
-rw-r--r--executor/test_executor_linux.cc (renamed from executor/test_executor.cc)2
-rw-r--r--executor/test_test.go2
-rw-r--r--pkg/ipc/ipc.go647
-rw-r--r--pkg/ipc/ipc_fuchsia.go30
-rw-r--r--pkg/ipc/ipc_linux.go650
-rw-r--r--pkg/serializer/serializer.go12
-rw-r--r--prog/target.go50
-rw-r--r--sys/fuchsia/amd64.go27
-rw-r--r--sys/fuchsia/arm64.go27
-rw-r--r--sys/fuchsia/init.go35
-rw-r--r--sys/fuchsia/sys.txt6
-rw-r--r--sys/fuchsia/sys_amd64.const5
-rw-r--r--sys/fuchsia/sys_arm64.const5
-rw-r--r--sys/linux/386.go4
-rw-r--r--sys/linux/amd64.go4
-rw-r--r--sys/linux/arm.go4
-rw-r--r--sys/linux/arm64.go4
-rw-r--r--sys/linux/init.go161
-rw-r--r--sys/linux/ppc64le.go4
-rw-r--r--sys/syz-sysgen/sysgen.go13
-rw-r--r--sys/targets/targets.go10
-rw-r--r--syz-fuzzer/fuzzer.go118
-rw-r--r--syz-fuzzer/fuzzer_fuchsia.go26
-rw-r--r--syz-fuzzer/fuzzer_linux.go132
-rw-r--r--tools/syz-execprog/execprog.go13
-rw-r--r--tools/syz-execprog/execprog_fuchsia.go9
-rw-r--r--tools/syz-execprog/execprog_linux.go23
32 files changed, 1143 insertions, 923 deletions
diff --git a/executor/executor_fuchsia.cc b/executor/executor_fuchsia.cc
index 6a312167b..3a214e4ac 100644
--- a/executor/executor_fuchsia.cc
+++ b/executor/executor_fuchsia.cc
@@ -3,7 +3,10 @@
// +build
+#include "syscalls_fuchsia.h"
+
int main()
{
+ (void)syscalls;
return 0;
}
diff --git a/executor/executor_linux.cc b/executor/executor_linux.cc
index cd0e406ff..d428fc1ea 100644
--- a/executor/executor_linux.cc
+++ b/executor/executor_linux.cc
@@ -26,7 +26,7 @@
#include <time.h>
#include <unistd.h>
-#include "syscalls.h"
+#include "syscalls_linux.h"
#define SYZ_EXECUTOR
#include "common.h"
diff --git a/executor/syscalls_fuchsia.h b/executor/syscalls_fuchsia.h
new file mode 100644
index 000000000..6919aec79
--- /dev/null
+++ b/executor/syscalls_fuchsia.h
@@ -0,0 +1,26 @@
+// AUTOGENERATED FILE
+
+struct call_t {
+ const char* name;
+ int sys_nr;
+};
+
+#if defined(__x86_64__) || 0
+#define GOARCH "amd64"
+#define SYZ_REVISION "d65b9adb4853817be6f471df44fc8347ebf6dfc6"
+
+static call_t syscalls[] = {
+ {"mx_time_get", 0},
+
+};
+#endif
+
+#if defined(__aarch64__) || 0
+#define GOARCH "arm64"
+#define SYZ_REVISION "bd2655e6d85f1fecceb1648649e1ad5adda81dc8"
+
+static call_t syscalls[] = {
+ {"mx_time_get", 0},
+
+};
+#endif
diff --git a/executor/syscalls.h b/executor/syscalls_linux.h
index 7ee207b0f..48ea44e1c 100644
--- a/executor/syscalls.h
+++ b/executor/syscalls_linux.h
@@ -7,7 +7,7 @@ struct call_t {
#if defined(__i386__) || 0
#define GOARCH "386"
-#define SYZ_REVISION "86d577076d75af98e3c800a3c65bf8e4869e3ea4"
+#define SYZ_REVISION "7b2f1949d48094cf3369932d0743db166049457b"
#define __NR_syz_emit_ethernet 1000000
#define __NR_syz_extract_tcp_res 1000001
#define __NR_syz_fuse_mount 1000002
@@ -1505,7 +1505,7 @@ static call_t syscalls[] = {
#if defined(__x86_64__) || 0
#define GOARCH "amd64"
-#define SYZ_REVISION "e482e82186ee2d78bed701630c062b7c5d5b23c6"
+#define SYZ_REVISION "8349df62e623f9c8d8bfaefcc8ba3febf463ce92"
#define __NR_syz_emit_ethernet 1000000
#define __NR_syz_extract_tcp_res 1000001
#define __NR_syz_fuse_mount 1000002
@@ -3064,7 +3064,7 @@ static call_t syscalls[] = {
#if defined(__arm__) || 0
#define GOARCH "arm"
-#define SYZ_REVISION "41372f11d60aad90c991926434f714d736320cb1"
+#define SYZ_REVISION "02567c0623e18214f0be8d059aad68c263064645"
#define __NR_syz_emit_ethernet 1000000
#define __NR_syz_extract_tcp_res 1000001
#define __NR_syz_fuse_mount 1000002
@@ -4576,7 +4576,7 @@ static call_t syscalls[] = {
#if defined(__aarch64__) || 0
#define GOARCH "arm64"
-#define SYZ_REVISION "3424aa8d1e2be36f2a093d39f49a45b6904827e0"
+#define SYZ_REVISION "0709fe60bdd20ea30d937d14615a40269fadb2b8"
#define __NR_syz_emit_ethernet 1000000
#define __NR_syz_extract_tcp_res 1000001
#define __NR_syz_fuse_mount 1000002
@@ -6063,7 +6063,7 @@ static call_t syscalls[] = {
#if defined(__ppc64__) || defined(__PPC64__) || defined(__powerpc64__) || 0
#define GOARCH "ppc64le"
-#define SYZ_REVISION "0af6f5872777fdaf20c06a1f982df39659c46b6a"
+#define SYZ_REVISION "8bfb8686625fa3398eb8d1a5d66834dec0d3fa06"
#define __NR_syz_emit_ethernet 1000000
#define __NR_syz_extract_tcp_res 1000001
#define __NR_syz_fuse_mount 1000002
diff --git a/executor/test.go b/executor/test.go
index 1f18dd3bf..7bab3933c 100644
--- a/executor/test.go
+++ b/executor/test.go
@@ -3,6 +3,8 @@
//go:generate bash -c "gcc kvm_gen.cc kvm.S -o kvm_gen && ./kvm_gen > kvm.S.h && rm ./kvm_gen"
+// +build linux
+
package executor
// int test_copyin();
diff --git a/executor/test_executor.cc b/executor/test_executor_linux.cc
index 5b30286bf..8bbd56910 100644
--- a/executor/test_executor.cc
+++ b/executor/test_executor_linux.cc
@@ -1,7 +1,7 @@
// 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.
-#include "syscalls.h"
+#include "syscalls_linux.h"
#define SYZ_EXECUTOR
#include "common.h"
diff --git a/executor/test_test.go b/executor/test_test.go
index 368454ac3..8048d50f0 100644
--- a/executor/test_test.go
+++ b/executor/test_test.go
@@ -1,6 +1,8 @@
// Copyright 2016 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.
+// +build linux
+
package executor
import "testing"
diff --git a/pkg/ipc/ipc.go b/pkg/ipc/ipc.go
index af0173c2e..a7e79b033 100644
--- a/pkg/ipc/ipc.go
+++ b/pkg/ipc/ipc.go
@@ -4,39 +4,13 @@
package ipc
import (
- "bytes"
"flag"
"fmt"
- "io/ioutil"
- "os"
- "os/exec"
- "path/filepath"
- "strings"
- "sync"
- "sync/atomic"
- "syscall"
"time"
- "unsafe"
- "github.com/google/syzkaller/pkg/osutil"
"github.com/google/syzkaller/prog"
)
-type Env struct {
- In []byte
- Out []byte
-
- cmd *command
- inFile *os.File
- outFile *os.File
- bin []string
- pid int
- config Config
-
- StatExecs uint64
- StatRestarts uint64
-}
-
// Configuration flags for Config.Flags.
const (
FlagDebug = uint64(1) << iota // debug output from executor
@@ -57,20 +31,6 @@ const (
FlagCollectComps // collect KCOV comparisons
)
-const (
- outputSize = 16 << 20
- signalOffset = 15 << 20
-
- statusFail = 67
- statusError = 68
- statusRetry = 69
-
- // Comparison types masks taken from KCOV headers.
- compSizeMask = 6
- compSize8 = 6
- compConstMask = 1
-)
-
var (
flagThreaded = flag.Bool("threaded", true, "use threaded mode in executor")
flagCollide = flag.Bool("collide", true, "collide syscalls to provoke data races")
@@ -107,9 +67,8 @@ type Config struct {
// Timeout is the execution timeout for a single program.
Timeout time.Duration
- // AbortSignal is the signal to send to the executor in error
- // conditions.
- AbortSignal syscall.Signal
+ // AbortSignal is the signal to send to the executor in error conditions.
+ AbortSignal int
// BufferSize is the size of the internal buffer for executor output.
BufferSize uint64
@@ -139,90 +98,11 @@ func DefaultConfig() (Config, error) {
c.Flags |= FlagDebug
}
c.Timeout = *flagTimeout
- c.AbortSignal = syscall.Signal(*flagAbortSignal)
+ c.AbortSignal = *flagAbortSignal
c.BufferSize = *flagBufferSize
return c, nil
}
-func MakeEnv(bin string, pid int, config Config) (*Env, error) {
- // IPC timeout must be larger then executor timeout.
- // Otherwise IPC will kill parent executor but leave child executor alive.
- if config.Timeout < 7*time.Second {
- config.Timeout = 7 * time.Second
- }
- inf, inmem, err := createMapping(prog.ExecBufferSize)
- if err != nil {
- return nil, err
- }
- defer func() {
- if inf != nil {
- closeMapping(inf, inmem)
- }
- }()
- outf, outmem, err := createMapping(outputSize)
- if err != nil {
- return nil, err
- }
- defer func() {
- if outf != nil {
- closeMapping(outf, outmem)
- }
- }()
- serializeUint64(inmem[0:], config.Flags)
- serializeUint64(inmem[8:], uint64(pid))
- inmem = inmem[16:]
- env := &Env{
- In: inmem,
- Out: outmem,
- inFile: inf,
- outFile: outf,
- bin: strings.Split(bin, " "),
- pid: pid,
- config: config,
- }
- if len(env.bin) == 0 {
- return nil, fmt.Errorf("binary is empty string")
- }
- env.bin[0], err = filepath.Abs(env.bin[0]) // we are going to chdir
- if err != nil {
- return nil, fmt.Errorf("filepath.Abs failed: %v", err)
- }
- // Append pid to binary name.
- // E.g. if binary is 'syz-executor' and pid=15,
- // we create a link from 'syz-executor15' to 'syz-executor' and use 'syz-executor15' as binary.
- // This allows to easily identify program that lead to a crash in the log.
- // Log contains pid in "executing program 15" and crashes usually contain "Comm: syz-executor15".
- base := filepath.Base(env.bin[0])
- pidStr := fmt.Sprint(pid)
- if len(base)+len(pidStr) >= 16 {
- // TASK_COMM_LEN is currently set to 16
- base = base[:15-len(pidStr)]
- }
- binCopy := filepath.Join(filepath.Dir(env.bin[0]), base+pidStr)
- if err := os.Link(env.bin[0], binCopy); err == nil {
- env.bin[0] = binCopy
- }
- inf = nil
- outf = nil
- return env, nil
-}
-
-func (env *Env) Close() error {
- if env.cmd != nil {
- env.cmd.close()
- }
- err1 := closeMapping(env.inFile, env.In)
- err2 := closeMapping(env.outFile, env.Out)
- switch {
- case err1 != nil:
- return err1
- case err2 != nil:
- return err2
- default:
- return nil
- }
-}
-
type CallInfo struct {
Signal []uint32 // feedback signal, filled if FlagSignal is set
Cover []uint32 // per-call coverage, filled if FlagSignal is set and cover == true,
@@ -239,524 +119,3 @@ func GetCompMaps(info []CallInfo) []prog.CompMap {
}
return compMaps
}
-
-// Exec starts executor binary to execute program p and returns information about the execution:
-// output: process output
-// info: per-call info
-// 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(opts *ExecOpts, p *prog.Prog) (output []byte, info []CallInfo, failed, hanged bool, err0 error) {
- if p != nil {
- // Copy-in serialized program.
- if err := p.SerializeForExec(env.In, env.pid); err != nil {
- err0 = fmt.Errorf("executor %v: failed to serialize: %v", env.pid, err)
- return
- }
- }
- if env.config.Flags&FlagSignal != 0 {
- // Zero out the first two words (ncmd and nsig), 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.config, env.inFile, env.outFile)
- if err0 != nil {
- return
- }
- }
- var restart bool
- output, failed, hanged, restart, err0 = env.cmd.exec(opts)
- if err0 != nil || restart {
- env.cmd.close()
- env.cmd = nil
- return
- }
-
- if p == nil || env.config.Flags&FlagSignal == 0 &&
- env.config.Flags&FlagCollectComps == 0 {
- return
- }
- info, err0 = env.readOutCoverage(p)
- return
-}
-
-func (env *Env) readOutCoverage(p *prog.Prog) (info []CallInfo, err0 error) {
- out := ((*[1 << 28]uint32)(unsafe.Pointer(&env.Out[0])))[:len(env.Out)/int(unsafe.Sizeof(uint32(0)))]
- readOut := func(v *uint32) bool {
- if len(out) == 0 {
- return false
- }
- *v = out[0]
- out = out[1:]
- return true
- }
-
- readOutAndSetErr := func(v *uint32, msg string, args ...interface{}) bool {
- if !readOut(v) {
- err0 = fmt.Errorf(msg, args)
- return false
- }
- return true
- }
-
- // Reads out a 64 bits int in Little-endian as two blocks of 32 bits.
- readOut64 := func(v *uint64, msg string, args ...interface{}) bool {
- var a, b uint32
- if !(readOutAndSetErr(&a, msg, args) && readOutAndSetErr(&b, msg, args)) {
- return false
- }
- *v = uint64(a) + uint64(b)<<32
- return true
- }
-
- var ncmd uint32
- if !readOutAndSetErr(&ncmd,
- "executor %v: failed to read output coverage", env.pid) {
- return
- }
- info = make([]CallInfo, len(p.Calls))
- for i := range info {
- info[i].Errno = -1 // not executed
- }
- dumpCov := func() string {
- buf := new(bytes.Buffer)
- for i, inf := range info {
- str := "nil"
- if inf.Signal != nil {
- str = fmt.Sprint(len(inf.Signal))
- }
- fmt.Fprintf(buf, "%v:%v|", i, str)
- }
- return buf.String()
- }
- for i := uint32(0); i < ncmd; i++ {
- var callIndex, callNum, errno, faultInjected, signalSize, coverSize, compsSize uint32
- if !readOut(&callIndex) || !readOut(&callNum) || !readOut(&errno) || !readOut(&faultInjected) || !readOut(&signalSize) || !readOut(&coverSize) || !readOut(&compsSize) {
- err0 = fmt.Errorf("executor %v: failed to read output coverage", env.pid)
- return
- }
- if int(callIndex) >= len(info) {
- err0 = fmt.Errorf("executor %v: failed to read output coverage: record %v, call %v, total calls %v (cov: %v)",
- env.pid, i, callIndex, len(info), dumpCov())
- return
- }
- c := p.Calls[callIndex]
- if num := c.Meta.ID; uint32(num) != callNum {
- err0 = fmt.Errorf("executor %v: failed to read output coverage: record %v call %v: expect syscall %v, got %v, executed %v (cov: %v)",
- env.pid, i, callIndex, num, callNum, ncmd, dumpCov())
- return
- }
- if info[callIndex].Signal != nil {
- err0 = fmt.Errorf("executor %v: failed to read output coverage: double coverage for call %v (cov: %v)",
- env.pid, callIndex, dumpCov())
- return
- }
- info[callIndex].Errno = int(errno)
- info[callIndex].FaultInjected = faultInjected != 0
- if signalSize > uint32(len(out)) {
- err0 = fmt.Errorf("executor %v: failed to read output signal: record %v, call %v, signalsize=%v coversize=%v",
- env.pid, i, callIndex, signalSize, coverSize)
- return
- }
- // Read out signals.
- info[callIndex].Signal = out[:signalSize:signalSize]
- out = out[signalSize:]
- // Read out coverage.
- if coverSize > uint32(len(out)) {
- err0 = fmt.Errorf("executor %v: failed to read output coverage: record %v, call %v, signalsize=%v coversize=%v",
- env.pid, i, callIndex, signalSize, coverSize)
- return
- }
- info[callIndex].Cover = out[:coverSize:coverSize]
- out = out[coverSize:]
- // Read out comparisons.
- compMap := make(prog.CompMap)
- for j := uint32(0); j < compsSize; j++ {
- var typ uint32
- var op1, op2 uint64
- if !readOutAndSetErr(&typ,
- "executor %v: failed while reading type of comparison %v", env.pid, j) {
- return
- }
- if typ > compConstMask|compSizeMask {
- err0 = fmt.Errorf("executor %v: got wrong value (%v) while reading type of comparison %v",
- env.pid, typ, j)
- return
- }
-
- isSize8 := (typ & compSizeMask) == compSize8
- isConst := (typ & compConstMask) != 0
- arg1ErrString := "executor %v: failed while reading op1 of comparison %v"
- arg2ErrString := "executor %v: failed while reading op2 of comparison %v"
- if isSize8 {
- var tmp1, tmp2 uint32
- if !readOutAndSetErr(&tmp1, arg1ErrString, env.pid, j) ||
- !readOutAndSetErr(&tmp2, arg2ErrString, env.pid, j) {
- return
- }
- op1 = uint64(tmp1)
- op2 = uint64(tmp2)
- } else {
- if !readOut64(&op1, arg1ErrString, env.pid, j) ||
- !readOut64(&op2, arg2ErrString, env.pid, j) {
- return
- }
- }
- if op1 == op2 {
- // It's useless to store such comparisons.
- continue
- }
- compMap.AddComp(op2, op1)
- if isConst {
- // If one of the operands was const, then this operand is always
- // placed first in the instrumented callbacks. Such an operand
- // could not be an argument of our syscalls (because otherwise
- // it wouldn't be const), thus we simply ignore it.
- continue
- }
- compMap.AddComp(op1, op2)
- }
- info[callIndex].Comps = compMap
- }
- return
-}
-
-func createMapping(size int) (f *os.File, mem []byte, err error) {
- f, err = ioutil.TempFile("./", "syzkaller-shm")
- if err != nil {
- err = fmt.Errorf("failed to create temp file: %v", err)
- return
- }
- if err = f.Truncate(int64(size)); err != nil {
- err = fmt.Errorf("failed to truncate shm file: %v", err)
- f.Close()
- os.Remove(f.Name())
- return
- }
- f.Close()
- fname := f.Name()
- f, err = os.OpenFile(f.Name(), os.O_RDWR, osutil.DefaultFilePerm)
- if err != nil {
- err = fmt.Errorf("failed to open shm file: %v", err)
- os.Remove(fname)
- return
- }
- mem, err = syscall.Mmap(int(f.Fd()), 0, size, syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED)
- if err != nil {
- err = fmt.Errorf("failed to mmap shm file: %v", err)
- f.Close()
- os.Remove(f.Name())
- return
- }
- return
-}
-
-func closeMapping(f *os.File, mem []byte) error {
- err1 := syscall.Munmap(mem)
- err2 := f.Close()
- err3 := os.Remove(f.Name())
- switch {
- case err1 != nil:
- return err1
- case err2 != nil:
- return err2
- case err3 != nil:
- return err3
- default:
- return nil
- }
-}
-
-type command struct {
- pid int
- config Config
- cmd *exec.Cmd
- dir string
- readDone chan []byte
- exited chan struct{}
- inrp *os.File
- outwp *os.File
-}
-
-func makeCommand(pid int, bin []string, config Config, inFile *os.File, outFile *os.File) (*command, error) {
- dir, err := ioutil.TempDir("./", "syzkaller-testdir")
- if err != nil {
- return nil, fmt.Errorf("failed to create temp dir: %v", err)
- }
-
- c := &command{
- pid: pid,
- config: config,
- dir: dir,
- }
- defer func() {
- if c != nil {
- c.close()
- }
- }()
-
- if config.Flags&(FlagSandboxSetuid|FlagSandboxNamespace) != 0 {
- if err := os.Chmod(dir, 0777); err != nil {
- return nil, fmt.Errorf("failed to chmod temp dir: %v", err)
- }
- }
-
- // Output capture pipe.
- rp, wp, err := os.Pipe()
- if err != nil {
- return nil, fmt.Errorf("failed to create pipe: %v", err)
- }
- defer wp.Close()
-
- // Input command pipe.
- inrp, inwp, err := os.Pipe()
- if err != nil {
- return nil, fmt.Errorf("failed to create pipe: %v", err)
- }
- defer inwp.Close()
- c.inrp = inrp
-
- // Output command pipe.
- outrp, outwp, err := os.Pipe()
- if err != nil {
- return nil, fmt.Errorf("failed to create pipe: %v", err)
- }
- defer outrp.Close()
- c.outwp = outwp
-
- c.readDone = make(chan []byte, 1)
- c.exited = make(chan struct{})
-
- cmd := exec.Command(bin[0], bin[1:]...)
- cmd.ExtraFiles = []*os.File{inFile, outFile, outrp, inwp}
- cmd.Env = []string{}
- cmd.Dir = dir
- if config.Flags&FlagDebug == 0 {
- cmd.Stdout = wp
- cmd.Stderr = wp
- go func(c *command) {
- // Read out output in case executor constantly prints something.
- bufSize := c.config.BufferSize
- if bufSize == 0 {
- bufSize = 128 << 10
- }
- output := make([]byte, bufSize)
- var size uint64
- for {
- n, err := rp.Read(output[size:])
- if n > 0 {
- size += uint64(n)
- if size >= bufSize*3/4 {
- copy(output, output[size-bufSize/2:size])
- size = bufSize / 2
- }
- }
- if err != nil {
- rp.Close()
- c.readDone <- output[:size]
- close(c.readDone)
- return
- }
- }
- }(c)
- } else {
- close(c.readDone)
- cmd.Stdout = os.Stdout
- cmd.Stderr = os.Stdout
- }
- if err := cmd.Start(); err != nil {
- return nil, fmt.Errorf("failed to start executor binary: %v", err)
- }
- c.cmd = cmd
- wp.Close()
- inwp.Close()
- if err := c.waitServing(); err != nil {
- return nil, err
- }
-
- tmp := c
- c = nil // disable defer above
- return tmp, nil
-}
-
-func (c *command) close() {
- if c.cmd != nil {
- c.abort()
- c.wait()
- }
- osutil.UmountAll(c.dir)
- os.RemoveAll(c.dir)
- if c.inrp != nil {
- c.inrp.Close()
- }
- if c.outwp != nil {
- c.outwp.Close()
- }
-}
-
-// Wait for executor to start serving (sandbox setup can take significant time).
-func (c *command) waitServing() error {
- read := make(chan error, 1)
- go func() {
- var buf [1]byte
- _, err := c.inrp.Read(buf[:])
- read <- err
- }()
- timeout := time.NewTimer(time.Minute)
- select {
- case err := <-read:
- timeout.Stop()
- if err != nil {
- c.abort()
- output := <-c.readDone
- err = fmt.Errorf("executor is not serving: %v\n%s", err, output)
- c.wait()
- if c.cmd.ProcessState != nil {
- sys := c.cmd.ProcessState.Sys()
- if ws, ok := sys.(syscall.WaitStatus); ok {
- // Magic values returned by executor.
- if ws.ExitStatus() == statusFail {
- err = ExecutorFailure(fmt.Sprintf("executor is not serving:\n%s", output))
- }
- }
- }
- }
- return err
- case <-timeout.C:
- return fmt.Errorf("executor is not serving")
- }
-}
-
-// abort sends the abort signal to the command and then SIGKILL if wait doesn't
-// return within 5s.
-func (c *command) abort() {
- sig := c.config.AbortSignal
- if sig <= 0 || sig >= 32 {
- sig = syscall.SIGKILL
- }
- syscall.Kill(c.cmd.Process.Pid, sig)
- if sig != syscall.SIGKILL {
- go func() {
- t := time.NewTimer(5 * time.Second)
- select {
- case <-t.C:
- syscall.Kill(c.cmd.Process.Pid, syscall.SIGKILL)
- case <-c.exited:
- t.Stop()
- }
- }()
- }
-}
-
-func (c *command) wait() error {
- err := c.cmd.Wait()
- select {
- case <-c.exited:
- // c.exited closed by an earlier call to wait.
- default:
- close(c.exited)
- }
- return err
-}
-
-func (c *command) exec(opts *ExecOpts) (output []byte, failed, hanged, restart bool, err0 error) {
- if opts.Flags&FlagInjectFault != 0 {
- enableFaultOnce.Do(enableFaultInjection)
- }
- var inCmd [24]byte
- serializeUint64(inCmd[0:], opts.Flags)
- serializeUint64(inCmd[8:], uint64(opts.FaultCall))
- serializeUint64(inCmd[16:], uint64(opts.FaultNth))
- if _, err := c.outwp.Write(inCmd[:]); err != nil {
- output = <-c.readDone
- err0 = fmt.Errorf("failed to write control pipe: %v", err)
- return
- }
- done := make(chan bool)
- hang := make(chan bool)
- go func() {
- t := time.NewTimer(c.config.Timeout)
- select {
- case <-t.C:
- c.abort()
- hang <- true
- case <-done:
- t.Stop()
- hang <- false
- }
- }()
- var reply [1]byte
- readN, readErr := c.inrp.Read(reply[:])
- close(done)
- status := 0
- if readErr == nil {
- if readN != len(reply) {
- panic(fmt.Sprintf("executor %v: read only %v bytes", c.pid, readN))
- }
- status = int(reply[0])
- if status == 0 {
- // Program was OK.
- <-hang
- return
- }
- // Executor writes magic values into the pipe before exiting,
- // so proceed with killing and joining it.
- }
- c.abort()
- output = <-c.readDone
- if err := c.wait(); <-hang {
- hanged = true
- // In all likelihood, this will be duplicated by the default
- // case below, but that's fine.
- output = append(output, []byte(err.Error())...)
- output = append(output, '\n')
- }
- // Handle magic values returned by executor.
- switch status {
- case statusFail:
- err0 = ExecutorFailure(fmt.Sprintf("executor failed: %s", output))
- case statusError:
- err0 = fmt.Errorf("executor detected kernel bug")
- failed = true
- case statusRetry:
- // This is a temporal error (ENOMEM) or an unfortunate
- // program that messes with testing setup (e.g. kills executor
- // loop process). Pretend that nothing happened.
- // It's better than a false crash report.
- err0 = nil
- hanged = false
- restart = true
- default:
- // Failed to get a valid (or perhaps any) status from the
- // executor.
- //
- // Once the executor is serving the status is always written to
- // the pipe, so we don't bother to check the specific exit
- // codes from wait.
- err0 = fmt.Errorf("invalid (or no) executor status received: %d, executor exit: %s", status, c.cmd.ProcessState)
- }
- return
-}
-
-func serializeUint64(buf []byte, v uint64) {
- for i := 0; i < 8; i++ {
- buf[i] = byte(v >> (8 * uint(i)))
- }
-}
-
-var enableFaultOnce sync.Once
-
-func enableFaultInjection() {
- if err := osutil.WriteFile("/sys/kernel/debug/failslab/ignore-gfp-wait", []byte("N")); err != nil {
- panic(fmt.Sprintf("failed to write /sys/kernel/debug/failslab/ignore-gfp-wait: %v", err))
- }
- if err := osutil.WriteFile("/sys/kernel/debug/fail_futex/ignore-private", []byte("N")); err != nil {
- panic(fmt.Sprintf("failed to write /sys/kernel/debug/fail_futex/ignore-private: %v", err))
- }
-}
diff --git a/pkg/ipc/ipc_fuchsia.go b/pkg/ipc/ipc_fuchsia.go
new file mode 100644
index 000000000..a95a47f85
--- /dev/null
+++ b/pkg/ipc/ipc_fuchsia.go
@@ -0,0 +1,30 @@
+// 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.
+
+// +build fuchsia
+
+package ipc
+
+import (
+ "github.com/google/syzkaller/prog"
+)
+
+type Env struct {
+ In []byte
+
+ StatExecs uint64
+ StatRestarts uint64
+}
+
+func MakeEnv(bin string, pid int, config Config) (*Env, error) {
+ env := &Env{}
+ return env, nil
+}
+
+func (env *Env) Close() error {
+ return nil
+}
+
+func (env *Env) Exec(opts *ExecOpts, p *prog.Prog) (output []byte, info []CallInfo, failed, hanged bool, err0 error) {
+ return
+}
diff --git a/pkg/ipc/ipc_linux.go b/pkg/ipc/ipc_linux.go
new file mode 100644
index 000000000..3023ac0a6
--- /dev/null
+++ b/pkg/ipc/ipc_linux.go
@@ -0,0 +1,650 @@
+// 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 ipc
+
+import (
+ "bytes"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "strings"
+ "sync"
+ "sync/atomic"
+ "syscall"
+ "time"
+ "unsafe"
+
+ "github.com/google/syzkaller/pkg/osutil"
+ "github.com/google/syzkaller/prog"
+)
+
+type Env struct {
+ In []byte
+ out []byte
+
+ cmd *command
+ inFile *os.File
+ outFile *os.File
+ bin []string
+ pid int
+ config Config
+
+ StatExecs uint64
+ StatRestarts uint64
+}
+
+const (
+ outputSize = 16 << 20
+
+ statusFail = 67
+ statusError = 68
+ statusRetry = 69
+
+ // Comparison types masks taken from KCOV headers.
+ compSizeMask = 6
+ compSize8 = 6
+ compConstMask = 1
+)
+
+func MakeEnv(bin string, pid int, config Config) (*Env, error) {
+ // IPC timeout must be larger then executor timeout.
+ // Otherwise IPC will kill parent executor but leave child executor alive.
+ if config.Timeout < 7*time.Second {
+ config.Timeout = 7 * time.Second
+ }
+ inf, inmem, err := createMapping(prog.ExecBufferSize)
+ if err != nil {
+ return nil, err
+ }
+ defer func() {
+ if inf != nil {
+ closeMapping(inf, inmem)
+ }
+ }()
+ outf, outmem, err := createMapping(outputSize)
+ if err != nil {
+ return nil, err
+ }
+ defer func() {
+ if outf != nil {
+ closeMapping(outf, outmem)
+ }
+ }()
+ serializeUint64(inmem[0:], config.Flags)
+ serializeUint64(inmem[8:], uint64(pid))
+ inmem = inmem[16:]
+ env := &Env{
+ In: inmem,
+ out: outmem,
+ inFile: inf,
+ outFile: outf,
+ bin: strings.Split(bin, " "),
+ pid: pid,
+ config: config,
+ }
+ if len(env.bin) == 0 {
+ return nil, fmt.Errorf("binary is empty string")
+ }
+ env.bin[0], err = filepath.Abs(env.bin[0]) // we are going to chdir
+ if err != nil {
+ return nil, fmt.Errorf("filepath.Abs failed: %v", err)
+ }
+ // Append pid to binary name.
+ // E.g. if binary is 'syz-executor' and pid=15,
+ // we create a link from 'syz-executor15' to 'syz-executor' and use 'syz-executor15' as binary.
+ // This allows to easily identify program that lead to a crash in the log.
+ // Log contains pid in "executing program 15" and crashes usually contain "Comm: syz-executor15".
+ base := filepath.Base(env.bin[0])
+ pidStr := fmt.Sprint(pid)
+ if len(base)+len(pidStr) >= 16 {
+ // TASK_COMM_LEN is currently set to 16
+ base = base[:15-len(pidStr)]
+ }
+ binCopy := filepath.Join(filepath.Dir(env.bin[0]), base+pidStr)
+ if err := os.Link(env.bin[0], binCopy); err == nil {
+ env.bin[0] = binCopy
+ }
+ inf = nil
+ outf = nil
+ return env, nil
+}
+
+func (env *Env) Close() error {
+ if env.cmd != nil {
+ env.cmd.close()
+ }
+ err1 := closeMapping(env.inFile, env.In)
+ err2 := closeMapping(env.outFile, env.out)
+ switch {
+ case err1 != nil:
+ return err1
+ case err2 != nil:
+ return err2
+ default:
+ return nil
+ }
+}
+
+// Exec starts executor binary to execute program p and returns information about the execution:
+// output: process output
+// info: per-call info
+// 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(opts *ExecOpts, p *prog.Prog) (output []byte, info []CallInfo, failed, hanged bool, err0 error) {
+ if p != nil {
+ // Copy-in serialized program.
+ if err := p.SerializeForExec(env.In, env.pid); err != nil {
+ err0 = fmt.Errorf("executor %v: failed to serialize: %v", env.pid, err)
+ return
+ }
+ }
+ if env.config.Flags&FlagSignal != 0 {
+ // Zero out the first two words (ncmd and nsig), 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.config, env.inFile, env.outFile)
+ if err0 != nil {
+ return
+ }
+ }
+ var restart bool
+ output, failed, hanged, restart, err0 = env.cmd.exec(opts)
+ if err0 != nil || restart {
+ env.cmd.close()
+ env.cmd = nil
+ return
+ }
+
+ if p == nil || env.config.Flags&FlagSignal == 0 &&
+ env.config.Flags&FlagCollectComps == 0 {
+ return
+ }
+ info, err0 = env.readOutCoverage(p)
+ return
+}
+
+func (env *Env) readOutCoverage(p *prog.Prog) (info []CallInfo, err0 error) {
+ out := ((*[1 << 28]uint32)(unsafe.Pointer(&env.out[0])))[:len(env.out)/int(unsafe.Sizeof(uint32(0)))]
+ readOut := func(v *uint32) bool {
+ if len(out) == 0 {
+ return false
+ }
+ *v = out[0]
+ out = out[1:]
+ return true
+ }
+
+ readOutAndSetErr := func(v *uint32, msg string, args ...interface{}) bool {
+ if !readOut(v) {
+ err0 = fmt.Errorf(msg, args)
+ return false
+ }
+ return true
+ }
+
+ // Reads out a 64 bits int in Little-endian as two blocks of 32 bits.
+ readOut64 := func(v *uint64, msg string, args ...interface{}) bool {
+ var a, b uint32
+ if !(readOutAndSetErr(&a, msg, args) && readOutAndSetErr(&b, msg, args)) {
+ return false
+ }
+ *v = uint64(a) + uint64(b)<<32
+ return true
+ }
+
+ var ncmd uint32
+ if !readOutAndSetErr(&ncmd,
+ "executor %v: failed to read output coverage", env.pid) {
+ return
+ }
+ info = make([]CallInfo, len(p.Calls))
+ for i := range info {
+ info[i].Errno = -1 // not executed
+ }
+ dumpCov := func() string {
+ buf := new(bytes.Buffer)
+ for i, inf := range info {
+ str := "nil"
+ if inf.Signal != nil {
+ str = fmt.Sprint(len(inf.Signal))
+ }
+ fmt.Fprintf(buf, "%v:%v|", i, str)
+ }
+ return buf.String()
+ }
+ for i := uint32(0); i < ncmd; i++ {
+ var callIndex, callNum, errno, faultInjected, signalSize, coverSize, compsSize uint32
+ if !readOut(&callIndex) || !readOut(&callNum) || !readOut(&errno) || !readOut(&faultInjected) || !readOut(&signalSize) || !readOut(&coverSize) || !readOut(&compsSize) {
+ err0 = fmt.Errorf("executor %v: failed to read output coverage", env.pid)
+ return
+ }
+ if int(callIndex) >= len(info) {
+ err0 = fmt.Errorf("executor %v: failed to read output coverage: record %v, call %v, total calls %v (cov: %v)",
+ env.pid, i, callIndex, len(info), dumpCov())
+ return
+ }
+ c := p.Calls[callIndex]
+ if num := c.Meta.ID; uint32(num) != callNum {
+ err0 = fmt.Errorf("executor %v: failed to read output coverage: record %v call %v: expect syscall %v, got %v, executed %v (cov: %v)",
+ env.pid, i, callIndex, num, callNum, ncmd, dumpCov())
+ return
+ }
+ if info[callIndex].Signal != nil {
+ err0 = fmt.Errorf("executor %v: failed to read output coverage: double coverage for call %v (cov: %v)",
+ env.pid, callIndex, dumpCov())
+ return
+ }
+ info[callIndex].Errno = int(errno)
+ info[callIndex].FaultInjected = faultInjected != 0
+ if signalSize > uint32(len(out)) {
+ err0 = fmt.Errorf("executor %v: failed to read output signal: record %v, call %v, signalsize=%v coversize=%v",
+ env.pid, i, callIndex, signalSize, coverSize)
+ return
+ }
+ // Read out signals.
+ info[callIndex].Signal = out[:signalSize:signalSize]
+ out = out[signalSize:]
+ // Read out coverage.
+ if coverSize > uint32(len(out)) {
+ err0 = fmt.Errorf("executor %v: failed to read output coverage: record %v, call %v, signalsize=%v coversize=%v",
+ env.pid, i, callIndex, signalSize, coverSize)
+ return
+ }
+ info[callIndex].Cover = out[:coverSize:coverSize]
+ out = out[coverSize:]
+ // Read out comparisons.
+ compMap := make(prog.CompMap)
+ for j := uint32(0); j < compsSize; j++ {
+ var typ uint32
+ var op1, op2 uint64
+ if !readOutAndSetErr(&typ,
+ "executor %v: failed while reading type of comparison %v", env.pid, j) {
+ return
+ }
+ if typ > compConstMask|compSizeMask {
+ err0 = fmt.Errorf("executor %v: got wrong value (%v) while reading type of comparison %v",
+ env.pid, typ, j)
+ return
+ }
+
+ isSize8 := (typ & compSizeMask) == compSize8
+ isConst := (typ & compConstMask) != 0
+ arg1ErrString := "executor %v: failed while reading op1 of comparison %v"
+ arg2ErrString := "executor %v: failed while reading op2 of comparison %v"
+ if isSize8 {
+ var tmp1, tmp2 uint32
+ if !readOutAndSetErr(&tmp1, arg1ErrString, env.pid, j) ||
+ !readOutAndSetErr(&tmp2, arg2ErrString, env.pid, j) {
+ return
+ }
+ op1 = uint64(tmp1)
+ op2 = uint64(tmp2)
+ } else {
+ if !readOut64(&op1, arg1ErrString, env.pid, j) ||
+ !readOut64(&op2, arg2ErrString, env.pid, j) {
+ return
+ }
+ }
+ if op1 == op2 {
+ // It's useless to store such comparisons.
+ continue
+ }
+ compMap.AddComp(op2, op1)
+ if isConst {
+ // If one of the operands was const, then this operand is always
+ // placed first in the instrumented callbacks. Such an operand
+ // could not be an argument of our syscalls (because otherwise
+ // it wouldn't be const), thus we simply ignore it.
+ continue
+ }
+ compMap.AddComp(op1, op2)
+ }
+ info[callIndex].Comps = compMap
+ }
+ return
+}
+
+func createMapping(size int) (f *os.File, mem []byte, err error) {
+ f, err = ioutil.TempFile("./", "syzkaller-shm")
+ if err != nil {
+ err = fmt.Errorf("failed to create temp file: %v", err)
+ return
+ }
+ if err = f.Truncate(int64(size)); err != nil {
+ err = fmt.Errorf("failed to truncate shm file: %v", err)
+ f.Close()
+ os.Remove(f.Name())
+ return
+ }
+ f.Close()
+ fname := f.Name()
+ f, err = os.OpenFile(f.Name(), os.O_RDWR, osutil.DefaultFilePerm)
+ if err != nil {
+ err = fmt.Errorf("failed to open shm file: %v", err)
+ os.Remove(fname)
+ return
+ }
+ mem, err = syscall.Mmap(int(f.Fd()), 0, size, syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED)
+ if err != nil {
+ err = fmt.Errorf("failed to mmap shm file: %v", err)
+ f.Close()
+ os.Remove(f.Name())
+ return
+ }
+ return
+}
+
+func closeMapping(f *os.File, mem []byte) error {
+ err1 := syscall.Munmap(mem)
+ err2 := f.Close()
+ err3 := os.Remove(f.Name())
+ switch {
+ case err1 != nil:
+ return err1
+ case err2 != nil:
+ return err2
+ case err3 != nil:
+ return err3
+ default:
+ return nil
+ }
+}
+
+type command struct {
+ pid int
+ config Config
+ cmd *exec.Cmd
+ dir string
+ readDone chan []byte
+ exited chan struct{}
+ inrp *os.File
+ outwp *os.File
+}
+
+func makeCommand(pid int, bin []string, config Config, inFile *os.File, outFile *os.File) (*command, error) {
+ dir, err := ioutil.TempDir("./", "syzkaller-testdir")
+ if err != nil {
+ return nil, fmt.Errorf("failed to create temp dir: %v", err)
+ }
+
+ c := &command{
+ pid: pid,
+ config: config,
+ dir: dir,
+ }
+ defer func() {
+ if c != nil {
+ c.close()
+ }
+ }()
+
+ if config.Flags&(FlagSandboxSetuid|FlagSandboxNamespace) != 0 {
+ if err := os.Chmod(dir, 0777); err != nil {
+ return nil, fmt.Errorf("failed to chmod temp dir: %v", err)
+ }
+ }
+
+ // Output capture pipe.
+ rp, wp, err := os.Pipe()
+ if err != nil {
+ return nil, fmt.Errorf("failed to create pipe: %v", err)
+ }
+ defer wp.Close()
+
+ // Input command pipe.
+ inrp, inwp, err := os.Pipe()
+ if err != nil {
+ return nil, fmt.Errorf("failed to create pipe: %v", err)
+ }
+ defer inwp.Close()
+ c.inrp = inrp
+
+ // Output command pipe.
+ outrp, outwp, err := os.Pipe()
+ if err != nil {
+ return nil, fmt.Errorf("failed to create pipe: %v", err)
+ }
+ defer outrp.Close()
+ c.outwp = outwp
+
+ c.readDone = make(chan []byte, 1)
+ c.exited = make(chan struct{})
+
+ cmd := exec.Command(bin[0], bin[1:]...)
+ cmd.ExtraFiles = []*os.File{inFile, outFile, outrp, inwp}
+ cmd.Env = []string{}
+ cmd.Dir = dir
+ if config.Flags&FlagDebug == 0 {
+ cmd.Stdout = wp
+ cmd.Stderr = wp
+ go func(c *command) {
+ // Read out output in case executor constantly prints something.
+ bufSize := c.config.BufferSize
+ if bufSize == 0 {
+ bufSize = 128 << 10
+ }
+ output := make([]byte, bufSize)
+ var size uint64
+ for {
+ n, err := rp.Read(output[size:])
+ if n > 0 {
+ size += uint64(n)
+ if size >= bufSize*3/4 {
+ copy(output, output[size-bufSize/2:size])
+ size = bufSize / 2
+ }
+ }
+ if err != nil {
+ rp.Close()
+ c.readDone <- output[:size]
+ close(c.readDone)
+ return
+ }
+ }
+ }(c)
+ } else {
+ close(c.readDone)
+ cmd.Stdout = os.Stdout
+ cmd.Stderr = os.Stdout
+ }
+ if err := cmd.Start(); err != nil {
+ return nil, fmt.Errorf("failed to start executor binary: %v", err)
+ }
+ c.cmd = cmd
+ wp.Close()
+ inwp.Close()
+ if err := c.waitServing(); err != nil {
+ return nil, err
+ }
+
+ tmp := c
+ c = nil // disable defer above
+ return tmp, nil
+}
+
+func (c *command) close() {
+ if c.cmd != nil {
+ c.abort()
+ c.wait()
+ }
+ osutil.UmountAll(c.dir)
+ os.RemoveAll(c.dir)
+ if c.inrp != nil {
+ c.inrp.Close()
+ }
+ if c.outwp != nil {
+ c.outwp.Close()
+ }
+}
+
+// Wait for executor to start serving (sandbox setup can take significant time).
+func (c *command) waitServing() error {
+ read := make(chan error, 1)
+ go func() {
+ var buf [1]byte
+ _, err := c.inrp.Read(buf[:])
+ read <- err
+ }()
+ timeout := time.NewTimer(time.Minute)
+ select {
+ case err := <-read:
+ timeout.Stop()
+ if err != nil {
+ c.abort()
+ output := <-c.readDone
+ err = fmt.Errorf("executor is not serving: %v\n%s", err, output)
+ c.wait()
+ if c.cmd.ProcessState != nil {
+ sys := c.cmd.ProcessState.Sys()
+ if ws, ok := sys.(syscall.WaitStatus); ok {
+ // Magic values returned by executor.
+ if ws.ExitStatus() == statusFail {
+ err = ExecutorFailure(fmt.Sprintf("executor is not serving:\n%s", output))
+ }
+ }
+ }
+ }
+ return err
+ case <-timeout.C:
+ return fmt.Errorf("executor is not serving")
+ }
+}
+
+// abort sends the abort signal to the command and then SIGKILL if wait doesn't
+// return within 5s.
+func (c *command) abort() {
+ sig := syscall.Signal(c.config.AbortSignal)
+ if sig <= 0 || sig >= 32 {
+ sig = syscall.SIGKILL
+ }
+ syscall.Kill(c.cmd.Process.Pid, sig)
+ if sig != syscall.SIGKILL {
+ go func() {
+ t := time.NewTimer(5 * time.Second)
+ select {
+ case <-t.C:
+ syscall.Kill(c.cmd.Process.Pid, syscall.SIGKILL)
+ case <-c.exited:
+ t.Stop()
+ }
+ }()
+ }
+}
+
+func (c *command) wait() error {
+ err := c.cmd.Wait()
+ select {
+ case <-c.exited:
+ // c.exited closed by an earlier call to wait.
+ default:
+ close(c.exited)
+ }
+ return err
+}
+
+func (c *command) exec(opts *ExecOpts) (output []byte, failed, hanged, restart bool, err0 error) {
+ if opts.Flags&FlagInjectFault != 0 {
+ enableFaultOnce.Do(enableFaultInjection)
+ }
+ var inCmd [24]byte
+ serializeUint64(inCmd[0:], opts.Flags)
+ serializeUint64(inCmd[8:], uint64(opts.FaultCall))
+ serializeUint64(inCmd[16:], uint64(opts.FaultNth))
+ if _, err := c.outwp.Write(inCmd[:]); err != nil {
+ output = <-c.readDone
+ err0 = fmt.Errorf("failed to write control pipe: %v", err)
+ return
+ }
+ done := make(chan bool)
+ hang := make(chan bool)
+ go func() {
+ t := time.NewTimer(c.config.Timeout)
+ select {
+ case <-t.C:
+ c.abort()
+ hang <- true
+ case <-done:
+ t.Stop()
+ hang <- false
+ }
+ }()
+ var reply [1]byte
+ readN, readErr := c.inrp.Read(reply[:])
+ close(done)
+ status := 0
+ if readErr == nil {
+ if readN != len(reply) {
+ panic(fmt.Sprintf("executor %v: read only %v bytes", c.pid, readN))
+ }
+ status = int(reply[0])
+ if status == 0 {
+ // Program was OK.
+ <-hang
+ return
+ }
+ // Executor writes magic values into the pipe before exiting,
+ // so proceed with killing and joining it.
+ }
+ c.abort()
+ output = <-c.readDone
+ if err := c.wait(); <-hang {
+ hanged = true
+ // In all likelihood, this will be duplicated by the default
+ // case below, but that's fine.
+ output = append(output, []byte(err.Error())...)
+ output = append(output, '\n')
+ }
+ // Handle magic values returned by executor.
+ switch status {
+ case statusFail:
+ err0 = ExecutorFailure(fmt.Sprintf("executor failed: %s", output))
+ case statusError:
+ err0 = fmt.Errorf("executor detected kernel bug")
+ failed = true
+ case statusRetry:
+ // This is a temporal error (ENOMEM) or an unfortunate
+ // program that messes with testing setup (e.g. kills executor
+ // loop process). Pretend that nothing happened.
+ // It's better than a false crash report.
+ err0 = nil
+ hanged = false
+ restart = true
+ default:
+ // Failed to get a valid (or perhaps any) status from the
+ // executor.
+ //
+ // Once the executor is serving the status is always written to
+ // the pipe, so we don't bother to check the specific exit
+ // codes from wait.
+ err0 = fmt.Errorf("invalid (or no) executor status received: %d, executor exit: %s", status, c.cmd.ProcessState)
+ }
+ return
+}
+
+func serializeUint64(buf []byte, v uint64) {
+ for i := 0; i < 8; i++ {
+ buf[i] = byte(v >> (8 * uint(i)))
+ }
+}
+
+var enableFaultOnce sync.Once
+
+func enableFaultInjection() {
+ if err := osutil.WriteFile("/sys/kernel/debug/failslab/ignore-gfp-wait", []byte("N")); err != nil {
+ panic(fmt.Sprintf("failed to write /sys/kernel/debug/failslab/ignore-gfp-wait: %v", err))
+ }
+ if err := osutil.WriteFile("/sys/kernel/debug/fail_futex/ignore-private", []byte("N")); err != nil {
+ panic(fmt.Sprintf("failed to write /sys/kernel/debug/fail_futex/ignore-private: %v", err))
+ }
+}
diff --git a/pkg/serializer/serializer.go b/pkg/serializer/serializer.go
index bbe13fc17..af2708fb3 100644
--- a/pkg/serializer/serializer.go
+++ b/pkg/serializer/serializer.go
@@ -15,9 +15,15 @@ import (
// does not write package names before types, omits struct fields with default values,
// omits type names where possible, etc. On the other hand, it currently does not
// support all types (e.g. channels and maps).
-func Write(w io.Writer, v interface{}) {
- ww := writer{w}
- ww.do(reflect.ValueOf(v), false)
+func Write(ww io.Writer, i interface{}) {
+ w := writer{ww}
+ v := reflect.ValueOf(i)
+ if v.Kind() == reflect.Slice && (v.IsNil() || v.Len() == 0) {
+ w.typ(v.Type())
+ w.string("(nil)")
+ return
+ }
+ w.do(v, false)
}
type writer struct {
diff --git a/prog/target.go b/prog/target.go
index 1a099bf8e..45a3e9c3d 100644
--- a/prog/target.go
+++ b/prog/target.go
@@ -19,6 +19,8 @@ type Target struct {
Syscalls []*Syscall
Resources []*ResourceDesc
+ Structs []*KeyedStruct
+ Consts []ConstValue
// Syscall used by MakeMmap.
// It has some special meaning because there are usually too many of them.
@@ -50,6 +52,7 @@ type Target struct {
// Filled by prog package:
SyscallMap map[string]*Syscall
+ ConstMap map[string]uint64
resourceMap map[string]*ResourceDesc
// Maps resource name to a list of calls that can create the resource.
resourceCtors map[string][]*Syscall
@@ -57,12 +60,15 @@ type Target struct {
var targets = make(map[string]*Target)
-func RegisterTarget(target *Target) {
+func RegisterTarget(target *Target, initArch func(target *Target)) {
key := target.OS + "/" + target.Arch
if targets[key] != nil {
panic(fmt.Sprintf("duplicate target %v", key))
}
+ target.SanitizeCall = func(c *Call) {}
initTarget(target)
+ initArch(target)
+ target.ConstMap = nil // currently used only by initArch
targets[key] = target
}
@@ -95,15 +101,49 @@ func AllTargets() []*Target {
}
func initTarget(target *Target) {
+ target.ConstMap = make(map[string]uint64)
+ for _, c := range target.Consts {
+ target.ConstMap[c.Name] = c.Value
+ }
+
+ target.resourceMap = make(map[string]*ResourceDesc)
+ for _, res := range target.Resources {
+ target.resourceMap[res.Name] = res
+ }
+
+ keyedStructs := make(map[StructKey]*StructDesc)
+ for _, desc := range target.Structs {
+ keyedStructs[desc.Key] = desc.Desc
+ }
+ target.Structs = nil
+
target.SyscallMap = make(map[string]*Syscall)
for _, c := range target.Syscalls {
target.SyscallMap[c.Name] = c
+ ForeachType(c, func(t0 Type) {
+ switch t := t0.(type) {
+ case *ResourceType:
+ t.Desc = target.resourceMap[t.TypeName]
+ if t.Desc == nil {
+ panic("no resource desc")
+ }
+ case *StructType:
+ t.StructDesc = keyedStructs[t.Key]
+ if t.StructDesc == nil {
+ panic("no struct desc")
+ }
+ case *UnionType:
+ t.StructDesc = keyedStructs[t.Key]
+ if t.StructDesc == nil {
+ panic("no union desc")
+ }
+ }
+ })
}
- target.resourceMap = make(map[string]*ResourceDesc)
+
target.resourceCtors = make(map[string][]*Syscall)
- for _, r := range target.Resources {
- target.resourceMap[r.Name] = r
- target.resourceCtors[r.Name] = target.calcResourceCtors(r.Kind, false)
+ for _, res := range target.Resources {
+ target.resourceCtors[res.Name] = target.calcResourceCtors(res.Kind, false)
}
}
diff --git a/sys/fuchsia/amd64.go b/sys/fuchsia/amd64.go
new file mode 100644
index 000000000..eb51d797d
--- /dev/null
+++ b/sys/fuchsia/amd64.go
@@ -0,0 +1,27 @@
+// AUTOGENERATED FILE
+package fuchsia
+
+import . "github.com/google/syzkaller/prog"
+
+func init() {
+ RegisterTarget(&Target{OS: "fuchsia", Arch: "amd64", Revision: revision_amd64, PtrSize: 8, Syscalls: syscalls_amd64, Resources: resources_amd64, Structs: structDescs_amd64, Consts: consts_amd64}, initTarget)
+}
+
+var resources_amd64 = []*ResourceDesc(nil)
+
+var structDescs_amd64 = []*KeyedStruct(nil)
+
+var syscalls_amd64 = []*Syscall{
+ {Name: "mx_time_get", CallName: "mx_time_get", Args: []Type{
+ &FlagsType{IntTypeCommon: IntTypeCommon{TypeCommon: TypeCommon{TypeName: "clock_id", FldName: "clock_id", TypeSize: 8}}, Vals: []uint64{0, 1, 2}},
+ }},
+}
+
+var consts_amd64 = []ConstValue{
+ {Name: "MX_CLOCK_MONOTONIC"},
+ {Name: "MX_CLOCK_THREAD", Value: 2},
+ {Name: "MX_CLOCK_UTC", Value: 1},
+ {Name: "__NR_mx_time_get"},
+}
+
+const revision_amd64 = "d65b9adb4853817be6f471df44fc8347ebf6dfc6"
diff --git a/sys/fuchsia/arm64.go b/sys/fuchsia/arm64.go
new file mode 100644
index 000000000..b39870ffa
--- /dev/null
+++ b/sys/fuchsia/arm64.go
@@ -0,0 +1,27 @@
+// AUTOGENERATED FILE
+package fuchsia
+
+import . "github.com/google/syzkaller/prog"
+
+func init() {
+ RegisterTarget(&Target{OS: "fuchsia", Arch: "arm64", Revision: revision_arm64, PtrSize: 8, Syscalls: syscalls_arm64, Resources: resources_arm64, Structs: structDescs_arm64, Consts: consts_arm64}, initTarget)
+}
+
+var resources_arm64 = []*ResourceDesc(nil)
+
+var structDescs_arm64 = []*KeyedStruct(nil)
+
+var syscalls_arm64 = []*Syscall{
+ {Name: "mx_time_get", CallName: "mx_time_get", Args: []Type{
+ &FlagsType{IntTypeCommon: IntTypeCommon{TypeCommon: TypeCommon{TypeName: "clock_id", FldName: "clock_id", TypeSize: 8}}, Vals: []uint64{0, 1, 2}},
+ }},
+}
+
+var consts_arm64 = []ConstValue{
+ {Name: "MX_CLOCK_MONOTONIC"},
+ {Name: "MX_CLOCK_THREAD", Value: 2},
+ {Name: "MX_CLOCK_UTC", Value: 1},
+ {Name: "__NR_mx_time_get"},
+}
+
+const revision_arm64 = "bd2655e6d85f1fecceb1648649e1ad5adda81dc8"
diff --git a/sys/fuchsia/init.go b/sys/fuchsia/init.go
new file mode 100644
index 000000000..0dfe874cc
--- /dev/null
+++ b/sys/fuchsia/init.go
@@ -0,0 +1,35 @@
+// 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.
+
+package fuchsia
+
+import (
+ "github.com/google/syzkaller/prog"
+)
+
+func initTarget(target *prog.Target) {
+ arch := &arch{}
+
+ target.PageSize = pageSize
+ target.DataOffset = dataOffset
+ target.MmapSyscall = arch.mmapSyscall
+ target.MakeMmap = arch.makeMmap
+ target.AnalyzeMmap = arch.analyzeMmap
+}
+
+const (
+ pageSize = 4 << 10
+ dataOffset = 512 << 20
+)
+
+type arch struct {
+}
+
+// createMmapCall creates a "normal" mmap call that maps [start, start+npages) page range.
+func (arch *arch) makeMmap(start, npages uint64) *prog.Call {
+ return nil
+}
+
+func (arch *arch) analyzeMmap(c *prog.Call) (start, npages uint64, mapped bool) {
+ return
+}
diff --git a/sys/fuchsia/sys.txt b/sys/fuchsia/sys.txt
new file mode 100644
index 000000000..543d5da01
--- /dev/null
+++ b/sys/fuchsia/sys.txt
@@ -0,0 +1,6 @@
+# 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.
+
+mx_time_get(clock_id flags[clock_id])
+
+clock_id = MX_CLOCK_MONOTONIC, MX_CLOCK_UTC, MX_CLOCK_THREAD
diff --git a/sys/fuchsia/sys_amd64.const b/sys/fuchsia/sys_amd64.const
new file mode 100644
index 000000000..c6aa53ea0
--- /dev/null
+++ b/sys/fuchsia/sys_amd64.const
@@ -0,0 +1,5 @@
+__NR_mx_time_get = 0
+
+MX_CLOCK_MONOTONIC = 0
+MX_CLOCK_UTC = 1
+MX_CLOCK_THREAD = 2
diff --git a/sys/fuchsia/sys_arm64.const b/sys/fuchsia/sys_arm64.const
new file mode 100644
index 000000000..c6aa53ea0
--- /dev/null
+++ b/sys/fuchsia/sys_arm64.const
@@ -0,0 +1,5 @@
+__NR_mx_time_get = 0
+
+MX_CLOCK_MONOTONIC = 0
+MX_CLOCK_UTC = 1
+MX_CLOCK_THREAD = 2
diff --git a/sys/linux/386.go b/sys/linux/386.go
index bb166ec6e..431f21d24 100644
--- a/sys/linux/386.go
+++ b/sys/linux/386.go
@@ -4,7 +4,7 @@ package linux
import . "github.com/google/syzkaller/prog"
func init() {
- initArch(revision_386, syscalls_386, resources_386, structDescs_386, consts_386, "386", 4)
+ RegisterTarget(&Target{OS: "linux", Arch: "386", Revision: revision_386, PtrSize: 4, Syscalls: syscalls_386, Resources: resources_386, Structs: structDescs_386, Consts: consts_386}, initTarget)
}
var resources_386 = []*ResourceDesc{
@@ -16734,4 +16734,4 @@ var consts_386 = []ConstValue{
{Name: "__WNOTHREAD", Value: 536870912},
}
-const revision_386 = "86d577076d75af98e3c800a3c65bf8e4869e3ea4"
+const revision_386 = "7b2f1949d48094cf3369932d0743db166049457b"
diff --git a/sys/linux/amd64.go b/sys/linux/amd64.go
index f3f058d81..1a43b6760 100644
--- a/sys/linux/amd64.go
+++ b/sys/linux/amd64.go
@@ -4,7 +4,7 @@ package linux
import . "github.com/google/syzkaller/prog"
func init() {
- initArch(revision_amd64, syscalls_amd64, resources_amd64, structDescs_amd64, consts_amd64, "amd64", 8)
+ RegisterTarget(&Target{OS: "linux", Arch: "amd64", Revision: revision_amd64, PtrSize: 8, Syscalls: syscalls_amd64, Resources: resources_amd64, Structs: structDescs_amd64, Consts: consts_amd64}, initTarget)
}
var resources_amd64 = []*ResourceDesc{
@@ -17264,4 +17264,4 @@ var consts_amd64 = []ConstValue{
{Name: "__WNOTHREAD", Value: 536870912},
}
-const revision_amd64 = "e482e82186ee2d78bed701630c062b7c5d5b23c6"
+const revision_amd64 = "8349df62e623f9c8d8bfaefcc8ba3febf463ce92"
diff --git a/sys/linux/arm.go b/sys/linux/arm.go
index 0a1509600..5c4fddab6 100644
--- a/sys/linux/arm.go
+++ b/sys/linux/arm.go
@@ -4,7 +4,7 @@ package linux
import . "github.com/google/syzkaller/prog"
func init() {
- initArch(revision_arm, syscalls_arm, resources_arm, structDescs_arm, consts_arm, "arm", 4)
+ RegisterTarget(&Target{OS: "linux", Arch: "arm", Revision: revision_arm, PtrSize: 4, Syscalls: syscalls_arm, Resources: resources_arm, Structs: structDescs_arm, Consts: consts_arm}, initTarget)
}
var resources_arm = []*ResourceDesc{
@@ -16631,4 +16631,4 @@ var consts_arm = []ConstValue{
{Name: "__WNOTHREAD", Value: 536870912},
}
-const revision_arm = "41372f11d60aad90c991926434f714d736320cb1"
+const revision_arm = "02567c0623e18214f0be8d059aad68c263064645"
diff --git a/sys/linux/arm64.go b/sys/linux/arm64.go
index 77d20bd0c..39061d67a 100644
--- a/sys/linux/arm64.go
+++ b/sys/linux/arm64.go
@@ -4,7 +4,7 @@ package linux
import . "github.com/google/syzkaller/prog"
func init() {
- initArch(revision_arm64, syscalls_arm64, resources_arm64, structDescs_arm64, consts_arm64, "arm64", 8)
+ RegisterTarget(&Target{OS: "linux", Arch: "arm64", Revision: revision_arm64, PtrSize: 8, Syscalls: syscalls_arm64, Resources: resources_arm64, Structs: structDescs_arm64, Consts: consts_arm64}, initTarget)
}
var resources_arm64 = []*ResourceDesc{
@@ -16641,4 +16641,4 @@ var consts_arm64 = []ConstValue{
{Name: "__WNOTHREAD", Value: 536870912},
}
-const revision_arm64 = "3424aa8d1e2be36f2a093d39f49a45b6904827e0"
+const revision_arm64 = "0709fe60bdd20ea30d937d14615a40269fadb2b8"
diff --git a/sys/linux/init.go b/sys/linux/init.go
index 509e2ab04..c727cb5b2 100644
--- a/sys/linux/init.go
+++ b/sys/linux/init.go
@@ -9,29 +9,48 @@ import (
"github.com/google/syzkaller/prog"
)
-func initArch(rev string, syscalls []*prog.Syscall, resources []*prog.ResourceDesc,
- structDescs []*prog.KeyedStruct, consts []prog.ConstValue, archName string, ptrSize uint64) {
- arch := makeArch(syscalls, resources, structDescs, consts, archName)
- target := &prog.Target{
- OS: "linux",
- Arch: archName,
- Revision: rev,
- PtrSize: ptrSize,
- PageSize: pageSize,
- DataOffset: dataOffset,
- Syscalls: syscalls,
- Resources: resources,
- MmapSyscall: arch.mmapSyscall,
- MakeMmap: arch.makeMmap,
- AnalyzeMmap: arch.analyzeMmap,
- SanitizeCall: arch.sanitizeCall,
- SpecialStructs: map[string]func(g *prog.Gen, typ *prog.StructType, old *prog.GroupArg) (prog.Arg, []*prog.Call){
- "timespec": arch.generateTimespec,
- "timeval": arch.generateTimespec,
- },
- StringDictionary: stringDictionary,
+func initTarget(target *prog.Target) {
+ arch := &arch{
+ mmapSyscall: target.SyscallMap["mmap"],
+ clockGettimeSyscall: target.SyscallMap["clock_gettime"],
+ PROT_READ: target.ConstMap["PROT_READ"],
+ PROT_WRITE: target.ConstMap["PROT_WRITE"],
+ MAP_ANONYMOUS: target.ConstMap["MAP_ANONYMOUS"],
+ MAP_PRIVATE: target.ConstMap["MAP_PRIVATE"],
+ MAP_FIXED: target.ConstMap["MAP_FIXED"],
+ MREMAP_MAYMOVE: target.ConstMap["MREMAP_MAYMOVE"],
+ MREMAP_FIXED: target.ConstMap["MREMAP_FIXED"],
+ S_IFREG: target.ConstMap["S_IFREG"],
+ S_IFCHR: target.ConstMap["S_IFCHR"],
+ S_IFBLK: target.ConstMap["S_IFBLK"],
+ S_IFIFO: target.ConstMap["S_IFIFO"],
+ S_IFSOCK: target.ConstMap["S_IFSOCK"],
+ SYSLOG_ACTION_CONSOLE_OFF: target.ConstMap["SYSLOG_ACTION_CONSOLE_OFF"],
+ SYSLOG_ACTION_CONSOLE_ON: target.ConstMap["SYSLOG_ACTION_CONSOLE_ON"],
+ SYSLOG_ACTION_SIZE_UNREAD: target.ConstMap["SYSLOG_ACTION_SIZE_UNREAD"],
+ FIFREEZE: target.ConstMap["FIFREEZE"],
+ FITHAW: target.ConstMap["FITHAW"],
+ PTRACE_TRACEME: target.ConstMap["PTRACE_TRACEME"],
+ CLOCK_REALTIME: target.ConstMap["CLOCK_REALTIME"],
+ }
+
+ target.PageSize = pageSize
+ target.DataOffset = dataOffset
+ target.MmapSyscall = arch.mmapSyscall
+ target.MakeMmap = arch.makeMmap
+ target.AnalyzeMmap = arch.analyzeMmap
+ target.SanitizeCall = arch.sanitizeCall
+ target.SpecialStructs = map[string]func(g *prog.Gen, typ *prog.StructType, old *prog.GroupArg) (prog.Arg, []*prog.Call){
+ "timespec": arch.generateTimespec,
+ "timeval": arch.generateTimespec,
+ }
+ target.StringDictionary = stringDictionary
+
+ if target.Arch == runtime.GOARCH {
+ KCOV_INIT_TRACE = uintptr(target.ConstMap["KCOV_INIT_TRACE"])
+ KCOV_ENABLE = uintptr(target.ConstMap["KCOV_ENABLE"])
+ KCOV_TRACE_CMP = uintptr(target.ConstMap["KCOV_TRACE_CMP"])
}
- prog.RegisterTarget(target)
}
const (
@@ -263,101 +282,3 @@ func (arch *arch) generateTimespec(g *prog.Gen, typ *prog.StructType, old *prog.
}
return
}
-
-func makeArch(syscalls []*prog.Syscall, resources []*prog.ResourceDesc,
- structDescs []*prog.KeyedStruct, consts []prog.ConstValue, archName string) *arch {
- resourceMap := make(map[string]*prog.ResourceDesc)
- for _, res := range resources {
- resourceMap[res.Name] = res
- }
-
- keyedStructs := make(map[prog.StructKey]*prog.StructDesc)
- for _, desc := range structDescs {
- keyedStructs[desc.Key] = desc.Desc
- }
-
- arch := &arch{}
- for _, c := range syscalls {
- prog.ForeachType(c, func(t0 prog.Type) {
- switch t := t0.(type) {
- case *prog.ResourceType:
- t.Desc = resourceMap[t.TypeName]
- if t.Desc == nil {
- panic("no resource desc")
- }
- case *prog.StructType:
- t.StructDesc = keyedStructs[t.Key]
- if t.StructDesc == nil {
- panic("no struct desc")
- }
- case *prog.UnionType:
- t.StructDesc = keyedStructs[t.Key]
- if t.StructDesc == nil {
- panic("no union desc")
- }
- }
- })
- switch c.Name {
- case "mmap":
- arch.mmapSyscall = c
- case "clock_gettime":
- arch.clockGettimeSyscall = c
- }
- }
-
- for _, c := range consts {
- switch c.Name {
- case "KCOV_INIT_TRACE":
- if archName == runtime.GOARCH {
- KCOV_INIT_TRACE = uintptr(c.Value)
- }
- case "KCOV_ENABLE":
- if archName == runtime.GOARCH {
- KCOV_ENABLE = uintptr(c.Value)
- }
- case "KCOV_TRACE_CMP":
- if archName == runtime.GOARCH {
- KCOV_TRACE_CMP = uintptr(c.Value)
- }
- case "PROT_READ":
- arch.PROT_READ = c.Value
- case "PROT_WRITE":
- arch.PROT_WRITE = c.Value
- case "MAP_ANONYMOUS":
- arch.MAP_ANONYMOUS = c.Value
- case "MAP_PRIVATE":
- arch.MAP_PRIVATE = c.Value
- case "MAP_FIXED":
- arch.MAP_FIXED = c.Value
- case "MREMAP_MAYMOVE":
- arch.MREMAP_MAYMOVE = c.Value
- case "MREMAP_FIXED":
- arch.MREMAP_FIXED = c.Value
- case "S_IFREG":
- arch.S_IFREG = c.Value
- case "S_IFCHR":
- arch.S_IFCHR = c.Value
- case "S_IFBLK":
- arch.S_IFBLK = c.Value
- case "S_IFIFO":
- arch.S_IFIFO = c.Value
- case "S_IFSOCK":
- arch.S_IFSOCK = c.Value
- case "SYSLOG_ACTION_CONSOLE_OFF":
- arch.SYSLOG_ACTION_CONSOLE_OFF = c.Value
- case "SYSLOG_ACTION_CONSOLE_ON":
- arch.SYSLOG_ACTION_CONSOLE_ON = c.Value
- case "SYSLOG_ACTION_SIZE_UNREAD":
- arch.SYSLOG_ACTION_SIZE_UNREAD = c.Value
- case "FIFREEZE":
- arch.FIFREEZE = c.Value
- case "FITHAW":
- arch.FITHAW = c.Value
- case "PTRACE_TRACEME":
- arch.PTRACE_TRACEME = c.Value
- case "CLOCK_REALTIME":
- arch.CLOCK_REALTIME = c.Value
- }
- }
- return arch
-}
diff --git a/sys/linux/ppc64le.go b/sys/linux/ppc64le.go
index 6686885b5..1c02c5a08 100644
--- a/sys/linux/ppc64le.go
+++ b/sys/linux/ppc64le.go
@@ -4,7 +4,7 @@ package linux
import . "github.com/google/syzkaller/prog"
func init() {
- initArch(revision_ppc64le, syscalls_ppc64le, resources_ppc64le, structDescs_ppc64le, consts_ppc64le, "ppc64le", 8)
+ RegisterTarget(&Target{OS: "linux", Arch: "ppc64le", Revision: revision_ppc64le, PtrSize: 8, Syscalls: syscalls_ppc64le, Resources: resources_ppc64le, Structs: structDescs_ppc64le, Consts: consts_ppc64le}, initTarget)
}
var resources_ppc64le = []*ResourceDesc{
@@ -16330,4 +16330,4 @@ var consts_ppc64le = []ConstValue{
{Name: "__WNOTHREAD", Value: 536870912},
}
-const revision_ppc64le = "0af6f5872777fdaf20c06a1f982df39659c46b6a"
+const revision_ppc64le = "8bfb8686625fa3398eb8d1a5d66834dec0d3fa06"
diff --git a/sys/syz-sysgen/sysgen.go b/sys/syz-sysgen/sysgen.go
index 56ad15f46..3aa98a16a 100644
--- a/sys/syz-sysgen/sysgen.go
+++ b/sys/syz-sysgen/sysgen.go
@@ -113,7 +113,7 @@ func main() {
}
}
- writeExecutorSyscalls(syscallArchs)
+ writeExecutorSyscalls(OS, syscallArchs)
}
if *flagMemProfile != "" {
@@ -135,8 +135,11 @@ func generate(target *targets.Target, prg *compiler.Prog, consts map[string]uint
fmt.Fprintf(out, "import . \"github.com/google/syzkaller/prog\"\n\n")
fmt.Fprintf(out, "func init() {\n")
- fmt.Fprintf(out, "\tinitArch(revision_%v, syscalls_%v, resources_%v, structDescs_%v, consts_%v, %q, %v)\n",
- target.Arch, target.Arch, target.Arch, target.Arch, target.Arch, target.Arch, target.PtrSize)
+ fmt.Fprintf(out, "\tRegisterTarget(&Target{OS: %q, Arch: %q, Revision: revision_%v, PtrSize: %v,"+
+ "Syscalls: syscalls_%v, Resources: resources_%v, Structs: structDescs_%v, Consts: consts_%v}, "+
+ "initTarget)\n",
+ target.OS, target.Arch, target.Arch, target.PtrSize,
+ target.Arch, target.Arch, target.Arch, target.Arch)
fmt.Fprintf(out, "}\n\n")
fmt.Fprintf(out, "var resources_%v = ", target.Arch)
@@ -203,13 +206,13 @@ func generateExecutorSyscalls(target *targets.Target, syscalls []*prog.Syscall,
return buf.Bytes()
}
-func writeExecutorSyscalls(archs [][]byte) {
+func writeExecutorSyscalls(OS string, archs [][]byte) {
buf := new(bytes.Buffer)
buf.WriteString(syscallsTempl)
for _, arch := range archs {
buf.Write(arch)
}
- writeFile("executor/syscalls.h", buf.Bytes())
+ writeFile(filepath.Join("executor", fmt.Sprintf("syscalls_%v.h", OS)), buf.Bytes())
}
func writeSource(file string, data []byte) {
diff --git a/sys/targets/targets.go b/sys/targets/targets.go
index 07f3ee8a7..6c5028c1d 100644
--- a/sys/targets/targets.go
+++ b/sys/targets/targets.go
@@ -62,6 +62,16 @@ var List = map[string]map[string]*Target{
KernelHeaderArch: "powerpc",
},
},
+ "fuchsia": map[string]*Target{
+ "amd64": {
+ PtrSize: 8,
+ CArch: []string{"__x86_64__"},
+ },
+ "arm64": {
+ PtrSize: 8,
+ CArch: []string{"__aarch64__"},
+ },
+ },
}
func init() {
diff --git a/syz-fuzzer/fuzzer.go b/syz-fuzzer/fuzzer.go
index 8c94c7d49..01003a665 100644
--- a/syz-fuzzer/fuzzer.go
+++ b/syz-fuzzer/fuzzer.go
@@ -11,7 +11,6 @@ import (
"net/http"
_ "net/http/pprof"
"os"
- "os/signal"
"runtime"
"runtime/debug"
"strconv"
@@ -30,7 +29,6 @@ import (
. "github.com/google/syzkaller/pkg/rpctype"
"github.com/google/syzkaller/prog"
"github.com/google/syzkaller/sys"
- "github.com/google/syzkaller/sys/linux"
)
var (
@@ -114,14 +112,7 @@ func main() {
Fatalf("%v", err)
}
- go func() {
- // Handles graceful preemption on GCE.
- c := make(chan os.Signal, 1)
- signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
- <-c
- Logf(0, "SYZ-FUZZER: PREEMPTED")
- os.Exit(1)
- }()
+ osInit()
if *flagPprof != "" {
go func() {
@@ -774,110 +765,3 @@ retry:
Logf(2, "result failed=%v hanged=%v: %v\n", failed, hanged, string(output))
return info
}
-
-func kmemleakInit() {
- fd, err := syscall.Open("/sys/kernel/debug/kmemleak", syscall.O_RDWR, 0)
- if err != nil {
- if *flagLeak {
- Fatalf("BUG: /sys/kernel/debug/kmemleak is missing (%v). Enable CONFIG_KMEMLEAK and mount debugfs.", err)
- } else {
- return
- }
- }
- defer syscall.Close(fd)
- what := "scan=off"
- if !*flagLeak {
- what = "off"
- }
- if _, err := syscall.Write(fd, []byte(what)); err != nil {
- // kmemleak returns EBUSY when kmemleak is already turned off.
- if err != syscall.EBUSY {
- panic(err)
- }
- }
-}
-
-var kmemleakBuf []byte
-
-func kmemleakScan(report bool) {
- fd, err := syscall.Open("/sys/kernel/debug/kmemleak", syscall.O_RDWR, 0)
- if err != nil {
- panic(err)
- }
- defer syscall.Close(fd)
- // Kmemleak has false positives. To mitigate most of them, it checksums
- // potentially leaked objects, and reports them only on the next scan
- // iff the checksum does not change. Because of that we do the following
- // intricate dance:
- // Scan, sleep, scan again. At this point we can get some leaks.
- // If there are leaks, we sleep and scan again, this can remove
- // false leaks. Then, read kmemleak again. If we get leaks now, then
- // hopefully these are true positives during the previous testing cycle.
- if _, err := syscall.Write(fd, []byte("scan")); err != nil {
- panic(err)
- }
- time.Sleep(time.Second)
- if _, err := syscall.Write(fd, []byte("scan")); err != nil {
- panic(err)
- }
- if report {
- if kmemleakBuf == nil {
- kmemleakBuf = make([]byte, 128<<10)
- }
- n, err := syscall.Read(fd, kmemleakBuf)
- if err != nil {
- panic(err)
- }
- if n != 0 {
- time.Sleep(time.Second)
- if _, err := syscall.Write(fd, []byte("scan")); err != nil {
- panic(err)
- }
- n, err := syscall.Read(fd, kmemleakBuf)
- if err != nil {
- panic(err)
- }
- if n != 0 {
- // BUG in output should be recognized by manager.
- Logf(0, "BUG: memory leak:\n%s\n", kmemleakBuf[:n])
- }
- }
- }
- if _, err := syscall.Write(fd, []byte("clear")); err != nil {
- panic(err)
- }
-}
-
-// Checks if the KCOV device supports comparisons.
-// Returns a pair of bools:
-// First - is the kcov device present in the system.
-// Second - is the kcov device supporting comparisons.
-func checkCompsSupported() (kcov, comps bool) {
- // TODO(dvyukov): this should run under target arch.
- // E.g. KCOV ioctls were initially not supported on 386 (missing compat_ioctl),
- // and a 386 executor won't be able to use them, but an amd64 fuzzer will be.
- fd, err := syscall.Open("/sys/kernel/debug/kcov", syscall.O_RDWR, 0)
- if err != nil {
- return
- }
- defer syscall.Close(fd)
- kcov = true
- coverSize := uintptr(64 << 10)
- _, _, errno := syscall.Syscall(
- syscall.SYS_IOCTL, uintptr(fd), linux.KCOV_INIT_TRACE, coverSize)
- if errno != 0 {
- Logf(1, "KCOV_CHECK: KCOV_INIT_TRACE = %v", errno)
- return
- }
- _, err = syscall.Mmap(fd, 0, int(coverSize*8),
- syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED)
- if err != nil {
- Logf(1, "KCOV_CHECK: mmap = %v", err)
- return
- }
- _, _, errno = syscall.Syscall(syscall.SYS_IOCTL,
- uintptr(fd), linux.KCOV_ENABLE, linux.KCOV_TRACE_CMP)
- Logf(1, "KCOV_CHECK: KCOV_ENABLE = %v", errno)
- comps = errno == 0
- return
-}
diff --git a/syz-fuzzer/fuzzer_fuchsia.go b/syz-fuzzer/fuzzer_fuchsia.go
new file mode 100644
index 000000000..9a5f0defe
--- /dev/null
+++ b/syz-fuzzer/fuzzer_fuchsia.go
@@ -0,0 +1,26 @@
+// 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.
+
+// +build fuchsia
+
+package main
+
+import (
+ "github.com/google/syzkaller/pkg/log"
+)
+
+func osInit() {
+}
+
+func kmemleakInit() {
+ if *flagLeak {
+ log.Fatalf("leak checking is not supported on fuchsia")
+ }
+}
+
+func kmemleakScan(report bool) {
+}
+
+func checkCompsSupported() (kcov, comps bool) {
+ return false, false
+}
diff --git a/syz-fuzzer/fuzzer_linux.go b/syz-fuzzer/fuzzer_linux.go
new file mode 100644
index 000000000..6443d3d32
--- /dev/null
+++ b/syz-fuzzer/fuzzer_linux.go
@@ -0,0 +1,132 @@
+// 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.
+
+package main
+
+import (
+ "os"
+ "os/signal"
+ "syscall"
+ "time"
+
+ "github.com/google/syzkaller/pkg/log"
+ "github.com/google/syzkaller/sys/linux"
+)
+
+func osInit() {
+ go func() {
+ // Handles graceful preemption on GCE.
+ c := make(chan os.Signal, 1)
+ signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
+ <-c
+ log.Logf(0, "SYZ-FUZZER: PREEMPTED")
+ os.Exit(1)
+ }()
+}
+
+func kmemleakInit() {
+ fd, err := syscall.Open("/sys/kernel/debug/kmemleak", syscall.O_RDWR, 0)
+ if err != nil {
+ if *flagLeak {
+ log.Fatalf("BUG: /sys/kernel/debug/kmemleak is missing (%v). Enable CONFIG_KMEMLEAK and mount debugfs.", err)
+ } else {
+ return
+ }
+ }
+ defer syscall.Close(fd)
+ what := "scan=off"
+ if !*flagLeak {
+ what = "off"
+ }
+ if _, err := syscall.Write(fd, []byte(what)); err != nil {
+ // kmemleak returns EBUSY when kmemleak is already turned off.
+ if err != syscall.EBUSY {
+ panic(err)
+ }
+ }
+}
+
+var kmemleakBuf []byte
+
+func kmemleakScan(report bool) {
+ fd, err := syscall.Open("/sys/kernel/debug/kmemleak", syscall.O_RDWR, 0)
+ if err != nil {
+ panic(err)
+ }
+ defer syscall.Close(fd)
+ // Kmemleak has false positives. To mitigate most of them, it checksums
+ // potentially leaked objects, and reports them only on the next scan
+ // iff the checksum does not change. Because of that we do the following
+ // intricate dance:
+ // Scan, sleep, scan again. At this point we can get some leaks.
+ // If there are leaks, we sleep and scan again, this can remove
+ // false leaks. Then, read kmemleak again. If we get leaks now, then
+ // hopefully these are true positives during the previous testing cycle.
+ if _, err := syscall.Write(fd, []byte("scan")); err != nil {
+ panic(err)
+ }
+ time.Sleep(time.Second)
+ if _, err := syscall.Write(fd, []byte("scan")); err != nil {
+ panic(err)
+ }
+ if report {
+ if kmemleakBuf == nil {
+ kmemleakBuf = make([]byte, 128<<10)
+ }
+ n, err := syscall.Read(fd, kmemleakBuf)
+ if err != nil {
+ panic(err)
+ }
+ if n != 0 {
+ time.Sleep(time.Second)
+ if _, err := syscall.Write(fd, []byte("scan")); err != nil {
+ panic(err)
+ }
+ n, err := syscall.Read(fd, kmemleakBuf)
+ if err != nil {
+ panic(err)
+ }
+ if n != 0 {
+ // BUG in output should be recognized by manager.
+ log.Logf(0, "BUG: memory leak:\n%s\n", kmemleakBuf[:n])
+ }
+ }
+ }
+ if _, err := syscall.Write(fd, []byte("clear")); err != nil {
+ panic(err)
+ }
+}
+
+// Checks if the KCOV device supports comparisons.
+// Returns a pair of bools:
+// First - is the kcov device present in the system.
+// Second - is the kcov device supporting comparisons.
+func checkCompsSupported() (kcov, comps bool) {
+ // TODO(dvyukov): this should run under target arch.
+ // E.g. KCOV ioctls were initially not supported on 386 (missing compat_ioctl),
+ // and a 386 executor won't be able to use them, but an amd64 fuzzer will be.
+ fd, err := syscall.Open("/sys/kernel/debug/kcov", syscall.O_RDWR, 0)
+ if err != nil {
+ return
+ }
+ defer syscall.Close(fd)
+ kcov = true
+ coverSize := uintptr(64 << 10)
+ _, _, errno := syscall.Syscall(
+ syscall.SYS_IOCTL, uintptr(fd), linux.KCOV_INIT_TRACE, coverSize)
+ if errno != 0 {
+ log.Logf(1, "KCOV_CHECK: KCOV_INIT_TRACE = %v", errno)
+ return
+ }
+ _, err = syscall.Mmap(fd, 0, int(coverSize*8),
+ syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED)
+ if err != nil {
+ log.Logf(1, "KCOV_CHECK: mmap = %v", err)
+ return
+ }
+ _, _, errno = syscall.Syscall(syscall.SYS_IOCTL,
+ uintptr(fd), linux.KCOV_ENABLE, linux.KCOV_TRACE_CMP)
+ log.Logf(1, "KCOV_CHECK: KCOV_ENABLE = %v", errno)
+ comps = errno == 0
+ return
+}
diff --git a/tools/syz-execprog/execprog.go b/tools/syz-execprog/execprog.go
index 282e416e3..9f2c45ba6 100644
--- a/tools/syz-execprog/execprog.go
+++ b/tools/syz-execprog/execprog.go
@@ -12,11 +12,9 @@ import (
"fmt"
"io/ioutil"
"os"
- "os/signal"
"runtime"
"sync"
"sync/atomic"
- "syscall"
"time"
"github.com/google/syzkaller/pkg/cover"
@@ -192,15 +190,6 @@ func main() {
}()
}
- go func() {
- c := make(chan os.Signal, 2)
- signal.Notify(c, syscall.SIGINT)
- <-c
- Logf(0, "shutting down...")
- atomic.StoreUint32(&shutdown, 1)
- <-c
- Fatalf("terminating")
- }()
-
+ go handleInterrupt(&shutdown)
wg.Wait()
}
diff --git a/tools/syz-execprog/execprog_fuchsia.go b/tools/syz-execprog/execprog_fuchsia.go
new file mode 100644
index 000000000..14eac9306
--- /dev/null
+++ b/tools/syz-execprog/execprog_fuchsia.go
@@ -0,0 +1,9 @@
+// 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.
+
+// +build fuchsia
+
+package main
+
+func handleInterrupt(shutdown *uint32) {
+}
diff --git a/tools/syz-execprog/execprog_linux.go b/tools/syz-execprog/execprog_linux.go
new file mode 100644
index 000000000..01e74cec5
--- /dev/null
+++ b/tools/syz-execprog/execprog_linux.go
@@ -0,0 +1,23 @@
+// 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.
+
+package main
+
+import (
+ "os"
+ "os/signal"
+ "sync/atomic"
+ "syscall"
+
+ "github.com/google/syzkaller/pkg/log"
+)
+
+func handleInterrupt(shutdown *uint32) {
+ c := make(chan os.Signal, 2)
+ signal.Notify(c, syscall.SIGINT)
+ <-c
+ log.Logf(0, "shutting down...")
+ atomic.StoreUint32(shutdown, 1)
+ <-c
+ log.Fatalf("terminating")
+}