blob: 1bc89b87ce69ff3e04ba48499285e74786f570eb [file] [log] [blame]
Jonathan Metzmand5ee1c62018-11-05 10:33:08 -08001# Copyright 2018 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""Unit tests for cros_fuzz."""
6
Chris McDonald59650c32021-07-20 15:29:28 -06007import logging
Jonathan Metzmand5ee1c62018-11-05 10:33:08 -08008import os
9
Jonathan Metzmand5ee1c62018-11-05 10:33:08 -080010from chromite.lib import cros_test_lib
11from chromite.scripts import cros_fuzz
12
Mike Frysinger03b983f2020-02-21 02:31:49 -050013
Jonathan Metzmand5ee1c62018-11-05 10:33:08 -080014DEFAULT_MAX_TOTAL_TIME_OPTION = cros_fuzz.GetLibFuzzerOption(
Alex Klein1699fab2022-09-08 08:46:06 -060015 cros_fuzz.MAX_TOTAL_TIME_OPTION_NAME, cros_fuzz.MAX_TOTAL_TIME_DEFAULT_VALUE
16)
Jonathan Metzmand5ee1c62018-11-05 10:33:08 -080017
Alex Klein1699fab2022-09-08 08:46:06 -060018FUZZ_TARGET = "fuzzer"
Jonathan Metzmand5ee1c62018-11-05 10:33:08 -080019FUZZER_COVERAGE_PATH = (
Alex Klein1699fab2022-09-08 08:46:06 -060020 "/build/amd64-generic/tmp/fuzz/coverage-report/%s" % FUZZ_TARGET
21)
Jonathan Metzmand5ee1c62018-11-05 10:33:08 -080022
Alex Klein1699fab2022-09-08 08:46:06 -060023BOARD = "amd64-generic"
Jonathan Metzmand5ee1c62018-11-05 10:33:08 -080024
25
26class SysrootPathTest(cros_test_lib.TestCase):
Alex Klein1699fab2022-09-08 08:46:06 -060027 """Tests the SysrootPath class."""
Jonathan Metzmand5ee1c62018-11-05 10:33:08 -080028
Alex Klein1699fab2022-09-08 08:46:06 -060029 def setUp(self):
30 self.path_to_sysroot = _SetPathToSysroot()
31 self.sysroot_relative_path = "/dir"
32 self.basename = os.path.basename(self.sysroot_relative_path)
33 # Chroot relative path of a path that is in the sysroot.
34 self.path_in_sysroot = os.path.join(self.path_to_sysroot, self.basename)
Jonathan Metzmand5ee1c62018-11-05 10:33:08 -080035
Alex Klein1699fab2022-09-08 08:46:06 -060036 def testSysroot(self):
37 """Tests that SysrootPath.sysroot returns expected result."""
38 sysroot_path = cros_fuzz.SysrootPath(self.sysroot_relative_path)
39 self.assertEqual(self.sysroot_relative_path, sysroot_path.sysroot)
Jonathan Metzmand5ee1c62018-11-05 10:33:08 -080040
Alex Klein1699fab2022-09-08 08:46:06 -060041 def testChroot(self):
42 """Tests that SysrootPath.chroot returns expected result."""
43 sysroot_path = cros_fuzz.SysrootPath(self.sysroot_relative_path)
44 expected = os.path.join(self.path_to_sysroot, self.basename)
45 self.assertEqual(expected, sysroot_path.chroot)
Jonathan Metzmand5ee1c62018-11-05 10:33:08 -080046
Alex Klein1699fab2022-09-08 08:46:06 -060047 def testIsSysrootPath(self):
48 """Tests that the IsSysrootPath can tell what is in the sysroot."""
49 self.assertTrue(
50 cros_fuzz.SysrootPath.IsPathInSysroot(self.path_to_sysroot)
51 )
52 self.assertTrue(
53 cros_fuzz.SysrootPath.IsPathInSysroot(self.path_in_sysroot)
54 )
55 path_not_in_sysroot_1 = os.path.join(
56 os.path.dirname(self.path_to_sysroot), self.basename
57 )
58 self.assertFalse(
59 cros_fuzz.SysrootPath.IsPathInSysroot(path_not_in_sysroot_1)
60 )
61 path_not_in_sysroot_2 = os.path.join("/dir/build/amd64-generic")
62 self.assertFalse(
63 cros_fuzz.SysrootPath.IsPathInSysroot(path_not_in_sysroot_2)
64 )
Jonathan Metzmand5ee1c62018-11-05 10:33:08 -080065
Alex Klein1699fab2022-09-08 08:46:06 -060066 def testFromChrootPathInSysroot(self):
67 """Tests that FromChrootPathInSysroot converts paths properly."""
68 # Test that it raises an assertion error when the path is not in the
69 # sysroot.
70 path_not_in_sysroot_1 = os.path.join(
71 os.path.dirname(self.path_to_sysroot), "dir"
72 )
73 with self.assertRaises(AssertionError):
74 cros_fuzz.SysrootPath.FromChrootPathInSysroot(path_not_in_sysroot_1)
Jonathan Metzmand5ee1c62018-11-05 10:33:08 -080075
Alex Klein1699fab2022-09-08 08:46:06 -060076 sysroot_path = cros_fuzz.SysrootPath.FromChrootPathInSysroot(
77 self.path_in_sysroot
78 )
79 self.assertEqual(self.sysroot_relative_path, sysroot_path)
Jonathan Metzmand5ee1c62018-11-05 10:33:08 -080080
81
82class GetPathForCopyTest(cros_test_lib.TestCase):
Alex Klein1699fab2022-09-08 08:46:06 -060083 """Tests GetPathForCopy."""
Jonathan Metzmand5ee1c62018-11-05 10:33:08 -080084
Alex Klein1699fab2022-09-08 08:46:06 -060085 def testGetPathForCopy(self):
86 """Test that GetPathForCopy gives us the correct sysroot directory."""
87 _SetPathToSysroot()
88 directory = "/path/to/directory"
89 parent = "parent"
90 child = os.path.basename(directory)
91 path_to_sysroot = cros_fuzz.SysrootPath.path_to_sysroot
92 storage_directory = cros_fuzz.SCRIPT_STORAGE_DIRECTORY
Jonathan Metzmand5ee1c62018-11-05 10:33:08 -080093
Alex Klein1699fab2022-09-08 08:46:06 -060094 sysroot_path = cros_fuzz.GetPathForCopy(parent, child)
Jonathan Metzmand5ee1c62018-11-05 10:33:08 -080095
Alex Klein1699fab2022-09-08 08:46:06 -060096 expected_chroot_path = os.path.join(
97 path_to_sysroot, "tmp", storage_directory, parent, child
98 )
99 self.assertEqual(expected_chroot_path, sysroot_path.chroot)
Jonathan Metzmand5ee1c62018-11-05 10:33:08 -0800100
Alex Klein1699fab2022-09-08 08:46:06 -0600101 expected_sysroot_path = os.path.join(
102 "/", "tmp", storage_directory, parent, child
103 )
104 self.assertEqual(expected_sysroot_path, sysroot_path.sysroot)
Jonathan Metzmand5ee1c62018-11-05 10:33:08 -0800105
106
107class GetLibFuzzerOptionTest(cros_test_lib.TestCase):
Alex Klein1699fab2022-09-08 08:46:06 -0600108 """Tests GetLibFuzzerOption."""
Jonathan Metzmand5ee1c62018-11-05 10:33:08 -0800109
Alex Klein1699fab2022-09-08 08:46:06 -0600110 def testGetLibFuzzerOption(self):
111 """Tests that GetLibFuzzerOption returns a correct libFuzzer option."""
112 expected = "-max_total_time=60"
113 self.assertEqual(
114 expected, cros_fuzz.GetLibFuzzerOption("max_total_time", 60)
115 )
Jonathan Metzmand5ee1c62018-11-05 10:33:08 -0800116
117
118class LimitFuzzingTest(cros_test_lib.TestCase):
Alex Klein1699fab2022-09-08 08:46:06 -0600119 """Tests LimitFuzzing."""
Jonathan Metzmand5ee1c62018-11-05 10:33:08 -0800120
Alex Klein1699fab2022-09-08 08:46:06 -0600121 def setUp(self):
122 self.fuzz_command = ["./fuzzer", "-rss_limit_mb=4096"]
123 self.corpus = None
Jonathan Metzmand5ee1c62018-11-05 10:33:08 -0800124
Alex Klein1699fab2022-09-08 08:46:06 -0600125 def _Helper(self, expected_command=None):
126 """Calls LimitFuzzing and asserts fuzz_command equals |expected_command|.
Jonathan Metzmand5ee1c62018-11-05 10:33:08 -0800127
Alex Klein1699fab2022-09-08 08:46:06 -0600128 If |expected| is None, then it is set to self.fuzz_command before calling
129 LimitFuzzing.
130 """
131 if expected_command is None:
132 expected_command = self.fuzz_command[:]
133 cros_fuzz.LimitFuzzing(self.fuzz_command, self.corpus)
134 self.assertEqual(expected_command, self.fuzz_command)
Jonathan Metzmand5ee1c62018-11-05 10:33:08 -0800135
Alex Klein1699fab2022-09-08 08:46:06 -0600136 def testCommandHasMaxTotalTime(self):
137 """Tests that no limit is added when user specifies -max_total_time."""
138 self.fuzz_command.append("-max_total_time=60")
139 self._Helper()
Jonathan Metzmand5ee1c62018-11-05 10:33:08 -0800140
Alex Klein1699fab2022-09-08 08:46:06 -0600141 def testCommandHasRuns(self):
142 """Tests that no limit is added when user specifies -runs"""
143 self.fuzz_command.append("-runs=1")
144 self._Helper()
Jonathan Metzmand5ee1c62018-11-05 10:33:08 -0800145
Alex Klein1699fab2022-09-08 08:46:06 -0600146 def testCommandHasCorpus(self):
147 """Tests that a limit is added when user specifies a corpus."""
148 self.corpus = "corpus"
149 expected = self.fuzz_command + ["-runs=0"]
150 self._Helper(expected)
Jonathan Metzmand5ee1c62018-11-05 10:33:08 -0800151
Alex Klein1699fab2022-09-08 08:46:06 -0600152 def testNoLimitOrCorpus(self):
153 """Tests that a limit is added when user specifies no corpus or limit."""
154 expected = self.fuzz_command + [DEFAULT_MAX_TOTAL_TIME_OPTION]
155 self._Helper(expected)
Jonathan Metzmand5ee1c62018-11-05 10:33:08 -0800156
157
158class RunSysrootCommandMockTestCase(cros_test_lib.MockTestCase):
Alex Klein1699fab2022-09-08 08:46:06 -0600159 """Class for TestCases that call RunSysrootCommand."""
Jonathan Metzmand5ee1c62018-11-05 10:33:08 -0800160
Alex Klein1699fab2022-09-08 08:46:06 -0600161 def setUp(self):
162 _SetPathToSysroot()
163 self.expected_command = None
164 self.expected_extra_env = None
165 self.PatchObject(
166 cros_fuzz,
167 "RunSysrootCommand",
168 side_effect=self.MockedRunSysrootCommand,
169 )
Jonathan Metzmand5ee1c62018-11-05 10:33:08 -0800170
Alex Klein1699fab2022-09-08 08:46:06 -0600171 def MockedRunSysrootCommand(
172 self, command, extra_env=None, **kwargs
173 ): # pylint: disable=unused-argument
174 """The mocked version of RunSysrootCommand.
Jonathan Metzmand5ee1c62018-11-05 10:33:08 -0800175
Alex Klein1699fab2022-09-08 08:46:06 -0600176 Asserts |command| and |extra_env| are what is expected.
177 """
178 self.assertEqual(self.expected_command, command)
179 self.assertEqual(self.expected_extra_env, extra_env)
Jonathan Metzmand5ee1c62018-11-05 10:33:08 -0800180
181
182class RunFuzzerTest(RunSysrootCommandMockTestCase):
Alex Klein1699fab2022-09-08 08:46:06 -0600183 """Tests RunFuzzer."""
Jonathan Metzmand5ee1c62018-11-05 10:33:08 -0800184
Alex Klein1699fab2022-09-08 08:46:06 -0600185 def setUp(self):
186 self.corpus_path = None
187 self.fuzz_args = ""
188 self.testcase_path = None
189 self.expected_command = [
190 cros_fuzz.GetFuzzerSysrootPath(FUZZ_TARGET).sysroot,
191 ]
192 self.expected_extra_env = {
193 "ASAN_OPTIONS": "log_path=stderr:detect_odr_violation=0",
194 "MSAN_OPTIONS": "log_path=stderr:detect_odr_violation=0",
195 "UBSAN_OPTIONS": "log_path=stderr:detect_odr_violation=0",
196 }
Jonathan Metzmand5ee1c62018-11-05 10:33:08 -0800197
Alex Klein1699fab2022-09-08 08:46:06 -0600198 def _Helper(self):
199 """Calls RunFuzzer."""
200 cros_fuzz.RunFuzzer(
201 FUZZ_TARGET, self.corpus_path, self.fuzz_args, self.testcase_path
202 )
Jonathan Metzmand5ee1c62018-11-05 10:33:08 -0800203
Alex Klein1699fab2022-09-08 08:46:06 -0600204 def testNoOptional(self):
205 """Tests correct command and env used when not specifying optional."""
206 self.expected_command.append(DEFAULT_MAX_TOTAL_TIME_OPTION)
207 self._Helper()
Jonathan Metzmand5ee1c62018-11-05 10:33:08 -0800208
Alex Klein1699fab2022-09-08 08:46:06 -0600209 def testFuzzArgs(self):
210 """Tests that the correct command is used when fuzz_args is specified."""
211 fuzz_args = [DEFAULT_MAX_TOTAL_TIME_OPTION, "-fake_arg=fake_value"]
212 self.expected_command.extend(fuzz_args)
213 self.fuzz_args = " ".join(fuzz_args)
214 self._Helper()
Jonathan Metzmand5ee1c62018-11-05 10:33:08 -0800215
Alex Klein1699fab2022-09-08 08:46:06 -0600216 def testTestCase(self):
217 """Tests a testcase is used when specified."""
218 self.testcase_path = "/path/to/testcase"
219 self.expected_command.append(self.testcase_path)
220 self._Helper()
Jonathan Metzmand5ee1c62018-11-05 10:33:08 -0800221
222
223class MergeProfrawTest(RunSysrootCommandMockTestCase):
Alex Klein1699fab2022-09-08 08:46:06 -0600224 """Tests MergeProfraw."""
Jonathan Metzmand5ee1c62018-11-05 10:33:08 -0800225
Alex Klein1699fab2022-09-08 08:46:06 -0600226 def testMergeProfraw(self):
227 """Tests that MergeProfraw works as expected."""
228 # Parent class will assert that these commands are used.
229 profdata_path = cros_fuzz.GetProfdataPath(FUZZ_TARGET)
230 self.expected_command = [
231 "llvm-profdata",
232 "merge",
233 "-sparse",
234 cros_fuzz.DEFAULT_PROFRAW_PATH,
235 "-o",
236 profdata_path.sysroot,
237 ]
238 cros_fuzz.MergeProfraw(FUZZ_TARGET)
Jonathan Metzmand5ee1c62018-11-05 10:33:08 -0800239
240
241class GenerateCoverageReportTest(cros_test_lib.RunCommandTestCase):
Alex Klein1699fab2022-09-08 08:46:06 -0600242 """Tests GenerateCoverageReport."""
Jonathan Metzmand5ee1c62018-11-05 10:33:08 -0800243
Alex Klein1699fab2022-09-08 08:46:06 -0600244 def setUp(self):
245 _SetPathToSysroot()
246 self.fuzzer_path = cros_fuzz.GetFuzzerSysrootPath(FUZZ_TARGET).chroot
247 self.profdata_path = cros_fuzz.GetProfdataPath(FUZZ_TARGET)
Jonathan Metzmand5ee1c62018-11-05 10:33:08 -0800248
Alex Klein1699fab2022-09-08 08:46:06 -0600249 def testWithSharedLibraries(self):
250 """Tests that right command is used when specifying shared libraries."""
251 shared_libraries = ["shared_lib.so"]
252 cros_fuzz.GenerateCoverageReport(FUZZ_TARGET, shared_libraries)
253 instr_profile_option = "-instr-profile=%s" % self.profdata_path.chroot
254 output_dir_option = "-output-dir=%s" % FUZZER_COVERAGE_PATH
255 expected_command = [
256 "llvm-cov",
257 "show",
258 "-object",
259 self.fuzzer_path,
260 "-object",
261 "shared_lib.so",
262 "-format=html",
263 instr_profile_option,
264 output_dir_option,
265 ]
266 self.assertCommandCalled(
267 expected_command, stderr=True, debug_level=logging.DEBUG
268 )
Jonathan Metzmand5ee1c62018-11-05 10:33:08 -0800269
Alex Klein1699fab2022-09-08 08:46:06 -0600270 def testNoSharedLibraries(self):
271 """Tests the right coverage command is used without shared libraries."""
272 shared_libraries = []
273 cros_fuzz.GenerateCoverageReport(FUZZ_TARGET, shared_libraries)
274 instr_profile_option = "-instr-profile=%s" % self.profdata_path.chroot
275 output_dir_option = "-output-dir=%s" % FUZZER_COVERAGE_PATH
276 expected_command = [
277 "llvm-cov",
278 "show",
279 "-object",
280 self.fuzzer_path,
281 "-format=html",
282 instr_profile_option,
283 output_dir_option,
284 ]
285 self.assertCommandCalled(
286 expected_command, stderr=True, debug_level=logging.DEBUG
287 )
Jonathan Metzmand5ee1c62018-11-05 10:33:08 -0800288
289
290class RunSysrootCommandTest(cros_test_lib.RunCommandTestCase):
Alex Klein1699fab2022-09-08 08:46:06 -0600291 """Tests RunSysrootCommand."""
Jonathan Metzmand5ee1c62018-11-05 10:33:08 -0800292
Alex Klein1699fab2022-09-08 08:46:06 -0600293 def testRunSysrootCommand(self):
294 """Tests RunSysrootCommand creates a proper command to run in sysroot."""
295 command = ["./fuzz", "-rss_limit_mb=4096"]
296 sysroot_path = _SetPathToSysroot()
297 cros_fuzz.RunSysrootCommand(command)
298 expected_command = ["sudo", "--", "chroot", sysroot_path]
299 expected_command.extend(command)
300 self.assertCommandCalled(expected_command, debug_level=logging.DEBUG)
Jonathan Metzmand5ee1c62018-11-05 10:33:08 -0800301
302
303class GetBuildExtraEnvTest(cros_test_lib.TestCase):
Alex Klein1699fab2022-09-08 08:46:06 -0600304 """Tests GetBuildExtraEnv."""
Jonathan Metzmand5ee1c62018-11-05 10:33:08 -0800305
Alex Klein1699fab2022-09-08 08:46:06 -0600306 TEST_ENV_VAR = "TEST_VAR"
Jonathan Metzmand5ee1c62018-11-05 10:33:08 -0800307
Alex Klein1699fab2022-09-08 08:46:06 -0600308 def testUseAndFeaturesNotClobbered(self):
309 """Tests that values of certain environment variables are appended to."""
310 vars_and_values = {"FEATURES": "foo", "USE": "bar"}
311 for var, value in vars_and_values.items():
312 os.environ[var] = value
313 extra_env = cros_fuzz.GetBuildExtraEnv(cros_fuzz.BuildType.COVERAGE)
314 for var, value in vars_and_values.items():
315 self.assertIn(value, extra_env[var])
Jonathan Metzmand5ee1c62018-11-05 10:33:08 -0800316
Alex Klein1699fab2022-09-08 08:46:06 -0600317 def testCoverageBuild(self):
318 """Tests that a proper environment is returned for a coverage build."""
319 extra_env = cros_fuzz.GetBuildExtraEnv(cros_fuzz.BuildType.COVERAGE)
320 for expected_flag in ["fuzzer", "coverage", "asan"]:
321 self.assertIn(expected_flag, extra_env["USE"])
322 self.assertIn("noclean", extra_env["FEATURES"])
Jonathan Metzmand5ee1c62018-11-05 10:33:08 -0800323
324
325def _SetPathToSysroot():
Alex Klein1699fab2022-09-08 08:46:06 -0600326 """Calls SysrootPath.SetPathToSysroot and returns result."""
327 return cros_fuzz.SysrootPath.SetPathToSysroot(BOARD)