| // Copyright 2024 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifdef UNSAFE_BUFFERS_BUILD |
| // TODO(crbug.com/40285824): Remove this and convert code to safer constructs. |
| #pragma allow_unsafe_buffers |
| #endif |
| |
| #import "ios/components/order_file/order_file_common.h" |
| |
| #import <stdlib.h> |
| |
| // Queue containing the ordered procedure calls. |
| OSQueueHead gCRWSanitizerQueue = OS_ATOMIC_QUEUE_INIT; |
| |
| // Whether the guard variables have been initialized. |
| bool gCRWGuardsInitialized = false; |
| |
| // Whether the addition of more procedure calls is allowed. |
| bool gCRWFinishedCollecting = false; |
| |
| void __sanitizer_cov_trace_pc_guard_init(uint32_t* start, |
| uint32_t* stop); // NOLINT |
| void __sanitizer_cov_trace_pc_guard(uint32_t* guard); // NOLINT |
| |
| // Initialize the guards for each procedure call. |
| // Per clang documentation, this function will be called at least once per |
| // Dynamic Shared Object (DSO) and may be called more than once with the same |
| // values of start/stop. |
| void __sanitizer_cov_trace_pc_guard_init(uint32_t* start_guard, |
| uint32_t* stop_guard) { // NOLINT |
| gCRWGuardsInitialized = true; |
| if (start_guard == stop_guard || *start_guard) { |
| return; |
| } |
| for (uint32_t* guard = start_guard; guard < stop_guard; guard++) { |
| // Per https://clang.llvm.org/docs/SanitizerCoverage.html, *guard is |
| // initialized to 1 and set to 0 below. |
| *guard = 1; |
| } |
| } |
| |
| // Collect each procedure call in order once for each distinct call. |
| // Per clang documentation, this callback is inserted by the compiler on every |
| // edge in the control flow (some optimizations apply). |
| void __sanitizer_cov_trace_pc_guard(uint32_t* guard) { // NOLINT |
| // If gGuardsInitialized is false, guard flags have not yet been initialized |
| // and this is probably recording a +load method which should still be |
| // included in the order file. |
| if (gCRWFinishedCollecting || (!(*guard) && gCRWGuardsInitialized)) { |
| return; |
| } |
| // Make sure each procedure call is only collected once. |
| *guard = 0; |
| |
| void* procedureCall = __builtin_return_address(0); |
| CRWProcedureCallNode* node = |
| (CRWProcedureCallNode*)malloc(sizeof(CRWProcedureCallNode)); |
| *node = (CRWProcedureCallNode){procedureCall, NULL}; |
| OSAtomicEnqueue(&gCRWSanitizerQueue, node, |
| offsetof(CRWProcedureCallNode, next)); |
| } |