tree-wide: add support for asan!

What if you could find memory bugs in Lix without really trying very
hard? I've had variously scuffed patches to do this, but this is
blocked on boost coroutines removal at this point tbh.

Change-Id: Id762af076aa06ad51e77a6c17ed10275929ed578
This commit is contained in:
jade 2024-06-17 23:47:51 -07:00
parent c347d3df8f
commit 19ae87e5ce
8 changed files with 59 additions and 7 deletions

View file

@ -16,3 +16,6 @@ Checks:
- -bugprone-unchecked-optional-access - -bugprone-unchecked-optional-access
# many warnings, seems like a questionable lint # many warnings, seems like a questionable lint
- -bugprone-branch-clone - -bugprone-branch-clone
CheckOptions:
bugprone-reserved-identifier.AllowedIdentifiers: '__asan_default_options'

View file

@ -199,7 +199,11 @@ configdata = { }
# Dependencies # Dependencies
# #
boehm = dependency('bdw-gc', required : get_option('gc'), version : '>=8.2.6') gc_opt = get_option('gc').disable_if(
'address' in get_option('b_sanitize'),
error_message: 'gc does far too many memory crimes for ASan'
)
boehm = dependency('bdw-gc', required : gc_opt, version : '>=8.2.6')
configdata += { configdata += {
'HAVE_BOEHMGC': boehm.found().to_int(), 'HAVE_BOEHMGC': boehm.found().to_int(),
} }
@ -482,7 +486,14 @@ if cxx.get_id() == 'clang' and get_option('b_sanitize') != ''
add_project_link_arguments('-shared-libsan', language : 'cpp') add_project_link_arguments('-shared-libsan', language : 'cpp')
endif endif
# Clang gets grumpy about missing libasan symbols if -shared-libasan is not
# passed when building shared libs, at least on Linux
if cxx.get_id() == 'clang' and 'address' in get_option('b_sanitize')
add_project_link_arguments('-shared-libasan', language : 'cpp')
endif
add_project_link_arguments('-pthread', language : 'cpp') add_project_link_arguments('-pthread', language : 'cpp')
if cxx.get_linker_id() in ['ld.bfd', 'ld.gold'] if cxx.get_linker_id() in ['ld.bfd', 'ld.gold']
add_project_link_arguments('-Wl,--no-copy-dt-needed-entries', language : 'cpp') add_project_link_arguments('-Wl,--no-copy-dt-needed-entries', language : 'cpp')
endif endif
@ -497,7 +508,7 @@ endif
# maintainers/buildtime_report.sh BUILD-DIR to simply work in clang builds. # maintainers/buildtime_report.sh BUILD-DIR to simply work in clang builds.
# #
# They can also be manually viewed at https://ui.perfetto.dev # They can also be manually viewed at https://ui.perfetto.dev
if get_option('profile-build').require(meson.get_compiler('cpp').get_id() == 'clang').enabled() if get_option('profile-build').require(cxx.get_id() == 'clang').enabled()
add_project_arguments('-ftime-trace', language: 'cpp') add_project_arguments('-ftime-trace', language: 'cpp')
endif endif

View file

@ -0,0 +1,17 @@
/// @file This is very bothersome code that has to be included in every
/// executable to get the correct default ASan options. I am so sorry.
extern "C" [[gnu::retain]] const char *__asan_default_options()
{
// We leak a bunch of memory knowingly on purpose. It's not worthwhile to
// diagnose that memory being leaked for now.
//
// Instruction bytes are useful for finding the actual code that
// corresponds to an ASan report.
//
// TODO: setting log_path=asan.log or not: neither works, since you can't
// write to the fs in certain places in the testsuite, but you also cannot
// write arbitrarily to stderr in other places so the reports get eaten.
// pain 🥖
return "halt_on_error=1:abort_on_error=1:detect_leaks=0:print_summary=1:dump_instruction_bytes=1";
}

View file

@ -12,10 +12,19 @@ subdir('libmain')
# libcmd depends on everything # libcmd depends on everything
subdir('libcmd') subdir('libcmd')
# The rest of the subdirectories aren't separate components, # The rest of the subdirectories aren't separate components,
# just source files in another directory, so we process them here. # just source files in another directory, so we process them here.
# Static library that just sets default ASan options. It needs to be included
# in every executable.
asanoptions = static_library(
'libasanoptions',
files('asan-options/asan-options.cc'),
)
libasanoptions = declare_dependency(
link_whole: asanoptions
)
build_remote_sources = files( build_remote_sources = files(
'build-remote/build-remote.cc', 'build-remote/build-remote.cc',
) )

View file

