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