mirror of
https://github.com/reactos/syzkaller.git
synced 2025-02-17 01:58:02 +00:00
sys/test/test: add a hanging test
Ensure that we can handle hanging syscalls in all modes.
This commit is contained in:
parent
cc8045ff1f
commit
306464056c
@ -151,7 +151,7 @@ static void kill_and_wait(int pid, int* status)
|
||||
|
||||
#if !GOOS_windows
|
||||
#if SYZ_EXECUTOR || SYZ_THREADED || SYZ_REPEAT && SYZ_EXECUTOR_USES_FORK_SERVER || \
|
||||
__NR_syz_usb_connect || __NR_syz_usb_connect_ath9k
|
||||
__NR_syz_usb_connect || __NR_syz_usb_connect_ath9k || __NR_syz_sleep_ms
|
||||
static void sleep_ms(uint64 ms)
|
||||
{
|
||||
usleep(ms * 1000);
|
||||
|
@ -36,6 +36,15 @@ static long syz_exit(volatile long status)
|
||||
}
|
||||
#endif
|
||||
|
||||
#if SYZ_EXECUTOR || __NR_syz_sleep_ms
|
||||
// syz_sleep_ms(ms intptr)
|
||||
static long syz_sleep_ms(volatile long ms)
|
||||
{
|
||||
sleep_ms(ms);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if SYZ_EXECUTOR || __NR_syz_compare
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
@ -950,7 +950,7 @@ void copyout_call_results(thread_t* th)
|
||||
void write_call_output(thread_t* th, bool finished)
|
||||
{
|
||||
uint32 reserrno = 999;
|
||||
const bool blocked = th != last_scheduled;
|
||||
const bool blocked = finished && th != last_scheduled;
|
||||
uint32 call_flags = call_flag_executed | (blocked ? call_flag_blocked : 0);
|
||||
if (finished) {
|
||||
reserrno = th->res != -1 ? 0 : th->reserrno;
|
||||
|
@ -131,7 +131,7 @@ static void kill_and_wait(int pid, int* status)
|
||||
|
||||
#if !GOOS_windows
|
||||
#if SYZ_EXECUTOR || SYZ_THREADED || SYZ_REPEAT && SYZ_EXECUTOR_USES_FORK_SERVER || \
|
||||
__NR_syz_usb_connect || __NR_syz_usb_connect_ath9k
|
||||
__NR_syz_usb_connect || __NR_syz_usb_connect_ath9k || __NR_syz_sleep_ms
|
||||
static void sleep_ms(uint64 ms)
|
||||
{
|
||||
usleep(ms * 1000);
|
||||
@ -9226,6 +9226,14 @@ static long syz_exit(volatile long status)
|
||||
}
|
||||
#endif
|
||||
|
||||
#if SYZ_EXECUTOR || __NR_syz_sleep_ms
|
||||
static long syz_sleep_ms(volatile long ms)
|
||||
{
|
||||
sleep_ms(ms);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if SYZ_EXECUTOR || __NR_syz_compare
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
@ -441,9 +441,16 @@ func (ctx *Context) createCTest(p *prog.Prog, sandbox string, threaded bool, tim
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to build C program: %v", err)
|
||||
}
|
||||
var ipcFlags ipc.ExecFlags
|
||||
if threaded {
|
||||
ipcFlags |= ipc.FlagThreaded | ipc.FlagCollide
|
||||
}
|
||||
req := &RunRequest{
|
||||
P: p,
|
||||
Bin: bin,
|
||||
P: p,
|
||||
Bin: bin,
|
||||
Opts: &ipc.ExecOpts{
|
||||
Flags: ipcFlags,
|
||||
},
|
||||
Repeat: times,
|
||||
}
|
||||
return req, nil
|
||||
@ -463,58 +470,73 @@ func checkResult(req *RunRequest) error {
|
||||
}
|
||||
calls := make(map[string]bool)
|
||||
for run, info := range req.Info {
|
||||
for i, inf := range info.Calls {
|
||||
want := req.results.Calls[i]
|
||||
for flag, what := range map[ipc.CallFlags]string{
|
||||
ipc.CallExecuted: "executed",
|
||||
ipc.CallBlocked: "blocked",
|
||||
ipc.CallFinished: "finished",
|
||||
} {
|
||||
if isC && flag == ipc.CallBlocked {
|
||||
// C code does not detect when a call was blocked.
|
||||
continue
|
||||
}
|
||||
if runtime.GOOS == "freebsd" && flag == ipc.CallBlocked {
|
||||
// Blocking detection is flaky on freebsd.
|
||||
// TODO(dvyukov): try to increase the timeout in executor to make it non-flaky.
|
||||
continue
|
||||
}
|
||||
if (inf.Flags^want.Flags)&flag != 0 {
|
||||
not := " not"
|
||||
if inf.Flags&flag != 0 {
|
||||
not = ""
|
||||
}
|
||||
return fmt.Errorf("run %v: call %v is%v %v", run, i, not, what)
|
||||
}
|
||||
for call := range info.Calls {
|
||||
if err := checkCallResult(req, isC, run, call, info, calls); err != nil {
|
||||
return err
|
||||
}
|
||||
if inf.Flags&ipc.CallFinished != 0 && inf.Errno != want.Errno {
|
||||
return fmt.Errorf("run %v: wrong call %v result %v, want %v",
|
||||
run, i, inf.Errno, want.Errno)
|
||||
}
|
||||
if isC || inf.Flags&ipc.CallExecuted == 0 {
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkCallResult(req *RunRequest, isC bool, run, call int, info *ipc.ProgInfo, calls map[string]bool) error {
|
||||
inf := info.Calls[call]
|
||||
want := req.results.Calls[call]
|
||||
for flag, what := range map[ipc.CallFlags]string{
|
||||
ipc.CallExecuted: "executed",
|
||||
ipc.CallBlocked: "blocked",
|
||||
ipc.CallFinished: "finished",
|
||||
} {
|
||||
if flag != ipc.CallFinished {
|
||||
if isC {
|
||||
// C code does not detect blocked/non-finished calls.
|
||||
continue
|
||||
}
|
||||
if req.Cfg.Flags&ipc.FlagSignal != 0 {
|
||||
// Signal is always deduplicated, so we may not get any signal
|
||||
// on a second invocation of the same syscall.
|
||||
// For calls that are not meant to collect synchronous coverage we
|
||||
// allow the signal to be empty as long as the extra signal is not.
|
||||
callName := req.P.Calls[i].Meta.CallName
|
||||
if len(inf.Signal) < 2 && !calls[callName] && len(info.Extra.Signal) == 0 {
|
||||
return fmt.Errorf("run %v: call %v: no signal", run, i)
|
||||
}
|
||||
// syz_btf_id_by_name is a pseudo-syscall that might not provide
|
||||
// any coverage when invoked.
|
||||
if len(inf.Cover) == 0 && callName != "syz_btf_id_by_name" {
|
||||
return fmt.Errorf("run %v: call %v: no cover", run, i)
|
||||
}
|
||||
calls[callName] = true
|
||||
} else {
|
||||
if len(inf.Signal) == 0 {
|
||||
return fmt.Errorf("run %v: call %v: no fallback signal", run, i)
|
||||
}
|
||||
if req.Opts.Flags&ipc.FlagThreaded == 0 {
|
||||
// In non-threaded mode blocked syscalls will block main thread
|
||||
// and we won't detect blocked/unfinished syscalls.
|
||||
continue
|
||||
}
|
||||
}
|
||||
if runtime.GOOS == "freebsd" && flag == ipc.CallBlocked {
|
||||
// Blocking detection is flaky on freebsd.
|
||||
// TODO(dvyukov): try to increase the timeout in executor to make it non-flaky.
|
||||
continue
|
||||
}
|
||||
if (inf.Flags^want.Flags)&flag != 0 {
|
||||
not := " not"
|
||||
if inf.Flags&flag != 0 {
|
||||
not = ""
|
||||
}
|
||||
return fmt.Errorf("run %v: call %v is%v %v", run, call, not, what)
|
||||
}
|
||||
}
|
||||
if inf.Flags&ipc.CallFinished != 0 && inf.Errno != want.Errno {
|
||||
return fmt.Errorf("run %v: wrong call %v result %v, want %v",
|
||||
run, call, inf.Errno, want.Errno)
|
||||
}
|
||||
if isC || inf.Flags&ipc.CallExecuted == 0 {
|
||||
return nil
|
||||
}
|
||||
if req.Cfg.Flags&ipc.FlagSignal != 0 {
|
||||
// Signal is always deduplicated, so we may not get any signal
|
||||
// on a second invocation of the same syscall.
|
||||
// For calls that are not meant to collect synchronous coverage we
|
||||
// allow the signal to be empty as long as the extra signal is not.
|
||||
callName := req.P.Calls[call].Meta.CallName
|
||||
if len(inf.Signal) < 2 && !calls[callName] && len(info.Extra.Signal) == 0 {
|
||||
return fmt.Errorf("run %v: call %v: no signal", run, call)
|
||||
}
|
||||
// syz_btf_id_by_name is a pseudo-syscall that might not provide
|
||||
// any coverage when invoked.
|
||||
if len(inf.Cover) == 0 && callName != "syz_btf_id_by_name" {
|
||||
return fmt.Errorf("run %v: call %v: no cover", run, call)
|
||||
}
|
||||
calls[callName] = true
|
||||
} else {
|
||||
if len(inf.Signal) == 0 {
|
||||
return fmt.Errorf("run %v: call %v: no fallback signal", run, call)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
syz_mmap(addr vma, len len[addr])
|
||||
syz_errno(v int32)
|
||||
syz_exit(status int32)
|
||||
syz_sleep_ms(ms intptr)
|
||||
syz_compare(want ptr[in, string], want_len bytesize[want], got ptr[in, compare_data], got_len bytesize[got])
|
||||
syz_compare_int$2(n const[2], v0 intptr, v1 intptr)
|
||||
syz_compare_int$3(n const[3], v0 intptr, v1 intptr, v2 intptr)
|
||||
|
3
sys/test/test/sleep
Normal file
3
sys/test/test/sleep
Normal file
@ -0,0 +1,3 @@
|
||||
# requires: threaded
|
||||
|
||||
syz_sleep_ms(0x1000000) # unfinished
|
Loading…
x
Reference in New Issue
Block a user