github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/prog/minimization_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 "math/rand" 8 "testing" 9 ) 10 11 // nolint:gocyclo 12 func TestMinimize(t *testing.T) { 13 // nolint: lll 14 tests := []struct { 15 os string 16 arch string 17 orig string 18 callIndex int 19 pred func(*Prog, int) bool 20 result string 21 resultCallIndex int 22 }{ 23 // Predicate always returns false, so must get the same program. 24 { 25 "linux", "amd64", 26 "mmap(&(0x7f0000000000/0x1000)=nil, 0x1000, 0x3, 0x32, 0xffffffffffffffff, 0x0)\n" + 27 "sched_yield()\n" + 28 "pipe2(&(0x7f0000000000), 0x0)\n", 29 2, 30 func(p *Prog, callIndex int) bool { 31 if len(p.Calls) == 0 { 32 t.Fatalf("got an empty program") 33 } 34 if p.Calls[len(p.Calls)-1].Meta.Name != "pipe2" { 35 t.Fatalf("last call is removed") 36 } 37 return false 38 }, 39 "mmap(&(0x7f0000000000/0x1000)=nil, 0x1000, 0x3, 0x32, 0xffffffffffffffff, 0x0)\n" + 40 "sched_yield()\n" + 41 "pipe2(&(0x7f0000000000), 0x0)\n", 42 2, 43 }, 44 // Remove a call. 45 { 46 "linux", "amd64", 47 "mmap(&(0x7f0000000000/0x1000)=nil, 0x1000, 0x3, 0x32, 0xffffffffffffffff, 0x0)\n" + 48 "sched_yield()\n" + 49 "pipe2(&(0x7f0000000000)={0xffffffffffffffff, 0xffffffffffffffff}, 0x0)\n", 50 2, 51 func(p *Prog, callIndex int) bool { 52 // Aim at removal of sched_yield. 53 return len(p.Calls) == 2 && p.Calls[0].Meta.Name == "mmap" && p.Calls[1].Meta.Name == "pipe2" 54 }, 55 "mmap(&(0x7f0000000000/0x1000)=nil, 0x1000, 0x0, 0x10, 0xffffffffffffffff, 0x0)\n" + 56 "pipe2(0x0, 0x0)\n", 57 1, 58 }, 59 // Remove two dependent calls. 60 { 61 "linux", "amd64", 62 "mmap(&(0x7f0000000000/0x1000)=nil, 0x1000, 0x3, 0x32, 0xffffffffffffffff, 0x0)\n" + 63 "pipe2(&(0x7f0000000000)={0x0, 0x0}, 0x0)\n" + 64 "sched_yield()\n", 65 2, 66 func(p *Prog, callIndex int) bool { 67 // Aim at removal of pipe2 and then mmap. 68 if len(p.Calls) == 2 && p.Calls[0].Meta.Name == "mmap" && p.Calls[1].Meta.Name == "sched_yield" { 69 return true 70 } 71 if len(p.Calls) == 1 && p.Calls[0].Meta.Name == "sched_yield" { 72 return true 73 } 74 return false 75 }, 76 "sched_yield()\n", 77 0, 78 }, 79 // Remove a call and replace results. 80 { 81 "linux", "amd64", 82 "mmap(&(0x7f0000000000/0x1000)=nil, 0x1000, 0x3, 0x32, 0xffffffffffffffff, 0x0)\n" + 83 "pipe2(&(0x7f0000000000)={<r0=>0x0, 0x0}, 0x0)\n" + 84 "write(r0, &(0x7f0000000000)=\"1155\", 0x2)\n" + 85 "sched_yield()\n", 86 3, 87 func(p *Prog, callIndex int) bool { 88 return p.String() == "mmap-write-sched_yield" 89 }, 90 "mmap(&(0x7f0000000000/0x1000)=nil, 0x1000, 0x0, 0x10, 0xffffffffffffffff, 0x0)\n" + 91 "write(0xffffffffffffffff, 0x0, 0x0)\n" + 92 "sched_yield()\n", 93 2, 94 }, 95 // Remove a call and replace results. 96 { 97 "linux", "amd64", 98 "mmap(&(0x7f0000000000/0x1000)=nil, 0x1000, 0x3, 0x32, 0xffffffffffffffff, 0x0)\n" + 99 "r0=open(&(0x7f0000000000)=\"1155\", 0x0, 0x0)\n" + 100 "write(r0, &(0x7f0000000000)=\"1155\", 0x2)\n" + 101 "sched_yield()\n", 102 -1, 103 func(p *Prog, callIndex int) bool { 104 return p.String() == "mmap-write-sched_yield" 105 }, 106 "mmap(&(0x7f0000000000/0x1000)=nil, 0x1000, 0x0, 0x10, 0xffffffffffffffff, 0x0)\n" + 107 "write(0xffffffffffffffff, 0x0, 0x0)\n" + 108 "sched_yield()\n", 109 -1, 110 }, 111 // Minimize pointer. 112 { 113 "linux", "amd64", 114 "pipe2(&(0x7f0000001000)={0xffffffffffffffff, 0xffffffffffffffff}, 0x0)\n", 115 -1, 116 func(p *Prog, callIndex int) bool { 117 return len(p.Calls) == 1 && p.Calls[0].Meta.Name == "pipe2" 118 }, 119 "pipe2(0x0, 0x0)\n", 120 -1, 121 }, 122 // Minimize pointee. 123 { 124 "linux", "amd64", 125 "pipe2(&(0x7f0000001000)={0xffffffffffffffff, 0xffffffffffffffff}, 0x0)\n", 126 -1, 127 func(p *Prog, callIndex int) bool { 128 return len(p.Calls) == 1 && p.Calls[0].Meta.Name == "pipe2" && p.Calls[0].Args[0].(*PointerArg).Address != 0 129 }, 130 "pipe2(&(0x7f0000001000), 0x0)\n", 131 -1, 132 }, 133 // Make sure we don't hang when minimizing resources. 134 { 135 "test", "64", 136 "r0 = test$res0()\n" + 137 "test$res1(r0)\n", 138 -1, 139 func(p *Prog, callIndex int) bool { 140 return false 141 }, 142 "r0 = test$res0()\n" + 143 "test$res1(r0)\n", 144 -1, 145 }, 146 { 147 "test", "64", 148 "minimize$0(0x1, 0x1)\n", 149 -1, 150 func(p *Prog, callIndex int) bool { return len(p.Calls) == 1 }, 151 "minimize$0(0x1, 0xffffffffffffffff)\n", 152 -1, 153 }, 154 // Clear unneeded fault injection. 155 { 156 "linux", "amd64", 157 "pipe2(0x0, 0x0) (fail_nth: 5)\n", 158 -1, 159 func(p *Prog, callIndex int) bool { 160 return len(p.Calls) == 1 && p.Calls[0].Meta.Name == "pipe2" 161 }, 162 "pipe2(0x0, 0x0)\n", 163 -1, 164 }, 165 // Keep important fault injection. 166 { 167 "linux", "amd64", 168 "pipe2(0x0, 0x0) (fail_nth: 5)\n", 169 -1, 170 func(p *Prog, callIndex int) bool { 171 return len(p.Calls) == 1 && p.Calls[0].Meta.Name == "pipe2" && p.Calls[0].Props.FailNth == 5 172 }, 173 "pipe2(0x0, 0x0) (fail_nth: 5)\n", 174 -1, 175 }, 176 // Clear unneeded async flag. 177 { 178 "linux", "amd64", 179 "pipe2(0x0, 0x0) (async)\n", 180 -1, 181 func(p *Prog, callIndex int) bool { 182 return len(p.Calls) == 1 && p.Calls[0].Meta.Name == "pipe2" 183 }, 184 "pipe2(0x0, 0x0)\n", 185 -1, 186 }, 187 // Keep important async flag. 188 { 189 "linux", "amd64", 190 "pipe2(0x0, 0x0) (async)\n", 191 -1, 192 func(p *Prog, callIndex int) bool { 193 return len(p.Calls) == 1 && p.Calls[0].Meta.Name == "pipe2" && p.Calls[0].Props.Async 194 }, 195 "pipe2(0x0, 0x0) (async)\n", 196 -1, 197 }, 198 // Clear unneeded rerun. 199 { 200 "linux", "amd64", 201 "pipe2(0x0, 0x0) (rerun: 100)\n", 202 -1, 203 func(p *Prog, callIndex int) bool { 204 return len(p.Calls) == 1 && p.Calls[0].Meta.Name == "pipe2" 205 }, 206 "pipe2(0x0, 0x0)\n", 207 -1, 208 }, 209 // Keep important rerun. 210 { 211 "linux", "amd64", 212 "pipe2(0x0, 0x0) (rerun: 100)\n", 213 -1, 214 func(p *Prog, callIndex int) bool { 215 return len(p.Calls) == 1 && p.Calls[0].Meta.Name == "pipe2" && p.Calls[0].Props.Rerun >= 100 216 }, 217 "pipe2(0x0, 0x0) (rerun: 100)\n", 218 -1, 219 }, 220 // Undo target.SpecialFileLenghts mutation (reduce file name length). 221 { 222 "test", "64", 223 "mutate9(&(0x7f0000000000)='./file0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\\x00')\n", 224 0, 225 func(p *Prog, callIndex int) bool { 226 return p.Calls[0].Args[0].(*PointerArg).Res != nil 227 }, 228 "mutate9(&(0x7f0000000000)='./file0\\x00')\n", 229 0, 230 }, 231 // Ensure `no_minimize` calls are untouched. 232 { 233 "linux", "amd64", 234 "syz_mount_image$ext4(&(0x7f0000000000)='ext4\\x00', &(0x7f0000000100)='./file0\\x00', 0x0, &(0x7f0000010020), 0x1, 0x15, &(0x7f0000000200)=\"$eJwqrqzKTszJSS0CBAAA//8TyQPi\")\n", 235 0, 236 func(p *Prog, callIndex int) bool { 237 // Anything is allowed except removing a call. 238 return len(p.Calls) > 0 239 }, 240 "syz_mount_image$ext4(&(0x7f0000000000)='ext4\\x00', &(0x7f0000000100)='./file0\\x00', 0x0, &(0x7f0000010020), 0x1, 0x15, &(0x7f0000000200)=\"$eJwqrqzKTszJSS0CBAAA//8TyQPi\")\n", 241 0, 242 }, 243 } 244 t.Parallel() 245 for ti, test := range tests { 246 target, err := GetTarget(test.os, test.arch) 247 if err != nil { 248 t.Fatal(err) 249 } 250 p, err := target.Deserialize([]byte(test.orig), Strict) 251 if err != nil { 252 t.Fatalf("failed to deserialize original program #%v: %v", ti, err) 253 } 254 p1, ci := Minimize(p, test.callIndex, false, test.pred) 255 res := p1.Serialize() 256 if string(res) != test.result { 257 t.Fatalf("minimization produced wrong result #%v\norig:\n%v\nexpect:\n%v\ngot:\n%v", 258 ti, test.orig, test.result, string(res)) 259 } 260 if ci != test.resultCallIndex { 261 t.Fatalf("minimization broke call index #%v: got %v, want %v", 262 ti, ci, test.resultCallIndex) 263 } 264 } 265 } 266 267 func TestMinimizeRandom(t *testing.T) { 268 target, rs, iters := initTest(t) 269 iters /= 10 // Long test. 270 ct := target.DefaultChoiceTable() 271 r := rand.New(rs) 272 for i := 0; i < iters; i++ { 273 for _, crash := range []bool{false, true} { 274 p := target.Generate(rs, 5, ct) 275 copyP := p.Clone() 276 minP, _ := Minimize(p, len(p.Calls)-1, crash, func(p1 *Prog, callIndex int) bool { 277 if r.Intn(2) == 0 { 278 return false 279 } 280 copyP = p1.Clone() 281 return true 282 }) 283 got := string(minP.Serialize()) 284 want := string(copyP.Serialize()) 285 if got != want { 286 t.Fatalf("program:\n%s\ngot:\n%v\nwant:\n%s", string(p.Serialize()), got, want) 287 } 288 } 289 } 290 } 291 292 func TestMinimizeCallIndex(t *testing.T) { 293 target, rs, iters := initTest(t) 294 ct := target.DefaultChoiceTable() 295 r := rand.New(rs) 296 for i := 0; i < iters; i++ { 297 p := target.Generate(rs, 5, ct) 298 ci := r.Intn(len(p.Calls)) 299 p1, ci1 := Minimize(p, ci, r.Intn(2) == 0, func(p1 *Prog, callIndex int) bool { 300 return r.Intn(2) == 0 301 }) 302 if ci1 < 0 || ci1 >= len(p1.Calls) || p.Calls[ci].Meta.Name != p1.Calls[ci1].Meta.Name { 303 t.Fatalf("bad call index after minimization") 304 } 305 } 306 }