| // Copyright 2026 Google LLC |
| // |
| // 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 |
| // |
| // http://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. |
| |
| // SAFETY: |
| // - `FuzzTestSetSanitizerErrorSummaryCallback` is declared with `extern "C"` linkage in |
| // `internal/sanitizer_interface.h` and defined in `internal/sanitizer_interface.cc`. |
| // - The signature matches `void FuzzTestSetSanitizerErrorSummaryCallback(void (*)(const char*, size_t))`: |
| // `*const u8` is layout- and ABI-compatible with `const char*`, and `usize` is ABI-compatible |
| // with `size_t`. |
| unsafe extern "C" { |
| fn FuzzTestSetSanitizerErrorSummaryCallback( |
| callback: unsafe extern "C" fn(crash_type_data: *const u8, crash_type_size: usize), |
| ); |
| } |
| |
| /// Sanitizer error summary callback invoked by the C++ sanitizer interface. |
| /// |
| /// # Safety |
| /// |
| /// The caller must uphold the following preconditions: |
| /// - If `crash_type_size > 0`, `crash_type_data` must be non-null and valid for reads of |
| /// `crash_type_size` consecutive, initialized `u8` bytes for the duration of the call. |
| /// - The pointed-to memory must not be mutated concurrently for the duration of the call. |
| /// - `crash_type_size` must not exceed `isize::MAX`. |
| unsafe extern "C" fn sanitizer_error_summary_callback( |
| crash_type_data: *const u8, |
| crash_type_size: usize, |
| ) { |
| let crash_type = if crash_type_data.is_null() || crash_type_size == 0 { |
| "Sanitizer crash" |
| } else { |
| // SAFETY: |
| // - Non-nullness: `crash_type_data` was verified non-null above. |
| // - Alignment: `u8` has alignment 1, so any non-null pointer is properly aligned. |
| // - Validity: The caller guarantees `crash_type_data` points to `crash_type_size` |
| // consecutive, initialized `u8` bytes valid for reads for the duration of this call. |
| // - Aliasing: The pointed-to memory is read-only and not mutated during the call. |
| // - Size: The caller guarantees `crash_type_size <= isize::MAX`. |
| let crash_type_bytes = |
| unsafe { std::slice::from_raw_parts(crash_type_data, crash_type_size) }; |
| std::str::from_utf8(crash_type_bytes).unwrap_or("Sanitizer crash (invalid utf8)") |
| }; |
| crate::worker::try_emit_finding(crash_type, crash_type); |
| } |
| |
| /// Registers the sanitizer error summary callback and ensures the sanitizer crash handler hook is |
| /// linked into the binary. |
| pub fn register_crash_handler() { |
| // SAFETY: |
| // - `sanitizer_error_summary_callback` is an `unsafe extern "C" fn` matching the C callback |
| // signature `FuzzTestSanitizerErrorSummaryCallback` (`void (*)(const char*, size_t)`). |
| // - As a function item, `sanitizer_error_summary_callback` has `'static` lifetime and remains |
| // valid for the entire duration of program execution. |
| // - `FuzzTestSetSanitizerErrorSummaryCallback` stores the function pointer in a `std::atomic` |
| // using `memory_order_relaxed`, so concurrent registration is data-race free. |
| unsafe { |
| FuzzTestSetSanitizerErrorSummaryCallback(sanitizer_error_summary_callback); |
| } |
| } |