]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
meson: Stop doing nested build when fuzzers are enabled
authorDaan De Meyer <daan.j.demeyer@gmail.com>
Sun, 11 May 2025 07:42:28 +0000 (09:42 +0200)
committerLuca Boccassi <luca.boccassi@gmail.com>
Tue, 24 Jun 2025 14:41:46 +0000 (15:41 +0100)
Currently, when fuzzers are enabled, we run meson from within meson
to build the fuzzer executables with sanitizers. The idea is that
we can build the fuzzers with different kinds of sanitizers
independently from the main build.

The issue with this setup is that we don't actually make use of it.
We only build the fuzzers with one set of sanitizers (address,undefined)
so we're adding a bunch of extra complexity without any benefit as we
can just setup the top level meson build with these sanitizers and get
the same result.

The other issue with this setup is that we don't pass on all the options
passed to the top level meson build to the nested meson build. The only things
we pass on are extra compiler arguments and the value of the auto_features
option, but none of the individual feature options if overridden are passed on,
which can lead to very hard to debug issues as an option enabled in the top
level build is not enabled in the nested build.

Since we're not getting anything useful out of this setup, let's simplify
and get rid of the nested meson build. Instead, sanitizers should be enabled
for the top level meson.build. This currently didn't work as we were overriding
the sanitizers passed to the meson build with the fuzzer sanitizer, so we
fix that as well by making sure we combine the fuzzer sanitizer with the ones
passed in by the user.

We also drop support for looking up libFuzzer as a separate library as
it has been shipped builtin in clang since clang 6.0, so we can assume
that -fsanitize=fuzzer is available.

To make sure we still run the fuzzing tests, we enable the fuzz-tests option
by default now to make sure they still always run (without instrumentation unless
one of llvm-fuzz or oss-fuzz is enabled).

(cherry picked from commit d8def5dc8745c3d77cf49ad5abbb6719ffd94035)

docs/HACKING.md
meson.build
meson_options.txt
test/fuzz/meson.build

index aaacbbe9d8231cf7a5d995346c4e5364c029a993..280e1524d1d7ce5b77e4a34fbef309db5a593df7 100644 (file)
@@ -190,13 +190,18 @@ See [Testing systemd using sanitizers](/TESTING_WITH_SANITIZERS) for more inform
 
 ## Fuzzers
 