@ -80,6 +80,7 @@ nix = executable(
profiles_md_gen, profiles_md_gen,
nix2_commands_sources, nix2_commands_sources,
dependencies : [ dependencies : [
libasanoptions,
liblixcmd, liblixcmd,
liblixutil_mstatic, liblixutil_mstatic,
liblixstore_mstatic, liblixstore_mstatic,

View file

@ -7,6 +7,7 @@ repl_characterization_tester = executable(
'test-repl-characterization', 'test-repl-characterization',
repl_characterization_tester_sources, repl_characterization_tester_sources,
dependencies : [ dependencies : [
libasanoptions,
liblixutil, liblixutil,
liblixutil_test_support, liblixutil_test_support,
sodium, sodium,

View file

@ -2,6 +2,7 @@ libstoreconsumer_tester = executable(
'test-libstoreconsumer', 'test-libstoreconsumer',
'main.cc', 'main.cc',
dependencies : [ dependencies : [
libasanoptions,
liblixutil, liblixutil,
liblixstore, liblixstore,
sodium, sodium,

View file

@ -11,6 +11,10 @@
# functions, the result would be way less readable than just a bit of copypasta. # functions, the result would be way less readable than just a bit of copypasta.
# It's only ~200 lines; better to just refactor the tests themselves which we'll want to do anyway. # It's only ~200 lines; better to just refactor the tests themselves which we'll want to do anyway.
default_test_env = {
'ASAN_OPTIONS': 'detect_leaks=0:halt_on_error=1:abort_on_error=1:print_summary=1:dump_instruction_bytes=1'
}
libutil_test_support_sources = files( libutil_test_support_sources = files(
'libutil-support/tests/cli-literate-parser.cc', 'libutil-support/tests/cli-literate-parser.cc',
'libutil-support/tests/hash.cc', 'libutil-support/tests/hash.cc',
@ -63,6 +67,7 @@ libutil_tester = executable(
'liblixutil-tests', 'liblixutil-tests',
libutil_tests_sources, libutil_tests_sources,
dependencies : [ dependencies : [
libasanoptions,
rapidcheck, rapidcheck,
gtest, gtest,
boehm, boehm,
@ -78,7 +83,7 @@ test(
'libutil-unit-tests', 'libutil-unit-tests',
libutil_tester, libutil_tester,
args : tests_args, args : tests_args,
env : { env : default_test_env + {
'_NIX_TEST_UNIT_DATA': meson.project_source_root() / 'tests/unit/libutil/data', '_NIX_TEST_UNIT_DATA': meson.project_source_root() / 'tests/unit/libutil/data',
}, },
suite : 'check', suite : 'check',
@ -132,6 +137,7 @@ libstore_tester = executable(
'liblixstore-tests', 'liblixstore-tests',
libstore_tests_sources, libstore_tests_sources,
dependencies : [ dependencies : [
libasanoptions,
liblixstore_test_support, liblixstore_test_support,
liblixutil_test_support, liblixutil_test_support,
liblixstore_mstatic, liblixstore_mstatic,
@ -147,7 +153,7 @@ test(
'libstore-unit-tests', 'libstore-unit-tests',
libstore_tester, libstore_tester,
args : tests_args, args : tests_args,
env : { env : default_test_env + {
'_NIX_TEST_UNIT_DATA': meson.project_source_root() / 'tests/unit/libstore/data', '_NIX_TEST_UNIT_DATA': meson.project_source_root() / 'tests/unit/libstore/data',
}, },
suite : 'check', suite : 'check',
@ -196,6 +202,7 @@ libexpr_tester = executable(
'liblixexpr-tests', 'liblixexpr-tests',
libexpr_tests_sources, libexpr_tests_sources,
dependencies : [ dependencies : [
libasanoptions,
liblixexpr_test_support, liblixexpr_test_support,
liblixstore_test_support, liblixstore_test_support,
liblixstore_mstatic, liblixstore_mstatic,
@ -214,7 +221,7 @@ test(
'libexpr-unit-tests', 'libexpr-unit-tests',
libexpr_tester, libexpr_tester,
args : tests_args, args : tests_args,
env : { env : default_test_env + {
'_NIX_TEST_UNIT_DATA': meson.project_source_root() / 'tests/unit/libexpr/data', '_NIX_TEST_UNIT_DATA': meson.project_source_root() / 'tests/unit/libexpr/data',
}, },
suite : 'check', suite : 'check',
@ -226,6 +233,7 @@ libcmd_tester = executable(
'liblixcmd-tests', 'liblixcmd-tests',
files('libcmd/args.cc'), files('libcmd/args.cc'),
dependencies : [ dependencies : [
libasanoptions,
liblixcmd, liblixcmd,
liblixutil, liblixutil,
liblixmain, liblixmain,
@ -241,7 +249,7 @@ test(
'libcmd-unit-tests', 'libcmd-unit-tests',
libcmd_tester, libcmd_tester,
args : tests_args, args : tests_args,
env : { env : default_test_env + {
# No special meaning here, it's just a file laying around that is unlikely to go anywhere # No special meaning here, it's just a file laying around that is unlikely to go anywhere
# any time soon. # any time soon.
'_NIX_TEST_UNIT_DATA': meson.project_source_root() / 'src/nix-env/buildenv.nix', '_NIX_TEST_UNIT_DATA': meson.project_source_root() / 'src/nix-env/buildenv.nix',
@ -272,6 +280,7 @@ test(
'libmain-unit-tests', 'libmain-unit-tests',
libmain_tester, libmain_tester,
args : tests_args, args : tests_args,
env : default_test_env,
suite : 'check', suite : 'check',
protocol : 'gtest', protocol : 'gtest',
) )