aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2024-04-15 14:54:59 +0200
committerDmitry Vyukov <dvyukov@google.com>2024-04-15 17:57:59 +0000
commit0d592ce46ebc504d579c07e5bc3f7f3f2038c4cf (patch)
tree8c942e9313a6d4c1e302e6aa55cd853d524f97db
parente634f4672260dac9c086a06bfe10e4e3859c77a1 (diff)
pkg/fuzzer: fix signal filtering during minimization
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.
-rw-r--r--pkg/fuzzer/fuzzer.go8
-rw-r--r--pkg/fuzzer/job.go9
-rw-r--r--pkg/rpctype/rpctype.go15
-rw-r--r--pkg/signal/signal.go9
-rw-r--r--syz-fuzzer/fuzzer.go35
-rw-r--r--syz-fuzzer/fuzzer_test.go52
-rw-r--r--syz-manager/rpc.go13
7 files changed, 53 insertions, 88 deletions
diff --git a/pkg/fuzzer/fuzzer.go b/pkg/fuzzer/fuzzer.go
index 2663d7b60..a5b338b13 100644
--- a/pkg/fuzzer/fuzzer.go
+++ b/pkg/fuzzer/fuzzer.go
@@ -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
@@ -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,
})
diff --git a/pkg/fuzzer/job.go b/pkg/fuzzer/job.go
index ede1f1a57..b5bb2aab1 100644
--- a/pkg/fuzzer/job.go
+++ b/pkg/fuzzer/job.go
@@ -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
diff --git a/pkg/rpctype/rpctype.go b/pkg/rpctype/rpctype.go
index 1130d11b7..7e15ba69c 100644
--- a/pkg/rpctype/rpctype.go
+++ b/pkg/rpctype/rpctype.go
@@ -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.
diff --git a/pkg/signal/signal.go b/pkg/signal/signal.go
index 48686de54..10a1ef0cb 100644
--- a/pkg/signal/signal.go
+++ b/pkg/signal/signal.go
@@ -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)
}
}
diff --git a/syz-fuzzer/fuzzer.go b/syz-fuzzer/fuzzer.go
index 52e8de1a5..eb44ff2ae 100644
--- a/syz-fuzzer/fuzzer.go
+++ b/syz-fuzzer/fuzzer.go
@@ -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
}
@@ -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) {
diff --git a/syz-fuzzer/fuzzer_test.go b/syz-fuzzer/fuzzer_test.go
index 9ce5514b6..d43b7bc74 100644
--- a/syz-fuzzer/fuzzer_test.go
+++ b/syz-fuzzer/fuzzer_test.go
@@ -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},
},
},
@@ -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)
}
diff --git a/syz-manager/rpc.go b/syz-manager/rpc.go
index 4655a1898..3603d3b42 100644
--- a/syz-manager/rpc.go
+++ b/syz-manager/rpc.go
@@ -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,
}
}