mirror of
https://github.com/reactos/syzkaller.git
synced 2024-11-23 11:29:46 +00:00
ipc: add ProgInfo struct
This patch add a new struct ProgInfo that for now holds info about each call in a program []CallInfo, but in the future will be expanded with remote coverage info. Update all the callers to use the new interface as well.
This commit is contained in:
parent
2ee778023a
commit
582e1f0d1d
@ -93,6 +93,11 @@ type CallInfo struct {
|
||||
Errno int // call errno (0 if the call was successful)
|
||||
}
|
||||
|
||||
type ProgInfo struct {
|
||||
Calls []CallInfo
|
||||
// TODO: remote coverage would go here.
|
||||
}
|
||||
|
||||
type Env struct {
|
||||
in []byte
|
||||
out []byte
|
||||
@ -215,7 +220,7 @@ var rateLimit = time.NewTicker(1 * time.Second)
|
||||
// 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) {
|
||||
func (env *Env) Exec(opts *ExecOpts, p *prog.Prog) (output []byte, info *ProgInfo, failed, hanged bool, err0 error) {
|
||||
// Copy-in serialized program.
|
||||
progSize, err := p.SerializeForExec(env.in)
|
||||
if err != nil {
|
||||
@ -267,9 +272,9 @@ func (env *Env) Exec(opts *ExecOpts, p *prog.Prog) (output []byte, info []CallIn
|
||||
// addFallbackSignal computes simple fallback signal in cases we don't have real coverage signal.
|
||||
// We use syscall number or-ed with returned errno value as signal.
|
||||
// At least this gives us all combinations of syscall+errno.
|
||||
func addFallbackSignal(p *prog.Prog, info []CallInfo) {
|
||||
callInfos := make([]prog.CallInfo, len(info))
|
||||
for i, inf := range info {
|
||||
func addFallbackSignal(p *prog.Prog, info *ProgInfo) {
|
||||
callInfos := make([]prog.CallInfo, len(info.Calls))
|
||||
for i, inf := range info.Calls {
|
||||
if inf.Flags&CallExecuted != 0 {
|
||||
callInfos[i].Flags |= prog.CallExecuted
|
||||
}
|
||||
@ -283,30 +288,30 @@ func addFallbackSignal(p *prog.Prog, info []CallInfo) {
|
||||
}
|
||||
p.FallbackSignal(callInfos)
|
||||
for i, inf := range callInfos {
|
||||
info[i].Signal = inf.Signal
|
||||
info.Calls[i].Signal = inf.Signal
|
||||
}
|
||||
}
|
||||
|
||||
func (env *Env) parseOutput(p *prog.Prog) ([]CallInfo, error) {
|
||||
func (env *Env) parseOutput(p *prog.Prog) (*ProgInfo, error) {
|
||||
out := env.out
|
||||
ncmd, ok := readUint32(&out)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("failed to read number of calls")
|
||||
}
|
||||
info := make([]CallInfo, len(p.Calls))
|
||||
info := &ProgInfo{Calls: make([]CallInfo, len(p.Calls))}
|
||||
for i := uint32(0); i < ncmd; i++ {
|
||||
if len(out) < int(unsafe.Sizeof(callReply{})) {
|
||||
return nil, fmt.Errorf("failed to read call %v reply", i)
|
||||
}
|
||||
reply := *(*callReply)(unsafe.Pointer(&out[0]))
|
||||
out = out[unsafe.Sizeof(callReply{}):]
|
||||
if int(reply.index) >= len(info) {
|
||||
return nil, fmt.Errorf("bad call %v index %v/%v", i, reply.index, len(info))
|
||||
if int(reply.index) >= len(info.Calls) {
|
||||
return nil, fmt.Errorf("bad call %v index %v/%v", i, reply.index, len(info.Calls))
|
||||
}
|
||||
if num := p.Calls[reply.index].Meta.ID; int(reply.num) != num {
|
||||
return nil, fmt.Errorf("wrong call %v num %v/%v", i, reply.num, num)
|
||||
}
|
||||
inf := &info[reply.index]
|
||||
inf := &info.Calls[reply.index]
|
||||
if inf.Flags != 0 || inf.Signal != nil {
|
||||
return nil, fmt.Errorf("duplicate reply for call %v/%v/%v", i, reply.index, reply.num)
|
||||
}
|
||||
|
@ -103,11 +103,11 @@ func TestExecute(t *testing.T) {
|
||||
if failed {
|
||||
t.Fatalf("program failed:\n%s", output)
|
||||
}
|
||||
if len(info) == 0 {
|
||||
if len(info.Calls) == 0 {
|
||||
t.Fatalf("no calls executed:\n%s", output)
|
||||
}
|
||||
if info[0].Errno != 0 {
|
||||
t.Fatalf("simple call failed: %v\n%s", info[0].Errno, output)
|
||||
if info.Calls[0].Errno != 0 {
|
||||
t.Fatalf("simple call failed: %v\n%s", info.Calls[0].Errno, output)
|
||||
}
|
||||
if len(output) != 0 {
|
||||
t.Fatalf("output on empty program")
|
||||
@ -152,12 +152,12 @@ func TestParallel(t *testing.T) {
|
||||
err = fmt.Errorf("program failed:\n%s", output)
|
||||
return
|
||||
}
|
||||
if len(info) == 0 {
|
||||
if len(info.Calls) == 0 {
|
||||
err = fmt.Errorf("no calls executed:\n%s", output)
|
||||
return
|
||||
}
|
||||
if info[0].Errno != 0 {
|
||||
err = fmt.Errorf("simple call failed: %v\n%s", info[0].Errno, output)
|
||||
if info.Calls[0].Errno != 0 {
|
||||
err = fmt.Errorf("simple call failed: %v\n%s", info.Calls[0].Errno, output)
|
||||
return
|
||||
}
|
||||
if len(output) != 0 {
|
||||
|
@ -124,6 +124,6 @@ type RunTestDoneArgs struct {
|
||||
Name string
|
||||
ID int
|
||||
Output []byte
|
||||
Info [][]ipc.CallInfo
|
||||
Info []*ipc.ProgInfo
|
||||
Error string
|
||||
}
|
||||
|
@ -41,10 +41,10 @@ type RunRequest struct {
|
||||
|
||||
Done chan struct{}
|
||||
Output []byte
|
||||
Info [][]ipc.CallInfo
|
||||
Info []*ipc.ProgInfo
|
||||
Err error
|
||||
|
||||
results []ipc.CallInfo
|
||||
results *ipc.ProgInfo
|
||||
name string
|
||||
broken string
|
||||
skip string
|
||||
@ -208,7 +208,7 @@ func (ctx *Context) generatePrograms(progs chan *RunRequest) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ctx *Context) parseProg(filename string) (*prog.Prog, map[string]bool, []ipc.CallInfo, error) {
|
||||
func (ctx *Context) parseProg(filename string) (*prog.Prog, map[string]bool, *ipc.ProgInfo, error) {
|
||||
data, err := ioutil.ReadFile(filepath.Join(ctx.Dir, filename))
|
||||
if err != nil {
|
||||
return nil, nil, nil, fmt.Errorf("failed to read %v: %v", filename, err)
|
||||
@ -242,28 +242,28 @@ func (ctx *Context) parseProg(filename string) (*prog.Prog, map[string]bool, []i
|
||||
"EACCES": 13,
|
||||
"EINVAL": 22,
|
||||
}
|
||||
info := make([]ipc.CallInfo, len(p.Calls))
|
||||
info := &ipc.ProgInfo{Calls: make([]ipc.CallInfo, len(p.Calls))}
|
||||
for i, call := range p.Calls {
|
||||
info[i].Flags |= ipc.CallExecuted | ipc.CallFinished
|
||||
info.Calls[i].Flags |= ipc.CallExecuted | ipc.CallFinished
|
||||
switch call.Comment {
|
||||
case "blocked":
|
||||
info[i].Flags |= ipc.CallBlocked
|
||||
info.Calls[i].Flags |= ipc.CallBlocked
|
||||
case "unfinished":
|
||||
info[i].Flags &^= ipc.CallFinished
|
||||
info.Calls[i].Flags &^= ipc.CallFinished
|
||||
default:
|
||||
res, ok := errnos[call.Comment]
|
||||
if !ok {
|
||||
return nil, nil, nil, fmt.Errorf("%v: unknown comment %q",
|
||||
filename, call.Comment)
|
||||
}
|
||||
info[i].Errno = res
|
||||
info.Calls[i].Errno = res
|
||||
}
|
||||
}
|
||||
return p, requires, info, nil
|
||||
}
|
||||
|
||||
func (ctx *Context) produceTest(progs chan *RunRequest, req *RunRequest, name string,
|
||||
properties, requires map[string]bool, results []ipc.CallInfo) {
|
||||
properties, requires map[string]bool, results *ipc.ProgInfo) {
|
||||
req.name = name
|
||||
req.results = results
|
||||
if match(properties, requires) {
|
||||
@ -386,8 +386,8 @@ func checkResult(req *RunRequest) error {
|
||||
}
|
||||
calls := make(map[string]bool)
|
||||
for run, info := range req.Info {
|
||||
for i, inf := range info {
|
||||
want := req.results[i]
|
||||
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",
|
||||
@ -433,13 +433,13 @@ func checkResult(req *RunRequest) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseBinOutput(req *RunRequest) ([][]ipc.CallInfo, error) {
|
||||
var infos [][]ipc.CallInfo
|
||||
func parseBinOutput(req *RunRequest) ([]*ipc.ProgInfo, error) {
|
||||
var infos []*ipc.ProgInfo
|
||||
s := bufio.NewScanner(bytes.NewReader(req.Output))
|
||||
re := regexp.MustCompile("^### call=([0-9]+) errno=([0-9]+)$")
|
||||
for s.Scan() {
|
||||
if s.Text() == "### start" {
|
||||
infos = append(infos, make([]ipc.CallInfo, len(req.P.Calls)))
|
||||
infos = append(infos, &ipc.ProgInfo{Calls: make([]ipc.CallInfo, len(req.P.Calls))})
|
||||
}
|
||||
match := re.FindSubmatch(s.Bytes())
|
||||
if match == nil {
|
||||
@ -459,18 +459,18 @@ func parseBinOutput(req *RunRequest) ([][]ipc.CallInfo, error) {
|
||||
string(match[2]), s.Text())
|
||||
}
|
||||
info := infos[len(infos)-1]
|
||||
if call >= uint64(len(info)) {
|
||||
if call >= uint64(len(info.Calls)) {
|
||||
return nil, fmt.Errorf("bad call index %v", call)
|
||||
}
|
||||
if info[call].Flags != 0 {
|
||||
if info.Calls[call].Flags != 0 {
|
||||
return nil, fmt.Errorf("double result for call %v", call)
|
||||
}
|
||||
info[call].Flags |= ipc.CallExecuted | ipc.CallFinished
|
||||
info[call].Errno = int(errno)
|
||||
info.Calls[call].Flags |= ipc.CallExecuted | ipc.CallFinished
|
||||
info.Calls[call].Errno = int(errno)
|
||||
}
|
||||
for _, info := range infos {
|
||||
for i := range info {
|
||||
info[i].Flags |= ipc.CallExecuted
|
||||
for i := range info.Calls {
|
||||
info.Calls[i].Flags |= ipc.CallExecuted
|
||||
}
|
||||
}
|
||||
return infos, nil
|
||||
@ -509,10 +509,10 @@ func RunTest(req *RunRequest, executor string) {
|
||||
req.Err = fmt.Errorf("run %v: hanged", run)
|
||||
return
|
||||
}
|
||||
for i := range info {
|
||||
for i := range info.Calls {
|
||||
// Detach them because they point into the output shmem region.
|
||||
info[i].Signal = append([]uint32{}, info[i].Signal...)
|
||||
info[i].Cover = append([]uint32{}, info[i].Cover...)
|
||||
info.Calls[i].Signal = append([]uint32{}, info.Calls[i].Signal...)
|
||||
info.Calls[i].Cover = append([]uint32{}, info.Calls[i].Cover...)
|
||||
}
|
||||
req.Info = append(req.Info, info)
|
||||
}
|
||||
|
@ -384,10 +384,10 @@ func (fuzzer *Fuzzer) corpusSignalDiff(sign signal.Signal) signal.Signal {
|
||||
return fuzzer.corpusSignal.Diff(sign)
|
||||
}
|
||||
|
||||
func (fuzzer *Fuzzer) checkNewSignal(p *prog.Prog, info []ipc.CallInfo) (calls []int) {
|
||||
func (fuzzer *Fuzzer) checkNewSignal(p *prog.Prog, info *ipc.ProgInfo) (calls []int) {
|
||||
fuzzer.signalMu.RLock()
|
||||
defer fuzzer.signalMu.RUnlock()
|
||||
for i, inf := range info {
|
||||
for i, inf := range info.Calls {
|
||||
diff := fuzzer.maxSignal.DiffRaw(inf.Signal, signalPrio(p.Target, p.Calls[i], &inf))
|
||||
if diff.Empty() {
|
||||
continue
|
||||
|
@ -122,8 +122,8 @@ func (proc *Proc) triageInput(item *WorkTriage) {
|
||||
notexecuted := 0
|
||||
for i := 0; i < signalRuns; i++ {
|
||||
info := proc.executeRaw(proc.execOptsCover, item.p, StatTriage)
|
||||
if len(info) == 0 || len(info[item.call].Signal) == 0 ||
|
||||
item.info.Errno == 0 && info[item.call].Errno != 0 {
|
||||
if len(info.Calls) == 0 || len(info.Calls[item.call].Signal) == 0 ||
|
||||
item.info.Errno == 0 && info.Calls[item.call].Errno != 0 {
|
||||
// The call was not executed or failed.
|
||||
notexecuted++
|
||||
if notexecuted > signalRuns/2+1 {
|
||||
@ -131,7 +131,7 @@ func (proc *Proc) triageInput(item *WorkTriage) {
|
||||
}
|
||||
continue
|
||||
}
|
||||
inf := info[item.call]
|
||||
inf := info.Calls[item.call]
|
||||
thisSignal := signal.FromRaw(inf.Signal, signalPrio(item.p.Target, call, &inf))
|
||||
newSignal = newSignal.Intersection(thisSignal)
|
||||
// Without !minimized check manager starts losing some considerable amount
|
||||
@ -146,10 +146,10 @@ func (proc *Proc) triageInput(item *WorkTriage) {
|
||||
func(p1 *prog.Prog, call1 int) bool {
|
||||
for i := 0; i < minimizeAttempts; i++ {
|
||||
info := proc.execute(proc.execOptsNoCollide, p1, ProgNormal, StatMinimize)
|
||||
if len(info) == 0 || len(info[call1].Signal) == 0 {
|
||||
if len(info.Calls) == 0 || len(info.Calls[call1].Signal) == 0 {
|
||||
continue // The call was not executed.
|
||||
}
|
||||
inf := info[call1]
|
||||
inf := info.Calls[call1]
|
||||
if item.info.Errno == 0 && inf.Errno != 0 {
|
||||
// Don't minimize calls from successful to unsuccessful.
|
||||
// Successful calls are much more valuable.
|
||||
@ -207,7 +207,7 @@ func (proc *Proc) failCall(p *prog.Prog, call int) {
|
||||
opts.FaultCall = call
|
||||
opts.FaultNth = nth
|
||||
info := proc.executeRaw(&opts, p, StatSmash)
|
||||
if info != nil && len(info) > call && info[call].Flags&ipc.CallFaultInjected == 0 {
|
||||
if info != nil && len(info.Calls) > call && info.Calls[call].Flags&ipc.CallFaultInjected == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
@ -224,16 +224,16 @@ func (proc *Proc) executeHintSeed(p *prog.Prog, call int) {
|
||||
// Then mutate the initial program for every match between
|
||||
// a syscall argument and a comparison operand.
|
||||
// Execute each of such mutants to check if it gives new coverage.
|
||||
p.MutateWithHints(call, info[call].Comps, func(p *prog.Prog) {
|
||||
p.MutateWithHints(call, info.Calls[call].Comps, func(p *prog.Prog) {
|
||||
log.Logf(1, "#%v: executing comparison hint", proc.pid)
|
||||
proc.execute(proc.execOpts, p, ProgNormal, StatHint)
|
||||
})
|
||||
}
|
||||
|
||||
func (proc *Proc) execute(execOpts *ipc.ExecOpts, p *prog.Prog, flags ProgTypes, stat Stat) []ipc.CallInfo {
|
||||
func (proc *Proc) execute(execOpts *ipc.ExecOpts, p *prog.Prog, flags ProgTypes, stat Stat) *ipc.ProgInfo {
|
||||
info := proc.executeRaw(execOpts, p, stat)
|
||||
for _, callIndex := range proc.fuzzer.checkNewSignal(p, info) {
|
||||
info := info[callIndex]
|
||||
info := info.Calls[callIndex]
|
||||
// info.Signal points to the output shmem region, detach it before queueing.
|
||||
info.Signal = append([]uint32{}, info.Signal...)
|
||||
// None of the caller use Cover, so just nil it instead of detaching.
|
||||
@ -249,7 +249,7 @@ func (proc *Proc) execute(execOpts *ipc.ExecOpts, p *prog.Prog, flags ProgTypes,
|
||||
return info
|
||||
}
|
||||
|
||||
func (proc *Proc) executeRaw(opts *ipc.ExecOpts, p *prog.Prog, stat Stat) []ipc.CallInfo {
|
||||
func (proc *Proc) executeRaw(opts *ipc.ExecOpts, p *prog.Prog, stat Stat) *ipc.ProgInfo {
|
||||
if opts.Flags&ipc.FlagDedupCover == 0 {
|
||||
log.Fatalf("dedup cover is not enabled")
|
||||
}
|
||||
|
@ -237,16 +237,16 @@ func checkSimpleProgram(args *checkArgs) error {
|
||||
if failed {
|
||||
return fmt.Errorf("program failed:\n%s", output)
|
||||
}
|
||||
if len(info) == 0 {
|
||||
if len(info.Calls) == 0 {
|
||||
return fmt.Errorf("no calls executed:\n%s", output)
|
||||
}
|
||||
if info[0].Errno != 0 {
|
||||
return fmt.Errorf("simple call failed: %+v\n%s", info[0], output)
|
||||
if info.Calls[0].Errno != 0 {
|
||||
return fmt.Errorf("simple call failed: %+v\n%s", info.Calls[0], output)
|
||||
}
|
||||
if args.ipcConfig.Flags&ipc.FlagSignal != 0 && len(info[0].Signal) < 2 {
|
||||
if args.ipcConfig.Flags&ipc.FlagSignal != 0 && len(info.Calls[0].Signal) < 2 {
|
||||
return fmt.Errorf("got no coverage:\n%s", output)
|
||||
}
|
||||
if len(info[0].Signal) < 1 {
|
||||
if len(info.Calls[0].Signal) < 1 {
|
||||
return fmt.Errorf("got no fallback coverage:\n%s", output)
|
||||
}
|
||||
return nil
|
||||
|
@ -148,7 +148,7 @@ func (ctx *Context) execute(pid int, env *ipc.Env, entry *prog.LogEntry) {
|
||||
log.Logf(0, "result: failed=%v hanged=%v err=%v\n\n%s",
|
||||
failed, hanged, err, output)
|
||||
}
|
||||
if len(info) != 0 {
|
||||
if len(info.Calls) != 0 {
|
||||
ctx.printCallResults(info)
|
||||
if *flagHints {
|
||||
ctx.printHints(entry.P, info)
|
||||
@ -173,8 +173,8 @@ func (ctx *Context) logProgram(pid int, p *prog.Prog, callOpts *ipc.ExecOpts) {
|
||||
ctx.logMu.Unlock()
|
||||
}
|
||||
|
||||
func (ctx *Context) printCallResults(info []ipc.CallInfo) {
|
||||
for i, inf := range info {
|
||||
func (ctx *Context) printCallResults(info *ipc.ProgInfo) {
|
||||
for i, inf := range info.Calls {
|
||||
if inf.Flags&ipc.CallExecuted == 0 {
|
||||
continue
|
||||
}
|
||||
@ -193,13 +193,13 @@ func (ctx *Context) printCallResults(info []ipc.CallInfo) {
|
||||
}
|
||||
}
|
||||
|
||||
func (ctx *Context) printHints(p *prog.Prog, info []ipc.CallInfo) {
|
||||
func (ctx *Context) printHints(p *prog.Prog, info *ipc.ProgInfo) {
|
||||
ncomps, ncandidates := 0, 0
|
||||
for i := range p.Calls {
|
||||
if *flagOutput {
|
||||
fmt.Printf("call %v:\n", i)
|
||||
}
|
||||
comps := info[i].Comps
|
||||
comps := info.Calls[i].Comps
|
||||
for v, args := range comps {
|
||||
ncomps += len(args)
|
||||
if *flagOutput {
|
||||
@ -220,8 +220,8 @@ func (ctx *Context) printHints(p *prog.Prog, info []ipc.CallInfo) {
|
||||
log.Logf(0, "ncomps=%v ncandidates=%v", ncomps, ncandidates)
|
||||
}
|
||||
|
||||
func (ctx *Context) dumpCoverage(coverFile string, info []ipc.CallInfo) {
|
||||
for i, inf := range info {
|
||||
func (ctx *Context) dumpCoverage(coverFile string, info *ipc.ProgInfo) {
|
||||
for i, inf := range info.Calls {
|
||||
log.Logf(0, "call #%v: signal %v, coverage %v", i, len(inf.Signal), len(inf.Cover))
|
||||
if len(inf.Cover) == 0 {
|
||||
continue
|
||||
|
Loading…
Reference in New Issue
Block a user