Skip to content

build: Fix linking for fuzz target when building with MSan#30778

Merged
fanquake merged 2 commits intobitcoin:masterfrom
hebasto:240831-ci-msan
Sep 3, 2024
Merged

build: Fix linking for fuzz target when building with MSan#30778
fanquake merged 2 commits intobitcoin:masterfrom
hebasto:240831-ci-msan

Conversation

@hebasto
Copy link
Member

@hebasto hebasto commented Aug 31, 2024

The first commit fixes #30760.

The second commit:

  1. Preserves -g -O1 set in MSAN_FLAGS. Since configuration-specific flags override general flags, these are set to empty strings. A similar approach is used in the OSS-Fuzz repository.
  2. Sets the "Debug" build configuration when depends are built with DEBUG=1, ensuring that linux_debug_CPPFLAGS from depends are passed to the main build system.

@DrahtBot
Copy link
Contributor

DrahtBot commented Aug 31, 2024

The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

Code Coverage

For detailed information about the code coverage, see the test coverage report.

Reviews

See the guideline for information on the review process.

Type Reviewers
ACK maflcko, fanquake

If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update.

Conflicts

Reviewers, this pull request conflicts with the following ones:

  • #28690 (build: Introduce internal kernel library by TheCharlatan)

If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.

@DrahtBot
Copy link
Contributor

🚧 At least one of the CI tasks failed.
Debug: https://github.com/bitcoin/bitcoin/runs/29506147599

Hints

Make sure to run all tests locally, according to the documentation.

The failure may happen due to a number of reasons, for example:

  • Possibly due to a silent merge conflict (the changes in this pull request being
    incompatible with the current code in the target branch). If so, make sure to rebase on the latest
    commit of the target branch.

  • A sanitizer issue, which can only be found by compiling with the sanitizer and running the
    affected test.

  • An intermittent issue.

Leave a comment here, if you need help tracking down a confusing failure.

@hebasto hebasto closed this Aug 31, 2024
@hebasto hebasto reopened this Aug 31, 2024
@hebasto hebasto force-pushed the 240831-ci-msan branch 2 times, most recently from 24c65a1 to 331e976 Compare August 31, 2024 09:15
@maflcko
Copy link
Member

maflcko commented Sep 2, 2024

review-only ACK 331e976

@hebasto
Copy link
Member Author

hebasto commented Sep 2, 2024

review-only ACK 331e976

I'm still working on it. MSan fuzzing CI job still fails when running locally.

Additionally, setting the "Debug" build configuration ensures that
`linux_debug_CPPFLAGS` from depends are passed to the main build system.
@hebasto hebasto changed the title ci: Do not override -g -O1 set in MSAN_FLAGS build: Fix linking for fuzz target when building with MSan Sep 2, 2024
@hebasto hebasto marked this pull request as ready for review September 2, 2024 22:57
@hebasto
Copy link
Member Author

hebasto commented Sep 2, 2024

review-only ACK 331e976

I'm still working on it. MSan fuzzing CI job still fails when running locally.

The issue has been resolved. The PR description has been updated.

Ready for review and testing.

test_fuzz
bitcoin_cli
bitcoin_common
bitcoin_util
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

26c460a:

Can you explain this a bit better? Why did it only fail on one CI config, and none of the others? Why was the error an msan runtime-error, as opposed to a link-time error?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

26c460a:

Can you explain this a bit better?

I'm afraid I can't (

Why did it only fail on one CI config, and none of the others? Why was the error an msan runtime-error, as opposed to a link-time error?

Those are the questions I asked myself :)

I'm not able to answer them at this moment. There are two reasons for that:

  1. The current set of internal libraries, including test_fuzz and test_util, has circular symbol dependencies, which is inherited from Autotools.
  2. I'm not fully aware of how the linker works with -fsanitize=fuzzer,memory.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, so I guess the reason is that this is required to mirror the autotools approach from

$(LIBBITCOIN_UTIL) \
?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Autotools tends to overlink, while in CMake, I aimed to link only the necessary components.

Yes, this PR commit mirrors the line from bitcoin/src/Makefile.test.include that you mentioned. However, simply removing that line from 80f00ca results in a link-time error.

@fanquake
Copy link
Member

fanquake commented Sep 3, 2024

Sets the "Debug" build configuration when depends are built with DEBUG=1, ensuring that linux_debug_CPPFLAGS from depends are passed to the main build system.

Is this not a bug? If depends is built (in any configuration/with any options/flags) then the expectation would be that everything is passed to CMake (regardless of the CMake build type). However it seems like your saying CMake will actually just ignore some flags depending on which build type has been set? If this was the intended behaviour, has this been documented somewhere? Otherwise how are builders meant to know that certains flags from depends might just be ignored, depending on which build type is selected in CMake?

@maflcko
Copy link
Member

maflcko commented Sep 3, 2024

review-only ACK 787dfaf

Only change since last review is linking with libbitcoin_util.

It would be nice to better understand this change, but this can be done later and is not a blocker to for a test-only change.

Also, the depends behavior can be documented better, or be changed, but this is probably best done in a separate issue or pull request, so that this test-only change stays focussed.

@hebasto
Copy link
Member Author

hebasto commented Sep 3, 2024

Sets the "Debug" build configuration when depends are built with DEBUG=1, ensuring that linux_debug_CPPFLAGS from depends are passed to the main build system.

Is this not a bug?

No, it is not. Rather it is a design feature.

If depends is built (in any configuration/with any options/flags) then the expectation would be that everything is passed to CMake (regardless of the CMake build type).

While both depends and CMake have configuration-specific flags, it seems unreasonable to expect from CMake configured for the "Release" build type pull in *_debug_* flags from depends.

If this was the intended behaviour, has this been documented somewhere?

Yes, it was intended. No, it has not been documented. FWIW, the fact that flags from depends override flags set in the main build system has also never been documented.


Here are more details regarding that design decision (C++ flags are used as an example, but the same applies for other tool flags):

  1. CMake has (a) flags applied for all build configurations (CMAKE_CXX_FLAGS) and (b) configuration-specific flags (CMAKE_CXX_FLAGS_<CONFIG>). The latter follow the former during the build tool invocation.

  2. The depends build subsystem has a similar design, and configuration-specific variables have _release_ and _debug_ infixes. The depends build configuration is determined by whether DEBUG variable is defined as 1 or not.

  3. IMPORTANT note about behaviour from Autotools: Flags set in depends override ones set in the main build system.

  4. The CMake's way of passing the toolchain details set in depends to the main build system is by using a toolchain file. There are dedicated CMake variables to be defined in the toolchain file, namely CMAKE_CXX_FLAGS_INIT and configuration-specific CMAKE_CXX_FLAGS_<CONFIG>_INIT. The former is assigned the host_CXXFLAGS value, and the latter use host_release_CXXFLAGS and host_debug_CXXFLAGS values. This helps to ensure that the behaviour described in item 3 still holds when using CMake.

@fanquake
Copy link
Member

fanquake commented Sep 3, 2024

it seems unreasonable to expect from CMake configured for the "Release" build type pull in debug flags from depends.

CMake shouldn't have a concept of "depends debug" flags (it shouldn't really even know about depends). It should just get flags/options passed to it in a toolchain file (which is the interface between the two), and it should use them. Having any special coupling/behaviour based on if things are a "depends" build or not, removes the ability for anyone else to bring a CMake toolchain file, and expect it to work properly.

We should open an issue to follow up on this, as at a minimum, it needs better documentation around expections / how things are meant to work, or even reworking, as at the moment, the behaviour seems inconsistent/unexpected.

Copy link
Member

@fanquake fanquake left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK 787dfaf - as a follow up it would be good to:

  • Actually figure out why this fix works, and why the other msan build wasn't broken: #30778 (comment).
  • Update oss-fuzz to match what is being done here? Now that they've diverged.
  • Open an issue to figure out expectations around depends-with-cmake behaviour.

@maflcko
Copy link
Member

maflcko commented Sep 3, 2024

For reference, before (https://cirrus-ci.com/task/6668539271053312?logs=ci#L1064):

Cross compiling ....................... FALSE
C++ compiler .......................... Clang 18.1.3, /usr/bin/clang++
CMAKE_BUILD_TYPE ...................... RelWithDebInfo
Preprocessor defined macros ........... 
C++ compiler flags .................... -fsanitize=memory -fsanitize-memory-track-origins=2 -fno-omit-frame-pointer -g -O1 -fno-optimize-sibling-calls -nostdinc++ -nostdlib++ -isystem /msan/cxx_build/include/c++/v1 -L/msan/cxx_build/lib -Wl,-rpath,/msan/cxx_build/lib -lc++ -lc++abi -lpthread -Wno-unused-command-line-argument -O2 -g -std=c++20 -fPIC -fdebug-prefix-map=/ci_container_base=. -fmacro-prefix-map=/ci_container_base=. -Werror -fsanitize=memory -Wall -Wextra -Wgnu -Wformat -Wformat-security -Wvla -Wshadow-field -Wthread-safety -Wloop-analysis -Wredundant-decls -Wunused-member-function -Wdate-time -Wconditional-uninitialized -Woverloaded-virtual -Wsuggest-override -Wimplicit-fallthrough -Wunreachable-code -Wdocumentation -Wself-assign -Wundef -Wno-unused-parameter -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=3 -Wstack-protector -fstack-protector-all -fcf-protection=full -fstack-clash-protection -U_FORTIFY_SOURCE
Linker flags .......................... -fsanitize=memory -fsanitize-memory-track-origins=2 -fno-omit-frame-pointer -g -O1 -fno-optimize-sibling-calls -nostdinc++ -nostdlib++ -isystem /msan/cxx_build/include/c++/v1 -L/msan/cxx_build/lib -Wl,-rpath,/msan/cxx_build/lib -lc++ -lc++abi -lpthread -Wno-unused-command-line-argument -O2 -g -fsanitize=memory -fstack-protector-all -fcf-protection=full -fstack-clash-protection -Wl,-z,relro -Wl,-z,now -Wl,-z,separate-code -fPIE -pie

After (https://api.cirrus-ci.com/v1/task/6661080322146304/logs/ci.log):

Cross compiling ....................... FALSE
C++ compiler .......................... Clang 18.1.3, /usr/bin/clang++
CMAKE_BUILD_TYPE ...................... Debug
Preprocessor defined macros ........... DEBUG DEBUG_LOCKORDER DEBUG_LOCKCONTENTION RPC_DOC_CHECK ABORT_ON_FAILED_ASSUME _GLIBCXX_DEBUG _GLIBCXX_DEBUG_PEDANTIC _LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_DEBUG
C++ compiler flags .................... -fsanitize=memory -fsanitize-memory-track-origins=2 -fno-omit-frame-pointer -g -O1 -fno-optimize-sibling-calls -nostdinc++ -nostdlib++ -isystem /msan/cxx_build/include/c++/v1 -L/msan/cxx_build/lib -Wl,-rpath,/msan/cxx_build/lib -lc++ -lc++abi -lpthread -Wno-unused-command-line-argument -std=c++20 -fPIC -fdebug-prefix-map=/ci_container_base=. -fmacro-prefix-map=/ci_container_base=. -Werror -fsanitize=memory -Wall -Wextra -Wgnu -Wformat -Wformat-security -Wvla -Wshadow-field -Wthread-safety -Wloop-analysis -Wredundant-decls -Wunused-member-function -Wdate-time -Wconditional-uninitialized -Woverloaded-virtual -Wsuggest-override -Wimplicit-fallthrough -Wunreachable-code -Wdocumentation -Wself-assign -Wundef -Wno-unused-parameter -Wstack-protector -fstack-protector-all -fcf-protection=full -fstack-clash-protection -U_FORTIFY_SOURCE
Linker flags .......................... -fsanitize=memory -fsanitize-memory-track-origins=2 -fno-omit-frame-pointer -g -O1 -fno-optimize-sibling-calls -nostdinc++ -nostdlib++ -isystem /msan/cxx_build/include/c++/v1 -L/msan/cxx_build/lib -Wl,-rpath,/msan/cxx_build/lib -lc++ -lc++abi -lpthread -Wno-unused-command-line-argument -fsanitize=memory -fstack-protector-all -fcf-protection=full -fstack-clash-protection -Wl,-z,relro -Wl,-z,now -Wl,-z,separate-code -fPIE -pie

@fanquake fanquake merged commit 9cb9651 into bitcoin:master Sep 3, 2024
@hebasto
Copy link
Member Author

hebasto commented Sep 3, 2024

... as a follow up it would be good to:

  • Update oss-fuzz to match what is being done here? Now that they've diverged.

The OSS-Fuzz script controls all flags explicitly, including https://github.com/google/oss-fuzz/blob/5403c34ff006baf410b94cad1112a6c385d1bade/projects/bitcoin-core/build.sh#L50:

export CPPFLAGS="-D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_DEBUG -DBOOST_MULTI_INDEX_ENABLE_SAFE_MODE"

@fanquake
Copy link
Member

fanquake commented Sep 3, 2024

The OSS-Fuzz script controls all flags explicitly

Sure, but you changed the CMake build type here, so why not change it in oss-fuzz? Otherwise, it'd be good to explain somewhere why they (should) differ.

@hebasto
Copy link
Member Author

hebasto commented Sep 3, 2024

The OSS-Fuzz script controls all flags explicitly

Sure, but you changed the CMake build type here, so why not change it in oss-fuzz? Otherwise, it'd be good to explain somewhere why they (should) differ.

Sure. Please consider google/oss-fuzz#12443.

@hebasto
Copy link
Member Author

hebasto commented Sep 3, 2024

# BDB generates false-positives and will be removed in future
export DEP_OPTS="DEBUG=1 NO_BDB=1 NO_QT=1 CC=clang CXX=clang++ CFLAGS='${MSAN_FLAGS}' CXXFLAGS='${MSAN_AND_LIBCXX_FLAGS}'"
export GOAL="install"
# Setting CMAKE_{C,CXX}_FLAGS_DEBUG flags to an empty string ensures that the flags set in MSAN_FLAGS remain unaltered.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I understand why this needs to pass BUILD_TYPE=Debug so the debug flags from the depends system will be applied. But I don't understand why FLAGS_DEBUG variables need to be set to empty stings. How does setting those to empty strings cause MSAN_FLAGS to remain unaltered? What code is altering MSAN_FLAGS if FLAGS_DEBUG variables are unset?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps I used the word 'unaltered' incorrectly. It would be more accurate to say that "flags set in MSAN_FLAGS won't be overridden". The CMAKE_CXX_FLAGS_DEBUG follow CMAKE_CXX_FLAGS in the compiler invocation string and competing flags may be overridden.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point that "overridden" is probably more appropriate word, but I think I already understood that this meant overridden.

The thing I don't understand is what causes MSAN_FLAGS to be overridden when FLAGS_DEBUG variables are unset, but not overridden when they are empty? Appreciate the clarification. I'm not asking for any particular reason, just curious to understand this better.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure. The logic is follows:

  1. The MSAN_FLAGS variable's value is ... -g -O1 ....
  2. This value is assigned to CXXFLAGS for building depends and become the value of CMAKE_CXX_FLAGS in the toolchain file.
  3. Being configured with -DCMAKE_BUILD_TYPE=Debug, the build system may assign -O0 -ftrapv -g3 to CMAKE_CXX_FLAGS_DEBUG.
  4. When invoking a compier, CMAKE_CXX_FLAGS and CMAKE_CXX_FLAGS_DEBUG are concatenated, resulting in .. -g -O1 ... -O0 ... -g3 .... This means that flags set in MSAN_FLAGS will not have any effect.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very helpful, thanks. I think I would write comment as "Set CMAKE_{C,CXX}_FLAGS_DEBUG options to empty so CMake's default options for debug builds do not interfere with MSAN flags"

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Followup points:

  • I misunderstood "the build system may assign" to mean "cmake may assign" but actually it is referring to our own ProcessConfigurations.cmake file. So I think a more accurate comment than what I suggested would be "Set CMAKE_{C,CXX}_FLAGS_DEBUG to empty so the default options normally used for debug builds do not override MSAN options in CMAKE_{C,CXX}_FLAGS"

  • I'm probably missing something, but maybe a cleaner workaround instead of setting Debug build type and then erasing all the debug flags is just to set a custom build type -DCMAKE_BUILD_TYPE=msan that doesn't have the flags to begin with?

  • Probably not applicable to this case, but it does seem unfortunate that only having FLAGS and FLAGS_<build_type> options forces a choice between specifying lower priority flags independent of build type, or higher priority flags tied to the build type. Ideally there would be a third option to specify high priority flags not depending on build type, maybe called FLAGS_ALL that would be applied to all build configurations. This seems like something that would not be too hard to support. Not important here, but maybe something to look into if there are more cases like this.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • I'm probably missing something, but maybe a cleaner workaround instead of setting Debug build type and then erasing all the debug flags is just to set a custom build type -DCMAKE_BUILD_TYPE=msan that doesn't have the flags to begin with?

I agree with you, and that was my initial preference when I worked on the CMake staging branch. However, using undefined build configurations is not documented. As a result, the rough consensus among the CMake staging branch contributors was to adhere to documented practices.

  • Probably not applicable to this case, but it does seem unfortunate that only having FLAGS and FLAGS_<build_type> options forces a choice between specifying lower priority flags independent of build type, or higher priority flags tied to the build type. Ideally there would be a third option to specify high priority flags not depending on build type, maybe called FLAGS_ALL that would be applied to all build configurations. This seems like something that would not be too hard to support. Not important here, but maybe something to look into if there are more cases like this.

Such an option is implemented in the APPEND_* variables, if I understood you correctly:

bitcoin/CMakeLists.txt

Lines 203 to 206 in 0e5cd60

set(APPEND_CPPFLAGS "" CACHE STRING "Preprocessor flags that are appended to the command line after all other flags added by the build system. This variable is intended for debugging and special builds.")
set(APPEND_CFLAGS "" CACHE STRING "C compiler flags that are appended to the command line after all other flags added by the build system. This variable is intended for debugging and special builds.")
set(APPEND_CXXFLAGS "" CACHE STRING "(Objective) C++ compiler flags that are appended to the command line after all other flags added by the build system. This variable is intended for debugging and special builds.")
set(APPEND_LDFLAGS "" CACHE STRING "Linker flags that are appended to the command line after all other flags added by the build system. This variable is intended for debugging and special builds.")

-DCMAKE_C_FLAGS_DEBUG='' \
-DCMAKE_CXX_FLAGS_DEBUG='' \
-DSANITIZERS=memory \
-DAPPEND_CPPFLAGS='-U_FORTIFY_SOURCE' \
Copy link
Member

@fanquake fanquake Sep 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So given the Build type change, this line, and the equivalent flag in the other CI config, are redundant and should have been removed. However I don't actually think they should be removed, because it's instead CMake that should be fixed, to remove the assumption that builds using the Debug build type, will always have no optimisations, as this isn't actually the case, for example, in oss-fuzz, or doing a depends build with DEBUG=1. See further discussion in google/oss-fuzz#12443.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Issue in #30800.

fanquake added a commit that referenced this pull request Sep 9, 2024
…ld type

30803a3 cmake: decouple FORTIFY_SOURCE check from Debug build type (fanquake)

Pull request description:

  `FORTIFY_SOURCE` should be used if `ENABLE_HARDENING=ON` and optimisations are being used. This should not be coupled to any particular build type, because even if the build type is `Debug`, optimisations might still be in use.

  Fixes: #30800.
  Also somewhat of a followup to #30778 (comment).

ACKs for top commit:
  ryanofsky:
    Code review ACK 30803a3
  TheCharlatan:
    ACK 30803a3

Tree-SHA512: 298f8805a5bb2f1ff54e51ea31324d712c2070cc3eba26561c31001ace4bfa37ae6d18531cbd45e2faf610a0a1b83b420fcde6e329e17f02b021d26563583913
@bitcoin bitcoin locked and limited conversation to collaborators Sep 6, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

Projects

No open projects
Status: CMake follow-ups

Development

Successfully merging this pull request may close these issues.

ci: fuzz_msan failed with ==4201==WARNING: MemorySanitizer: use-of-uninitialized-value #0 0x55f0c9bdeffb in SetArgs

5 participants