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
|
// 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"
)
// Result stores the results of executing a program.
type Result 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.
Crashed bool
}
type ResultReport struct {
// Prog is the serialized program.
Prog string
// Reports contains information about each system call.
Reports []*CallReport
}
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
}
// Verify checks whether the Results of the same program, executed on different
// kernels are the same. If that's not the case, it returns a ResultReport
// which highlights the differences.
func Verify(res []*Result, prog *prog.Prog, s *Stats) *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
s.Calls[cn].Occurrences++
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)
}
var send bool
pool0 := res[0].Pool
for _, cr := range rr.Reports {
cs := s.Calls[cr.Call]
mismatch := false
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
send = true
mismatch = true
cs.States[state] = true
cs.States[state0] = true
}
}
if mismatch {
cs.Mismatches++
s.TotalMismatches++
}
}
if send {
return rr
}
return nil
}
|