github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/prog/rand_test.go (about) 1 // Copyright 2018 syzkaller project authors. All rights reserved. 2 // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 4 package prog 5 6 import ( 7 "bytes" 8 "fmt" 9 "math/rand" 10 "sort" 11 "testing" 12 ) 13 14 func TestNotEscaping(t *testing.T) { 15 target, err := GetTarget("test", "64") 16 if err != nil { 17 t.Fatal(err) 18 } 19 r := newRand(target, rand.NewSource(0)) 20 s := &state{ 21 files: map[string]bool{"./file0": true}, 22 } 23 bound := 1000000 24 if testing.Short() { 25 bound = 1000 26 } 27 for i := 0; i < bound; i++ { 28 fn := r.filenameImpl(s) 29 if escapingFilename(fn) { 30 t.Errorf("sandbox escaping file name %q", fn) 31 } 32 } 33 } 34 35 func TestDeterminism(t *testing.T) { 36 target, rs, iters := initTest(t) 37 iters /= 10 // takes too long 38 ct := target.DefaultChoiceTable() 39 var corpus []*Prog 40 for i := 0; i < iters; i++ { 41 seed := rs.Int63() 42 rs1 := rand.NewSource(seed) 43 p1 := generateProg(t, target, rs1, ct, corpus) 44 rs2 := rand.NewSource(seed) 45 p2 := generateProg(t, target, rs2, ct, corpus) 46 ps1 := string(p1.Serialize()) 47 ps2 := string(p2.Serialize()) 48 r1 := rs1.Int63() 49 r2 := rs2.Int63() 50 if r1 != r2 || ps1 != ps2 { 51 t.Errorf("seed=%v\nprog 1 (%v):\n%v\nprog 2 (%v):\n%v", seed, r1, ps1, r2, ps2) 52 } 53 corpus = append(corpus, p1) 54 } 55 } 56 57 func generateProg(t *testing.T, target *Target, rs rand.Source, ct *ChoiceTable, corpus []*Prog) *Prog { 58 p := target.Generate(rs, 5, ct) 59 p.Mutate(rs, 10, ct, nil, corpus) 60 for i, c := range p.Calls { 61 comps := make(CompMap) 62 for v := range extractValues(c) { 63 comps.AddComp(v, v+1) 64 comps.AddComp(v, v+10) 65 } 66 // If unbounded, this code may take O(N^2) time to complete. 67 // Since large programs are not uncommon, let's limit the number of hint iterations. 68 limit := 100 69 p.MutateWithHints(i, comps, func(p1 *Prog) bool { 70 p = p1.Clone() 71 limit-- 72 return limit > 0 73 }) 74 } 75 for _, crash := range []bool{false, true} { 76 p, _ = Minimize(p, -1, crash, func(*Prog, int) bool { 77 return rs.Int63()%10 == 0 78 }) 79 } 80 data := p.Serialize() 81 var err error 82 p, err = target.Deserialize(data, NonStrict) 83 if err != nil { 84 t.Fatal(err) 85 } 86 return p 87 } 88 89 // Checks that a generated program contains only enabled syscalls. 90 func TestEnabledCalls(t *testing.T) { 91 target, rs, iters := initTest(t) 92 enabledCalls := make(map[string]bool) 93 enabled := make(map[*Syscall]bool) 94 rnd := rand.New(rs) 95 for i := 0; i < len(target.Syscalls)/10; i++ { 96 c := target.Syscalls[rnd.Intn(len(target.Syscalls))] 97 enabled[c] = true 98 enabledCalls[c.Name] = true 99 } 100 c := target.SyscallMap["clock_gettime"] 101 enabled[c] = true 102 enabledCalls[c.Name] = true 103 104 ct := target.BuildChoiceTable(nil, enabled) 105 const tries = 10 106 for i := 0; i < tries; i++ { 107 p := target.Generate(rs, 50, ct) 108 for it := 0; it < iters/tries; it++ { 109 p.Mutate(rs, 50, ct, nil, nil) 110 } 111 for _, c := range p.Calls { 112 if _, ok := enabledCalls[c.Meta.Name]; !ok { 113 t.Fatalf("program contains a syscall that is not enabled: %v", c.Meta.Name) 114 } 115 } 116 } 117 } 118 119 func TestSizeGenerateConstArg(t *testing.T) { 120 target, rs, iters := initRandomTargetTest(t, "test", "64") 121 r := newRand(target, rs) 122 ForeachType(target.Syscalls, func(typ Type, ctx *TypeCtx) { 123 if _, ok := typ.(*IntType); !ok { 124 return 125 } 126 bits := typ.TypeBitSize() 127 limit := uint64(1<<bits - 1) 128 for i := 0; i < iters; i++ { 129 newArg, _ := typ.generate(r, nil, ctx.Dir) 130 newVal := newArg.(*ConstArg).Val 131 if newVal > limit { 132 t.Fatalf("invalid generated value: %d. (arg bitsize: %d; max value: %d)", newVal, bits, limit) 133 } 134 } 135 }) 136 } 137 138 func TestFlags(t *testing.T) { 139 // This test does not test anything, it just prints resulting 140 // distribution of values for different scenarios. 141 tests := []struct { 142 vv []uint64 143 bitmask bool 144 old uint64 145 }{ 146 {[]uint64{0, 1, 2, 3}, false, 0}, 147 {[]uint64{0, 1, 2, 3}, false, 2}, 148 {[]uint64{1, 2, 3, 4}, false, 0}, 149 {[]uint64{1, 2, 3, 4}, false, 2}, 150 {[]uint64{1, 2, 4, 8}, true, 0}, 151 {[]uint64{1, 2, 4, 8}, true, 2}, 152 {[]uint64{7}, false, 0}, 153 {[]uint64{7}, false, 7}, 154 {[]uint64{1, 2}, true, 0}, 155 {[]uint64{1, 2}, true, 2}, 156 } 157 target, rs, _ := initRandomTargetTest(t, "test", "64") 158 r := newRand(target, rs) 159 for _, test := range tests { 160 results := make(map[uint64]uint64) 161 const throws = 1e4 162 for i := 0; i < throws; i++ { 163 var v uint64 164 for { 165 v = r.flags(test.vv, test.bitmask, test.old) 166 if test.old == 0 || test.old != v { 167 break 168 } 169 } 170 if v > 100 { 171 v = 999 // to not print all possible random values we generated 172 } 173 results[v]++ 174 } 175 var sorted [][2]uint64 176 for v, c := range results { 177 sorted = append(sorted, [2]uint64{v, c}) 178 } 179 sort.Slice(sorted, func(i, j int) bool { 180 return sorted[i][0] < sorted[j][0] 181 }) 182 buf := new(bytes.Buffer) 183 for _, p := range sorted { 184 fmt.Fprintf(buf, "%v\t%v\n", p[0], p[1]) 185 } 186 t.Logf("test: vv=%+v bitmask=%v old=%v\nvalue\ttimes (out of %v)\n%v", 187 test.vv, test.bitmask, test.old, throws, buf.String()) 188 } 189 } 190 191 func TestTruncateToBitSize(t *testing.T) { 192 tests := []struct{ v, bits, res uint64 }{ 193 {0, 1, 0}, 194 {1, 1, 1}, 195 {0x123, 4, 0x3}, 196 {0xabc, 4, 0xc}, 197 {0x123, 8, 0x23}, 198 {0xabc, 8, 0xbc}, 199 {0x12345678abcdabcd, 64, 0x12345678abcdabcd}, 200 {0xf2345678abcdabcd, 64, 0xf2345678abcdabcd}, 201 } 202 for i, test := range tests { 203 t.Run(fmt.Sprint(i), func(t *testing.T) { 204 res := truncateToBitSize(test.v, test.bits) 205 if res != test.res { 206 t.Fatalf("truncateToBitSize(0x%x, %v)=0x%x, want 0x%x", test.v, test.bits, res, test.res) 207 } 208 }) 209 } 210 } 211 212 // Checks that a generated program does not contain any "no_generate" syscalls. 213 func TestNoGenerate(t *testing.T) { 214 target, rs, iters := initTest(t) 215 216 // Enable all "no_generate" syscalls and ~10% of all others. 217 enabled := make(map[*Syscall]bool) 218 rnd := newRand(target, rs) 219 for i := 0; i < len(target.Syscalls); i++ { 220 syscall := target.Syscalls[rnd.Intn(len(target.Syscalls))] 221 if syscall.Attrs.NoGenerate || rnd.oneOf(10) { 222 enabled[syscall] = true 223 } 224 } 225 c := target.SyscallMap["clock_gettime"] 226 enabled[c] = true 227 ct := target.BuildChoiceTable(nil, enabled) 228 229 const tries = 10 230 for i := 0; i < tries; i++ { 231 p := target.Generate(rs, 50, ct) 232 for it := 0; it < iters/tries; it++ { 233 p.Mutate(rs, 50, ct, nil, nil) 234 } 235 for _, c := range p.Calls { 236 if c.Meta.Attrs.NoGenerate { 237 t.Fatalf("program contains a no_generate syscall: %v", c.Meta.Name) 238 } 239 } 240 } 241 }