sanitycheck: native: Add option to enable UBSAN

Add option for native platform to enable undefined behaviour sanitizer.

Signed-off-by: Christian Taedcke <christian.taedcke@lemonbeat.com>
This commit is contained in:
Christian Taedcke 2020-07-06 16:00:57 +02:00 committed by Anas Nashif
commit 3dbe9f2960
3 changed files with 37 additions and 13 deletions

View file

@ -337,6 +337,7 @@ class BinaryHandler(Handler):
self.valgrind = False
self.lsan = False
self.asan = False
self.ubsan = False
self.coverage = False
def try_kill_process_by_pid(self):
@ -414,6 +415,10 @@ class BinaryHandler(Handler):
if not self.lsan:
env["ASAN_OPTIONS"] += "detect_leaks=0"
if self.ubsan:
env["UBSAN_OPTIONS"] = "log_path=stdout:halt_on_error=1:" + \
env.get("UBSAN_OPTIONS", "")
with subprocess.Popen(command, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, cwd=self.build_dir, env=env) as proc:
logger.debug("Spawning BinaryHandler Thread for %s" % self.name)
@ -1623,7 +1628,7 @@ class TestInstance(DisablePyTestCollectionMixin):
self.run = not self.build_only
return
def create_overlay(self, platform, enable_asan=False, enable_coverage=False, coverage_platform=[]):
def create_overlay(self, platform, enable_asan=False, enable_ubsan=False, enable_coverage=False, coverage_platform=[]):
# Create this in a "sanitycheck/" subdirectory otherwise this
# will pass this overlay to kconfig.py *twice* and kconfig.cmake
# will silently give that second time precedence over any
@ -1647,6 +1652,10 @@ class TestInstance(DisablePyTestCollectionMixin):
if platform.type == "native":
content = content + "\nCONFIG_ASAN=y"
if enable_ubsan:
if platform.type == "native":
content = content + "\nCONFIG_UBSAN=y"
f.write(content)
return content
@ -1896,6 +1905,7 @@ class ProjectBuilder(FilterBuilder):
self.lsan = kwargs.get('lsan', False)
self.asan = kwargs.get('asan', False)
self.ubsan = kwargs.get('ubsan', False)
self.valgrind = kwargs.get('valgrind', False)
self.extra_args = kwargs.get('extra_args', [])
self.device_testing = kwargs.get('device_testing', False)
@ -1962,6 +1972,7 @@ class ProjectBuilder(FilterBuilder):
handler.asan = self.asan
handler.valgrind = self.valgrind
handler.lsan = self.lsan
handler.ubsan = self.ubsan
handler.coverage = self.coverage
handler.binary = os.path.join(instance.build_dir, "zephyr", "zephyr.exe")
@ -2168,7 +2179,7 @@ class ProjectBuilder(FilterBuilder):
overlays = extract_overlays(args)
if (self.testcase.extra_configs or self.coverage or
self.asan):
self.asan or self.ubsan):
overlays.append(os.path.join(instance.build_dir,
"sanitycheck", "testcase_extra.conf"))
@ -2274,6 +2285,7 @@ class TestSuite(DisablePyTestCollectionMixin):
self.device_testing = False
self.fixtures = []
self.enable_coverage = False
self.enable_ubsan = False
self.enable_lsan = False
self.enable_asan = False
self.enable_valgrind = False
@ -2620,7 +2632,7 @@ class TestSuite(DisablePyTestCollectionMixin):
self.device_testing,
self.fixtures
)
instance.create_overlay(platform, self.enable_asan, self.enable_coverage, self.coverage_platform)
instance.create_overlay(platform, self.enable_asan, self.enable_ubsan, self.enable_coverage, self.coverage_platform)
instance_list.append(instance)
self.add_instances(instance_list)
@ -2815,7 +2827,7 @@ class TestSuite(DisablePyTestCollectionMixin):
self.add_instances(instance_list)
for _, case in self.instances.items():
case.create_overlay(case.platform, self.enable_asan, self.enable_coverage, self.coverage_platform)
case.create_overlay(case.platform, self.enable_asan, self.enable_ubsan, self.enable_coverage, self.coverage_platform)
self.discards = discards
self.selected_platforms = set(p.platform.name for p in self.instances.values())
@ -2876,6 +2888,7 @@ class TestSuite(DisablePyTestCollectionMixin):
test,
lsan=self.enable_lsan,
asan=self.enable_asan,
ubsan=self.enable_ubsan,
coverage=self.enable_coverage,
extra_args=self.extra_args,
device_testing=self.device_testing,

