aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--executor/common_fuchsia.h2
-rw-r--r--executor/executor.h98
-rw-r--r--executor/executor_fuchsia.cc1
-rw-r--r--pkg/csource/fuchsia_common.go2
-rw-r--r--pkg/ipc/ipc.go125
-rw-r--r--syz-fuzzer/testing.go5
-rw-r--r--tools/syz-execprog/execprog.go6
7 files changed, 135 insertions, 104 deletions
diff --git a/executor/common_fuchsia.h b/executor/common_fuchsia.h
index 7db03e05e..f7b36ee48 100644
--- a/executor/common_fuchsia.h
+++ b/executor/common_fuchsia.h
@@ -67,7 +67,7 @@ static void segv_handler()
longjmp(segv_env, 1);
}
debug("recover: exiting\n");
- doexit(1);
+ doexit(SIGSEGV);
}
static void* ex_handler(void* arg)
diff --git a/executor/executor.h b/executor/executor.h
index 15e1aa40b..34c7efd48 100644
--- a/executor/executor.h
+++ b/executor/executor.h
@@ -142,6 +142,18 @@ struct execute_reply {
uint32 status;
};
+struct call_reply {
+ execute_reply header;
+ uint32 call_index;
+ uint32 call_num;
+ uint32 reserrno;
+ uint32 fault_injected;
+ uint32 signal_size;
+ uint32 cover_size;
+ uint32 comps_size;
+ // signal/cover/comps follow
+};
+
enum {
KCOV_CMP_CONST = 1,
KCOV_CMP_SIZE1 = 0,
@@ -581,44 +593,60 @@ void handle_completion(thread_t* th)
}
}
if (!collide && !th->colliding) {
- write_output(th->call_index);
- write_output(th->call_num);
uint32 reserrno = th->res != -1 ? 0 : th->reserrno;
- write_output(reserrno);
- write_output(th->fault_injected);
- uint32* signal_count_pos = write_output(0); // filled in later
- uint32* cover_count_pos = write_output(0); // filled in later
- uint32* comps_count_pos = write_output(0); // filled in later
-
- if (flag_collect_comps) {
- // Collect only the comparisons
- uint32 ncomps = th->cover_size;
- kcov_comparison_t* start = (kcov_comparison_t*)(th->cover_data + sizeof(uint64));
- kcov_comparison_t* end = start + ncomps;
- if ((char*)end > th->cover_end)
- fail("too many comparisons %u", ncomps);
- std::sort(start, end);
- ncomps = std::unique(start, end) - start;
- uint32 comps_size = 0;
- for (uint32 i = 0; i < ncomps; ++i) {
- if (start[i].ignore())
- continue;
- comps_size++;
- start[i].write();
+ if (SYZ_EXECUTOR_USES_SHMEM) {
+ write_output(th->call_index);
+ write_output(th->call_num);
+ write_output(reserrno);
+ write_output(th->fault_injected);
+ uint32* signal_count_pos = write_output(0); // filled in later
+ uint32* cover_count_pos = write_output(0); // filled in later
+ uint32* comps_count_pos = write_output(0); // filled in later
+
+ if (flag_collect_comps) {
+ // Collect only the comparisons
+ uint32 ncomps = th->cover_size;
+ kcov_comparison_t* start = (kcov_comparison_t*)(th->cover_data + sizeof(uint64));
+ kcov_comparison_t* end = start + ncomps;
+ if ((char*)end > th->cover_end)
+ fail("too many comparisons %u", ncomps);
+ std::sort(start, end);
+ ncomps = std::unique(start, end) - start;
+ uint32 comps_size = 0;
+ for (uint32 i = 0; i < ncomps; ++i) {
+ if (start[i].ignore())
+ continue;
+ comps_size++;
+ start[i].write();
+ }
+ // Write out number of comparisons.
+ *comps_count_pos = comps_size;
+ } else if (flag_cover) {
+ if (is_kernel_64_bit)
+ write_coverage_signal<uint64>(th, signal_count_pos, cover_count_pos);
+ else
+ write_coverage_signal<uint32>(th, signal_count_pos, cover_count_pos);
}
- // Write out number of comparisons.
- *comps_count_pos = comps_size;
- } else if (flag_cover) {
- if (is_kernel_64_bit)
- write_coverage_signal<uint64>(th, signal_count_pos, cover_count_pos);
- else
- write_coverage_signal<uint32>(th, signal_count_pos, cover_count_pos);
+ debug("out #%u: index=%u num=%u errno=%d sig=%u cover=%u comps=%u\n",
+ completed, th->call_index, th->call_num, reserrno,
+ *signal_count_pos, *cover_count_pos, *comps_count_pos);
+ completed++;
+ write_completed(completed);
+ } else {
+ call_reply reply;
+ reply.header.magic = kOutMagic;
+ reply.header.done = 0;
+ reply.header.status = 0;
+ reply.call_index = th->call_index;
+ reply.call_num = th->call_num;
+ reply.reserrno = reserrno;
+ reply.fault_injected = th->fault_injected;
+ reply.signal_size = 0;
+ reply.cover_size = 0;
+ reply.comps_size = 0;
+ if (write(kOutPipeFd, &reply, sizeof(reply)) != sizeof(reply))
+ fail("control pipe call write failed");
}
- debug("out #%u: index=%u num=%u errno=%d sig=%u cover=%u comps=%u\n",
- completed, th->call_index, th->call_num, reserrno,
- *signal_count_pos, *cover_count_pos, *comps_count_pos);
- completed++;
- write_completed(completed);
}
th->handled = true;
running--;
diff --git a/executor/executor_fuchsia.cc b/executor/executor_fuchsia.cc
index ab6d627a3..2377ad475 100644
--- a/executor/executor_fuchsia.cc
+++ b/executor/executor_fuchsia.cc
@@ -27,6 +27,7 @@ int main(int argc, char** argv)
install_segv_handler();
main_init();
execute_one();
+ reply_execute(0);
(void)error; // prevent unused function warning
return 0;
}
diff --git a/pkg/csource/fuchsia_common.go b/pkg/csource/fuchsia_common.go
index 5fe83bb72..9d14c5e8d 100644
--- a/pkg/csource/fuchsia_common.go
+++ b/pkg/csource/fuchsia_common.go
@@ -248,7 +248,7 @@ static void segv_handler()
longjmp(segv_env, 1);
}
debug("recover: exiting\n");
- doexit(1);
+ doexit(SIGSEGV);
}
static void* ex_handler(void* arg)
diff --git a/pkg/ipc/ipc.go b/pkg/ipc/ipc.go
index b25573483..b488da277 100644
--- a/pkg/ipc/ipc.go
+++ b/pkg/ipc/ipc.go
@@ -214,6 +214,7 @@ func MakeEnv(config *Config, pid int) (*Env, error) {
}()
} else {
inmem = make([]byte, prog.ExecBufferSize)
+ outmem = make([]byte, outputSize)
}
env := &Env{
in: inmem,
@@ -290,35 +291,35 @@ func (env *Env) Exec(opts *ExecOpts, p *prog.Prog) (output []byte, info []CallIn
if env.config.Flags&FlagUseShmem == 0 {
progData = env.in[:progSize]
}
- if env.out != nil {
- // 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
- }
+ // 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)
+ env.cmd, err0 = makeCommand(env.pid, env.bin, env.config, env.inFile, env.outFile, env.out)
if err0 != nil {
return
}
}
var restart bool
output, failed, hanged, restart, err0 = env.cmd.exec(opts, progData)
- if err0 != nil || restart {
+ if err0 != nil {
env.cmd.close()
env.cmd = nil
return
}
- if env.out != nil {
- info, err0 = env.readOutCoverage(p)
- if info != nil && env.config.Flags&FlagSignal == 0 {
- addFallbackSignal(p, info)
- }
+ info, err0 = env.readOutCoverage(p)
+ if info != nil && env.config.Flags&FlagSignal == 0 {
+ addFallbackSignal(p, info)
+ }
+ if restart {
+ env.cmd.close()
+ env.cmd = nil
}
return
}
@@ -489,6 +490,7 @@ type command struct {
exited chan struct{}
inrp *os.File
outwp *os.File
+ outmem []byte
}
const (
@@ -526,21 +528,18 @@ type executeReply struct {
}
// TODO(dvyukov): we currently parse this manually, should cast output to this struct instead.
-/*
type callReply struct {
callIndex uint32
callNum uint32
errno uint32
- blocked uint32
faultInjected uint32
signalSize uint32
coverSize uint32
compsSize uint32
// signal/cover/comps follow
}
-*/
-func makeCommand(pid int, bin []string, config *Config, inFile *os.File, outFile *os.File) (*command, error) {
+func makeCommand(pid int, bin []string, config *Config, inFile *os.File, outFile *os.File, outmem []byte) (*command, error) {
dir, err := ioutil.TempDir("./", "syzkaller-testdir")
if err != nil {
return nil, fmt.Errorf("failed to create temp dir: %v", err)
@@ -552,6 +551,7 @@ func makeCommand(pid int, bin []string, config *Config, inFile *os.File, outFile
config: config,
timeout: sanitizeTimeout(config),
dir: dir,
+ outmem: outmem,
}
defer func() {
if c != nil {
@@ -784,44 +784,57 @@ func (c *command) exec(opts *ExecOpts, progData []byte) (output []byte, failed,
hang <- false
}
}()
- var exitStatus int
- if c.config.Flags&FlagUseForkServer == 0 {
- restart = true
- c.cmd.Wait()
- close(done)
- <-hang
- exitStatus = osutil.ProcessExitStatus(c.cmd.ProcessState)
- } else {
+ restart = c.config.Flags&FlagUseForkServer == 0
+ exitStatus := -1
+ completedCalls := (*uint32)(unsafe.Pointer(&c.outmem[0]))
+ outmem := c.outmem[4:]
+ for {
reply := &executeReply{}
replyData := (*[unsafe.Sizeof(*reply)]byte)(unsafe.Pointer(reply))[:]
- _, readErr := io.ReadFull(c.inrp, replyData)
- close(done)
- if readErr == nil {
- if reply.magic != outMagic {
- panic(fmt.Sprintf("executor %v: got bad reply magic 0x%x", c.pid, reply.magic))
- }
- if reply.done == 0 {
- // TODO: call completion/coverage over the control pipe is not supported yet.
- panic(fmt.Sprintf("executor %v: got call reply", c.pid))
- }
- if reply.status == 0 {
- // Program was OK.
- <-hang
- return
- }
- // Executor writes magic values into the pipe before exiting,
- // so proceed with killing and joining it.
+ if _, err := io.ReadFull(c.inrp, replyData); err != nil {
+ break
}
- 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')
+ if reply.magic != outMagic {
+ fmt.Fprintf(os.Stderr, "executor %v: got bad reply magic 0x%x\n", c.pid, reply.magic)
+ os.Exit(1)
+ }
+ if reply.done != 0 {
+ exitStatus = int(reply.status)
+ break
+ }
+ callReply := &callReply{}
+ callReplyData := (*[unsafe.Sizeof(*callReply)]byte)(unsafe.Pointer(callReply))[:]
+ if _, err := io.ReadFull(c.inrp, callReplyData); err != nil {
+ break
+ }
+ if callReply.signalSize != 0 || callReply.coverSize != 0 || callReply.compsSize != 0 {
+ // This is unsupported yet.
+ fmt.Fprintf(os.Stderr, "executor %v: got call reply with coverage\n", c.pid)
+ os.Exit(1)
+ }
+ copy(outmem, callReplyData)
+ outmem = outmem[len(callReplyData):]
+ *completedCalls++
+ }
+ close(done)
+ if exitStatus == 0 {
+ // Program was OK.
+ <-hang
+ return
+ }
+ 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')
+ } else if exitStatus == -1 {
+ exitStatus = osutil.ProcessExitStatus(c.cmd.ProcessState)
+ if exitStatus == 0 {
+ exitStatus = statusRetry // fuchsia always returns wrong exit status 0
}
- exitStatus = int(reply.status)
}
// Handle magic values returned by executor.
switch exitStatus {
@@ -839,15 +852,7 @@ func (c *command) exec(opts *ExecOpts, progData []byte) (output []byte, failed,
hanged = false
restart = true
default:
- if c.config.Flags&FlagUseForkServer == 0 {
- return
- }
- // 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("executor %v: invalid status %d, exit status: %s",
- c.pid, exitStatus, c.cmd.ProcessState)
+ err0 = fmt.Errorf("executor %v: exit status %d", c.pid, exitStatus)
}
return
}
diff --git a/syz-fuzzer/testing.go b/syz-fuzzer/testing.go
index 0819d4dc4..6418bf3b4 100644
--- a/syz-fuzzer/testing.go
+++ b/syz-fuzzer/testing.go
@@ -140,11 +140,6 @@ func checkSimpleProgram(args *checkArgs) error {
if failed {
return fmt.Errorf("program failed:\n%s", output)
}
- if args.ipcConfig.Flags&ipc.FlagUseShmem == 0 {
- // Output is currently only supported via shmem.
- // So if we are using pipes, we won't get any info.
- return nil
- }
if len(info) == 0 {
return fmt.Errorf("no calls executed:\n%s", output)
}
diff --git a/tools/syz-execprog/execprog.go b/tools/syz-execprog/execprog.go
index 8fc0a4aa1..d6b9822fe 100644
--- a/tools/syz-execprog/execprog.go
+++ b/tools/syz-execprog/execprog.go
@@ -131,8 +131,10 @@ func main() {
failed, hanged, err, output)
}
if len(info) != 0 {
- log.Logf(1, "RESULT: signal %v, coverage %v errno %v",
- len(info[0].Signal), len(info[0].Cover), info[0].Errno)
+ for i, inf := range info {
+ log.Logf(1, "CALL %v: signal %v, coverage %v errno %v",
+ i, len(inf.Signal), len(inf.Cover), inf.Errno)
+ }
} else {
log.Logf(1, "RESULT: no calls executed")
}