-
Notifications
You must be signed in to change notification settings - Fork 1.2k
/
execresult.go
141 lines (119 loc) · 3.44 KB
/
execresult.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
// Copyright 2021 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 (
"fmt"
"syscall"
"github.com/google/syzkaller/pkg/ipc"
"github.com/google/syzkaller/prog"
)
// ExecResult stores the results of executing a program.
type ExecResult struct {
// Pool is the index of the pool.
Pool int
// Hanged is set to true when a program was killed due to hanging.
Hanged bool
// Info contains information about the execution of each system call
// in the generated programs.
Info ipc.ProgInfo
// Crashed is set to true if a crash occurred while executing the program.
// TODO: is not used properly. Crashes are just an errors now.
Crashed bool
// Source task ID is used to route result back to the caller.
ExecTaskID int64
// To signal the processing errors.
Error error
}
func (l *ExecResult) IsEqual(r *ExecResult) bool {
if l.Crashed || r.Crashed {
return false
}
lCalls := l.Info.Calls
rCalls := r.Info.Calls
if len(lCalls) != len(rCalls) {
return false
}
for i := 0; i < len(lCalls); i++ {
if lCalls[i].Errno != rCalls[i].Errno ||
lCalls[i].Flags != rCalls[i].Flags {
return false
}
}
return true
}
type ResultReport struct {
// Prog is the serialized program.
Prog string
// Reports contains information about each system call.
Reports []*CallReport
// Mismatch says whether the Reports differ.
Mismatch bool
}
type CallReport struct {
// Call is the name of the system call.
Call string
// States is a map between pools and their return state when executing the system call.
States map[int]ReturnState
// Mismatch is set to true if the returned error codes were not the same.
Mismatch bool
}
// ReturnState stores the results of executing a system call.
type ReturnState struct {
// Errno is returned by executing the system call.
Errno int
// Flags stores the call flags (see pkg/ipc/ipc.go).
Flags ipc.CallFlags
// Crashed is set to true if the kernel crashed while executing the program
// that contains the system call.
Crashed bool
}
func (s ReturnState) String() string {
state := ""
if s.Crashed {
return "Crashed"
}
state += fmt.Sprintf("Flags: %d, ", s.Flags)
errDesc := "success"
if s.Errno != 0 {
errDesc = syscall.Errno(s.Errno).Error()
}
state += fmt.Sprintf("Errno: %d (%s)", s.Errno, errDesc)
return state
}
// CompareResults checks whether the ExecResult of the same program,
// executed on different kernels, are the same.
// It returns s ResultReport, highlighting the differences.
func CompareResults(res []*ExecResult, prog *prog.Prog) *ResultReport {
rr := &ResultReport{
Prog: string(prog.Serialize()),
}
// Build the CallReport for each system call in the program.
for idx, call := range prog.Calls {
cn := call.Meta.Name
cr := &CallReport{
Call: cn,
States: map[int]ReturnState{},
}
for _, r := range res {
if r.Crashed {
cr.States[r.Pool] = ReturnState{Crashed: true}
continue
}
ci := r.Info.Calls[idx]
cr.States[r.Pool] = ReturnState{Errno: ci.Errno, Flags: ci.Flags}
}
rr.Reports = append(rr.Reports, cr)
}
pool0 := res[0].Pool
for _, cr := range rr.Reports {
for _, state := range cr.States {
// For each CallReport, verify whether the ReturnStates from all
// the pools that executed the program are the same
if state0 := cr.States[pool0]; state0 != state {
cr.Mismatch = true
rr.Mismatch = true
}
}
}
return rr
}