Refactor sanitizer configuration to use Meson's built-in `b_sanitize` option instead of manually adding flags to `qemu_cflags` and `qemu_ldflags`. This ensures that sanitizer flags are correctly passed to both C and Rust targets, fixing linker errors when Rust and UBSan are both enabled.
Explicitly parse `--enable-ubsan`, `--enable-asan`, and `--enable-tsan` in `configure` and map them to `-Db_sanitize` meson option. Remove redundant custom options from `meson_options.txt`. How to test: `./configure --target-list=aarch64-softmmu --enable-ubsan --enable-rust && make -j all` Before this change it will fail at the end of make, since the linker will have different flags, after this change it will link properly Signed-off-by: Nabih Estefan <[email protected]> --- configure | 35 ++++++++++++++++++ meson.build | 55 +++++++++++++---------------- meson_options.txt | 7 +--- scripts/meson-buildoptions.sh | 9 ----- tests/functional/x86_64/meson.build | 2 +- tests/unit/meson.build | 2 +- 6 files changed, 62 insertions(+), 48 deletions(-) diff --git a/configure b/configure index d8bc10060e..579df2f2c8 100755 --- a/configure +++ b/configure @@ -281,6 +281,9 @@ cfi="false" # which requires knowing whether --static is enabled. pie="" static="no" +ubsan="false" +asan="false" +tsan="false" # Preferred compiler: # ${CC} (if set) @@ -746,6 +749,18 @@ for opt do ;; --wasm64-32bit-address-limit) ;; + --enable-ubsan) ubsan="true" + ;; + --disable-ubsan) ubsan="false" + ;; + --enable-asan) asan="true" + ;; + --disable-asan) asan="false" + ;; + --enable-tsan) tsan="true" + ;; + --disable-tsan) tsan="false" + ;; # everything else has the same name in configure and meson --*) meson_option_parse "$opt" "$optarg" ;; @@ -1939,6 +1954,26 @@ if test "$skip_meson" = no; then # QEMU options test "$rust" != "disabled" && meson_option_add "-Drust=$rust" + + # Translate asan/ubsan/tsan to b_sanitize + sanitizers="" + if test "$ubsan" = "true" && test "$asan" = "true"; then + sanitizers="undefined,address" + elif test "$ubsan" = "true"; then + sanitizers="undefined" + elif test "$asan" = "true"; then + sanitizers="address" + fi + if test "$tsan" = "true"; then + if test -n "$sanitizers"; then + error_exit "TSAN is not supported with other sanitizers" + fi + sanitizers="thread" + fi + if test -n "$sanitizers"; then + meson_option_add "-Db_sanitize=$sanitizers" + fi + test "$cfi" != false && meson_option_add "-Dcfi=$cfi" "-Db_lto=$cfi" test "$docs" != auto && meson_option_add "-Ddocs=$docs" test -n "${LIB_FUZZING_ENGINE+xxx}" && meson_option_add "-Dfuzzing_engine=$LIB_FUZZING_ENGINE" diff --git a/meson.build b/meson.build index e026851309..417c27002c 100644 --- a/meson.build +++ b/meson.build @@ -541,43 +541,35 @@ if get_option('safe_stack') and coroutine_backend != 'ucontext' error('SafeStack is only supported with the ucontext coroutine backend') endif -if get_option('asan') - if cc.has_argument('-fsanitize=address') - qemu_cflags = ['-fsanitize=address'] + qemu_cflags - qemu_ldflags = ['-fsanitize=address'] + qemu_ldflags - else - error('Your compiler does not support -fsanitize=address') - endif +sanitize = get_option('b_sanitize') + +if sanitize.contains('address') + # Meson handles -fsanitize=address automatically endif -if get_option('ubsan') +if sanitize.contains('undefined') # Detect static linking issue with ubsan: # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=84285 - if cc.links('int main(int argc, char **argv) { return argc + 1; }', - args: [qemu_ldflags, '-fsanitize=undefined']) - qemu_cflags += ['-fsanitize=undefined'] - qemu_ldflags += ['-fsanitize=undefined'] - - # Suppress undefined behaviour from function call to mismatched type. - # In addition, tcg prologue does not emit function type prefix - # required by function call sanitizer. - if cc.has_argument('-fno-sanitize=function') - qemu_cflags += ['-fno-sanitize=function'] - endif - else + if not cc.links('int main(int argc, char **argv) { return argc + 1; }', + args: [qemu_ldflags, '-fsanitize=undefined']) error('Your compiler does not support -fsanitize=undefined') endif + + # Suppress undefined behaviour from function call to mismatched type. + # In addition, tcg prologue does not emit function type prefix + # required by function call sanitizer. + if cc.has_argument('-fno-sanitize=function') + qemu_cflags += ['-fno-sanitize=function'] + endif endif -# Thread sanitizer is, for now, much noisier than the other sanitizers; -# keep it separate until that is not the case. -if get_option('tsan') - if get_option('asan') or get_option('ubsan') +if sanitize.contains('thread') + if sanitize.contains('address') or sanitize.contains('undefined') error('TSAN is not supported with other sanitizers') endif if not cc.has_function('__tsan_create_fiber', - args: '-fsanitize=thread', - prefix: '#include <sanitizer/tsan_interface.h>') + args: '-fsanitize=thread', + prefix: '#include <sanitizer/tsan_interface.h>') error('Cannot enable TSAN due to missing fiber annotation interface') endif tsan_warn_suppress = [] @@ -588,8 +580,7 @@ if get_option('tsan') if cc.has_argument('-Wno-tsan') tsan_warn_suppress = ['-Wno-tsan'] endif - qemu_cflags = ['-fsanitize=thread'] + tsan_warn_suppress + qemu_cflags - qemu_ldflags = ['-fsanitize=thread'] + qemu_ldflags + qemu_cflags += tsan_warn_suppress endif # Detect support for PT_GNU_RELRO + DT_BIND_NOW. @@ -2482,7 +2473,7 @@ if have_tcg config_host_data.set('CONFIG_TCG_INTERPRETER', tcg_arch == 'tci') endif config_host_data.set('CONFIG_TPM', have_tpm) -config_host_data.set('CONFIG_TSAN', get_option('tsan')) +config_host_data.set('CONFIG_TSAN', sanitize.contains('thread')) config_host_data.set('CONFIG_USB_LIBUSB', libusb.found()) config_host_data.set('CONFIG_VDE', vde.found()) config_host_data.set('CONFIG_VHOST', have_vhost) @@ -2639,7 +2630,7 @@ if rdma.found() endif have_asan_fiber = false -if get_option('asan') and \ +if sanitize.contains('address') and \ not cc.has_function('__sanitizer_start_switch_fiber', args: '-fsanitize=address', prefix: '#include <sanitizer/asan_interface.h>') @@ -4768,7 +4759,9 @@ summary_info += {'memory allocator': get_option('malloc')} summary_info += {'avx2 optimization': config_host_data.get('CONFIG_AVX2_OPT')} summary_info += {'avx512bw optimization': config_host_data.get('CONFIG_AVX512BW_OPT')} summary_info += {'gcov': get_option('b_coverage')} -summary_info += {'thread sanitizer': get_option('tsan')} +summary_info += {'address sanitizer': sanitize.contains('address')} +summary_info += {'undefined behavior sanitizer': sanitize.contains('undefined')} +summary_info += {'thread sanitizer': sanitize.contains('thread')} summary_info += {'CFI support': get_option('cfi')} if get_option('cfi') summary_info += {'CFI debug support': get_option('cfi_debug')} diff --git a/meson_options.txt b/meson_options.txt index a07cb47d35..d61c0241cc 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -99,12 +99,7 @@ option('tcg_interpreter', type: 'boolean', value: false, description: 'TCG with bytecode interpreter (slow)') option('safe_stack', type: 'boolean', value: false, description: 'SafeStack Stack Smash Protection (requires clang/llvm and coroutine backend ucontext)') -option('asan', type: 'boolean', value: false, - description: 'enable address sanitizer') -option('ubsan', type: 'boolean', value: false, - description: 'enable undefined behaviour sanitizer') -option('tsan', type: 'boolean', value: false, - description: 'enable thread sanitizer') + option('stack_protector', type: 'feature', value: 'auto', description: 'compiler-provided stack protection') option('cfi', type: 'boolean', value: false, diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh index c003985047..8d3b992ed7 100644 --- a/scripts/meson-buildoptions.sh +++ b/scripts/meson-buildoptions.sh @@ -21,7 +21,6 @@ meson_options_help() { printf "%s\n" ' --disable-relocatable toggle relocatable install' printf "%s\n" ' --docdir=VALUE Base directory for documentation installation' printf "%s\n" ' (can be empty) [share/doc]' - printf "%s\n" ' --enable-asan enable address sanitizer' printf "%s\n" ' --enable-block-drv-whitelist-in-tools' printf "%s\n" ' use block whitelist also in tools instead of only' printf "%s\n" ' QEMU' @@ -54,8 +53,6 @@ meson_options_help() { printf "%s\n" ' --enable-trace-backends=CHOICES' printf "%s\n" ' Set available tracing backends [log] (choices:' printf "%s\n" ' dtrace/ftrace/log/nop/simple/syslog/ust)' - printf "%s\n" ' --enable-tsan enable thread sanitizer' - printf "%s\n" ' --enable-ubsan enable undefined behaviour sanitizer' printf "%s\n" ' --firmwarepath=VALUES search PATH for firmware files [share/qemu-' printf "%s\n" ' firmware]' printf "%s\n" ' --iasl=VALUE Path to ACPI disassembler' @@ -238,8 +235,6 @@ _meson_option_parse() { --disable-af-xdp) printf "%s" -Daf_xdp=disabled ;; --enable-alsa) printf "%s" -Dalsa=enabled ;; --disable-alsa) printf "%s" -Dalsa=disabled ;; - --enable-asan) printf "%s" -Dasan=true ;; - --disable-asan) printf "%s" -Dasan=false ;; --enable-attr) printf "%s" -Dattr=enabled ;; --disable-attr) printf "%s" -Dattr=disabled ;; --audio-drv-list=*) quote_sh "-Daudio_drv_list=$2" ;; @@ -521,14 +516,10 @@ _meson_option_parse() { --disable-tpm) printf "%s" -Dtpm=disabled ;; --enable-trace-backends=*) quote_sh "-Dtrace_backends=$2" ;; --with-trace-file=*) quote_sh "-Dtrace_file=$2" ;; - --enable-tsan) printf "%s" -Dtsan=true ;; - --disable-tsan) printf "%s" -Dtsan=false ;; --enable-u2f) printf "%s" -Du2f=enabled ;; --disable-u2f) printf "%s" -Du2f=disabled ;; --enable-uadk) printf "%s" -Duadk=enabled ;; --disable-uadk) printf "%s" -Duadk=disabled ;; - --enable-ubsan) printf "%s" -Dubsan=true ;; - --disable-ubsan) printf "%s" -Dubsan=false ;; --enable-usb-redir) printf "%s" -Dusb_redir=enabled ;; --disable-usb-redir) printf "%s" -Dusb_redir=disabled ;; --enable-valgrind) printf "%s" -Dvalgrind=enabled ;; diff --git a/tests/functional/x86_64/meson.build b/tests/functional/x86_64/meson.build index 1ed10ad6c2..54af748f15 100644 --- a/tests/functional/x86_64/meson.build +++ b/tests/functional/x86_64/meson.build @@ -24,7 +24,7 @@ tests_x86_64_system_quick = [ # interacts badly with the sanitizer; this means the memlock # test (which checks via /proc for whether pages were locked) # will always fail on a sanitizer build, so don't run it. -if not get_option('asan') +if not sanitize.contains('address') tests_x86_64_system_quick += [ 'memlock' ] endif diff --git a/tests/unit/meson.build b/tests/unit/meson.build index 01cc540a45..b0ba64d0ec 100644 --- a/tests/unit/meson.build +++ b/tests/unit/meson.build @@ -147,7 +147,7 @@ if have_system # Some tests: test-char, test-qdev-global-props, and test-qga, # are not runnable under TSan due to a known issue. # https://github.com/google/sanitizers/issues/1116 - if not get_option('tsan') + if not sanitize.contains('thread') if host_os != 'windows' tests += { 'test-char': ['socket-helpers.c', qom, io, chardev] -- 2.55.0.rc0.799.gd6f94ed593-goog
