Skip to content

Commit

Permalink
pkg/fuzzer: fix signal filtering during minimization
Browse files Browse the repository at this point in the history
This fixes 2 issues:
1. We still want to get new coverage for syscalls during minimization.
We run lots of new programs, and some of them can give new coverage.
2. The signal filter should apply only to the target syscall.
Other syscalls probably can't even reach any of that code.

So add SignalFilterCall field and combine new and filtered signal
for that call. Other calls just collect new coverage as usual.
  • Loading branch information
dvyukov committed Apr 15, 2024
1 parent e634f46 commit 0d592ce
Show file tree
Hide file tree
Showing 7 changed files with 53 additions and 88 deletions.
8 changes: 7 additions & 1 deletion pkg/fuzzer/fuzzer.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,10 @@ type Request struct {
NeedRawCover bool
NeedSignal rpctype.SignalType
NeedHints bool
SignalFilter signal.Signal // If specified, the resulting signal MAY be a subset of it.
// If specified, the resulting signal for call SignalFilterCall
// will include subset of it even if it's not new.
SignalFilter signal.Signal
SignalFilterCall int
// Fields that are only relevant within pkg/fuzzer.
flags ProgTypes
stat *stats.Val
Expand Down Expand Up @@ -249,6 +252,9 @@ func (fuzzer *Fuzzer) pushExec(req *Request, prio priority) {
if req.NeedHints && (req.NeedCover || req.NeedSignal != rpctype.NoSignal) {
panic("Request.NeedHints is mutually exclusive with other fields")
}
if req.SignalFilter != nil && req.NeedSignal != rpctype.NewSignal {
panic("SignalFilter must be used with NewSignal")
}
fuzzer.nextExec.push(&priorityQueueItem[*Request]{
value: req, prio: prio,
})
Expand Down
9 changes: 5 additions & 4 deletions pkg/fuzzer/job.go
Original file line number Diff line number Diff line change
Expand Up @@ -237,10 +237,11 @@ func (job *triageJob) minimize(fuzzer *Fuzzer, newSignal signal.Signal) (stop bo
}
for i := 0; i < minimizeAttempts; i++ {
result := fuzzer.exec(job, &Request{
Prog: p1,
NeedSignal: rpctype.AllSignal,
SignalFilter: newSignal,
stat: fuzzer.statExecMinimize,
Prog: p1,
NeedSignal: rpctype.NewSignal,
SignalFilter: newSignal,
SignalFilterCall: call1,
stat: fuzzer.statExecMinimize,
})
if result.Stop {
stop = true
Expand Down
15 changes: 8 additions & 7 deletions pkg/rpctype/rpctype.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,14 @@ const (
// ExecutionRequest describes the task of executing a particular program.
// Corresponds to Fuzzer.Request.
type ExecutionRequest struct {
ID int64
ProgData []byte
NeedCover bool
NeedRawCover bool
NeedHints bool
NeedSignal SignalType
SignalFilter signal.Signal
ID int64
ProgData []byte
NeedCover bool
NeedRawCover bool
NeedHints bool
NeedSignal SignalType
SignalFilter signal.Signal
SignalFilterCall int
}

// ExecutionResult is sent after ExecutionRequest is completed.
Expand Down
9 changes: 6 additions & 3 deletions pkg/signal/signal.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,11 +159,14 @@ func (s Signal) RandomSubset(r *rand.Rand, size int) Signal {
return ret
}

// FilterRaw returns a subset of original raw elements that coincides with the one in Signal.
func (s Signal) FilterRaw(raw []uint32) []uint32 {
// FilterRaw returns a subset of original raw elements that either are not present in ignore,
// or coincides with the one in alwaysTake.
func FilterRaw(raw []uint32, ignore, alwaysTake Signal) []uint32 {
var ret []uint32
for _, e := range raw {
if _, ok := s[elemType(e)]; ok {
if _, ok := alwaysTake[elemType(e)]; ok {
ret = append(ret, e)
} else if _, ok := ignore[elemType(e)]; !ok {
ret = append(ret, e)
}
}
Expand Down
35 changes: 13 additions & 22 deletions syz-fuzzer/fuzzer.go
Original file line number Diff line number Diff line change
Expand Up @@ -381,11 +381,7 @@ func (tool *FuzzerTool) convertExecutionResult(res executionResult) rpctype.Exec
ret := rpctype.ExecutionResult{ID: res.ID}
if res.info != nil {
if res.NeedSignal == rpctype.NewSignal {
tool.diffMaxSignal(res.info)
}
if res.SignalFilter != nil {
// TODO: we can filter without maps if req.SignalFilter is sorted.
filterProgInfo(res.info, res.SignalFilter)
tool.diffMaxSignal(res.info, res.SignalFilter, res.SignalFilterCall)
}
ret.Info = *res.info
}
Expand Down Expand Up @@ -416,29 +412,24 @@ func (tool *FuzzerTool) deserializeInput(inp []byte) *prog.Prog {
return p
}

// The linter is too aggressive.
// nolint: dupl
func filterProgInfo(info *ipc.ProgInfo, mask signal.Signal) {
info.Extra.Signal = mask.FilterRaw(info.Extra.Signal)
for i := 0; i < len(info.Calls); i++ {
info.Calls[i].Signal = mask.FilterRaw(info.Calls[i].Signal)
}
func (tool *FuzzerTool) diffMaxSignal(info *ipc.ProgInfo, mask signal.Signal, maskCall int) {
tool.signalMu.RLock()
defer tool.signalMu.RUnlock()
diffMaxSignal(info, tool.maxSignal, mask, maskCall)
}

// The linter is too aggressive.
// nolint: dupl
func diffProgInfo(info *ipc.ProgInfo, base signal.Signal) {
info.Extra.Signal = base.DiffFromRaw(info.Extra.Signal)
func diffMaxSignal(info *ipc.ProgInfo, max, mask signal.Signal, maskCall int) {
info.Extra.Signal = diffCallSignal(info.Extra.Signal, max, mask, -1, maskCall)
for i := 0; i < len(info.Calls); i++ {
info.Calls[i].Signal = base.DiffFromRaw(info.Calls[i].Signal)
info.Calls[i].Signal = diffCallSignal(info.Calls[i].Signal, max, mask, i, maskCall)
}
}

func (tool *FuzzerTool) diffMaxSignal(info *ipc.ProgInfo) {
tool.signalMu.RLock()
defer tool.signalMu.RUnlock()

diffProgInfo(info, tool.maxSignal)
func diffCallSignal(raw []uint32, max, mask signal.Signal, call, maskCall int) []uint32 {
if mask != nil && call == maskCall {
return signal.FilterRaw(raw, max, mask)
}
return max.DiffFromRaw(raw)
}

func (tool *FuzzerTool) updateMaxSignal(add, drop []uint32) {
Expand Down
52 changes: 7 additions & 45 deletions syz-fuzzer/fuzzer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,17 @@ import (
"github.com/stretchr/testify/assert"
)

// nolint: dupl
func TestFilterProgInfo(t *testing.T) {
max := signal.FromRaw([]uint32{5, 6, 7}, 0)
mask := signal.FromRaw([]uint32{2, 4, 6, 8}, 0)
info := ipc.ProgInfo{
Calls: []ipc.CallInfo{
{
Signal: []uint32{1, 2, 3},
Signal: []uint32{1, 2, 3, 5, 6},
Cover: []uint32{1, 2, 3},
},
{
Signal: []uint32{2, 3, 4},
Signal: []uint32{2, 3, 4, 6, 7},
Cover: []uint32{2, 3, 4},
},
},
Expand All @@ -30,59 +30,21 @@ func TestFilterProgInfo(t *testing.T) {
Cover: []uint32{3, 4, 5},
},
}
filterProgInfo(&info, mask)
diffMaxSignal(&info, max, mask, 1)
assert.Equal(t, ipc.ProgInfo{
Calls: []ipc.CallInfo{
{
Signal: []uint32{2},
Cover: []uint32{1, 2, 3},
},
{
Signal: []uint32{2, 4},
Cover: []uint32{2, 3, 4},
},
},
Extra: ipc.CallInfo{
Signal: []uint32{4},
Cover: []uint32{3, 4, 5},
},
}, info)
}

// nolint: dupl
func TestDiffProgInfo(t *testing.T) {
base := signal.FromRaw([]uint32{0, 1, 2}, 0)
info := ipc.ProgInfo{
Calls: []ipc.CallInfo{
{
Signal: []uint32{0, 1, 2},
Cover: []uint32{0, 1, 2},
},
{
Signal: []uint32{1, 2, 3},
Cover: []uint32{1, 2, 3},
},
},
Extra: ipc.CallInfo{
Signal: []uint32{2, 3, 4},
Cover: []uint32{2, 3, 4},
},
}
diffProgInfo(&info, base)
assert.Equal(t, ipc.ProgInfo{
Calls: []ipc.CallInfo{
{
Signal: nil,
Cover: []uint32{0, 1, 2},
},
{
Signal: []uint32{3},
Cover: []uint32{1, 2, 3},
Signal: []uint32{2, 3, 4, 6},
Cover: []uint32{2, 3, 4},
},
},
Extra: ipc.CallInfo{
Signal: []uint32{3, 4},
Cover: []uint32{2, 3, 4},
Cover: []uint32{3, 4, 5},
},
}, info)
}
13 changes: 7 additions & 6 deletions syz-manager/rpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -358,11 +358,12 @@ func (runner *Runner) newRequest(req *fuzzer.Request) rpctype.ExecutionRequest {
}
runner.mu.Unlock()
return rpctype.ExecutionRequest{
ID: id,
ProgData: req.Prog.Serialize(),
NeedCover: req.NeedCover,
NeedSignal: req.NeedSignal,
SignalFilter: signalFilter,
NeedHints: req.NeedHints,
ID: id,
ProgData: req.Prog.Serialize(),
NeedCover: req.NeedCover,
NeedSignal: req.NeedSignal,
SignalFilter: signalFilter,
SignalFilterCall: req.SignalFilterCall,
NeedHints: req.NeedHints,
}
}

0 comments on commit 0d592ce

Please sign in to comment.