github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/pkg/csource/csource_test.go (about) 1 // Copyright 2015 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 csource 5 6 import ( 7 "fmt" 8 "math/rand" 9 "os" 10 "regexp" 11 "runtime" 12 "strings" 13 "sync/atomic" 14 "testing" 15 16 "github.com/google/syzkaller/executor" 17 "github.com/google/syzkaller/pkg/testutil" 18 "github.com/google/syzkaller/prog" 19 _ "github.com/google/syzkaller/sys" 20 "github.com/google/syzkaller/sys/targets" 21 "github.com/stretchr/testify/assert" 22 ) 23 24 func init() { 25 // csource tests consume too much memory under race detector (>1GB), 26 // and periodically timeout on Travis. So we skip them. 27 if testutil.RaceEnabled { 28 for _, arg := range os.Args[1:] { 29 if strings.Contains(arg, "-test.short") { 30 fmt.Printf("skipping race testing in short mode\n") 31 os.Exit(0) 32 } 33 } 34 } 35 } 36 37 func TestGenerate(t *testing.T) { 38 t.Parallel() 39 checked := make(map[string]bool) 40 for _, target := range prog.AllTargets() { 41 target := target 42 sysTarget := targets.Get(target.OS, target.Arch) 43 if runtime.GOOS != sysTarget.BuildOS { 44 continue 45 } 46 t.Run(target.OS+"/"+target.Arch, func(t *testing.T) { 47 full := !checked[target.OS] 48 if !full && testing.Short() { 49 return 50 } 51 if err := sysTarget.BrokenCompiler; err != "" { 52 t.Skipf("target compiler is broken: %v", err) 53 } 54 checked[target.OS] = true 55 t.Parallel() 56 testTarget(t, target, full) 57 testPseudoSyscalls(t, target) 58 }) 59 } 60 } 61 62 func testPseudoSyscalls(t *testing.T, target *prog.Target) { 63 // Use options that are as minimal as possible. 64 // We want to ensure that the code can always be compiled. 65 opts := Options{ 66 Slowdown: 1, 67 } 68 rs := testutil.RandSource(t) 69 for _, meta := range target.PseudoSyscalls() { 70 p := target.GenSampleProg(meta, rs) 71 t.Run(fmt.Sprintf("single_%s", meta.CallName), func(t *testing.T) { 72 t.Parallel() 73 testOne(t, p, opts) 74 }) 75 } 76 } 77 78 func testTarget(t *testing.T, target *prog.Target, full bool) { 79 rs := testutil.RandSource(t) 80 p := target.Generate(rs, 10, target.DefaultChoiceTable()) 81 // Turns out that fully minimized program can trigger new interesting warnings, 82 // e.g. about NULL arguments for functions that require non-NULL arguments in syz_ functions. 83 // We could append both AllSyzProg as-is and a minimized version of it, 84 // but this makes the NULL argument warnings go away (they showed up in ".constprop" versions). 85 // Testing 2 programs takes too long since we have lots of options permutations and OS/arch. 86 // So we use the as-is in short tests and minimized version in full tests. 87 syzProg := target.GenerateAllSyzProg(rs) 88 var opts []Options 89 if !full || testing.Short() { 90 p.Calls = append(p.Calls, syzProg.Calls...) 91 opts = allOptionsSingle(target.OS) 92 opts = append(opts, ExecutorOpts) 93 } else { 94 minimized, _ := prog.Minimize(syzProg, -1, false, func(p *prog.Prog, call int) bool { 95 return len(p.Calls) == len(syzProg.Calls) 96 }) 97 p.Calls = append(p.Calls, minimized.Calls...) 98 opts = allOptionsPermutations(target.OS) 99 } 100 // Test various call properties. 101 if len(p.Calls) > 0 { 102 p.Calls[0].Props.FailNth = 1 103 } 104 if len(p.Calls) > 1 { 105 p.Calls[1].Props.Async = true 106 } 107 if len(p.Calls) > 2 { 108 p.Calls[2].Props.Rerun = 4 109 } 110 for opti, opts := range opts { 111 if testing.Short() && opts.HandleSegv { 112 // HandleSegv can radically increase compilation time/memory consumption on large programs. 113 // For example, for one program captured from this test enabling HandleSegv increases 114 // compilation time from 1.94s to 104.73s and memory consumption from 136MB to 8116MB. 115 continue 116 } 117 opts := opts 118 t.Run(fmt.Sprintf("%v", opti), func(t *testing.T) { 119 t.Parallel() 120 testOne(t, p, opts) 121 }) 122 } 123 } 124 125 var failedTests uint32 126 127 func testOne(t *testing.T, p *prog.Prog, opts Options) { 128 // Each failure produces lots of output (including full C source). 129 // Frequently lots of tests fail at the same, which produces/tmp/log 130 // tens of thounds of lines of output. Limit amount of output. 131 maxFailures := uint32(10) 132 if os.Getenv("CI") != "" { 133 maxFailures = 1 134 } 135 if atomic.LoadUint32(&failedTests) > maxFailures { 136 return 137 } 138 src, err := Write(p, opts) 139 if err != nil { 140 if atomic.AddUint32(&failedTests, 1) > maxFailures { 141 t.Fatal() 142 } 143 t.Logf("opts: %+v\nprogram:\n%s", opts, p.Serialize()) 144 t.Fatalf("%v", err) 145 } 146 bin, err := Build(p.Target, src) 147 if err != nil { 148 if atomic.AddUint32(&failedTests, 1) > maxFailures { 149 t.Fatal() 150 } 151 t.Logf("opts: %+v\nprogram:\n%s", opts, p.Serialize()) 152 t.Fatalf("%v", err) 153 } 154 defer os.Remove(bin) 155 } 156 157 func TestExecutorMacros(t *testing.T) { 158 // Ensure that executor does not mis-spell any of the SYZ_* macros. 159 target, _ := prog.GetTarget(targets.TestOS, targets.TestArch64) 160 p := target.Generate(rand.NewSource(0), 1, target.DefaultChoiceTable()) 161 expected := commonDefines(p, Options{}) 162 expected["SYZ_EXECUTOR"] = true 163 expected["SYZ_HAVE_SETUP_LOOP"] = true 164 expected["SYZ_HAVE_RESET_LOOP"] = true 165 expected["SYZ_HAVE_SETUP_TEST"] = true 166 expected["SYZ_TEST_COMMON_EXT_EXAMPLE"] = true 167 macros := regexp.MustCompile("SYZ_[A-Za-z0-9_]+").FindAllString(string(executor.CommonHeader), -1) 168 for _, macro := range macros { 169 if strings.HasPrefix(macro, "SYZ_HAVE_") { 170 continue 171 } 172 if _, ok := expected[macro]; !ok { 173 t.Errorf("unexpected macro: %v", macro) 174 } 175 } 176 } 177 178 func TestSource(t *testing.T) { 179 t.Parallel() 180 target, err := prog.GetTarget(targets.TestOS, targets.TestArch64) 181 if err != nil { 182 t.Fatal(err) 183 } 184 type Test struct { 185 input string 186 output string 187 } 188 tests := []Test{ 189 { 190 input: ` 191 r0 = csource0(0x1) 192 csource1(r0) 193 `, 194 output: ` 195 res = syscall(SYS_csource0, /*num=*/1); 196 if (res != -1) 197 r[0] = res; 198 syscall(SYS_csource1, /*fd=*/r[0]); 199 `, 200 }, 201 { 202 input: ` 203 csource2(&AUTO="12345678") 204 csource3(&AUTO) 205 csource4(&AUTO) 206 csource5(&AUTO) 207 csource6(&AUTO) 208 `, 209 output: fmt.Sprintf(` 210 NONFAILING(memcpy((void*)0x%x, "\x12\x34\x56\x78", 4)); 211 syscall(SYS_csource2, /*buf=*/0x%xul); 212 NONFAILING(memset((void*)0x%x, 0, 10)); 213 syscall(SYS_csource3, /*buf=*/0x%xul); 214 NONFAILING(memset((void*)0x%x, 48, 10)); 215 syscall(SYS_csource4, /*buf=*/0x%xul); 216 NONFAILING(memcpy((void*)0x%x, "0101010101", 10)); 217 syscall(SYS_csource5, /*buf=*/0x%xul); 218 NONFAILING(memcpy((void*)0x%x, "101010101010", 12)); 219 syscall(SYS_csource6, /*buf=*/0x%xul); 220 `, 221 target.DataOffset+0x40, target.DataOffset+0x40, 222 target.DataOffset+0x80, target.DataOffset+0x80, 223 target.DataOffset+0xc0, target.DataOffset+0xc0, 224 target.DataOffset+0x100, target.DataOffset+0x100, 225 target.DataOffset+0x140, target.DataOffset+0x140), 226 }, 227 { 228 input: ` 229 csource7(0x0) 230 csource7(0x1) 231 csource7(0x2) 232 csource7(0x3) 233 csource7(0x4) 234 csource7(0x5) 235 `, 236 output: ` 237 syscall(SYS_csource7, /*flag=*/0ul); 238 syscall(SYS_csource7, /*flag=BIT_0*/1ul); 239 syscall(SYS_csource7, /*flag=BIT_1*/2ul); 240 syscall(SYS_csource7, /*flag=BIT_0_AND_1*/3ul); 241 syscall(SYS_csource7, /*flag=*/4ul); 242 syscall(SYS_csource7, /*flag=BIT_0|0x4*/5ul); 243 `, 244 }, 245 } 246 for i, test := range tests { 247 t.Run(fmt.Sprint(i), func(t *testing.T) { 248 p, err := target.Deserialize([]byte(test.input), prog.Strict) 249 if err != nil { 250 t.Fatal(err) 251 } 252 ctx := &context{ 253 p: p, 254 target: target, 255 sysTarget: targets.Get(target.OS, target.Arch), 256 } 257 calls, _, err := ctx.generateProgCalls(p, false) 258 if err != nil { 259 t.Fatal(err) 260 } 261 got := regexp.MustCompile(`(\n|^)\t`).ReplaceAllString(strings.Join(calls, ""), "\n") 262 if test.output != got { 263 t.Fatalf("input:\n%v\nwant:\n%v\ngot:\n%v", test.input, test.output, got) 264 } 265 }) 266 } 267 } 268 269 func generateSandboxFunctionSignatureTestCase(t *testing.T, sandbox string, sandboxArg int, expected, message string) { 270 actual := generateSandboxFunctionSignature(sandbox, sandboxArg) 271 assert.Equal(t, actual, expected, message) 272 } 273 274 func TestGenerateSandboxFunctionSignature(t *testing.T) { 275 // This test-case intentionally omits the following edge cases: 276 // - sandbox name as whitespaces, tabs 277 // - control chars \r, \n and unprintables 278 // - unsuitable chars - punctuation, emojis, '#', '*', etc 279 // - character case mismatching function prototype defined in common_linux.h. 280 // For example 'do_sandbox_android' and 'AnDroid'. 281 // - non english letters, unicode compound characters 282 // and focuses on correct handling of sandboxes supporting and not 'sandbox_arg' 283 // config setting. 284 generateSandboxFunctionSignatureTestCase(t, 285 "", // sandbox name 286 0, // sandbox arg 287 "loop();", // expected 288 "Empty sandbox name should produce 'loop();'") 289 290 generateSandboxFunctionSignatureTestCase(t, 291 "abrakadabra", // sandbox name 292 0, // sandbox arg 293 "do_sandbox_abrakadabra();", // expected 294 "Empty sandbox name should produce 'loop();'") 295 296 generateSandboxFunctionSignatureTestCase(t, 297 "android", // sandbox name 298 -1234, // sandbox arg 299 "do_sandbox_android(-1234);", // expected 300 "Android sandbox function requires an argument") 301 }