diff options
| author | Dmitry Vyukov <dvyukov@google.com> | 2020-05-04 08:58:32 +0200 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2020-05-04 20:56:20 +0200 |
| commit | a4d38b39a8e23244bea7a53e9d7a759474f85dae (patch) | |
| tree | 6bdb1f795fc5b670c9d2bad96599820cdb1eea85 | |
| parent | 58ae5e18624eaaac79cab00e63d6f32c9bd64ee0 (diff) | |
prog: support disabled attribute
Update #477
Update #502
| -rw-r--r-- | pkg/csource/csource_test.go | 4 | ||||
| -rw-r--r-- | pkg/host/syscalls.go | 68 | ||||
| -rw-r--r-- | pkg/mgrconfig/load.go | 5 | ||||
| -rw-r--r-- | prog/any_test.go | 6 | ||||
| -rw-r--r-- | prog/checksum_test.go | 5 | ||||
| -rw-r--r-- | prog/encoding_test.go | 6 | ||||
| -rw-r--r-- | prog/encodingexec_test.go | 3 | ||||
| -rw-r--r-- | prog/hints_test.go | 6 | ||||
| -rw-r--r-- | prog/minimization_test.go | 6 | ||||
| -rw-r--r-- | prog/mutation_test.go | 38 | ||||
| -rw-r--r-- | prog/prio.go | 71 | ||||
| -rw-r--r-- | prog/prio_test.go | 36 | ||||
| -rw-r--r-- | prog/prog_test.go | 24 | ||||
| -rw-r--r-- | prog/rand.go | 24 | ||||
| -rw-r--r-- | prog/rand_test.go | 11 | ||||
| -rw-r--r-- | prog/size_test.go | 5 | ||||
| -rw-r--r-- | prog/target.go | 11 | ||||
| -rw-r--r-- | prog/test/fuzz.go | 6 | ||||
| -rw-r--r-- | prog/validation.go | 3 | ||||
| -rw-r--r-- | sys/test/test.txt | 8 | ||||
| -rw-r--r-- | syz-fuzzer/fuzzer.go | 3 | ||||
| -rw-r--r-- | syz-fuzzer/fuzzer_test.go | 2 | ||||
| -rw-r--r-- | tools/syz-mutate/mutate.go | 3 | ||||
| -rw-r--r-- | tools/syz-stress/stress.go | 3 |
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 { |