-systemd includes fuzzers in `src/fuzz/` that use libFuzzer and are automatically run by [OSS-Fuzz](https://github.com/google/oss-fuzz) with sanitizers.
-To add a fuzz target, create a new `src/fuzz/fuzz-foo.c` file with a `LLVMFuzzerTestOneInput` function and add it to the list in `src/fuzz/meson.build`.
+systemd includes fuzzers in `src/fuzz/` that use libFuzzer and are automatically
+run by [OSS-Fuzz](https://github.com/google/oss-fuzz) with sanitizers. To add a
+fuzz target, create a new `src/fuzz/fuzz-foo.c` file with a
+`LLVMFuzzerTestOneInput` function and add it to the list in
+`src/fuzz/meson.build`.
 
-Whenever possible, a seed corpus and a dictionary should also be added with new fuzz targets.
-The dictionary should be named `src/fuzz/fuzz-foo.dict` and the seed corpus should be built and exported as `$OUT/fuzz-foo_seed_corpus.zip` in `tools/oss-fuzz.sh`.
+Whenever possible, a seed corpus and a dictionary should also be added with new
+fuzz targets. The dictionary should be named `src/fuzz/fuzz-foo.dict` and the
+seed corpus should be built and exported as `$OUT/fuzz-foo_seed_corpus.zip` in
+`tools/oss-fuzz.sh`.
 
-The fuzzers can be built locally if you have libFuzzer installed by running `tools/oss-fuzz.sh`, or by running:
+The fuzzers can be built locally by running `tools/oss-fuzz.sh`, or by running:
 
 ```sh
 CC=clang CXX=clang++ \
@@ -205,15 +210,16 @@ meson setup build-libfuzz -Dllvm-fuzz=true -Db_sanitize=address,undefined -Db_lu
 ninja -C build-libfuzz fuzzers
 ```
 
-Each fuzzer then can be then run manually together with a directory containing the initial corpus:
+Each fuzzer then can be then run manually together with a directory containing
+the initial corpus:
 
 ```
 export UBSAN_OPTIONS=print_stacktrace=1:print_summary=1:halt_on_error=1
 build-libfuzz/fuzz-varlink-idl test/fuzz/fuzz-varlink-idl/
 ```
 
-Note: the `halt_on_error=1` UBSan option is especially important,
-otherwise the fuzzer won't crash when undefined behavior is triggered.
+Note: the `halt_on_error=1` UBSan option is especially important, otherwise the
+fuzzer won't crash when undefined behavior is triggered.
 
 You should also confirm that the fuzzers can be built and run using
 [the OSS-Fuzz toolchain](https://google.github.io/oss-fuzz/advanced-topics/reproducing/#building-using-docker):
@@ -241,8 +247,9 @@ done
 ./infra/helper.py coverage --no-corpus-download systemd
 ```
 
-If you find a bug that impacts the security of systemd,
-please follow the guidance in [CONTRIBUTING.md](/CONTRIBUTING) on how to report a security vulnerability.
+If you find a bug that impacts the security of systemd, please follow the
+guidance in [CONTRIBUTING.md](/CONTRIBUTING) on how to report a security
+vulnerability.
 
 For more details on building fuzzers and integrating with OSS-Fuzz, visit:
 
index 957e00ecadfd5061e9ba89af6fc0dcc9a5d2f315..1fb34b06e6289a6e44e025840fc6a84772d8a6b9 100644 (file)
@@ -347,13 +347,10 @@ else
 endif
 
 if want_libfuzzer
-        fuzzing_engine = meson.get_compiler('cpp').find_library('Fuzzer', required : false)
-        if fuzzing_engine.found()
-                userspace_c_args += '-fsanitize-coverage=trace-pc-guard,trace-cmp'
-        elif cc.has_argument('-fsanitize=fuzzer-no-link')
+        if cc.has_argument('-fsanitize=fuzzer-no-link')
                 userspace_c_args += '-fsanitize=fuzzer-no-link'
         else
-                error('Looks like neither libFuzzer nor -fsanitize=fuzzer-no-link is supported')
+                error('Looks like -fsanitize=fuzzer-no-link is not supported')
         endif
 elif want_ossfuzz
         fuzzing_engine = meson.get_compiler('cpp').find_library('FuzzingEngine')
@@ -2285,11 +2282,11 @@ fuzz_template = executable_template + {
         'install' : false,
 }
 
-if want_ossfuzz or (want_libfuzzer and fuzzing_engine.found())
+if want_ossfuzz
         fuzz_additional_kwargs = {
                 'dependencies' : fuzzing_engine,
         }
-elif want_libfuzzer and not fuzzing_engine.found()
+elif want_libfuzzer
         fuzz_additional_kwargs = {
                 'link_args' : ['-fsanitize=fuzzer'],
         }
@@ -2575,9 +2572,7 @@ foreach dict : executables
         if is_fuzz
                 fuzzer_exes += exe
 
-                if want_tests != 'false'
-                        # Run the fuzz regression tests without any sanitizers enabled.
-                        # Additional invocations with sanitizers may get added below.
+                if want_tests != 'false' and want_fuzz_tests
                         fuzz_ins = fuzz_regression_tests.get(name, {})
                         foreach directive : fuzz_ins.get('directives', [])
                                 tt = '@0@_@1@'.format(name, fs.name(directive.full_path()))
@@ -2589,6 +2584,8 @@ foreach dict : executables
                                      exe,
                                      suite : 'fuzz',
                                      args : directive.full_path(),
+                                     env : ['UBSAN_OPTIONS=print_stacktrace=1:print_summary=1:halt_on_error=1'],
+                                     timeout : 60,
                                      depends : directive)
                         endforeach
                         foreach file : fuzz_ins.get('files', [])
@@ -2600,6 +2597,8 @@ foreach dict : executables
                                 test(tt,
                                      exe,
                                      suite : 'fuzz',
+                                     env : ['UBSAN_OPTIONS=print_stacktrace=1:print_summary=1:halt_on_error=1'],
+                                     timeout : 60,
                                      args : file)
                         endforeach
                 endif
@@ -2837,52 +2836,6 @@ foreach exec : public_programs
         endif
 endforeach
 
-# Enable tests for all supported sanitizers
-foreach tuple : fuzz_sanitizers
-        sanitizer = tuple[0]
-        build = tuple[1]
-
-        if cc.has_link_argument('-fsanitize=@0@'.format(sanitizer))
-                foreach fuzzer, fuzz_ins : fuzz_regression_tests
-                        name = '@0@:@1@'.format(fuzzer, sanitizer)
-                        if want_tests == 'false'
-                                message('Not compiling @0@ because tests is set to false'.format(name))
-                                continue
-                        endif
-                        if not want_fuzz_tests
-                                message('Not compiling @0@ because fuzz-tests is set to false'.format(name))
-                                continue
-                        endif
-                        exe = custom_target(
-                                name,
-                                output : name,
-                                depends : build,
-                                command : [ln, '-fs',
-                                           build.full_path() / fuzzer,
-                                           '@OUTPUT@'],
-                                build_by_default : true)
-
-                        foreach directive : fuzz_ins.get('directives', [])
-                                test('@0@_@1@_@2@'.format(fuzzer, fs.name(directive.full_path()), sanitizer),
-                                     env,
-                                     suite : 'fuzz+san',
-                                     env : ['UBSAN_OPTIONS=print_stacktrace=1:print_summary=1:halt_on_error=1'],
-                                     timeout : 60,
-                                     args : [exe.full_path(), directive.full_path()],
-                                     depends : directive)
-                        endforeach
-                        foreach file : fuzz_ins.get('files', [])
-                                test('@0@_@1@_@2@'.format(fuzzer, fs.name(file), sanitizer),
-                                     env,
-                                     suite : 'fuzz+san',
-                                     env : ['UBSAN_OPTIONS=print_stacktrace=1:print_summary=1:halt_on_error=1'],
-                                     timeout : 60,
-                                     args : [exe.full_path(), file])
-                        endforeach
-                endforeach
-        endif
-endforeach
-
 #####################################################################
 
 if git.found()
index f30b3f52389440a7b14199d1267cdf12e470e20c..aedc37413d994f8f7f73e49b984a21deccde2143 100644 (file)
@@ -508,8 +508,8 @@ option('tests', type : 'combo', choices : ['true', 'unsafe', 'false'],
        description : 'enable extra tests with =unsafe')
 option('slow-tests', type : 'boolean', value : false,
        description : 'run the slow tests by default')
-option('fuzz-tests', type : 'boolean', value : false,
-       description : 'run the fuzzer regression tests by default (with sanitizers)')
+option('fuzz-tests', type : 'boolean', value : true,
+       description : 'run the fuzzer regression tests')
 option('install-tests', type : 'boolean', value : false,
        description : 'install test executables')
 option('log-message-verification', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
index 71e731b70f50ae57f69274f98b5c0de72f55e5d1..54115909a24ec9cfa2d048ae07f8cb63d5107dfc 100644 (file)
@@ -36,36 +36,6 @@ fuzz_regression_tests += { 'fuzz-unit-file' : dict }
 
 ############################################################
 
-# TODO: Use native string formatting with meson >= 1.3.0
-if get_option('auto_features').enabled()
-        sanitize_auto_features = 'enabled'
-elif get_option('auto_features').disabled()
-        sanitize_auto_features = 'disabled'
-else
-        sanitize_auto_features = 'auto'
-endif
-
-fuzz_c_args = get_option('c_args')
-fuzz_cpp_args = cxx_cmd != '' ? get_option('cpp_args') : []
-
-sanitize_address_undefined = custom_target(
-        'sanitize-address-undefined-fuzzers',
-        output : 'sanitize-address-undefined-fuzzers',
-        command : [meson_build_sh,
-                   meson.project_source_root(),
-                   '@OUTPUT@',
-                   'fuzzers',
-                   ' '.join(fuzz_c_args + '-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION'),
-                   ' '.join(fuzz_cpp_args + '-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION'),
-                   '-Dfuzz-tests=true -Db_lundef=false -Db_sanitize=address,undefined --optimization=@0@ @1@ --auto-features=@2@'.format(
-                           get_option('optimization'),
-                           get_option('werror') ? '--werror' : '',
-                           sanitize_auto_features
-                   ),
-                   ' '.join(cc.cmd_array()),
-                   cxx_cmd])
-
-fuzz_sanitizers = [['address,undefined', sanitize_address_undefined]]
 fuzz_testsdir = 'test/fuzz'
 
 if git.found() and fs.is_dir(meson.project_source_root() / '.git')