aboutsummaryrefslogtreecommitdiffstats
path: root/syz-verifier/verifier.go
blob: a9c584d46e2d85497d0835552d2d0ea2789644e7 (plain)
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
// 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
}

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
}

func (s ReturnState) String() string {
	state := ""
	if s.Flags != 0 {
		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)\n", 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 {
			ci := r.Info.Calls[idx]
			cr.States[r.Pool] = ReturnState{ci.Errno, 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 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
}