aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--pkg/csource/csource_test.go4
-rw-r--r--pkg/host/syscalls.go68
-rw-r--r--pkg/mgrconfig/load.go5
-rw-r--r--prog/any_test.go6
-rw-r--r--prog/checksum_test.go5
-rw-r--r--prog/encoding_test.go6
-rw-r--r--prog/encodingexec_test.go3
-rw-r--r--prog/hints_test.go6
-rw-r--r--prog/minimization_test.go6
-rw-r--r--prog/mutation_test.go38
-rw-r--r--prog/prio.go71
-rw-r--r--prog/prio_test.go36
-rw-r--r--prog/prog_test.go24
-rw-r--r--prog/rand.go24
-rw-r--r--prog/rand_test.go11
-rw-r--r--prog/size_test.go5
-rw-r--r--prog/target.go11
-rw-r--r--prog/test/fuzz.go6
-rw-r--r--prog/validation.go3
-rw-r--r--sys/test/test.txt8
-rw-r--r--syz-fuzzer/fuzzer.go3
-rw-r--r--syz-fuzzer/fuzzer_test.go2
-rw-r--r--tools/syz-mutate/mutate.go3
-rw-r--r--tools/syz-stress/stress.go3
24 files changed, 187 insertions, 170 deletions
diff --git a/pkg/csource/csource_test.go b/pkg/csource/csource_test.go
index 78145c8fa..32ff9da28 100644
--- a/pkg/csource/csource_test.go
+++ b/pkg/csource/csource_test.go
@@ -62,7 +62,7 @@ func testTarget(t *testing.T, target *prog.Target, full bool) {
}
rs := rand.NewSource(seed)
t.Logf("seed=%v", seed)
- p := target.Generate(rs, 10, nil)
+ p := target.Generate(rs, 10, target.DefaultChoiceTable())
// Turns out that fully minimized program can trigger new interesting warnings,
// e.g. about NULL arguments for functions that require non-NULL arguments in syz_ functions.
// We could append both AllSyzProg as-is and a minimized version of it,
@@ -166,7 +166,7 @@ func TestSysTests(t *testing.T) {
func TestExecutorMacros(t *testing.T) {
// Ensure that executor does not mis-spell any of the SYZ_* macros.
target, _ := prog.GetTarget("test", "64")
- p := target.Generate(rand.NewSource(0), 1, nil)
+ p := target.Generate(rand.NewSource(0), 1, target.DefaultChoiceTable())
expected := commonDefines(p, Options{})
expected["SYZ_EXECUTOR"] = true
expected["SYZ_HAVE_SETUP_LOOP"] = true
diff --git a/pkg/host/syscalls.go b/pkg/host/syscalls.go
index 17ec0f25a..d63c7ceef 100644
--- a/pkg/host/syscalls.go
+++ b/pkg/host/syscalls.go
@@ -21,38 +21,44 @@ func DetectSupportedSyscalls(target *prog.Target, sandbox string) (
for _, c := range target.Syscalls {
supported[c] = true
}
- return supported, unsupported, nil
- }
- for _, c := range target.Syscalls {
- ok, reason := false, ""
- switch c.CallName {
- case "syz_execute_func":
- // syz_execute_func caused multiple problems:
- // 1. First it lead to corpus exploision. The program used existing values in registers
- // to pollute output area. We tried to zero registers (though, not reliably).
- // 2. It lead to explosion again. The exact mechanics are unknown, here is one sample:
- // syz_execute_func(&(0x7f0000000440)="f2af91930f0124eda133fa20430fbafce842f66188d0d4
- // 430fc7f314c1ab5bf9e2f9660f3a0fae5e090000ba023c1fb63ac4817d73d74ec482310d46f44
- // 9f216c863fa438036a91bdbae95aaaa420f383c02c401405c6bfd49d768d768f833fefbab6464
- // 660f38323c8f26dbc1a1fe5ff6f6df0804f4c4efa59c0f01c4288ba6452e000054c4431d5cc100")
- // 3. The code can also execute syscalls (and it is know to), but it's not subject to
- // target.SanitizeCall. As the result it can do things that programs are not supposed to do.
- // 4. Besides linux, corpus explosion also happens on freebsd and is clearly attributable
- // to syz_execute_func based on corpus contents. Mechanics are also not known.
- // It also did not cause finding of any new bugs (at least not that I know of).
- // Let's disable it for now until we figure out how to resolve all these problems.
- ok = false
- reason = "always disabled for now"
- default:
- ok, reason = isSupported(c, target, sandbox)
- }
- if ok {
- supported[c] = true
- } else {
- if reason == "" {
- reason = "unknown"
+ } else {
+ for _, c := range target.Syscalls {
+ ok, reason := false, ""
+ switch c.CallName {
+ case "syz_execute_func":
+ // syz_execute_func caused multiple problems:
+ // 1. First it lead to corpus exploision. The program used existing values in registers
+ // to pollute output area. We tried to zero registers (though, not reliably).
+ // 2. It lead to explosion again. The exact mechanics are unknown, here is one sample:
+ // syz_execute_func(&(0x7f0000000440)="f2af91930f0124eda133fa20430fbafce842f66188d0d4
+ // 430fc7f314c1ab5bf9e2f9660f3a0fae5e090000ba023c1fb63ac4817d73d74ec482310d46f44
+ // 9f216c863fa438036a91bdbae95aaaa420f383c02c401405c6bfd49d768d768f833fefbab6464
+ // 660f38323c8f26dbc1a1fe5ff6f6df0804f4c4efa59c0f01c4288ba6452e000054c4431d5cc100")
+ // 3. The code can also execute syscalls (and it is know to), but it's not subject to
+ // target.SanitizeCall. As the result it can do things that programs are not supposed to do.
+ // 4. Besides linux, corpus explosion also happens on freebsd and is clearly attributable
+ // to syz_execute_func based on corpus contents. Mechanics are also not known.
+ // It also did not cause finding of any new bugs (at least not that I know of).
+ // Let's disable it for now until we figure out how to resolve all these problems.
+ ok = false
+ reason = "always disabled for now"
+ default:
+ ok, reason = isSupported(c, target, sandbox)
}
- unsupported[c] = reason
+ if ok {
+ supported[c] = true
+ } else {
+ if reason == "" {
+ reason = "unknown"
+ }
+ unsupported[c] = reason
+ }
+ }
+ }
+ for c := range supported {
+ if c.Attrs.Disabled {
+ delete(supported, c)
+ unsupported[c] = "has disabled attribute in descriptions"
}
}
return supported, unsupported, nil
diff --git a/pkg/mgrconfig/load.go b/pkg/mgrconfig/load.go
index 4e4136362..c17f7cd15 100644
--- a/pkg/mgrconfig/load.go
+++ b/pkg/mgrconfig/load.go
@@ -216,6 +216,11 @@ func ParseEnabledSyscalls(target *prog.Target, enabled, disabled []string) ([]in
syscalls[call.ID] = true
}
}
+ for call := range syscalls {
+ if target.Syscalls[call].Attrs.Disabled {
+ delete(syscalls, call)
+ }
+ }
for _, c := range disabled {
n := 0
for _, call := range target.Syscalls {
diff --git a/prog/any_test.go b/prog/any_test.go
index e481ef1c1..f021eeda7 100644
--- a/prog/any_test.go
+++ b/prog/any_test.go
@@ -12,6 +12,7 @@ import (
func TestIsComplexPtr(t *testing.T) {
target, rs, _ := initRandomTargetTest(t, "linux", "amd64")
+ ct := target.DefaultChoiceTable()
iters := 10
if testing.Short() {
iters = 1
@@ -19,8 +20,11 @@ func TestIsComplexPtr(t *testing.T) {
r := newRand(target, rs)
compl := make(map[string]bool)
for _, meta := range target.Syscalls {
+ if meta.Attrs.Disabled {
+ continue
+ }
for i := 0; i < iters; i++ {
- s := newState(target, nil, nil)
+ s := newState(target, ct, nil)
calls := r.generateParticularCall(s, meta)
p := &Prog{Target: target, Calls: calls}
for _, arg := range p.complexPtrs() {
diff --git a/prog/checksum_test.go b/prog/checksum_test.go
index 75e18ab70..d8fb849bb 100644
--- a/prog/checksum_test.go
+++ b/prog/checksum_test.go
@@ -12,13 +12,14 @@ import (
func TestChecksumCalcRandom(t *testing.T) {
target, rs, iters := InitTest(t)
+ ct := target.DefaultChoiceTable()
for i := 0; i < iters; i++ {
- p := target.Generate(rs, 10, nil)
+ p := target.Generate(rs, 10, ct)
for _, call := range p.Calls {
CalcChecksumsCall(call)
}
for try := 0; try <= 10; try++ {
- p.Mutate(rs, 10, nil, nil)
+ p.Mutate(rs, 10, ct, nil)
for _, call := range p.Calls {
CalcChecksumsCall(call)
}
diff --git a/prog/encoding_test.go b/prog/encoding_test.go
index 2464f35da..a38e9deab 100644
--- a/prog/encoding_test.go
+++ b/prog/encoding_test.go
@@ -114,9 +114,10 @@ func TestCallSet(t *testing.T) {
func TestCallSetRandom(t *testing.T) {
target, rs, iters := initTest(t)
+ ct := target.DefaultChoiceTable()
for i := 0; i < iters; i++ {
const ncalls = 10
- p := target.Generate(rs, ncalls, nil)
+ p := target.Generate(rs, ncalls, ct)
calls0 := make(map[string]struct{})
for _, c := range p.Calls {
calls0[c.Meta.Name] = struct{}{}
@@ -328,10 +329,11 @@ func TestSerializeDeserialize(t *testing.T) {
func TestSerializeDeserializeRandom(t *testing.T) {
testEachTargetRandom(t, func(t *testing.T, target *Target, rs rand.Source, iters int) {
+ ct := target.DefaultChoiceTable()
data0 := make([]byte, ExecBufferSize)
data1 := make([]byte, ExecBufferSize)
for i := 0; i < iters; i++ {
- p0 := target.Generate(rs, 10, nil)
+ p0 := target.Generate(rs, 10, ct)
if ok, _, _ := testSerializeDeserialize(t, p0, data0, data1); ok {
continue
}
diff --git a/prog/encodingexec_test.go b/prog/encodingexec_test.go
index 6e2e5f61f..7114ab30f 100644
--- a/prog/encodingexec_test.go
+++ b/prog/encodingexec_test.go
@@ -13,9 +13,10 @@ import (
func TestSerializeForExecRandom(t *testing.T) {
target, rs, iters := initTest(t)
+ ct := target.DefaultChoiceTable()
buf := make([]byte, ExecBufferSize)
for i := 0; i < iters; i++ {
- p := target.Generate(rs, 10, nil)
+ p := target.Generate(rs, 10, ct)
n, err := p.SerializeForExec(buf)
if err != nil {
t.Fatalf("failed to serialize: %v", err)
diff --git a/prog/hints_test.go b/prog/hints_test.go
index 852f7bd86..54b6569a2 100644
--- a/prog/hints_test.go
+++ b/prog/hints_test.go
@@ -478,10 +478,11 @@ func TestHintsShrinkExpand(t *testing.T) {
func TestHintsRandom(t *testing.T) {
target, rs, iters := initTest(t)
+ ct := target.DefaultChoiceTable()
iters /= 10 // the test takes long
r := newRand(target, rs)
for i := 0; i < iters; i++ {
- p := target.Generate(rs, 5, nil)
+ p := target.Generate(rs, 5, ct)
for i, c := range p.Calls {
vals := extractValues(c)
for j := 0; j < 5; j++ {
@@ -570,7 +571,8 @@ func BenchmarkHints(b *testing.B) {
defer cleanup()
rs := rand.NewSource(0)
r := newRand(target, rs)
- p := target.Generate(rs, 30, nil)
+ ct := target.DefaultChoiceTable()
+ p := target.Generate(rs, 30, ct)
comps := make([]CompMap, len(p.Calls))
for i, c := range p.Calls {
vals := extractValues(c)
diff --git a/prog/minimization_test.go b/prog/minimization_test.go
index 3fde47c77..36b65763d 100644
--- a/prog/minimization_test.go
+++ b/prog/minimization_test.go
@@ -176,10 +176,11 @@ func TestMinimize(t *testing.T) {
func TestMinimizeRandom(t *testing.T) {
target, rs, iters := initTest(t)
iters /= 10 // Long test.
+ ct := target.DefaultChoiceTable()
r := rand.New(rs)
for i := 0; i < iters; i++ {
for _, crash := range []bool{false, true} {
- p := target.Generate(rs, 5, nil)
+ p := target.Generate(rs, 5, ct)
copyP := p.Clone()
minP, _ := Minimize(p, len(p.Calls)-1, crash, func(p1 *Prog, callIndex int) bool {
if r.Intn(2) == 0 {
@@ -199,9 +200,10 @@ func TestMinimizeRandom(t *testing.T) {
func TestMinimizeCallIndex(t *testing.T) {
target, rs, iters := initTest(t)
+ ct := target.DefaultChoiceTable()
r := rand.New(rs)
for i := 0; i < iters; i++ {
- p := target.Generate(rs, 5, nil)
+ p := target.Generate(rs, 5, ct)
ci := r.Intn(len(p.Calls))
p1, ci1 := Minimize(p, ci, r.Intn(2) == 0, func(p1 *Prog, callIndex int) bool {
return r.Intn(2) == 0
diff --git a/prog/mutation_test.go b/prog/mutation_test.go
index 7708a286f..a64cc6010 100644
--- a/prog/mutation_test.go
+++ b/prog/mutation_test.go
@@ -8,7 +8,6 @@ import (
"fmt"
"math"
"math/rand"
- "sync"
"testing"
)
@@ -155,9 +154,9 @@ func TestMutateArgument(t *testing.T) {
func TestSizeMutateArg(t *testing.T) {
target, rs, iters := initRandomTargetTest(t, "test", "64")
r := newRand(target, rs)
- ct := target.BuildChoiceTable(nil, nil)
+ ct := target.DefaultChoiceTable()
for i := 0; i < 100; i++ {
- p := target.Generate(rs, 10, nil)
+ p := target.Generate(rs, 10, ct)
for it := 0; it < iters; it++ {
p1 := p.Clone()
ctx := &mutator{
@@ -217,8 +216,9 @@ func TestRandomChoice(t *testing.T) {
func TestClone(t *testing.T) {
target, rs, iters := initTest(t)
+ ct := target.DefaultChoiceTable()
for i := 0; i < iters; i++ {
- p := target.Generate(rs, 10, nil)
+ p := target.Generate(rs, 10, ct)
p1 := p.Clone()
data := p.Serialize()
data1 := p1.Serialize()
@@ -230,15 +230,16 @@ func TestClone(t *testing.T) {
func TestMutateRandom(t *testing.T) {
testEachTargetRandom(t, func(t *testing.T, target *Target, rs rand.Source, iters int) {
+ ct := target.DefaultChoiceTable()
next:
for i := 0; i < iters; i++ {
- p := target.Generate(rs, 10, nil)
+ p := target.Generate(rs, 10, ct)
data0 := p.Serialize()
p1 := p.Clone()
// There is a chance that mutation will produce the same program.
// So we check that at least 1 out of 20 mutations actually change the program.
for try := 0; try < 20; try++ {
- p1.Mutate(rs, 10, nil, nil)
+ p1.Mutate(rs, 10, ct, nil)
data := p.Serialize()
if !bytes.Equal(data0, data) {
t.Fatalf("program changed after mutate\noriginal:\n%s\n\nnew:\n%s\n",
@@ -260,14 +261,15 @@ func TestMutateRandom(t *testing.T) {
func TestMutateCorpus(t *testing.T) {
target, rs, iters := initTest(t)
+ ct := target.DefaultChoiceTable()
var corpus []*Prog
for i := 0; i < 100; i++ {
- p := target.Generate(rs, 10, nil)
+ p := target.Generate(rs, 10, ct)
corpus = append(corpus, p)
}
for i := 0; i < iters; i++ {
- p1 := target.Generate(rs, 10, nil)
- p1.Mutate(rs, 10, nil, corpus)
+ p1 := target.Generate(rs, 10, ct)
+ p1.Mutate(rs, 10, ct, corpus)
}
}
@@ -402,9 +404,9 @@ mutate_rangedbuffer(&(0x7f00000000c0)=""/11)
func BenchmarkMutate(b *testing.B) {
target, cleanup := initBench(b)
defer cleanup()
- ct := linuxAmd64ChoiceTable(target)
+ ct := target.DefaultChoiceTable()
const progLen = 30
- p := target.Generate(rand.NewSource(0), progLen, nil)
+ p := target.Generate(rand.NewSource(0), progLen, ct)
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
rs := rand.NewSource(0)
@@ -417,7 +419,7 @@ func BenchmarkMutate(b *testing.B) {
func BenchmarkGenerate(b *testing.B) {
target, cleanup := initBench(b)
defer cleanup()
- ct := linuxAmd64ChoiceTable(target)
+ ct := target.DefaultChoiceTable()
const progLen = 30
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
@@ -428,18 +430,6 @@ func BenchmarkGenerate(b *testing.B) {
})
}
-var (
- linuxCTOnce sync.Once
- linuxCT *ChoiceTable
-)
-
-func linuxAmd64ChoiceTable(target *Target) *ChoiceTable {
- linuxCTOnce.Do(func() {
- linuxCT = target.BuildChoiceTable(target.CalculatePriorities(nil), nil)
- })
- return linuxCT
-}
-
func runMutationTests(t *testing.T, tests [][2]string, valid bool) {
target := initTargetTest(t, "test", "64")
for ti, test := range tests {
diff --git a/prog/prio.go b/prog/prio.go
index 648af0422..ccdab7bda 100644
--- a/prog/prio.go
+++ b/prog/prio.go
@@ -26,13 +26,15 @@ import (
func (target *Target) CalculatePriorities(corpus []*Prog) [][]float32 {
static := target.calcStaticPriorities()
- dynamic := target.calcDynamicPrio(corpus)
- for i, prios := range static {
- for j, p := range prios {
- dynamic[i][j] *= p
+ if len(corpus) != 0 {
+ dynamic := target.calcDynamicPrio(corpus)
+ for i, prios := range dynamic {
+ for j, p := range prios {
+ static[i][j] *= p
+ }
}
}
- return dynamic
+ return static
}
func (target *Target) calcStaticPriorities() [][]float32 {
@@ -200,29 +202,41 @@ func normalizePrio(prios [][]float32) {
// ChooseTable allows to do a weighted choice of a syscall for a given syscall
// based on call-to-call priorities and a set of enabled syscalls.
type ChoiceTable struct {
- target *Target
- run [][]int
- enabledCalls []*Syscall
- enabled map[*Syscall]bool
+ target *Target
+ runs [][]int
+ calls []*Syscall
}
-func (target *Target) BuildChoiceTable(prios [][]float32, enabled map[*Syscall]bool) *ChoiceTable {
+func (target *Target) BuildChoiceTable(corpus []*Prog, enabled map[*Syscall]bool) *ChoiceTable {
if enabled == nil {
enabled = make(map[*Syscall]bool)
for _, c := range target.Syscalls {
enabled[c] = true
}
}
+ for call := range enabled {
+ if call.Attrs.Disabled {
+ delete(enabled, call)
+ }
+ }
var enabledCalls []*Syscall
for c := range enabled {
enabledCalls = append(enabledCalls, c)
}
if len(enabledCalls) == 0 {
- panic(fmt.Sprintf("empty enabledCalls, len(target.Syscalls)=%v", len(target.Syscalls)))
+ panic("no syscalls enabled")
}
sort.Slice(enabledCalls, func(i, j int) bool {
return enabledCalls[i].ID < enabledCalls[j].ID
})
+ for _, p := range corpus {
+ for _, call := range p.Calls {
+ if !enabled[call.Meta] {
+ panic(fmt.Sprintf("corpus contains disabled syscall %v", call.Meta.Name))
+ }
+ }
+ }
+ prios := target.CalculatePriorities(corpus)
run := make([][]int, len(target.Syscalls))
for i := range run {
if !enabled[target.Syscalls[i]] {
@@ -232,31 +246,30 @@ func (target *Target) BuildChoiceTable(prios [][]float32, enabled map[*Syscall]b
sum := 0
for j := range run[i] {
if enabled[target.Syscalls[j]] {
- w := 1
- if prios != nil {
- w = int(prios[i][j] * 1000)
- }
- sum += w
+ sum += int(prios[i][j] * 1000)
}
run[i][j] = sum
}
}
- return &ChoiceTable{target, run, enabledCalls, enabled}
+ return &ChoiceTable{target, run, enabledCalls}
}
-func (ct *ChoiceTable) Choose(r *rand.Rand, call int) int {
- if call < 0 {
- return ct.enabledCalls[r.Intn(len(ct.enabledCalls))].ID
+func (ct *ChoiceTable) enabled(call int) bool {
+ return ct.runs[call] != nil
+}
+
+func (ct *ChoiceTable) choose(r *rand.Rand, bias int) int {
+ if bias < 0 {
+ bias = ct.calls[r.Intn(len(ct.calls))].ID
}
- run := ct.run[call]
- if run == nil {
- return ct.enabledCalls[r.Intn(len(ct.enabledCalls))].ID
+ if !ct.enabled(bias) {
+ panic("bias to disabled syscall")
}
- for {
- x := r.Intn(run[len(run)-1]) + 1
- i := sort.SearchInts(run, x)
- if ct.enabled[ct.target.Syscalls[i]] {
- return i
- }
+ run := ct.runs[bias]
+ x := r.Intn(run[len(run)-1]) + 1
+ res := sort.SearchInts(run, x)
+ if !ct.enabled(res) {
+ panic("selected disabled syscall")
}
+ return res
}
diff --git a/prog/prio_test.go b/prog/prio_test.go
index 2fd059910..29ffa26d7 100644
--- a/prog/prio_test.go
+++ b/prog/prio_test.go
@@ -7,8 +7,6 @@ import (
"math/rand"
"reflect"
"testing"
-
- "github.com/google/go-cmp/cmp"
)
func TestNormalizePrio(t *testing.T) {
@@ -30,36 +28,6 @@ func TestNormalizePrio(t *testing.T) {
}
}
-// TestPrioChoice tests that we select all syscalls with equal probability.
-func TestPrioChoice(t *testing.T) {
- t.Parallel()
- target := &Target{
- Syscalls: []*Syscall{
- {ID: 0},
- {ID: 1},
- {ID: 2},
- {ID: 3},
- },
- }
- prios := [][]float32{
- {1, 1, 1, 1},
- {1, 1, 1, 1},
- {1, 1, 1, 1},
- {1, 1, 1, 1},
- }
- ct := target.BuildChoiceTable(prios, nil)
- r := rand.New(rand.NewSource(0))
- var res [4]int
- for i := 0; i < 10000; i++ {
- res[ct.Choose(r, 0)]++
- }
- // If this fails too frequently we can do some ranges, but for now it's just hardcoded.
- want := [4]int{2552, 2459, 2491, 2498}
- if diff := cmp.Diff(res, want); diff != "" {
- t.Fatal(diff)
- }
-}
-
// Test static priorities assigned based on argument direction.
func TestStaticPriorities(t *testing.T) {
target, rs, iters := initTest(t)
@@ -73,7 +41,7 @@ func TestStaticPriorities(t *testing.T) {
{"open", "read", "write", "mmap"},
{"socket", "listen", "setsockopt"},
}
- ct := target.BuildChoiceTable(target.CalculatePriorities(nil), nil)
+ ct := target.DefaultChoiceTable()
r := rand.New(rs)
for _, syscalls := range tests {
// Counts the number of times a call is chosen after a call that creates a resource (referenceCall).
@@ -82,7 +50,7 @@ func TestStaticPriorities(t *testing.T) {
for _, call := range syscalls {
count := 0
for it := 0; it < iters*10000; it++ {
- chosenCall := target.Syscalls[ct.Choose(r, target.SyscallMap[call].ID)].Name
+ chosenCall := target.Syscalls[ct.choose(r, target.SyscallMap[call].ID)].Name
if call == referenceCall {
counter[chosenCall]++
} else if chosenCall == referenceCall {
diff --git a/prog/prog_test.go b/prog/prog_test.go
index 6374b6d25..d04b08e17 100644
--- a/prog/prog_test.go
+++ b/prog/prog_test.go
@@ -13,8 +13,9 @@ import (
func TestGeneration(t *testing.T) {
target, rs, iters := initTest(t)
+ ct := target.DefaultChoiceTable()
for i := 0; i < iters; i++ {
- target.Generate(rs, 20, nil)
+ target.Generate(rs, 20, ct)
}
}
@@ -32,6 +33,9 @@ func TestDefault(t *testing.T) {
func TestDefaultCallArgs(t *testing.T) {
target, _, _ := initTest(t)
for _, meta := range target.SyscallMap {
+ if meta.Attrs.Disabled {
+ continue
+ }
// Ensure that we can restore all arguments of all calls.
prog := fmt.Sprintf("%v()", meta.Name)
p, err := target.Deserialize([]byte(prog), NonStrict)
@@ -46,8 +50,9 @@ func TestDefaultCallArgs(t *testing.T) {
func testSerialize(t *testing.T, verbose bool) {
target, rs, iters := initTest(t)
+ ct := target.DefaultChoiceTable()
for i := 0; i < iters; i++ {
- p := target.Generate(rs, 10, nil)
+ p := target.Generate(rs, 10, ct)
var data []byte
mode := NonStrict
if verbose {
@@ -88,11 +93,12 @@ func TestSerializeVerbose(t *testing.T) {
func TestVmaType(t *testing.T) {
target, rs, iters := initRandomTargetTest(t, "test", "64")
+ ct := target.DefaultChoiceTable()
meta := target.SyscallMap["test$vma0"]
r := newRand(target, rs)
pageSize := target.PageSize
for i := 0; i < iters; i++ {
- s := newState(target, nil, nil)
+ s := newState(target, ct, nil)
calls := r.generateParticularCall(s, meta)
c := calls[len(calls)-1]
if c.Meta.Name != "test$vma0" {
@@ -161,20 +167,21 @@ func TestCrossTarget(t *testing.T) {
}
func testCrossTarget(t *testing.T, target *Target, crossTargets []*Target) {
+ ct := target.DefaultChoiceTable()
rs := randSource(t)
iters := 100
if testing.Short() {
iters /= 10
}
for i := 0; i < iters; i++ {
- p := target.Generate(rs, 20, nil)
+ p := target.Generate(rs, 20, ct)
testCrossArchProg(t, p, crossTargets)
p, err := target.Deserialize(p.Serialize(), NonStrict)
if err != nil {
t.Fatal(err)
}
testCrossArchProg(t, p, crossTargets)
- p.Mutate(rs, 20, nil, nil)
+ p.Mutate(rs, 20, ct, nil)
testCrossArchProg(t, p, crossTargets)
p, _ = Minimize(p, -1, false, func(*Prog, int) bool {
return rs.Int63()%2 == 0
@@ -197,6 +204,8 @@ func testCrossArchProg(t *testing.T, p *Prog, crossTargets []*Target) {
func TestSpecialStructs(t *testing.T) {
testEachTargetRandom(t, func(t *testing.T, target *Target, rs rand.Source, iters int) {
+ _ = target.GenerateAllSyzProg(rs)
+ ct := target.DefaultChoiceTable()
for special, gen := range target.SpecialTypes {
t.Run(special, func(t *testing.T) {
var typ Type
@@ -216,7 +225,7 @@ func TestSpecialStructs(t *testing.T) {
if typ == nil {
t.Fatal("can't find struct description")
}
- g := &Gen{newRand(target, rs), newState(target, nil, nil)}
+ g := &Gen{newRand(target, rs), newState(target, ct, nil)}
for i := 0; i < iters/len(target.SpecialTypes); i++ {
var arg Arg
for i := 0; i < 2; i++ {
@@ -441,8 +450,9 @@ fallback$0()
func TestSanitizeRandom(t *testing.T) {
testEachTargetRandom(t, func(t *testing.T, target *Target, rs rand.Source, iters int) {
+ ct := target.DefaultChoiceTable()
for i := 0; i < iters; i++ {
- p := target.Generate(rs, 10, nil)
+ p := target.Generate(rs, 10, ct)
s0 := string(p.Serialize())
p.sanitizeFix()
s1 := string(p.Serialize())
diff --git a/prog/rand.go b/prog/rand.go
index 135469fe7..150f4b266 100644
--- a/prog/rand.go
+++ b/prog/rand.go
@@ -379,10 +379,9 @@ func (r *randGen) createResource(s *state, res *ResourceType, dir Dir) (arg Arg,
// TODO: reduce priority of less specialized ctors.
var metas []*Syscall
for _, meta := range metas0 {
- if s.ct == nil || s.ct.run[meta.ID] == nil {
- continue
+ if s.ct.enabled(meta.ID) {
+ metas = append(metas, meta)
}
- metas = append(metas, meta)
}
if len(metas) == 0 {
return res.DefaultArg(dir), nil
@@ -537,19 +536,12 @@ func (r *randGen) nOutOf(n, outOf int) bool {
}
func (r *randGen) generateCall(s *state, p *Prog, insertionPoint int) []*Call {
- idx := 0
- if s.ct == nil {
- idx = r.Intn(len(r.target.Syscalls))
- } else if insertionPoint <= 0 {
- idx = s.ct.enabledCalls[r.Intn(len(s.ct.enabledCalls))].ID
- } else {
- call := -1
- if len(p.Calls) != 0 {
- // Choosing the base call is based on the insertion point of the new calls sequence.
- call = p.Calls[r.Intn(insertionPoint)].Meta.ID
- }
- idx = s.ct.Choose(r.Rand, call)
+ biasCall := -1
+ if insertionPoint > 0 {
+ // Choosing the base call is based on the insertion point of the new calls sequence.
+ biasCall = p.Calls[r.Intn(insertionPoint)].Meta.ID
}
+ idx := s.ct.choose(r.Rand, biasCall)
meta := r.target.Syscalls[idx]
return r.generateParticularCall(s, meta)
}
@@ -573,7 +565,7 @@ func (target *Target) GenerateAllSyzProg(rs rand.Source) *Prog {
Target: target,
}
r := newRand(target, rs)
- s := newState(target, nil, nil)
+ s := newState(target, target.DefaultChoiceTable(), nil)
handled := make(map[string]bool)
for _, meta := range target.Syscalls {
if !strings.HasPrefix(meta.CallName, "syz_") || handled[meta.CallName] {
diff --git a/prog/rand_test.go b/prog/rand_test.go
index da0871505..960f9735f 100644
--- a/prog/rand_test.go
+++ b/prog/rand_test.go
@@ -31,13 +31,14 @@ func TestNotEscaping(t *testing.T) {
func TestDeterminism(t *testing.T) {
target, rs, iters := initTest(t)
iters /= 10 // takes too long
+ ct := target.DefaultChoiceTable()
var corpus []*Prog
for i := 0; i < iters; i++ {
seed := rs.Int63()
rs1 := rand.NewSource(seed)
- p1 := generateProg(t, target, rs1, corpus)
+ p1 := generateProg(t, target, rs1, ct, corpus)
rs2 := rand.NewSource(seed)
- p2 := generateProg(t, target, rs2, corpus)
+ p2 := generateProg(t, target, rs2, ct, corpus)
ps1 := string(p1.Serialize())
ps2 := string(p2.Serialize())
r1 := rs1.Int63()
@@ -49,9 +50,9 @@ func TestDeterminism(t *testing.T) {
}
}
-func generateProg(t *testing.T, target *Target, rs rand.Source, corpus []*Prog) *Prog {
- p := target.Generate(rs, 5, nil)
- p.Mutate(rs, 10, nil, corpus)
+func generateProg(t *testing.T, target *Target, rs rand.Source, ct *ChoiceTable, corpus []*Prog) *Prog {
+ p := target.Generate(rs, 5, ct)
+ p.Mutate(rs, 10, ct, corpus)
for i, c := range p.Calls {
comps := make(CompMap)
for v := range extractValues(c) {
diff --git a/prog/size_test.go b/prog/size_test.go
index 49945273b..6e8a28c4d 100644
--- a/prog/size_test.go
+++ b/prog/size_test.go
@@ -10,8 +10,9 @@ import (
func TestAssignSizeRandom(t *testing.T) {
target, rs, iters := initTest(t)
+ ct := target.DefaultChoiceTable()
for i := 0; i < iters; i++ {
- p := target.Generate(rs, 10, nil)
+ p := target.Generate(rs, 10, ct)
data0 := p.Serialize()
for _, call := range p.Calls {
target.assignSizesCall(call)
@@ -19,7 +20,7 @@ func TestAssignSizeRandom(t *testing.T) {
if data1 := p.Serialize(); !bytes.Equal(data0, data1) {
t.Fatalf("different lens assigned, initial:\n%s\nnew:\n%s\n", data0, data1)
}
- p.Mutate(rs, 10, nil, nil)
+ p.Mutate(rs, 10, ct, nil)
p.Serialize()
for _, call := range p.Calls {
target.assignSizesCall(call)
diff --git a/prog/target.go b/prog/target.go
index 8999f25ac..af03dc47f 100644
--- a/prog/target.go
+++ b/prog/target.go
@@ -65,6 +65,10 @@ type Target struct {
// Maps resource name to a list of calls that can create the resource.
resourceCtors map[string][]*Syscall
any anyTypes
+
+ // The default ChoiceTable is used only by tests and utilities, so we initialize it lazily.
+ defaultOnce sync.Once
+ defaultChoiceTable *ChoiceTable
}
const maxSpecialPointers = 16
@@ -195,6 +199,13 @@ func restoreLinks(syscalls []*Syscall, resources []*ResourceDesc, types []Type)
return resourceMap
}
+func (target *Target) DefaultChoiceTable() *ChoiceTable {
+ target.defaultOnce.Do(func() {
+ target.defaultChoiceTable = target.BuildChoiceTable(nil, nil)
+ })
+ return target.defaultChoiceTable
+}
+
type Gen struct {
r *randGen
s *state
diff --git a/prog/test/fuzz.go b/prog/test/fuzz.go
index e86509237..f3742f6c3 100644
--- a/prog/test/fuzz.go
+++ b/prog/test/fuzz.go
@@ -52,7 +52,7 @@ func FuzzDeserialize(data []byte) int {
panic(err)
}
}
- p3.Mutate(rand.NewSource(0), 3, nil, nil)
+ p3.Mutate(rand.NewSource(0), 3, fuzzChoiceTable, nil)
return 0
}
@@ -64,11 +64,11 @@ func FuzzParseLog(data []byte) int {
}
var fuzzBuffer = make([]byte, prog.ExecBufferSize)
-var fuzzTarget = func() *prog.Target {
+var fuzzTarget, fuzzChoiceTable = func() (*prog.Target, *prog.ChoiceTable) {
prog.Debug()
target, err := prog.GetTarget("test", "64")
if err != nil {
panic(err)
}
- return target
+ return target, target.DefaultChoiceTable()
}()
diff --git a/prog/validation.go b/prog/validation.go
index e4613600c..e0e2eaaf7 100644
--- a/prog/validation.go
+++ b/prog/validation.go
@@ -50,6 +50,9 @@ func (p *Prog) validate() error {
}
func (ctx *validCtx) validateCall(c *Call) error {
+ if c.Meta.Attrs.Disabled {
+ return fmt.Errorf("use of a disabled call")
+ }
if len(c.Args) != len(c.Meta.Args) {
return fmt.Errorf("wrong number of arguments, want %v, got %v",
len(c.Meta.Args), len(c.Args))
diff --git a/sys/test/test.txt b/sys/test/test.txt
index 1fa8406d1..68eb4f8df 100644
--- a/sys/test/test.txt
+++ b/sys/test/test.txt
@@ -797,3 +797,11 @@ auto_struct0 {
f1 const[0x43, int32]
f2 int32
}
+
+# Attributes
+
+resource disabled_resource[int32]
+
+disabled0() (disabled)
+disabled1() disabled_resource (disabled)
+disabled2(a disabled_resource) (disabled)
diff --git a/syz-fuzzer/fuzzer.go b/syz-fuzzer/fuzzer.go
index b6e8be4b4..3694190a1 100644
--- a/syz-fuzzer/fuzzer.go
+++ b/syz-fuzzer/fuzzer.go
@@ -247,8 +247,7 @@ func main() {
for _, id := range r.CheckResult.EnabledCalls[sandbox] {
calls[target.Syscalls[id]] = true
}
- prios := target.CalculatePriorities(fuzzer.corpus)
- fuzzer.choiceTable = target.BuildChoiceTable(prios, calls)
+ fuzzer.choiceTable = target.BuildChoiceTable(fuzzer.corpus, calls)
for pid := 0; pid < *flagProcs; pid++ {
proc, err := newProc(fuzzer, pid)
diff --git a/syz-fuzzer/fuzzer_test.go b/syz-fuzzer/fuzzer_test.go
index 41887896a..916e75ef5 100644
--- a/syz-fuzzer/fuzzer_test.go
+++ b/syz-fuzzer/fuzzer_test.go
@@ -79,7 +79,7 @@ func TestAddInputConcurrency(t *testing.T) {
}
func generateInput(target *prog.Target, rs rand.Source, ncalls int, sizeSig int) (inp InputTest) {
- inp.p = target.Generate(rs, ncalls, nil)
+ inp.p = target.Generate(rs, ncalls, target.DefaultChoiceTable())
var raw []uint32
for i := 1; i <= sizeSig; i++ {
raw = append(raw, uint32(i))
diff --git a/tools/syz-mutate/mutate.go b/tools/syz-mutate/mutate.go
index 3dcc446c6..af92e14a1 100644
--- a/tools/syz-mutate/mutate.go
+++ b/tools/syz-mutate/mutate.go
@@ -64,8 +64,7 @@ func main() {
os.Exit(1)
}
rs := rand.NewSource(seed)
- prios := target.CalculatePriorities(corpus)
- ct := target.BuildChoiceTable(prios, syscalls)
+ ct := target.BuildChoiceTable(corpus, syscalls)
var p *prog.Prog
if flag.NArg() == 0 {
p = target.Generate(rs, *flagLen, ct)
diff --git a/tools/syz-stress/stress.go b/tools/syz-stress/stress.go
index 18d4fa872..b2fa4cb8e 100644
--- a/tools/syz-stress/stress.go
+++ b/tools/syz-stress/stress.go
@@ -74,8 +74,7 @@ func main() {
syscalls = strings.Split(*flagSyscalls, ",")
}
calls := buildCallList(target, syscalls)
- prios := target.CalculatePriorities(corpus)
- ct := target.BuildChoiceTable(prios, calls)
+ ct := target.BuildChoiceTable(corpus, calls)
config, execOpts, err := createIPCConfig(target, features, featuresFlags)
if err != nil {