View file

@ -641,6 +641,14 @@ structure in the main Zephyr tree: boards/<arch>/<board_name>/""")
configuration and when --enable-asan is given.
""")
parser.add_argument(
"--enable-ubsan", action="store_true",
help="""Enable undefined behavior sanitizer to check for undefined
behaviour during program execution. It uses an optional runtime library
to provide better error diagnostics. This option only works with host
binaries such as those generated for the native_posix configuration.
""")
parser.add_argument("--enable-coverage", action="store_true",
help="Enable code coverage using gcov.")
@ -777,6 +785,7 @@ def main():
suite.fixtures = options.fixture
suite.enable_asan = options.enable_asan
suite.enable_lsan = options.enable_lsan
suite.enable_ubsan = options.enable_ubsan
suite.enable_coverage = options.enable_coverage
suite.enable_valgrind = options.enable_valgrind
suite.coverage_platform = options.coverage_platform

View file

@ -53,16 +53,18 @@ def test_check_build_or_run(class_testsuite, monkeypatch, all_testcases_dict, pl
assert testinstance.build_only and not testinstance.run
TESTDATA_2 = [
(True, True, ["demo_board_2"], "native", '\nCONFIG_COVERAGE=y\nCONFIG_COVERAGE_DUMP=y\nCONFIG_ASAN=y'),
(False, True, ["demo_board_2"], 'native', '\nCONFIG_COVERAGE=y\nCONFIG_COVERAGE_DUMP=y'),
(True, True, ["demo_board_2"], 'mcu', '\nCONFIG_COVERAGE=y\nCONFIG_COVERAGE_DUMP=y'),
(False, False, ["demo_board_2"], 'native', ''),
(False, True, ['demo_board_1'], 'native', ''),
(True, False, ["demo_board_2"], 'native', '\nCONFIG_ASAN=y'),
(True, True, True, ["demo_board_2"], "native", '\nCONFIG_COVERAGE=y\nCONFIG_COVERAGE_DUMP=y\nCONFIG_ASAN=y\nCONFIG_UBSAN=y'),
(True, False, True, ["demo_board_2"], "native", '\nCONFIG_COVERAGE=y\nCONFIG_COVERAGE_DUMP=y\nCONFIG_ASAN=y'),
(False, False, True, ["demo_board_2"], 'native', '\nCONFIG_COVERAGE=y\nCONFIG_COVERAGE_DUMP=y'),
(True, False, True, ["demo_board_2"], 'mcu', '\nCONFIG_COVERAGE=y\nCONFIG_COVERAGE_DUMP=y'),
(False, False, False, ["demo_board_2"], 'native', ''),
(False, False, True, ['demo_board_1'], 'native', ''),
(True, False, False, ["demo_board_2"], 'native', '\nCONFIG_ASAN=y'),
(False, True, False, ["demo_board_2"], 'native', '\nCONFIG_UBSAN=y'),
]
@pytest.mark.parametrize("enable_asan, enable_coverage, coverage_platform, platform_type, expected_content", TESTDATA_2)
def test_create_overlay(class_testsuite, all_testcases_dict, platforms_list, enable_asan, enable_coverage, coverage_platform, platform_type, expected_content):
@pytest.mark.parametrize("enable_asan, enable_ubsan, enable_coverage, coverage_platform, platform_type, expected_content", TESTDATA_2)
def test_create_overlay(class_testsuite, all_testcases_dict, platforms_list, enable_asan, enable_ubsan, enable_coverage, coverage_platform, platform_type, expected_content):
"""Test correct content is written to testcase_extra.conf based on if conditions
TO DO: Add extra_configs to the input list"""
class_testsuite.testcases = all_testcases_dict
@ -72,7 +74,7 @@ def test_create_overlay(class_testsuite, all_testcases_dict, platforms_list, ena
testinstance = TestInstance(testcase, platform, class_testsuite.outdir)
platform.type = platform_type
assert testinstance.create_overlay(platform, enable_asan, enable_coverage, coverage_platform) == expected_content
assert testinstance.create_overlay(platform, enable_asan, enable_ubsan, enable_coverage, coverage_platform) == expected_content
def test_calculate_sizes(class_testsuite, all_testcases_dict, platforms_list):
""" Test Calculate sizes method for zephyr elf"""