Skip to content

build: fix clang sanitizer and fuzzing builds, add build.sh fuzz - #4222

Merged
igaw merged 7 commits into
linux-nvme:masterfrom
dwsuse:fix-fuzz-build
Oct 19, 2026
Merged

igaw merged 7 commits into
linux-nvme:masterfrom
dwsuse:fix-fuzz-build

Conversation

@dwsuse

@dwsuse dwsuse commented Oct 19, 2026

Copy link
Copy Markdown
Contributor

Sanitizer and libFuzzer builds with clang don't work as documented, and long fuzzing runs need a lot of manual setup.

Build setup. Three setups fail late, with misleading errors. meson now stops at the start of the setup and names the fix:

  • Mixed toolchains: with only CC=clang, libnvme's C++ tests are built and linked with gcc's c++ and the gcc sanitizer runtime. Where that runtime isn't installed: ERROR: Linker cc does not support sanitizer arguments. Now: "The C compiler is clang, but the C++ compiler is gcc … e.g. CC=clang CXX=clang++".
  • clang and b_lundef: clang links the sanitizer runtime only into executables, so shared libraries such as libmock-ifaddrs.so have undefined __asan_*/__ubsan_* symbols, which -Wl,--no-undefined rejects. meson only warns about this. Now: "Sanitizer and fuzzing builds with clang need -Db_lundef=false".
  • -Dfuzzing=true without a sanitizer: every test program fails to link for lack of the coverage hooks (__sanitizer_cov_*), which come from the sanitizer runtime. Now: "-Dfuzzing=true needs a sanitizer".

scripts/build.sh -c clang -s also sets CXX=clang++ and -Db_lundef=false.

Docs. Documentation/TESTING.md gets the clang sanitizer setup, and its libFuzzer instructions are fixed:

  • the setup command missed CXX and b_lundef
  • the device example ran on the argconfig corpus
  • the examples wrote new inputs into the source tree
  • allocator_may_return_null alone doesn't stop fuzz-device from aborting, because libFuzzer reports a device-sized allocation as out of memory first; max_allocation_size_mb makes ASan fail it instead

scripts/build.sh fuzz. A new config builds the targets for libFuzzer and runs each in turn:

scripts/build.sh --fuzz-seconds 3600 fuzz
scripts/build.sh --fuzz-targets device --fuzz-jobs 8 fuzz
  • Options: --fuzz-seconds (600), --fuzz-jobs (one per CPU), --fuzz-targets (all) and --fuzz-dir (.build-ci/fuzz).
  • Jobs: they run as independent processes on a shared corpus (-jobs). The newer -fork mode crashes in the libFuzzer of clang 22.
  • Results: summary.txt, plus per target the job logs, the inputs which found a bug and the corpus. A UBSan finding counts like a crash, and the exit status is non-zero if a target had a finding.
  • Prep commit: config-specific options are now checked in one place, which the --e2e-* options use as well.

Tested with clang 22 and 23:

  • each combination of compiler and options fails or configures as intended
  • the documented commands work as written
  • build.sh fuzz reports a deliberately injected heap overflow (summary, logs, crash inputs, exit status) and finishes cleanly without one
  • a 16-job run of all five targets ran millions of inputs without findings

With only CC=clang, the C++ tests of libnvme are built and linked by the
gcc C++ compiler, with the gcc sanitizer runtime. Where that is not
installed, meson fails while generating the targets:

  ERROR: Linker cc does not support sanitizer arguments
  ['-fsanitize=address,undefined']

And mixing the runtimes of two toolchains does not work anyway. Fail at
the start of the setup instead, and name the fix: CC=clang
CXX=clang++.

Signed-off-by: Daniel Wagner <dwagner@suse.de>
clang links the sanitizer runtime only into executables, so a shared
library of a sanitizer build has undefined references to it, which
-Wl,--no-undefined rejects:

  FAILED: libnvme/tests/libmock-ifaddrs.so
  undefined reference to `__asan_report_load8'

meson only warns about b_lundef with a clang sanitizer build. Fail the
setup with the option to set instead.

Signed-off-by: Daniel Wagner <dwagner@suse.de>
-Dfuzzing=true adds -fsanitize=fuzzer-no-link to all C code. The fuzz
targets get the coverage hooks from libFuzzer, every other program from
the sanitizer runtime. Without a sanitizer the test programs fail to
link:

  undefined reference to `__sanitizer_cov_trace_const_cmp4'

Fail the setup instead.

Signed-off-by: Daniel Wagner <dwagner@suse.de>
…builds

build.sh -c clang -s sets only CC, so the build mixes the clang and gcc
toolchains, and with clang the shared libraries need b_lundef=false.
Set CXX to the clang++ next to the clang in CC, unless CXX is set, and
pass -Db_lundef=false.

Signed-off-by: Daniel Wagner <dwagner@suse.de>
The libFuzzer setup misses CXX=clang++ and -Db_lundef=false, so it does
not build. The device example runs on the argconfig corpus, and both
examples write the inputs libFuzzer finds into the source tree; the
comment in tests/fuzz/meson.build runs a target on the seeds in the tree
directly. And allocator_may_return_null alone does not stop the device
target from aborting: libFuzzer reports a device sized allocation as out
of memory before ASan can fail it.

Add the clang setup to the sanitizer section and use it for the
libFuzzer build. Run each target on a copy of its own seeds in the build
directory, cap the ASan allocation size for the device target, and say
where a crashing input goes. Point the comment to the documentation.

Signed-off-by: Daniel Wagner <dwagner@suse.de>
The --e2e-* and --plugin-tests options are only valid with the tests
config, which one condition over all their variables checks. Let every
config specific option record its config instead, and check them all in
one loop. Another config can then have options of its own, and the
error names the option which does not fit.

Signed-off-by: Daniel Wagner <dwagner@suse.de>
A long fuzzing run needs the clang sanitizer build, a copy of the seeds
per target, the right options per target and a place for the results.
Add a fuzz config which builds the targets for libFuzzer and runs each
of them in turn:

  --fuzz-seconds n     seconds per target (600)
  --fuzz-jobs n        libFuzzer jobs per target (one per CPU)
  --fuzz-targets list  the targets (all)
  --fuzz-dir dir       the results (.build-ci/fuzz)

The jobs run as independent processes on a shared corpus (-jobs).
The newer -fork mode would do the same, but the libFuzzer of clang 22
crashes in it.

The results are summary.txt with the findings, and per target the logs
of the jobs, the inputs which found a bug and the corpus. A finding
stops its job, a UBSan finding like a crash. The exit status is not
zero if a target had a finding.

Signed-off-by: Daniel Wagner <dwagner@suse.de>
@igaw
igaw merged commit f437810 into linux-nvme:master Oct 19, 2026
39 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants