diff --git a/centipede/BUILD b/centipede/BUILD index 1d0e7ee9d..acb49ebd4 100644 --- a/centipede/BUILD +++ b/centipede/BUILD @@ -921,7 +921,6 @@ RUNNER_SOURCES_NO_MAIN = [ "runner_interceptors.cc", "runner_interface.h", "runner_sancov.cc", - "runner_sanitizer.cc", "shared_memory_blob_sequence.cc", "shared_memory_blob_sequence.h", ] diff --git a/centipede/puzzles/puzzle.bzl b/centipede/puzzles/puzzle.bzl index 97d443044..3be024219 100644 --- a/centipede/puzzles/puzzle.bzl +++ b/centipede/puzzles/puzzle.bzl @@ -38,7 +38,7 @@ def puzzle(name): data = [ ":" + name, name + ".cc", - "@com_google_fuzztest//centipede", + "@com_google_fuzztest//centipede:centipede_uninstrumented", "@com_google_fuzztest//centipede:test_util_sh", ], ) diff --git a/centipede/puzzles/run_puzzle.sh b/centipede/puzzles/run_puzzle.sh index cfe0e55fa..799f72123 100755 --- a/centipede/puzzles/run_puzzle.sh +++ b/centipede/puzzles/run_puzzle.sh @@ -28,7 +28,7 @@ ls -la "$(dirname "$0")" source "$(dirname "$0")/../test_util.sh" readonly centipede_dir="$(centipede::get_centipede_test_srcdir)" -centipede::maybe_set_var_to_executable_path centipede "${centipede_dir}/centipede" +centipede::maybe_set_var_to_executable_path centipede "${centipede_dir}/centipede_uninstrumented" readonly centipede readonly target_name="$(basename "$0")" diff --git a/centipede/run_test_workflow.sh b/centipede/run_test_workflow.sh index a88054aaa..e15016f1d 100755 --- a/centipede/run_test_workflow.sh +++ b/centipede/run_test_workflow.sh @@ -66,6 +66,8 @@ bazel test "${BAZEL_ARGS[@]}" --local_test_jobs=1 --test_output=streamed \ centipede:all && bazel test "${BAZEL_ARGS[@]}" centipede/testing:instrumentation_test centipede/testing:runner_test && bazel test "${BAZEL_ARGS[@]}" centipede/puzzles:all +bazel test "${BAZEL_ARGS[@]}" --linkopt=-fsanitize=address --copt=-fsanitize=address centipede/puzzles:all + declare -ri exit_code=$? set -e diff --git a/centipede/runner.cc b/centipede/runner.cc index 8bffdd972..ecb18785a 100644 --- a/centipede/runner.cc +++ b/centipede/runner.cc @@ -908,9 +908,10 @@ extern void ForkServerCallMeVeryEarly(); // * linker sees them and decides to drop runner_sancov.o. extern void RunnerSancov(); [[maybe_unused]] auto fake_reference_for_runner_sancov = &RunnerSancov; -// Same for runner_sanitizer.cc. -extern void RunnerSanitizer(); -[[maybe_unused]] auto fake_reference_for_runner_sanitizer = &RunnerSanitizer; +// Same for runner_interceptor.cc. +extern void RunnerInterceptor(); +[[maybe_unused]] auto fake_reference_for_runner_interceptor = + &RunnerInterceptor; GlobalRunnerState::GlobalRunnerState() { // TODO(kcc): move some code from CentipedeRunnerMain() here so that it works diff --git a/centipede/runner_interceptors.cc b/centipede/runner_interceptors.cc index 7f94fbeb1..62776b8d9 100644 --- a/centipede/runner_interceptors.cc +++ b/centipede/runner_interceptors.cc @@ -13,13 +13,7 @@ // limitations under the License. // Function interceptors for Centipede. -// Interceptors are disabled under ASAN/TSAN/MSAN because those sanitizers -// have their own conflicting interceptors. -// The typical usage of sanitizers with Centipede is via the --extra_binaries -// flag, where the sanitized binary does not produce coverage output and thus -// doesn't need (most of?) interceptors. -#if !defined(ADDRESS_SANITIZER) && !defined(THREAD_SANITIZER) && \ - !defined(MEMORY_SANITIZER) + #include // for dlsym() #include @@ -77,19 +71,49 @@ int NormalizeCmpResult(int result) { } // namespace -// Initialize original function pointers at the module startup. This may still -// be too late, since the functions may be used before this module is -// initialized. So, the interceptor may not assume that X_orig != nullptr for -// function X. -static auto memcmp_orig = - FuncAddr("memcmp"); -static auto strcmp_orig = - FuncAddr("strcmp"); - -// TODO(kcc): as we implement more functions like memcmp_fallback and -// length_of_common_prefix, move them into a separate module and unittest. +namespace centipede { +void RunnerInterceptor() {} // to be referenced in runner.cc +} // namespace centipede -// Fallback for the case memcmp_orig is null. +// A sanitizer-compatible way to intercept functions that are potentially +// intercepted by sanitizers, in which case the symbol __interceptor_X would be +// defined for intercepted function X. So we always forward an intercepted call +// to the sanitizer interceptor if it exists, and fall back to the next +// definition following dlsym. +// +// We define the X_orig pointers that are statically initialized to GetOrig_X() +// with the aforementioned logic to fill the pointers early, but they might +// still be too late. So the Centipede interceptors might need to handle the +// nullptr case and/or use REAL(X), which calls GetOrig_X() when needed. Also +// see compiler-rt/lib/interception/interception.h in the llvm-project source +// code. +// +// Note that since LLVM 17 it allows three interceptions (from the original +// binary, an external tool, and a sanitizer) to co-exist under a new scheme, +// while it is still compatible with the old way used here. +#define SANITIZER_INTERCEPTOR_NAME(orig_func_name) \ + __interceptor_##orig_func_name +#define DECLARE_CENTIPEDE_ORIG_FUNC(ret_type, orig_func_name, args) \ + extern "C" __attribute__((weak)) \ + ret_type(SANITIZER_INTERCEPTOR_NAME(orig_func_name)) args; \ + static decltype(&SANITIZER_INTERCEPTOR_NAME( \ + orig_func_name)) GetOrig_##orig_func_name() { \ + if (auto p = &SANITIZER_INTERCEPTOR_NAME(orig_func_name)) return p; \ + return FuncAddr( \ + #orig_func_name); \ + } \ + static ret_type(*orig_func_name##_orig) args = GetOrig_##orig_func_name() +#define REAL(orig_func_name) \ + (orig_func_name##_orig ? orig_func_name##_orig : GetOrig_##orig_func_name()) + +DECLARE_CENTIPEDE_ORIG_FUNC(int, memcmp, + (const void *s1, const void *s2, size_t n)); +DECLARE_CENTIPEDE_ORIG_FUNC(int, strcmp, (const char *s1, const char *s2)); +DECLARE_CENTIPEDE_ORIG_FUNC(int, pthread_create, + (pthread_t * thread, const pthread_attr_t *attr, + void *(*start_routine)(void *), void *arg)); + +// Fallback for the case *cmp_orig is null. // Will be executed several times at process startup, if at all. static int memcmp_fallback(const void *s1, const void *s2, size_t n) { const auto *p1 = static_cast(s1); @@ -131,12 +155,8 @@ extern "C" int strcmp(const char *s1, const char *s2) { // Calls real pthread_create, but wraps the start_routine() in MyThreadStart. extern "C" int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg) { - static auto pthread_create_orig = - FuncAddr("pthread_create"); // Wrap the arguments. Will be deleted in MyThreadStart. auto *wrapped_args = new ThreadCreateArgs{start_routine, arg}; // Run the actual pthread_create. - return pthread_create_orig(thread, attr, MyThreadStart, wrapped_args); + return REAL(pthread_create)(thread, attr, MyThreadStart, wrapped_args); } -#endif // not ASAN/TSAN/MSAN diff --git a/centipede/runner_sanitizer.cc b/centipede/runner_sanitizer.cc deleted file mode 100644 index db38fb472..000000000 --- a/centipede/runner_sanitizer.cc +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2023 The Centipede Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Instrumentation callbacks for sanitizers. See -// compiler-rt/include/sanitizer/common_interface_defs.h in the LLVM -// repo. - -#include -#include - -#include "./centipede/runner.h" - -#define NO_SANITIZE __attribute__((no_sanitize("all"))) - -namespace centipede { -void RunnerSanitizer() {} // to be referenced in runner.cc -} // namespace centipede - -NO_SANITIZE -extern "C" void __sanitizer_weak_hook_memcmp(void *caller_pc, const void *s1, - const void *s2, size_t n, - int result) { - if (s1 == nullptr || s2 == nullptr) return; - centipede::tls.TraceMemCmp(reinterpret_cast(caller_pc), - reinterpret_cast(s1), - reinterpret_cast(s2), n, - result == 0); -} -NO_SANITIZE -extern "C" void __sanitizer_weak_hook_strcmp(void *caller_pc, const char *s1, - const char *s2, int result) { - if (s1 == nullptr || s2 == nullptr) return; - size_t len = 0; - while (s1[len] && s2[len]) ++len; - centipede::tls.TraceMemCmp(reinterpret_cast(caller_pc), - reinterpret_cast(s1), - reinterpret_cast(s2), len, - result == 0); -} diff --git a/centipede/testing/build_defs.bzl b/centipede/testing/build_defs.bzl index d7d8d2d17..ec3878f0e 100644 --- a/centipede/testing/build_defs.bzl +++ b/centipede/testing/build_defs.bzl @@ -23,7 +23,7 @@ affect all its transitive dependencies as well. # Change the flags from the default ones to sancov: # https://clang.llvm.org/docs/SanitizerCoverage.html. def _sancov_transition_impl(settings, attr): - features_to_strip = ["asan", "tsan", "msan"] + features_to_strip = ["tsan", "msan"] filtered_features = [ x for x in settings["//command_line_option:features"]