github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/prog/expr_test.go (about) 1 // Copyright 2023 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 "strings" 10 "testing" 11 12 "github.com/stretchr/testify/assert" 13 "github.com/stretchr/testify/require" 14 ) 15 16 func TestGenerateConditionalFields(t *testing.T) { 17 // Ensure that we reach different combinations of conditional fields. 18 target, rs, _ := initRandomTargetTest(t, "test", "64") 19 ct := target.DefaultChoiceTable() 20 r := newRand(target, rs) 21 22 combinations := [][]bool{ 23 {false, false}, 24 {false, false}, 25 } 26 b2i := func(b bool) int { 27 if b { 28 return 1 29 } 30 return 0 31 } 32 for i := 0; i < 150; i++ { 33 p := genConditionalFieldProg(target, ct, r) 34 f1, f2 := parseConditionalStructCall(t, p.Calls[len(p.Calls)-1]) 35 combinations[b2i(f1)][b2i(f2)] = true 36 } 37 for _, first := range []int{0, 1} { 38 for _, second := range []int{0, 1} { 39 if !combinations[first][second] { 40 t.Fatalf("did not generate a combination f1=%v f2=%v", first, second) 41 } 42 } 43 } 44 } 45 46 func TestConditionalResources(t *testing.T) { 47 // Let's stress test the code and rely on various internal checks. 48 target, rs, _ := initRandomTargetTest(t, "test", "64") 49 ct := target.BuildChoiceTable(nil, map[*Syscall]bool{ 50 target.SyscallMap["test$create_cond_resource"]: true, 51 target.SyscallMap["test$use_cond_resource"]: true, 52 }) 53 iters := 500 54 if testing.Short() { 55 iters /= 10 56 } 57 for i := 0; i < iters; i++ { 58 p := target.Generate(rs, 10, ct) 59 p.Mutate(rs, 10, ct, nil, nil) 60 } 61 } 62 63 func TestMutateConditionalFields(t *testing.T) { 64 target, rs, _ := initRandomTargetTest(t, "test", "64") 65 ct := target.DefaultChoiceTable() 66 r := newRand(target, rs) 67 iters := 500 68 if testing.Short() { 69 iters /= 10 70 } 71 nonAny := 0 72 for i := 0; i < iters; i++ { 73 prog := genConditionalFieldProg(target, ct, r) 74 for j := 0; j < 5; j++ { 75 prog.Mutate(rs, 10, ct, nil, nil) 76 hasAny := bytes.Contains(prog.Serialize(), []byte("ANY=")) 77 if hasAny { 78 // No sense to verify these. 79 break 80 } 81 nonAny++ 82 validateConditionalProg(t, prog) 83 } 84 } 85 assert.Greater(t, nonAny, 10) // Just in case. 86 } 87 88 func TestEvaluateConditionalFields(t *testing.T) { 89 target := InitTargetTest(t, "test", "64") 90 tests := []struct { 91 good []string 92 bad []string 93 }{ 94 { 95 good: []string{ 96 `test$conditional_struct(&AUTO={0x0, @void, @void, @void})`, 97 `test$conditional_struct(&AUTO={0x2, @value={AUTO}, @void, @value=0x456})`, 98 `test$conditional_struct(&AUTO={0x4, @void, @value=0x123, @value=0x456})`, 99 `test$conditional_struct(&AUTO={0x6, @value={AUTO}, @value=0x123, @void})`, 100 }, 101 bad: []string{ 102 `test$conditional_struct(&AUTO={0x0, @void, @void, @value=0x123})`, 103 `test$conditional_struct(&AUTO={0x0, @value={AUTO}, @void, @value=0x123})`, 104 `test$conditional_struct(&AUTO={0x0, @void, @value=0x123, @void})`, 105 `test$conditional_struct(&AUTO={0x0, @void, @void, @value=0x123})`, 106 `test$conditional_struct(&AUTO={0x2, @value={AUTO}, @void, @void})`, 107 `test$conditional_struct(&AUTO={0x2, @value={AUTO}, @value=0x123, @void})`, 108 `test$conditional_struct(&AUTO={0x4, @void, @value=0x123, @void})`, 109 `test$conditional_struct(&AUTO={0x4, @void, @void, @value=0x123})`, 110 }, 111 }, 112 { 113 good: []string{ 114 `test$parent_conditions(&AUTO={0x0, @without_flag1=0x123, {0x0, @void}})`, 115 `test$parent_conditions(&AUTO={0x2, @with_flag1=0x123, {0x0, @void}})`, 116 `test$parent_conditions(&AUTO={0x4, @without_flag1=0x123, {0x0, @value=0x0}})`, 117 `test$parent_conditions(&AUTO={0x6, @with_flag1=0x123, {0x0, @value=0x0}})`, 118 // The @without_flag1 option is still possible. 119 `test$parent_conditions(&AUTO={0x2, @without_flag1=0x123, {0x0, @void}})`, 120 }, 121 bad: []string{ 122 `test$parent_conditions(&AUTO={0x0, @with_flag1=0x123, {0x0, @void}})`, 123 `test$parent_conditions(&AUTO={0x4, @with_flag1=0x123, {0x0, @void}})`, 124 `test$parent_conditions(&AUTO={0x4, @with_flag1=0x123, {0x0, @value=0x0}})`, 125 }, 126 }, 127 } 128 129 for i, test := range tests { 130 t.Run(fmt.Sprintf("%d", i), func(tt *testing.T) { 131 for _, good := range test.good { 132 _, err := target.Deserialize([]byte(good), Strict) 133 assert.NoError(tt, err) 134 } 135 for _, bad := range test.bad { 136 _, err := target.Deserialize([]byte(bad), Strict) 137 assert.ErrorIs(tt, err, ErrViolatedConditions, 138 "prog: %s", bad) 139 } 140 }) 141 } 142 } 143 144 func TestConditionalMinimize(t *testing.T) { 145 tests := []struct { 146 mode MinimizeMode 147 input string 148 pred func(*Prog, int) bool 149 output string 150 }{ 151 { 152 input: `test$conditional_struct(&AUTO={0x6, @value={AUTO}, @value=0x123, @void})`, 153 pred: func(p *Prog, _ int) bool { 154 return len(p.Calls) == 1 && p.Calls[0].Meta.Name == `test$conditional_struct` 155 }, 156 output: `test$conditional_struct(0x0)`, 157 }, 158 { 159 input: `test$conditional_struct(&(0x7f0000000040)={0x6, @value, @value=0x123, @void})`, 160 pred: func(p *Prog, _ int) bool { 161 return bytes.Contains(p.Serialize(), []byte("0x123")) 162 }, 163 // We don't drop individual bits from integers, so there's no chance 164 // to turn 0x6 into 0x4. 165 output: `test$conditional_struct(&(0x7f0000000040)={0x6, @value, @value=0x123})`, 166 }, 167 { 168 mode: MinimizeCrashSnapshot, 169 input: `test$conditional_struct_minimize(&(0x7f0000000040)={0x1, @value=0xaa, 0x1, @value=0xbb})`, 170 pred: func(p *Prog, _ int) bool { 171 return bytes.Contains(p.Serialize(), []byte("0xaa")) 172 }, 173 output: `test$conditional_struct_minimize(&(0x7f0000000040)={0x1, @value=0xaa})`, 174 }, 175 { 176 mode: MinimizeCrashSnapshot, 177 input: `test$conditional_struct_minimize(&(0x7f0000000040)={0x1, @value=0xaa, 0x1, @value=0xbb})`, 178 pred: func(p *Prog, _ int) bool { 179 return bytes.Contains(p.Serialize(), []byte("0xbb")) 180 }, 181 output: `test$conditional_struct_minimize(&(0x7f0000000040)={0x0, @void, 0x1, @value=0xbb})`, 182 }, 183 { 184 input: `test$conditional_struct_minimize(&(0x7f0000000040)={0x1, @value=0xaa, 0x1, @value=0xbb})`, 185 pred: func(p *Prog, _ int) bool { 186 serialized := p.Serialize() 187 return bytes.Contains(serialized, []byte("0xaa")) && 188 bytes.Contains(serialized, []byte("0xbb")) 189 }, 190 output: `test$conditional_struct_minimize(&(0x7f0000000040)={0x1, @value=0xaa, 0x1, @value=0xbb})`, 191 }, 192 } 193 194 for i, test := range tests { 195 t.Run(fmt.Sprintf("%d", i), func(tt *testing.T) { 196 target, err := GetTarget("test", "64") 197 assert.NoError(tt, err) 198 p, err := target.Deserialize([]byte(test.input), Strict) 199 assert.NoError(tt, err) 200 p1, _ := Minimize(p, 0, test.mode, test.pred) 201 res := p1.Serialize() 202 assert.Equal(tt, test.output, strings.TrimSpace(string(res))) 203 }) 204 } 205 } 206 207 func genConditionalFieldProg(target *Target, ct *ChoiceTable, r *randGen) *Prog { 208 s := newState(target, ct, nil) 209 calls := r.generateParticularCall(s, target.SyscallMap["test$conditional_struct"]) 210 return &Prog{ 211 Target: target, 212 Calls: calls, 213 } 214 } 215 216 const FLAG1 = 2 217 const FLAG2 = 4 218 219 func validateConditionalProg(t *testing.T, p *Prog) { 220 for _, call := range p.Calls { 221 if call.Meta.Name == "test$conditional_struct" { 222 parseConditionalStructCall(t, call) 223 } 224 } 225 } 226 227 // Validates a test$conditional_struct call. 228 func parseConditionalStructCall(t *testing.T, c *Call) (bool, bool) { 229 if c.Meta.Name != "test$conditional_struct" { 230 t.Fatalf("generated wrong call %v", c.Meta.Name) 231 } 232 if len(c.Args) != 1 { 233 t.Fatalf("generated wrong number of args %v", len(c.Args)) 234 } 235 va, ok := c.Args[0].(*PointerArg) 236 if !ok { 237 t.Fatalf("expected PointerArg: %v", c.Args[0]) 238 } 239 if va.Res == nil { 240 // Cannot validate. 241 return false, false 242 } 243 ga, ok := va.Res.(*GroupArg) 244 if !ok { 245 t.Fatalf("expected GroupArg: %v", va.Res) 246 } 247 if len(ga.Inner) != 4 { 248 t.Fatalf("wrong number of struct args %v", len(ga.Inner)) 249 } 250 mask := ga.Inner[0].(*ConstArg).Val 251 f1 := ga.Inner[1].(*UnionArg).Index == 0 252 f2 := ga.Inner[2].(*UnionArg).Index == 0 253 assert.Equal(t, mask&FLAG1 != 0, f1, "flag1 must only be set if mask&FLAG1") 254 assert.Equal(t, mask&FLAG2 != 0, f2, "flag2 must only be set if mask&FLAG2") 255 return f1, f2 256 } 257 258 func TestConditionalUnionFields(t *testing.T) { 259 // Ensure that we reach different combinations of conditional fields. 260 target, rs, _ := initRandomTargetTest(t, "test", "64") 261 ct := target.DefaultChoiceTable() 262 r := newRand(target, rs) 263 264 var zeroU1, zeroU2 int 265 var nonzeroU2 int 266 for i := 0; i < 100; i++ { 267 s := newState(target, ct, nil) 268 p := &Prog{ 269 Target: target, 270 Calls: r.generateParticularCall(s, target.SyscallMap["test$conditional_union"]), 271 } 272 if len(p.Calls) > 1 { 273 continue 274 } 275 text := string(p.SerializeVerbose()) 276 if strings.Contains(text, "{0x0,") { 277 if strings.Contains(text, "@u1") { 278 zeroU1++ 279 } else if strings.Contains(text, "@u2") { 280 zeroU2++ 281 } 282 } else { 283 assert.NotContains(t, text, "@u1") 284 nonzeroU2++ 285 } 286 } 287 assert.Greater(t, zeroU1, 0) 288 assert.Greater(t, zeroU2, 0) 289 assert.Greater(t, nonzeroU2, 0) 290 } 291 292 func TestNestedConditionalCall(t *testing.T) { 293 // Ensure that we reach different combinations of conditional fields. 294 target, rs, _ := initRandomTargetTest(t, "test", "64") 295 ct := target.DefaultChoiceTable() 296 r := newRand(target, rs) 297 298 for i := 0; i < 100; i++ { 299 for _, name := range []string{"test$conditional_struct_nested", "test$conditional_struct_nested2"} { 300 s := newState(target, ct, nil) 301 calls := r.generateParticularCall(s, target.SyscallMap[name]) 302 p := &Prog{ 303 Target: target, 304 Calls: calls, 305 } 306 err := p.checkConditions() 307 if err != nil { 308 t.Fatal(err) 309 } 310 } 311 } 312 } 313 314 func TestDefaultConditionalSerialize(t *testing.T) { 315 // Serialize() omits default-valued fields for a more compact representation, 316 // but that shouldn't mess with the selected option (see #6105). 317 const rawProg = "test$parent_conditions(&(0x7f0000000000)={0xa3})\n" 318 target := initTargetTest(t, "test", "64") 319 prog, err := target.Deserialize([]byte(rawProg), NonStrict) 320 require.NoError(t, err) 321 serialized := prog.Serialize() 322 prog2, err := target.Deserialize(serialized, NonStrict) 323 require.NoError(t, err) 324 assert.Equal(t, rawProg, string(prog2.Serialize())) 325 }