├── .bazelrc ├── .clang-format ├── .github └── workflows │ └── tests.yml ├── .gitignore ├── AUTHORS ├── BUILD ├── CONTRIBUTING.md ├── CONTRIBUTORS ├── LICENSE ├── MODULE.bazel ├── README.md ├── WORKSPACE ├── check-license.sh ├── docs ├── dependencies.md ├── design.md └── tools.md └── gwpsan ├── BUILD ├── base ├── BUILD ├── algorithm.h ├── algorithm_test.cpp ├── allocator.cpp ├── allocator.h ├── array.h ├── bazel.cpp ├── bazel.h ├── bazel_test.cpp ├── cc_implicit_output.bzl ├── common.h ├── common_test.cpp ├── config.h ├── defs.bzl ├── env.cpp ├── env.h ├── fault_inject.cpp ├── fault_inject.h ├── flags.cpp ├── flags.h ├── flags_test.cpp ├── linux.h ├── log.cpp ├── log.h ├── memory.cpp ├── memory.h ├── memory_test.cpp ├── metric.h ├── metric_collection.cpp ├── metric_collection.h ├── metric_test.cpp ├── module_list.cpp ├── module_list.h ├── module_list_test.cpp ├── numeric.cpp ├── numeric.h ├── numeric_test.cpp ├── optional.h ├── os.cpp ├── os.h ├── os_test.cpp ├── printf.cpp ├── printf.h ├── printf_test.cpp ├── sanitizer.h ├── signal.cpp ├── signal.h ├── signal_test.cpp ├── signal_test_foreign_interceptor.cpp ├── span.h ├── stdlib.h ├── stdlib_disallow.inc ├── stdlib_disallow_update.sh ├── string.cpp ├── string.h ├── string_test.cpp ├── synchronization.cpp ├── synchronization.h ├── syscall.h ├── syscall_arm64.h ├── syscall_x86.h ├── test_flags.cpp ├── test_report_interceptor.cpp ├── test_report_interceptor.h ├── test_report_interceptor_test.cpp ├── test_signal_listener.h ├── timer.cpp ├── timer.h ├── timer_test.cpp ├── type_id.h ├── units.h ├── units_test_printer.cpp ├── unwind.cpp ├── unwind.h ├── vector.h ├── vector_test.cpp ├── weak_imports.h ├── weak_imports_test.cpp └── weak_imports_use.cpp ├── core ├── BUILD ├── arch.h ├── breakmanager.cpp ├── breakmanager.h ├── breakmanager_benchmark.cpp ├── breakmanager_perf_test.cpp ├── breakmanager_stress_test.cpp ├── breakmanager_test.cpp ├── breakpoint.cpp ├── breakpoint.h ├── breakpoint_benchmark.cpp ├── breakpoint_test.cpp ├── context.cpp ├── context.h ├── context_arm64.cpp ├── context_benchmark.cpp ├── context_x86.cpp ├── core_fwd.h ├── decode.h ├── decoder.cpp ├── decoder.h ├── decoder_arm64.cpp ├── decoder_arm64.h ├── decoder_dumper.cpp ├── decoder_dynamorio.cpp ├── decoder_dynamorio.h ├── decoder_executor.cpp ├── decoder_executor.h ├── decoder_fuzzer.cpp ├── decoder_test.cpp ├── decoder_x86.cpp ├── decoder_x86.h ├── disable_test.cpp ├── env.cpp ├── env.h ├── env_test.cpp ├── flags.cpp ├── flags.h ├── init.cpp ├── init.h ├── init_for_test.cpp ├── instruction.cpp ├── instruction.h ├── known_functions.cpp ├── known_functions.h ├── known_functions_test.cpp ├── meta.cpp ├── meta.h ├── meta_test.cpp ├── operation.cpp ├── operation.h ├── operation_test.cpp ├── origin.cpp ├── origin.h ├── origin_test.cpp ├── regset.cpp ├── regset.h ├── regset_test.cpp ├── report.cpp ├── report.h ├── report_test.cpp ├── semantic_metadata.cpp ├── semantic_metadata.h ├── semantic_metadata_test.cpp ├── semantic_metadata_test_lib1.cpp ├── semantic_metadata_test_lib1_uncovered.cpp ├── semantic_metadata_test_lib2.cpp ├── semantic_metadata_test_linkstatic.cpp ├── store_buffer.cpp ├── store_buffer.h ├── store_buffer_test.cpp ├── unwind_instruction.cpp ├── unwind_instruction.h └── unwind_instruction_test.cpp ├── import ├── BUILD ├── int_lib.h ├── udivmodti4.cpp └── udivti3.cpp ├── lmsan ├── BUILD ├── lmsan.cpp └── lmsan_test.cpp ├── tsan ├── BUILD ├── tsan.cpp ├── tsan.h └── tsan_test.cpp ├── uar ├── BUILD ├── interceptors.cpp ├── uar.cpp ├── uar.h ├── uar_benchmark.cpp └── uar_test.cpp └── unified ├── BUILD ├── init.cpp ├── redefined.syms ├── tool.h ├── unified.cpp ├── unified.h ├── unified_notool_test.cpp └── unified_test.cpp /.bazelrc: -------------------------------------------------------------------------------- 1 | # GWPSan depends on Clang. Currently no GCC support is planned. 2 | build --config=clang 3 | build --cxxopt=-std=c++20 4 | build --cxxopt=-Wno-mismatched-tags # Don't care about Microsoft ABI 5 | build --copt=--system-header-prefix=external/ # Do not warn in dependencies 6 | 7 | # Common flags for Clang 8 | build:clang --action_env=BAZEL_COMPILER=clang 9 | build:clang --action_env=CC=clang --action_env=CXX=clang++ 10 | build:clang --linkopt=-fuse-ld=lld 11 | 12 | # LLVM libc++ 13 | build:libc++ --action_env=CXXFLAGS=-stdlib=libc++ 14 | build:libc++ --action_env=LDFLAGS=-stdlib=libc++ 15 | build:libc++ --action_env=BAZEL_CXXOPTS=-stdlib=libc++ 16 | build:libc++ --action_env=BAZEL_LINKOPTS=-lc++:-lm 17 | 18 | # x86-64: --config=x86_64 19 | build:x86_64 --copt=-mcx16 # We rely on 16-byte atomics 20 | build:x86_64 --copt=-mcrc32 # Need CRC32 builtins 21 | 22 | # Developer/CI config with more compiler warnings: --config=dev 23 | build:dev --copt=-Wall 24 | build:dev --copt=-Werror 25 | 26 | # ASan: --config=asan 27 | build:asan --//gwpsan:sanitizer=asan 28 | build:asan --copt=-fsanitize=address 29 | build:asan --copt=-fsanitize-address-use-after-scope 30 | build:asan --copt=-fsanitize-address-use-after-return=runtime 31 | build:asan --copt=-DADDRESS_SANITIZER 32 | build:asan --linkopt=-fsanitize=address 33 | build:asan --cc_output_directory_tag=asan 34 | 35 | # MSan: --config=msan (Note: need MSan'ified stdlibs!) 36 | build:msan --//gwpsan:sanitizer=msan 37 | build:msan --copt=-fsanitize=memory 38 | build:msan --copt=-fsanitize-memory-track-origins 39 | build:msan --copt=-DMEMORY_SANITIZER 40 | build:msan --linkopt=-fsanitize=memory 41 | build:msan --cc_output_directory_tag=msan 42 | 43 | # ASan-enabled fuzzer: --config=asan-libfuzzer 44 | build:asan-libfuzzer --@rules_fuzzing//fuzzing:cc_engine=@rules_fuzzing//fuzzing/engines:libfuzzer 45 | build:asan-libfuzzer --@rules_fuzzing//fuzzing:cc_engine_instrumentation=libfuzzer 46 | build:asan-libfuzzer --@rules_fuzzing//fuzzing:cc_engine_sanitizer=asan 47 | build:asan-libfuzzer --cc_output_directory_tag=asan-libfuzzer 48 | 49 | # MSan-enabled fuzzer: --config=msan-libfuzzer 50 | build:msan-libfuzzer --@rules_fuzzing//fuzzing:cc_engine=@rules_fuzzing//fuzzing/engines:libfuzzer 51 | build:msan-libfuzzer --@rules_fuzzing//fuzzing:cc_engine_instrumentation=libfuzzer 52 | build:msan-libfuzzer --@rules_fuzzing//fuzzing:cc_engine_sanitizer=msan 53 | build:msan-libfuzzer --cc_output_directory_tag=msan-libfuzzer 54 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: Google 2 | DerivePointerAlignment: false 3 | PointerAlignment: Left 4 | IndentCaseLabels: false 5 | AllowShortBlocksOnASingleLine: Empty 6 | AllowShortFunctionsOnASingleLine: Empty 7 | AllowShortIfStatementsOnASingleLine: false 8 | AllowShortLoopsOnASingleLine: false 9 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 10 | BreakConstructorInitializersBeforeComma: true 11 | AlignArrayOfStructures: Left 12 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2024 The GWPSan Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | name: Tests 16 | 17 | on: 18 | push: 19 | branches: 20 | - main 21 | pull_request: 22 | schedule: # Triggered nightly to reset the Bazel cache. 23 | - cron: '42 5 * * *' 24 | workflow_dispatch: 25 | 26 | jobs: 27 | run_tests: 28 | runs-on: ubuntu-latest 29 | timeout-minutes: 60 30 | 31 | strategy: 32 | matrix: 33 | compilation_mode: ['fastbuild', 'opt'] 34 | 35 | steps: 36 | - name: Checkout repository 37 | uses: actions/checkout@v4 38 | 39 | - name: Check license headers 40 | run: "./check-license.sh" 41 | 42 | - name: Install dependencies 43 | # We need: 44 | # - LLVM C++ Standard library (some GNU libstdc++ fail with newer Clang) 45 | # - Clang 18 or later 46 | run: | 47 | set -e 48 | sudo apt-get update 49 | wget https://apt.llvm.org/llvm.sh -O /tmp/llvm.sh 50 | yes '' | sudo bash /tmp/llvm.sh 18 51 | sudo apt-get install -yq libc++-18-dev libc++abi-18-dev 52 | - name: Configure system 53 | # Ubuntu systems disallow perf events completely with the undocumented 54 | # perf_event_paranoid=4. Restore default kernel setting. 55 | run: | 56 | set -e 57 | uname -a 58 | echo "old perf_event_paranoid: $(< /proc/sys/kernel/perf_event_paranoid)" 59 | echo 2 | sudo tee /proc/sys/kernel/perf_event_paranoid 60 | 61 | - name: Restore Bazel cache 62 | uses: actions/cache/restore@v4 63 | with: 64 | path: "~/.cache/bazel" 65 | key: bazel-cache-${{ matrix.compilation_mode }} 66 | restore-keys: bazel-cache-${{ matrix.compilation_mode }}- 67 | - name: Run Bazel tests 68 | env: 69 | CC: clang-18 70 | CXX: clang++-18 71 | run: | 72 | set -ue 73 | case "$(uname -m)" in 74 | x86_64) extra_args=(--config=x86_64) ;; 75 | *) extra_args=() ;; 76 | esac 77 | bazel test --test_output=errors -c ${{ matrix.compilation_mode }} \ 78 | --config=dev --config=libc++ "${extra_args[@]}" \ 79 | --action_env=CC="$CC" --action_env=CXX="$CXX" -- //gwpsan/... 80 | - name: Save Bazel cache 81 | if: github.ref == 'refs/heads/main' 82 | uses: actions/cache/save@v4 83 | with: 84 | path: "~/.cache/bazel" 85 | key: bazel-cache-${{ matrix.compilation_mode }}-${{ github.run_id }} 86 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /bazel-* 2 | MODULE.bazel.lock 3 | # Temp files created by most text editors. 4 | *~ 5 | *.swp 6 | # Merge files created by git. 7 | *.orig 8 | # Reject files created by patch. 9 | *.rej 10 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | # This is the list of GWPSan's significant contributors. 2 | # 3 | # This does not necessarily list everyone who has contributed code, 4 | # especially since many employees of one corporation may be contributing. 5 | # To see the full list of contributors, see the revision history in 6 | # source control. 7 | 8 | Google LLC 9 | -------------------------------------------------------------------------------- /BUILD: -------------------------------------------------------------------------------- 1 | # Copyright 2024 The GWPSan Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | load("@rules_license//rules:license.bzl", "license") 16 | 17 | package( 18 | default_applicable_licenses = ["//:license"], 19 | default_visibility = ["//gwpsan:__subpackages__"], 20 | ) 21 | 22 | license( 23 | name = "license", 24 | package_name = "gwpsan", 25 | copyright_notice = "Copyright © 2024 The GWPSan Authors. All rights reserved.", 26 | license_kinds = [ 27 | "@rules_license//licenses/spdx:Apache-2.0", 28 | ], 29 | license_text = "LICENSE", 30 | ) 31 | 32 | licenses(["notice"]) 33 | 34 | exports_files(["LICENSE"]) 35 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to Contribute 2 | 3 | We would love to accept your patches and contributions to this project. 4 | 5 | ## Before you begin 6 | 7 | ### Sign our Contributor License Agreement 8 | 9 | Contributions to this project must be accompanied by a 10 | [Contributor License Agreement](https://cla.developers.google.com/about) (CLA). 11 | You (or your employer) retain the copyright to your contribution; this simply 12 | gives us permission to use and redistribute your contributions as part of the 13 | project. 14 | 15 | If you or your current employer have already signed the Google CLA (even if it 16 | was for a different project), you probably don't need to do it again. 17 | 18 | Visit to see your current agreements or to 19 | sign a new one. 20 | 21 | ### Review our Community Guidelines 22 | 23 | This project follows [Google's Open Source Community 24 | Guidelines](https://opensource.google/conduct/). 25 | 26 | ## Contribution process 27 | 28 | ### Code Reviews 29 | 30 | All submissions, including submissions by project members, require review. We 31 | use [GitHub pull requests](https://docs.github.com/articles/about-pull-requests) 32 | for this purpose. 33 | -------------------------------------------------------------------------------- /CONTRIBUTORS: -------------------------------------------------------------------------------- 1 | # This is the official list of people who can contribute (and who have contributed) 2 | # code to the GWPSan project repository. The AUTHORS file lists the copyright holders; 3 | # this file lists people. For example, Google employees are listed here 4 | # but not in AUTHORS, because Google holds the copyright. 5 | 6 | Google LLC 7 | Dmitry Vyukov 8 | Marco Elver 9 | -------------------------------------------------------------------------------- /MODULE.bazel: -------------------------------------------------------------------------------- 1 | # Copyright 2024 The GWPSan Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | module(name = "gwpsan") 16 | 17 | http_archive = use_repo_rule("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") 18 | 19 | bazel_dep(name = "abseil-cpp", version = "20240116.2") 20 | bazel_dep(name = "bazel_skylib", version = "1.7.1") 21 | bazel_dep(name = "google_benchmark", version = "1.8.3") 22 | bazel_dep(name = "googletest", version = "1.14.0") 23 | bazel_dep(name = "platforms", version = "0.0.11") 24 | bazel_dep(name = "re2", version = "2024-02-01") 25 | bazel_dep(name = "rules_foreign_cc", version = "0.10.1") 26 | bazel_dep(name = "rules_fuzzing", version = "0.5.2") 27 | bazel_dep(name = "rules_license", version = "0.0.8") 28 | 29 | # -------- Non-Bazel Archives -------- # 30 | 31 | _ALL_CONTENT = """\ 32 | filegroup( 33 | name = "all_srcs", 34 | srcs = glob(["**"]), 35 | visibility = ["//visibility:public"], 36 | ) 37 | """ 38 | 39 | http_archive( 40 | name = "dynamorio", 41 | build_file_content = _ALL_CONTENT, 42 | strip_prefix = "dynamorio-cronbuild-10.92.19874", 43 | url = "https://github.com/DynamoRIO/dynamorio/archive/refs/tags/cronbuild-10.92.19874.tar.gz", 44 | sha256 = "92b070faeeae4c60f4c3b9f42438e12dfa953f4dd34827e3b78101968d73120b", 45 | ) 46 | -------------------------------------------------------------------------------- /WORKSPACE: -------------------------------------------------------------------------------- 1 | # Copyright 2024 The GWPSan Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # See MODULE.bazel for external dependencies setup. 16 | -------------------------------------------------------------------------------- /check-license.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Copyright 2024 The GWPSan Authors 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # https://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | set -ue 18 | 19 | cd "${0%/*}" 20 | 21 | LICENSE_TEXT="\ 22 | [#/ ]* Copyright 20[,\-[:digit:]]* The GWPSan Authors\n\ 23 | [#/ ]*\n\ 24 | [#/ ]* Licensed under the Apache License, Version 2\.0 \(the \"License\"\);\n\ 25 | [#/ ]* you may not use this file except in compliance with the License\.\n\ 26 | [#/ ]* You may obtain a copy of the License at\n\ 27 | [#/ ]*\n\ 28 | [#/ ]* https?://www\.apache\.org/licenses/LICENSE-2\.0\n\ 29 | [#/ ]*\n\ 30 | [#/ ]* Unless required by applicable law or agreed to in writing, software\n\ 31 | [#/ ]* distributed under the License is distributed on an \"AS IS\" BASIS,\n\ 32 | [#/ ]* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied\.\n\ 33 | [#/ ]* See the License for the specific language governing permissions and\n\ 34 | [#/ ]* limitations under the License\." 35 | 36 | MATCH=( 37 | -name "*.cpp" -o 38 | -name "*.h" -o 39 | -name "*.sh" -o 40 | -name "*.bzl" -o 41 | -name "*.yml" -o 42 | -name "BUILD" -o 43 | -name "WORKSPACE" -o 44 | -name "MODULE.bazel" 45 | ) 46 | 47 | IGNORE=( 48 | # May not replicate above header exactly. 49 | -wholename "./gwpsan/import/*" -o 50 | -wholename "./import/*" -o 51 | -wholename "./google/*" 52 | ) 53 | 54 | find -type f \( "${MATCH[@]}" \) -a \! \( "${IGNORE[@]}" \) | 55 | ( 56 | fails=0 57 | pass=0 58 | while read path; do 59 | if ! grep -Pzoq "$LICENSE_TEXT" "$path"; then 60 | echo "${path}: does not have standard license header" 61 | (( fails++ )) || : 62 | else 63 | (( pass++ )) || : 64 | fi 65 | done 66 | if (( fails )); then 67 | exit 1 68 | fi 69 | echo "${pass} files checked" 70 | ) 71 | -------------------------------------------------------------------------------- /docs/design.md: -------------------------------------------------------------------------------- 1 | # Runtime Design 2 | 3 | GWPSan [depends](dependencies.md) on specific features of compiler, and kernel, 4 | with the runtime implementing a binary analysis framework based on decoding and 5 | emulating instructions. In the below we describe the runtime components. 6 | 7 | ### Binary Analysis 8 | 9 | Machine code is decoded into abstract ISA instructions (`Instr` class in 10 | `instruction.h`) by `InstrDecoder::Decode` method using 11 | [DynamoRIO](https://dynamorio.org/) framework. Abstract instructions are 12 | executed/emulated by `CPUContext::Execute`. `CPUContext` class holds machine 13 | context (registers) along with meta information (taint bits/origins). `Env` 14 | class abstracts machine memory (e.g. can do actual stores to memory, or discard 15 | stores) and stores meta information for values in memory. 16 | 17 | ### Unified Tool 18 | 19 | The [unified 20 | tool](https://github.com/google/gwpsan/blob/main/gwpsan/unified/unified.cpp) receives 21 | periodic timer signals, and speculatively executes some amount of instructions 22 | for the thread. As it executes instructions it looks for potential instructions 23 | of interest (memory accesses for data races detection, etc). If it finds any, 24 | it dispatches to the concrete tools to do the checking. 25 | -------------------------------------------------------------------------------- /docs/tools.md: -------------------------------------------------------------------------------- 1 | # Tools 2 | 3 | Here we briefly describe each GWPSan tool in more detail. 4 | 5 | > Note: The lower-case short name of each tool also corresponds to the 6 | > `GWPSAN_OPTIONS` variable. 7 | 8 | ## UAR: Use-after-return detector 9 | 10 | [Source](https://github.com/google/gwpsan/blob/main/gwpsan/uar/uar.cpp) 11 | 12 | *UAR* detects use-after-return bugs for stack variables. It can be enabled with 13 | `GWPSAN_OPTIONS=uar=1`. 14 | 15 | In a nutshell, it doubles the size of a thread stack and effectively creates 16 | two stacks within: the main stack where the thread starts running and a second 17 | stack that is used for use-after-return detection: 18 | 19 | ``` 20 | ╔═════════════╦═══════════════╦═════════════╦═══════════════╗ 21 | ║ guard ║ second stack ║ guard ║ main stack ║ 22 | ║(guard_size_)║ (stack_size_) ║(guard_size_)║ (stack_size_) ║ 23 | ╚═════════════╩═══════════════╩═════════════╩═══════════════╝ 24 | ``` 25 | 26 | Then the tool catches function entry with timer sampling and switches execution 27 | to the second stack. On return from the function execution is switched back to 28 | the main stack, and the second stack is protected as `PROT_NONE`. Any 29 | subsequent accesses via dangling references to the second stack will cause a 30 | paging fault. 31 | 32 | The changed stack layout may cause issues for programs that track 33 | used/remaining stack space, and try to do other unusual things with stack. 34 | 35 | ## TSan: Data-race detector 36 | 37 | [Source](https://github.com/google/gwpsan/blob/main/gwpsan/tsan/tsan.cpp) 38 | 39 | *TSan (Thread Sanitizer)* detects [data 40 | races](https://en.cppreference.com/w/cpp/language/multithread#Data_races). It 41 | can be enabled with `GWPSAN_OPTIONS=tsan=1`. 42 | 43 | > Note: The name "TSan" is inspired by the similarly named 44 | > compiler-instrumentation based tool [ThreadSanitizer 45 | > (TSan)](https://clang.llvm.org/docs/ThreadSanitizer.html). While both detect 46 | > data races, their runtime properties and implementations are completely 47 | > different. 48 | 49 | The basic algorithm is based on the idea described by [Data 50 | Collider](https://www.usenix.org/legacy/event/osdi10/tech/full_papers/Erickson.pdf). 51 | In a nutshell, it stops a thread at a memory access, arms a hardware watchpoint 52 | for the address of the memory access, and pauses the thread for a bit. If the 53 | watchpoint fires in another thread, we caught a data race. Compiler-based 54 | metadata is used to filter out atomic accesses. 55 | 56 | ## LMSan: Use-of-uninitialized memory detector 57 | 58 | [Source](https://github.com/google/gwpsan/blob/main/gwpsan/lmsan/lmsan.cpp) 59 | 60 | *LMSan (Lightweight Memory Sanitizer)* detects uses of uninitialized memory. 61 | *Currently experimental and not yet ready for use.* 62 | -------------------------------------------------------------------------------- /gwpsan/BUILD: -------------------------------------------------------------------------------- 1 | # Copyright 2024 The GWPSan Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | load("@bazel_skylib//rules:common_settings.bzl", "string_flag") 16 | 17 | package(default_applicable_licenses = ["//:license"]) 18 | 19 | config_setting( 20 | name = "mode_dbg", 21 | values = { 22 | "compilation_mode": "dbg", 23 | }, 24 | ) 25 | 26 | config_setting( 27 | name = "mode_fastbuild", 28 | values = { 29 | "compilation_mode": "fastbuild", 30 | }, 31 | ) 32 | 33 | config_setting( 34 | name = "mode_opt", 35 | values = { 36 | "compilation_mode": "opt", 37 | }, 38 | ) 39 | 40 | string_flag( 41 | name = "sanitizer", 42 | build_setting_default = "", 43 | ) 44 | 45 | config_setting( 46 | name = "sanitizer_msan", 47 | flag_values = { 48 | ":sanitizer": "msan", 49 | }, 50 | ) 51 | -------------------------------------------------------------------------------- /gwpsan/base/algorithm.h: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef GWPSAN_BASE_ALGORITHM_H_ 16 | #define GWPSAN_BASE_ALGORITHM_H_ 17 | 18 | #include "gwpsan/base/common.h" 19 | 20 | namespace gwpsan SAN_LOCAL { 21 | 22 | template 23 | struct less { 24 | bool operator()(const T& a, const T& b) const { 25 | return a < b; 26 | } 27 | }; 28 | 29 | template 30 | bool is_sorted(Iter first, Iter last, Comp comp) { 31 | if (first == last) 32 | return true; 33 | for (auto it = first; ++it != last; first = it) { 34 | if (comp(*it, *first)) 35 | return false; 36 | } 37 | return true; 38 | } 39 | 40 | template 41 | bool is_sorted(Iter first, Iter last) { 42 | return gwpsan::is_sorted(first, last, 43 | less>()); 44 | } 45 | 46 | template 47 | void sort(Iter first, Iter last, Comp comp) { 48 | // Use shell sort b/c it's short and does not use additional memory. See: 49 | // https://en.wikipedia.org/wiki/Shellsort 50 | constexpr uptr kGaps[] = {701, 301, 132, 57, 23, 10, 4, 1}; 51 | const uptr size = last - first; 52 | for (uptr gap : kGaps) { 53 | for (uptr i = gap; i < size; i++) { 54 | auto tmp = move(first[i]); 55 | uptr j = i; 56 | for (; (j >= gap) && !comp(first[j - gap], tmp); j -= gap) 57 | first[j] = move(first[j - gap]); 58 | first[j] = move(tmp); 59 | } 60 | } 61 | } 62 | 63 | template 64 | void sort(Iter first, Iter last) { 65 | gwpsan::sort(first, last, less>()); 66 | } 67 | 68 | template 69 | Iter upper_bound(Iter first, Iter last, const T& v, Comp comp) { 70 | uptr size = last - first; 71 | while (size != 0) { 72 | auto half = size / 2; 73 | auto mid = first + half; 74 | if (comp(v, *mid)) { 75 | size = half; 76 | } else { 77 | first = ++mid; 78 | size -= half + 1; 79 | } 80 | } 81 | return first; 82 | } 83 | 84 | template 85 | Iter upper_bound(Iter first, Iter last, const T& v) { 86 | return gwpsan::upper_bound(first, last, v, 87 | less>()); 88 | } 89 | 90 | } // namespace gwpsan SAN_LOCAL 91 | 92 | #endif // GWPSAN_BASE_ARRAY_H_ 93 | -------------------------------------------------------------------------------- /gwpsan/base/algorithm_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "gwpsan/base/algorithm.h" 16 | 17 | #include 18 | 19 | #include 20 | #include 21 | 22 | #include "gtest/gtest.h" 23 | 24 | namespace { 25 | 26 | using Sorting = testing::TestWithParam>; 27 | 28 | TEST_P(Sorting, Test) { 29 | const auto& data = GetParam(); 30 | EXPECT_EQ(std::is_sorted(data.begin(), data.end()), 31 | gwpsan::is_sorted(data.begin(), data.end())); 32 | auto sorted = data; 33 | gwpsan::sort(sorted.begin(), sorted.end()); 34 | auto sorted2 = data; 35 | std::sort(sorted2.begin(), sorted2.end()); 36 | EXPECT_EQ(sorted, sorted2); 37 | EXPECT_TRUE(gwpsan::is_sorted(sorted.begin(), sorted.end())); 38 | auto augmented = sorted; 39 | augmented.emplace_back(0); 40 | if (!sorted.empty()) { 41 | augmented.emplace_back(sorted.front() - 1); 42 | augmented.emplace_back(sorted.back() + 1); 43 | } 44 | for (int v : augmented) { 45 | auto it = gwpsan::upper_bound(sorted.begin(), sorted.end(), v); 46 | auto it2 = std::upper_bound(sorted.begin(), sorted.end(), v); 47 | EXPECT_EQ(it, it2); 48 | } 49 | } 50 | 51 | std::vector> SortingTestCases() { 52 | std::vector> cases = { 53 | {}, 54 | {0, 1, 2, 3}, 55 | {3, 2, 1, 0}, 56 | {2, 2, 1, 0, 3}, 57 | }; 58 | unsigned seed = 0; 59 | for (int i = 0; i < 10; i++) { 60 | std::vector data; 61 | for (int i = rand_r(&seed) % 20; i >= 0; i--) 62 | data.push_back(rand_r(&seed)); 63 | cases.push_back(data); 64 | } 65 | return cases; 66 | } 67 | 68 | INSTANTIATE_TEST_SUITE_P(Sorting, Sorting, 69 | testing::ValuesIn(SortingTestCases())); 70 | 71 | } // namespace 72 | -------------------------------------------------------------------------------- /gwpsan/base/array.h: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef GWPSAN_BASE_ARRAY_H_ 16 | #define GWPSAN_BASE_ARRAY_H_ 17 | 18 | #include "gwpsan/base/common.h" 19 | 20 | namespace gwpsan SAN_LOCAL { 21 | 22 | // Internal replacement for std::array. 23 | template 24 | struct Array { 25 | using value_type = T; 26 | using iterator = T*; 27 | using const_iterator = const T*; 28 | 29 | // This is public, to simplify initializer list construction without having 30 | // to define the constructors (similar to std::array). 31 | T data__[kSize]; 32 | 33 | constexpr iterator begin() { 34 | return iterator(data()); 35 | } 36 | 37 | constexpr const_iterator begin() const { 38 | return const_iterator(data()); 39 | } 40 | 41 | constexpr iterator end() { 42 | return iterator(data() + kSize); 43 | } 44 | 45 | constexpr const_iterator end() const { 46 | return const_iterator(data() + kSize); 47 | } 48 | 49 | constexpr uptr size() const { 50 | return kSize; 51 | } 52 | 53 | constexpr bool empty() const { 54 | return !kSize; 55 | } 56 | 57 | constexpr T& operator[](uptr n) { 58 | SAN_DCHECK_LT(n, kSize); 59 | return data__[n]; 60 | } 61 | 62 | constexpr const T& operator[](uptr n) const { 63 | SAN_DCHECK_LT(n, kSize); 64 | return data__[n]; 65 | } 66 | 67 | constexpr T& at(uptr n) { 68 | SAN_CHECK_LT(n, kSize); 69 | return data__[n]; 70 | } 71 | 72 | constexpr const T& at(uptr n) const { 73 | SAN_CHECK_LT(n, kSize); 74 | return data__[n]; 75 | } 76 | 77 | constexpr T& front() { 78 | return (*this)[0]; 79 | } 80 | 81 | constexpr const T& front() const { 82 | return (*this)[0]; 83 | } 84 | 85 | constexpr T& back() { 86 | return (*this)[kSize - 1]; 87 | } 88 | 89 | constexpr const T& back() const { 90 | return (*this)[kSize - 1]; 91 | } 92 | 93 | constexpr T* data() { 94 | return data__; 95 | } 96 | 97 | constexpr const T* data() const { 98 | return data__; 99 | } 100 | }; 101 | 102 | } // namespace gwpsan SAN_LOCAL 103 | 104 | #endif // GWPSAN_BASE_ARRAY_H_ 105 | -------------------------------------------------------------------------------- /gwpsan/base/bazel.h: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef GWPSAN_BASE_BAZEL_H_ 16 | #define GWPSAN_BASE_BAZEL_H_ 17 | 18 | #include "gwpsan/base/common.h" 19 | #include "gwpsan/base/span.h" 20 | 21 | namespace gwpsan SAN_LOCAL { 22 | 23 | void BazelOnPrint(Span text); 24 | void BazelOnReport(const char* summary); 25 | bool BazelReportedWarning(); 26 | 27 | } // namespace gwpsan SAN_LOCAL 28 | 29 | #endif // GWPSAN_BASE_BAZEL_H_ 30 | -------------------------------------------------------------------------------- /gwpsan/base/bazel_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "gwpsan/base/bazel.h" 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | #include 22 | 23 | #include "gtest/gtest.h" 24 | #include "gwpsan/base/common.h" 25 | #include "gwpsan/base/log.h" 26 | #include "gwpsan/base/os.h" 27 | 28 | namespace gwpsan { 29 | namespace { 30 | 31 | TEST(Bazel, OnPrint) { 32 | const char* path = getenv("TEST_UNDECLARED_OUTPUTS_DIR"); 33 | if (!path) 34 | GTEST_SKIP() << "not running under Bazel"; 35 | // Print something and check that it's in the file. 36 | const char* kMyOutput = "Hello, Bazel test!\n"; 37 | Printf("%s", kMyOutput); 38 | std::ostringstream file; 39 | file << path << "/gwpsan." << getpid() << ".txt"; 40 | char output[4096]; 41 | EXPECT_TRUE(!!ReadFile(file.str().c_str(), output)); 42 | EXPECT_TRUE(strstr(output, kMyOutput)); 43 | 44 | // Now log something and check that it's in the file. 45 | const char* kMyLog = "Logging for Bazel"; 46 | bool old_log_enabled = log_enabled; 47 | log_enabled = true; 48 | SAN_LOG("%s", kMyLog); 49 | log_enabled = old_log_enabled; 50 | EXPECT_TRUE(!!ReadFile(file.str().c_str(), output)); 51 | EXPECT_TRUE(strstr(output, kMyLog)); 52 | } 53 | 54 | TEST(Bazel, Warning) { 55 | const char* file = getenv("TEST_WARNINGS_OUTPUT_FILE"); 56 | if (!file) 57 | GTEST_SKIP() << "not running under Bazel"; 58 | EXPECT_FALSE(BazelReportedWarning()); 59 | BazelOnReport("a warning"); 60 | EXPECT_TRUE(BazelReportedWarning()); 61 | char output[4096]; 62 | EXPECT_TRUE(!!ReadFile(file, output)); 63 | EXPECT_TRUE(strstr(output, "[gwpsan] a warning\n")); 64 | } 65 | 66 | } // namespace 67 | } // namespace gwpsan 68 | -------------------------------------------------------------------------------- /gwpsan/base/cc_implicit_output.bzl: -------------------------------------------------------------------------------- 1 | # Copyright 2024 The GWPSan Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | """cc_implicit_output rule to expose implicit outputs from cc_library""" 16 | 17 | def _get_file_from_list(artifacts, file_short_path): 18 | for artifact in artifacts: 19 | if artifact != None and artifact.short_path == file_short_path: 20 | return [artifact] 21 | 22 | fail("Could not find implicit output '" + file_short_path + "' in the listed dependency") 23 | 24 | def _create_short_path(ctx): 25 | target = ctx.attr.files[0] 26 | if not target.startswith("//"): 27 | target = "//" + ctx.label.package + target 28 | 29 | return target[2:].replace(":", "/") 30 | 31 | def _cc_implicit_output_impl(ctx): 32 | if len(ctx.attr.deps) != 1: 33 | fail("Exactly one dependency required.") 34 | if len(ctx.attr.files) != 1: 35 | fail("Exactly one file required.") 36 | 37 | linker_inputs = ctx.attr.deps[0][CcInfo].linking_context.linker_inputs.to_list() 38 | if len(linker_inputs) == 0 or len(linker_inputs[0].libraries) == 0: 39 | return DefaultInfo() 40 | 41 | library_to_link = linker_inputs[0].libraries[0] 42 | 43 | file_short_path = _create_short_path(ctx) 44 | files = _get_file_from_list([ 45 | library_to_link.static_library, 46 | library_to_link.pic_static_library, 47 | library_to_link.dynamic_library, 48 | library_to_link.resolved_symlink_dynamic_library, 49 | ], file_short_path) 50 | 51 | return DefaultInfo( 52 | files = depset(files), 53 | runfiles = ctx.runfiles(files = files), 54 | ) 55 | 56 | cc_implicit_output = rule( 57 | implementation = _cc_implicit_output_impl, 58 | attrs = { 59 | "deps": attr.label_list( 60 | providers = [CcInfo], 61 | ), 62 | "files": attr.string_list(), 63 | }, 64 | ) 65 | -------------------------------------------------------------------------------- /gwpsan/base/common_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "gwpsan/base/common.h" 16 | 17 | #include "gtest/gtest.h" 18 | #include "gwpsan/base/os.h" 19 | #include "gwpsan/base/syscall.h" 20 | 21 | namespace gwpsan { 22 | namespace { 23 | 24 | int WithFunctionRef(FunctionRef fun) { 25 | return fun(); 26 | } 27 | 28 | int Return42() { 29 | return 42; 30 | } 31 | 32 | TEST(Common, FunctionRef) { 33 | EXPECT_EQ(WithFunctionRef([] { return 123; }), 123); 34 | // Function pointer template type deduction works. 35 | EXPECT_EQ(FunctionRef(Return42)(), 42); 36 | // Test capturing lambda 37 | bool called = false; 38 | EXPECT_EQ(WithFunctionRef([&] { 39 | called = true; 40 | return 123; 41 | }), 42 | 123); 43 | EXPECT_TRUE(called); 44 | // Test copy constructor. 45 | FunctionRef funref1(Return42); 46 | auto funref2 = funref1; 47 | EXPECT_EQ(funref1(), 42); 48 | EXPECT_EQ(funref2(), 42); 49 | auto funref3 = move(funref1); 50 | EXPECT_EQ(funref3(), 42); 51 | } 52 | 53 | TEST(Common, CleanupRef) { 54 | bool called = false; 55 | auto set_called = [&] { called = true; }; 56 | { 57 | EXPECT_FALSE(called); 58 | CleanupRef cleanup(set_called); 59 | EXPECT_FALSE(called); 60 | } 61 | EXPECT_TRUE(called); 62 | } 63 | 64 | TEST(Common, Warn) { 65 | #define SAN_INVALID_FD -1 66 | EXPECT_DEATH(({ 67 | if (SAN_WARN(!sys_close(SAN_INVALID_FD)) && !GWPSAN_DEBUG) 68 | Die(); 69 | }), 70 | "T[0-9]+ common_test.cpp:[0-9]+: WARN: " 71 | "\\(!sys_close\\(SAN_INVALID_FD\\)\\)"); 72 | EXPECT_DEATH( 73 | ({ 74 | if (SAN_WARN_IF_ERR(sys_close(SAN_INVALID_FD)) && !GWPSAN_DEBUG) 75 | Die(); 76 | }), 77 | "T[0-9]+ common_test.cpp:[0-9]+: WARN: " 78 | "sys_close\\(SAN_INVALID_FD\\) failed \\(errno=9\\)"); 79 | EXPECT_DEATH( 80 | ({ 81 | if (SAN_WARN_IF_ERR(sys_close(SAN_INVALID_FD), "more info: %d", 1) && 82 | !GWPSAN_DEBUG) 83 | Die(); 84 | }), 85 | "T[0-9]+ common_test.cpp:[0-9]+: WARN: " 86 | "sys_close\\(SAN_INVALID_FD\\) failed \\(errno=9\\) more info: 1"); 87 | 88 | EXPECT_DEATH(({ SAN_CHECK(SAN_INVALID_FD != -1, "more info %d", 1); }), 89 | "T[0-9]+ common_test.cpp:[0-9]+: CHECK: SAN_INVALID_FD != -1 " 90 | "\\(false\\) more info 1 in"); 91 | EXPECT_DEATH(({ SAN_CHECK_NE(SAN_INVALID_FD, -1); }), 92 | "T[0-9]+ common_test.cpp:[0-9]+: CHECK: SAN_INVALID_FD != -1 " 93 | "\\(-1 != -1\\)"); 94 | if (GWPSAN_DEBUG) { 95 | EXPECT_DEATH(({ SAN_DCHECK(SAN_INVALID_FD != -1, "more info %d", 1); }), 96 | "T[0-9]+ common_test.cpp:[0-9]+: CHECK: SAN_INVALID_FD != -1 " 97 | "\\(false\\) more info 1 in"); 98 | EXPECT_DEATH(({ SAN_DCHECK_NE(SAN_INVALID_FD, -1); }), 99 | "T[0-9]+ common_test.cpp:[0-9]+: CHECK: SAN_INVALID_FD != -1 " 100 | "\\(-1 != -1\\)"); 101 | } 102 | #undef SAN_INVALID_FD 103 | } 104 | 105 | } // namespace 106 | } // namespace gwpsan 107 | -------------------------------------------------------------------------------- /gwpsan/base/config.h: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef GWPSAN_BASE_CONFIG_H_ 16 | #define GWPSAN_BASE_CONFIG_H_ 17 | 18 | // IWYU pragma: private, include "gwpsan/base/common.h" 19 | 20 | // Allow optimized builds to override GWPSAN_DEBUG with build options. The 21 | // default for optimized builds is no debugging checks. 22 | #if GWPSAN_OPTIMIZE && !defined(GWPSAN_DEBUG) 23 | #define GWPSAN_DEBUG 0 24 | #endif 25 | 26 | #if defined(__x86_64__) 27 | #define GWPSAN_X64 1 28 | #else 29 | #define GWPSAN_X64 0 30 | #endif 31 | 32 | #if defined(__aarch64__) 33 | #define GWPSAN_ARM64 1 34 | #else 35 | #define GWPSAN_ARM64 0 36 | #endif 37 | 38 | #define GWPSAN_INSTRUMENTED_ASAN __has_feature(address_sanitizer) 39 | #define GWPSAN_INSTRUMENTED_HWASAN __has_feature(hwaddress_sanitizer) 40 | #define GWPSAN_INSTRUMENTED_MSAN __has_feature(memory_sanitizer) 41 | #define GWPSAN_INSTRUMENTED_TSAN __has_feature(thread_sanitizer) 42 | #define GWPSAN_INSTRUMENTED_DFSAN __has_feature(dataflow_sanitizer) 43 | #define GWPSAN_INSTRUMENTED_UBSAN __has_feature(undefined_behavior_sanitizer) 44 | #define GWPSAN_INSTRUMENTED_COVSAN __has_feature(coverage_sanitizer) 45 | 46 | // GWPSan inherently conflicts with compiler-based instrumentation tools - list 47 | // all of potential conflicting tools here. 48 | #define GWPSAN_INSTRUMENTED \ 49 | (GWPSAN_INSTRUMENTED_ASAN || GWPSAN_INSTRUMENTED_HWASAN || \ 50 | GWPSAN_INSTRUMENTED_MSAN || GWPSAN_INSTRUMENTED_TSAN || \ 51 | GWPSAN_INSTRUMENTED_DFSAN || GWPSAN_INSTRUMENTED_UBSAN || \ 52 | GWPSAN_INSTRUMENTED_COVSAN) 53 | 54 | #endif // GWPSAN_BASE_CONFIG_H_ 55 | -------------------------------------------------------------------------------- /gwpsan/base/env.h: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef GWPSAN_BASE_ENV_H_ 16 | #define GWPSAN_BASE_ENV_H_ 17 | 18 | #include "gwpsan/base/common.h" 19 | #include "gwpsan/base/units.h" 20 | 21 | namespace gwpsan SAN_LOCAL { 22 | 23 | // Copies `size` bytes from `src` to `dst`; if the copy was completed, returns 24 | // true, false if an access fault occurred during the copy. 25 | bool NonFailingMemcpy(void* dst, const void* src, uptr size); 26 | 27 | inline bool NonFailingLoad(Addr addr, ByteSize size, void* dst) { 28 | auto* src = reinterpret_cast(Bytes(addr)); 29 | return NonFailingMemcpy(dst, src, Bytes(size)); 30 | } 31 | 32 | inline bool NonFailingStore(Addr addr, ByteSize size, const void* src) { 33 | auto* dst = reinterpret_cast(Bytes(addr)); 34 | return NonFailingMemcpy(dst, src, Bytes(size)); 35 | } 36 | 37 | bool InitNonFailing(); 38 | bool HandleNonFailingAccess(int sig, void* uctx); 39 | 40 | } // namespace gwpsan SAN_LOCAL 41 | 42 | #endif // GWPSAN_BASE_ENV_H_ 43 | -------------------------------------------------------------------------------- /gwpsan/base/fault_inject.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #if defined(GWPSAN_FAULT_INJECT) || GWPSAN_DEBUG 16 | #include "gwpsan/base/fault_inject.h" 17 | 18 | #include "gwpsan/base/common.h" 19 | #include "gwpsan/base/numeric.h" 20 | #include "gwpsan/base/optional.h" 21 | #include "gwpsan/base/synchronization.h" 22 | 23 | namespace gwpsan { 24 | namespace { 25 | #ifdef GWPSAN_FAULT_INJECT 26 | static_assert(GWPSAN_FAULT_INJECT > 5, 27 | "Fault injection inverse probability must be greater than 5"); 28 | constexpr uptr kUnlikelyInverseProb = GWPSAN_FAULT_INJECT; 29 | constexpr uptr kLikelyInverseProb = kUnlikelyInverseProb / 5; // 5x more likely 30 | #else // GWPSAN_FAULT_INJECT 31 | // Choose a sane default for debug builds. 32 | constexpr uptr kLikelyInverseProb = 128; 33 | #endif // GWPSAN_FAULT_INJECT 34 | 35 | SAN_THREAD_LOCAL int fault_inject_disable_count; 36 | bool fault_inject_disabled; 37 | 38 | bool FaultInjectRand(uptr inverse_prob) { 39 | static constinit bool init; 40 | static constinit OptionalBase rand; 41 | 42 | if (fault_inject_disable_count) 43 | return false; 44 | 45 | if (__atomic_load_n(&fault_inject_disabled, __ATOMIC_RELAXED)) 46 | return false; 47 | 48 | // Double-checked locking. 49 | if (!__atomic_load_n(&init, __ATOMIC_ACQUIRE)) { 50 | static constinit Mutex init_mu; 51 | TryLock lock(init_mu); 52 | if (!lock) 53 | return false; 54 | if (!__atomic_load_n(&init, __ATOMIC_ACQUIRE)) 55 | rand.emplace(); 56 | __atomic_store_n(&init, true, __ATOMIC_RELEASE); 57 | } 58 | 59 | return rand->OneOf(inverse_prob); 60 | } 61 | } // namespace 62 | 63 | void FaultInjectDisableCurrent() { 64 | SAN_CHECK_GE(fault_inject_disable_count, 0); 65 | fault_inject_disable_count++; 66 | } 67 | 68 | void FaultInjectEnableCurrent() { 69 | SAN_CHECK_GT(fault_inject_disable_count, 0); 70 | fault_inject_disable_count--; 71 | } 72 | 73 | void FaultInjectDisableGlobal() { 74 | __atomic_store_n(&fault_inject_disabled, true, __ATOMIC_RELAXED); 75 | } 76 | 77 | bool FaultInjectLikely() { 78 | return FaultInjectRand(kLikelyInverseProb); 79 | } 80 | 81 | #ifdef GWPSAN_FAULT_INJECT 82 | static_assert(!GWPSAN_DEBUG, 83 | "Unlikely-fault injection triggers too many debug-only checks"); 84 | bool FaultInjectUnlikely() { 85 | return FaultInjectRand(kUnlikelyInverseProb); 86 | } 87 | #endif // GWPSAN_FAULT_INJECT 88 | 89 | } // namespace gwpsan 90 | #endif // GWPSAN_FAULT_INJECT || GWPSAN_DEBUG 91 | -------------------------------------------------------------------------------- /gwpsan/base/fault_inject.h: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef GWPSAN_BASE_FAULT_INJECT_H_ 16 | #define GWPSAN_BASE_FAULT_INJECT_H_ 17 | 18 | #include "gwpsan/base/common.h" 19 | 20 | namespace gwpsan SAN_LOCAL { 21 | 22 | // If GWPSAN_FAULT_INJECT is not defined, we only enable likely fault injection 23 | // with a fixed probability of 1/128. 24 | // 25 | // We do not want to compile unlikely-fault injection in by default to avoid 26 | // either accidental or malicous enablement in production. To test with unlikely 27 | // fault injection, compile the whole project with: 28 | // 29 | // GWPSAN_FAULT_INJECT= 30 | // 31 | // Where the constant is the inverse probability of an "unlikely" 32 | // fault occurring, i.e. a fault will occur with probability 1/. 33 | // "Likely" faults are 5x more likely (1/ * 5). 34 | 35 | #if defined(GWPSAN_FAULT_INJECT) || GWPSAN_DEBUG 36 | // Disables all fault injection for the current thread. 37 | void FaultInjectDisableCurrent(); 38 | // Re-enables all fault injection for the current thread. 39 | void FaultInjectEnableCurrent(); 40 | // Disables fault injection globally. 41 | void FaultInjectDisableGlobal(); 42 | 43 | // Returns true if a fault should be injected. Use this for likely faults. 44 | // 45 | // Note: In debugging mode, enable likely-fault injection by default. These 46 | // types of faults may happen more frequently in production. 47 | bool FaultInjectLikely(); 48 | #else // GWPSAN_FAULT_INJECT 49 | inline void FaultInjectDisableCurrent() {} 50 | inline void FaultInjectEnableCurrent() {} 51 | inline void FaultInjectDisableGlobal() {} 52 | 53 | inline bool FaultInjectLikely() { 54 | return false; 55 | } 56 | #endif // GWPSAN_FAULT_INJECT || GWPSAN_DEBUG 57 | 58 | #ifdef GWPSAN_FAULT_INJECT 59 | bool FaultInjectUnlikely(); 60 | #else // GWPSAN_FAULT_INJECT 61 | inline bool FaultInjectUnlikely() { 62 | return false; 63 | } 64 | #endif // GWPSAN_FAULT_INJECT 65 | 66 | // Scoped disables of fault injection for the current thread. 67 | class ScopedFaultInjectDisable { 68 | public: 69 | ScopedFaultInjectDisable() { 70 | FaultInjectDisableCurrent(); 71 | } 72 | ~ScopedFaultInjectDisable() { 73 | FaultInjectEnableCurrent(); 74 | } 75 | 76 | private: 77 | ScopedFaultInjectDisable(const ScopedFaultInjectDisable&) = delete; 78 | ScopedFaultInjectDisable& operator=(const ScopedFaultInjectDisable&) = delete; 79 | }; 80 | 81 | } // namespace gwpsan SAN_LOCAL 82 | 83 | #endif // GWPSAN_BASE_FAULT_INJECT_H_ 84 | -------------------------------------------------------------------------------- /gwpsan/base/flags.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "gwpsan/base/flags.h" 16 | 17 | #include "gwpsan/base/common.h" 18 | #include "gwpsan/base/memory.h" 19 | #include "gwpsan/base/numeric.h" 20 | #include "gwpsan/base/optional.h" 21 | #include "gwpsan/base/os.h" 22 | #include "gwpsan/base/span.h" 23 | 24 | namespace gwpsan { 25 | namespace { 26 | bool SetFlag(bool& var, const char* val) { 27 | if (val[0] == 0 || !internal_strcmp(val, "1") || 28 | !internal_strcmp(val, "true")) 29 | var = true; 30 | else if (!internal_strcmp(val, "0") || !internal_strcmp(val, "false")) 31 | var = false; 32 | else 33 | return false; 34 | return true; 35 | } 36 | 37 | template 38 | bool SetIntFlag(T& var, const char* val) { 39 | const Optional num = Atoi(val); 40 | var = static_cast(num.value_or(0)); 41 | return num.has_value(); 42 | } 43 | 44 | bool SetFlag(const char*& var, const char* val) { 45 | var = PersistentStrDup(val); 46 | return true; 47 | } 48 | 49 | bool ParseFlag(const char* name, const char* value, 50 | const Span& flags) { 51 | for (const auto& flag : flags) { 52 | if (internal_strcmp(name, flag.name)) 53 | continue; 54 | switch (flag.var.type) { 55 | case FlagDesc::Type::kBool: 56 | return SetFlag(*static_cast(flag.var.ptr), value); 57 | case FlagDesc::Type::kInt: 58 | return SetIntFlag(*static_cast(flag.var.ptr), value); 59 | case FlagDesc::Type::kUptr: 60 | return SetIntFlag(*static_cast(flag.var.ptr), value); 61 | case FlagDesc::Type::kString: 62 | return SetFlag(*static_cast(flag.var.ptr), value); 63 | } 64 | } 65 | return false; 66 | } 67 | 68 | void PrintHelp(const Span& flags) { 69 | Printf("gwpsan: supported flags\n"); 70 | for (const auto& flag : flags) { 71 | Printf("%s\n %s\n Value: ", flag.name, flag.desc); 72 | switch (flag.var.type) { 73 | case FlagDesc::Type::kBool: 74 | Printf("%s", *static_cast(flag.var.ptr) ? "true" : "false"); 75 | break; 76 | case FlagDesc::Type::kInt: 77 | Printf("%d", *static_cast(flag.var.ptr)); 78 | break; 79 | case FlagDesc::Type::kUptr: 80 | Printf("%zu", *static_cast(flag.var.ptr)); 81 | break; 82 | case FlagDesc::Type::kString: 83 | Printf("%s", *static_cast(flag.var.ptr) ?: ""); 84 | break; 85 | } 86 | Printf("\n"); 87 | } 88 | } 89 | } // namespace 90 | 91 | bool ParseFlagsFromStr(char* str, const Span& flags) { 92 | bool help = false; 93 | for (char* next = str; next && *next;) { 94 | char* name = next; 95 | next = internal_strchr(next, ':'); 96 | if (next) 97 | *next++ = 0; 98 | char* val = internal_strchr(name, '='); 99 | if (val) 100 | *val++ = 0; 101 | if (!internal_strcmp(name, "help")) { 102 | help = true; 103 | } else if (!ParseFlag(name, val ?: "", flags)) { 104 | Printf("gwpsan: failed to parse flag '%s'='%s'\n", name, val); 105 | return false; 106 | } 107 | } 108 | if (help) { 109 | PrintHelp(flags); 110 | Die(); 111 | } 112 | return true; 113 | } 114 | 115 | } // namespace gwpsan 116 | -------------------------------------------------------------------------------- /gwpsan/base/flags.h: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef GWPSAN_BASE_FLAGS_H_ 16 | #define GWPSAN_BASE_FLAGS_H_ 17 | 18 | #include "gwpsan/base/common.h" 19 | #include "gwpsan/base/span.h" 20 | 21 | namespace gwpsan SAN_LOCAL { 22 | 23 | struct FlagDesc { 24 | enum class Type { 25 | kBool, 26 | kInt, 27 | kUptr, 28 | kString, 29 | }; 30 | struct TypedVar { 31 | constexpr TypedVar(bool* ptr) 32 | : type(Type::kBool) 33 | , ptr(ptr) {} 34 | constexpr TypedVar(int* ptr) 35 | : type(Type::kInt) 36 | , ptr(ptr) {} 37 | constexpr TypedVar(uptr* ptr) 38 | : type(Type::kUptr) 39 | , ptr(ptr) {} 40 | constexpr TypedVar(const char** ptr) 41 | : type(Type::kString) 42 | , ptr(ptr) {} 43 | const Type type; 44 | void* const ptr; 45 | }; 46 | constexpr FlagDesc(TypedVar var, const char* name, const char* desc) 47 | : var(var) 48 | , name(name) 49 | , desc(desc) {} 50 | 51 | const TypedVar var; 52 | const char* const name; 53 | const char* const desc; 54 | }; 55 | 56 | bool ParseFlagsFromStr(char* str, const Span& flags); 57 | 58 | } // namespace gwpsan SAN_LOCAL 59 | 60 | #endif // GWPSAN_BASE_FLAGS_H_ 61 | -------------------------------------------------------------------------------- /gwpsan/base/flags_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "gwpsan/base/flags.h" 16 | 17 | #include "gtest/gtest.h" 18 | #include "gwpsan/base/common.h" 19 | 20 | namespace gwpsan { 21 | namespace { 22 | 23 | TEST(Flags, Empty) { 24 | bool bf = true; 25 | const FlagDesc flags[] = { 26 | {&bf, "bf", "bf"}, 27 | }; 28 | char str[] = ""; 29 | ASSERT_TRUE(ParseFlagsFromStr(str, flags)); 30 | EXPECT_EQ(bf, true); 31 | } 32 | 33 | TEST(Flags, Bad) { 34 | { 35 | char str[] = "unknown=0"; 36 | ASSERT_FALSE(ParseFlagsFromStr(str, {})); 37 | } 38 | { 39 | bool bf = true; 40 | const FlagDesc flags[] = { 41 | {&bf, "bf", "bf"}, 42 | }; 43 | char str[] = "bf=foo"; 44 | ASSERT_FALSE(ParseFlagsFromStr(str, flags)); 45 | } 46 | { 47 | uptr uf = 0; 48 | const FlagDesc flags[] = { 49 | {&uf, "uf", "uf"}, 50 | }; 51 | char str[] = "uf=foo"; 52 | ASSERT_FALSE(ParseFlagsFromStr(str, flags)); 53 | } 54 | { 55 | uptr intf = 0; 56 | const FlagDesc flags[] = { 57 | {&intf, "intf", "intf"}, 58 | }; 59 | char str[] = "intf=foo"; 60 | ASSERT_FALSE(ParseFlagsFromStr(str, flags)); 61 | } 62 | { 63 | uptr uf = 0; 64 | const FlagDesc flags[] = { 65 | {&uf, "uf", "uf"}, 66 | }; 67 | char str[] = "uf"; 68 | ASSERT_FALSE(ParseFlagsFromStr(str, flags)); 69 | } 70 | { 71 | int intf = 0; 72 | const FlagDesc flags[] = { 73 | {&intf, "intf", "intf"}, 74 | }; 75 | char str[] = "intf"; 76 | ASSERT_FALSE(ParseFlagsFromStr(str, flags)); 77 | } 78 | } 79 | 80 | TEST(Flags, Good) { 81 | bool bf0 = true, bf1 = true, bf2 = false, bf3 = false, bf4 = false, 82 | bf5 = false, bf6 = true; 83 | uptr uf0 = 0; 84 | int if0 = 0, if1 = 0; 85 | const char *sf0 = nullptr, *sf1 = nullptr, *sf2 = nullptr, *sf3 = nullptr, 86 | *sf4 = nullptr; 87 | const FlagDesc flags[] = { 88 | {&bf0, "bf0", "bf0"}, 89 | {&bf1, "bf1", "bf1"}, 90 | {&bf2, "bf2", "bf2"}, 91 | {&bf3, "bf3", "bf3"}, 92 | {&bf4, "bf4", "bf4"}, 93 | {&bf5, "bf5", "bf5"}, 94 | {&bf6, "bf6", "bf6"}, 95 | {&uf0, "uf0", "uf0"}, 96 | {&if0, "if0", "if0"}, 97 | {&if1, "if1", "if1"}, 98 | {&sf0, "sf0", "sf0"}, 99 | {&sf1, "sf1", "sf1"}, 100 | {&sf2, "sf2", "sf2"}, 101 | {&sf3, "sf3", "sf3"}, 102 | {&sf4, "sf4", "sf4"}, 103 | }; 104 | char str[] = 105 | "bf0=0:bf1=false:bf2=1:bf3=true:bf4:uf0=123:if0=42:if1=-22:sf0:sf1=:sf2=" 106 | "foo:sf3=bar"; 107 | ASSERT_TRUE(ParseFlagsFromStr(str, flags)); 108 | EXPECT_EQ(bf0, false); 109 | EXPECT_EQ(bf1, false); 110 | EXPECT_EQ(bf2, true); 111 | EXPECT_EQ(bf3, true); 112 | EXPECT_EQ(bf4, true); 113 | EXPECT_EQ(bf5, false); 114 | EXPECT_EQ(bf6, true); 115 | EXPECT_EQ(uf0, 123); 116 | EXPECT_EQ(if0, 42); 117 | EXPECT_EQ(if1, -22); 118 | EXPECT_STREQ(sf0, ""); 119 | EXPECT_STREQ(sf1, ""); 120 | EXPECT_STREQ(sf2, "foo"); 121 | EXPECT_STREQ(sf3, "bar"); 122 | EXPECT_EQ(sf4, nullptr); 123 | } 124 | 125 | } // namespace 126 | } // namespace gwpsan 127 | -------------------------------------------------------------------------------- /gwpsan/base/linux.h: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Helpers and definitions not yet found in Linux UAPI headers distributed with 16 | // most Linux distributions. We should eventually be able to remove this header. 17 | 18 | #ifndef GWPSAN_BASE_LINUX_H_ 19 | #define GWPSAN_BASE_LINUX_H_ 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | #include "gwpsan/base/common.h" 26 | 27 | namespace gwpsan SAN_LOCAL { 28 | 29 | #ifndef TRAP_PERF 30 | inline constexpr int TRAP_PERF = 6; 31 | #endif 32 | #ifndef TRAP_PERF_FLAG_ASYNC 33 | inline constexpr u32 TRAP_PERF_FLAG_ASYNC = (1u << 0); 34 | #endif 35 | #ifndef PERF_EVENT_IOC_MODIFY_ATTRIBUTES 36 | #define PERF_EVENT_IOC_MODIFY_ATTRIBUTES _IOW('$', 11, struct perf_event_attr*) 37 | #endif 38 | #ifndef PERF_ATTR_SIZE_VER7 39 | inline constexpr uptr PERF_ATTR_SIZE_VER7 = 128; 40 | #endif 41 | 42 | union perf_event_attr_v7 { 43 | public: 44 | ::perf_event_attr attr; 45 | struct { 46 | char v7_padding[PERF_ATTR_SIZE_VER7 - sizeof(u64)]; 47 | u64 sig_data; 48 | }; 49 | 50 | // These bitfields may not be defined in older kernel headers. 51 | 52 | void set_inherit_thread() { 53 | bits() |= 0x0800000000; 54 | } 55 | void set_remove_on_exec() { 56 | bits() |= 0x1000000000; 57 | } 58 | void set_sigtrap() { 59 | bits() |= 0x2000000000; 60 | } 61 | 62 | private: 63 | u64& bits() { 64 | return *reinterpret_cast(&attr.read_format + 1); 65 | } 66 | }; 67 | 68 | // si_perf_* is not defined in older kernel headers. 69 | struct SigInfoPerf { 70 | unsigned long data; 71 | u32 type; 72 | u32 flags; 73 | 74 | bool async() const { 75 | return flags & TRAP_PERF_FLAG_ASYNC; 76 | } 77 | }; 78 | 79 | inline const SigInfoPerf& GetSigInfoPerf(const siginfo_t& siginfo) { 80 | return reinterpret_cast(siginfo.si_addr_lsb); 81 | } 82 | 83 | struct kernel_timespec { 84 | s64 tv_sec; 85 | long long tv_nsec; 86 | }; 87 | 88 | struct linux_dirent64 { 89 | u64 d_ino; 90 | s64 d_off; 91 | unsigned short d_reclen; 92 | unsigned char d_type; 93 | char d_name[]; 94 | }; 95 | 96 | } // namespace gwpsan SAN_LOCAL 97 | 98 | #endif // GWPSAN_BASE_LINUX_H_ 99 | -------------------------------------------------------------------------------- /gwpsan/base/log.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "gwpsan/base/log.h" 16 | 17 | #include 18 | #include 19 | 20 | #include "gwpsan/base/bazel.h" 21 | #include "gwpsan/base/common.h" 22 | #include "gwpsan/base/memory.h" 23 | #include "gwpsan/base/module_list.h" 24 | #include "gwpsan/base/os.h" 25 | #include "gwpsan/base/printf.h" 26 | #include "gwpsan/base/syscall.h" 27 | 28 | namespace gwpsan { 29 | 30 | LogBuf::LogBuf() { 31 | buf_ = pos_ = Freelist::Alloc(); 32 | buf_[0] = 0; 33 | } 34 | 35 | LogBuf::LogBuf(const LogBuf& other) { 36 | buf_ = Freelist::Alloc(); 37 | uptr size = other.pos_ - other.buf_; 38 | internal_memcpy(buf_, other.buf_, size); 39 | buf_[size < kSize ? size : kSize - 1] = 0; 40 | pos_ = buf_ + size; 41 | } 42 | 43 | LogBuf::LogBuf(LogBuf&& other) { 44 | buf_ = other.buf_; 45 | pos_ = other.pos_; 46 | other.buf_ = other.pos_ = nullptr; 47 | } 48 | 49 | LogBuf::~LogBuf() { 50 | if (buf_) 51 | Freelist::Free(buf_); 52 | } 53 | 54 | LogBuf& LogBuf::Append(const char* format, ...) { 55 | // Update position in the case something was written manually. 56 | pos_ += internal_strlen(pos_); 57 | uptr remain = buf_ + kSize - pos_; 58 | if (remain == 0) 59 | return *this; 60 | va_list args; 61 | va_start(args, format); 62 | pos_ += VSPrintf(pos_, remain, format, args); 63 | va_end(args); 64 | return *this; 65 | } 66 | 67 | LogFile::LogFile() { 68 | if (!log_path) 69 | return; 70 | LogBuf buf; 71 | buf.Append("%s.%d", log_path, GetPid()); 72 | auto fd = sys_openat(AT_FDCWD, &buf, O_WRONLY | O_APPEND | O_CREAT, 0600); 73 | if (SAN_WARN_IF_ERR(fd)) { 74 | log_path = nullptr; 75 | return; 76 | } 77 | fd_ = fd.val(); 78 | } 79 | 80 | LogFile::~LogFile() { 81 | if (fd_ >= 0) 82 | sys_close(fd_); 83 | } 84 | 85 | void Logf(const char* msg, ...) { 86 | va_list args; 87 | va_start(args, msg); 88 | LogBuf buf; 89 | uptr len = VSPrintf(&buf, LogBuf::kSize, msg, args); 90 | va_end(args); 91 | sys_write(LogFile().fd(), &buf, len); 92 | BazelOnPrint({&buf, len}); 93 | } 94 | 95 | LogBuf DumpModuleImpl(uptr pc) { 96 | LogBuf buf; 97 | if (const auto* mod = FindModule(pc)) 98 | buf.Append("(%s+0x%zx)", mod->name, pc - mod->pc_offset); 99 | return buf; 100 | } 101 | 102 | // These are provided as stubs, so that base can use them elsewhere and link; 103 | // core code may override this with something better. 104 | 105 | SAN_WEAK_LOCAL LogBuf DumpInstr(uptr pc, DumpWhat what) { 106 | return DumpModuleImpl(pc); 107 | } 108 | 109 | SAN_WEAK_LOCAL LogBuf DumpInstr(uptr pc, uptr pc_copy, DumpWhat what) { 110 | return DumpModuleImpl(pc); 111 | } 112 | 113 | bool log_enabled; 114 | const char* log_path; 115 | 116 | } // namespace gwpsan 117 | -------------------------------------------------------------------------------- /gwpsan/base/memory.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "gwpsan/base/memory.h" 16 | 17 | #include 18 | 19 | #include "gwpsan/base/common.h" 20 | #include "gwpsan/base/metric.h" 21 | #include "gwpsan/base/os.h" 22 | #include "gwpsan/base/synchronization.h" 23 | 24 | namespace gwpsan { 25 | 26 | DEFINE_METRIC(heap_alloc, 0, "Heap bytes allocated by GWPSan"); 27 | DEFINE_METRIC(heap_free, 0, "Heap bytes freed by GWPSan"); 28 | DEFINE_METRIC(heap_current, 0, "Currently allocated heap bytes by GWPSan"); 29 | 30 | void AccountHeapAlloc(uptr size) { 31 | metric_heap_alloc.LossyAdd(size); 32 | metric_heap_current.LossyAdd(size); 33 | } 34 | 35 | void AccountHeapFree(uptr size) { 36 | metric_heap_free.LossyAdd(size); 37 | metric_heap_current.LossyAdd(-size); 38 | } 39 | 40 | char* PersistentAlloc(uptr size) { 41 | size = RoundUpTo(size, alignof(max_align_t)); 42 | 43 | // Special fast path case for multiple of page-size allocations. 44 | if (size % kPageSize == 0) 45 | return Mmap(size); 46 | 47 | static Mutex mu; 48 | static char* cache; 49 | static uptr remain; 50 | 51 | Lock lock(mu); 52 | if (remain < size) { 53 | const uptr num_pages = size / kPageSize + 1; 54 | const uptr alloc_size = num_pages * kPageSize; 55 | cache = Mmap(alloc_size); 56 | if (SAN_UNLIKELY(!cache)) { 57 | // We ran out of memory. Inform the user that they should try again 58 | // without GWPSan. 59 | SAN_BUG( 60 | "Out of memory! Try again without GWPSan: export " 61 | "GWPSAN_OPTIONS=sample_interval_usec=0"); 62 | } 63 | remain = alloc_size; 64 | } 65 | char* res = cache; 66 | cache += size; 67 | remain -= size; 68 | return res; 69 | } 70 | 71 | char* PersistentStrDup(const char* str) { 72 | uptr len = internal_strlen(str); 73 | char* dup = PersistentAlloc(len + 1); 74 | internal_memcpy(dup, str, len + 1); 75 | return dup; 76 | } 77 | 78 | } // namespace gwpsan 79 | -------------------------------------------------------------------------------- /gwpsan/base/memory_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "gwpsan/base/memory.h" 16 | 17 | #include "gtest/gtest.h" 18 | #include "gwpsan/base/common.h" 19 | 20 | namespace gwpsan { 21 | namespace { 22 | 23 | class TestObject { 24 | public: 25 | TestObject(int& ref) 26 | : ref_(ref) { 27 | ref++; 28 | } 29 | ~TestObject() { 30 | ref_--; 31 | } 32 | 33 | private: 34 | int& ref_; 35 | }; 36 | 37 | TEST(Memory, UniquePtrReset) { 38 | int ref = 0; 39 | auto obj = MakeUniqueFreelist(ref); 40 | EXPECT_EQ(ref, 1); 41 | obj.reset(); 42 | EXPECT_EQ(ref, 0); 43 | } 44 | 45 | TEST(Memory, UniquePtrMove) { 46 | int ref1 = 0; 47 | int ref2 = 0; 48 | auto obj1 = MakeUniqueFreelist(ref1); 49 | auto obj2 = MakeUniqueFreelist(ref2); 50 | TestObject* ptr1 = obj1.get(); 51 | TestObject* ptr2 = obj2.get(); 52 | EXPECT_NE(ptr1, ptr2); 53 | EXPECT_EQ(ref1, 1); 54 | EXPECT_EQ(ref2, 1); 55 | 56 | // move 57 | obj2 = move(obj1); 58 | EXPECT_EQ(obj1.get(), nullptr); 59 | EXPECT_EQ(obj2.get(), ptr1); 60 | EXPECT_EQ(ref1, 1); 61 | EXPECT_EQ(ref2, 0); 62 | 63 | obj2 = {}; 64 | EXPECT_EQ(obj2.get(), nullptr); 65 | EXPECT_EQ(ref1, 0); 66 | } 67 | 68 | } // namespace 69 | } // namespace gwpsan 70 | -------------------------------------------------------------------------------- /gwpsan/base/metric_collection.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "gwpsan/base/metric_collection.h" 16 | 17 | #include "gwpsan/base/common.h" 18 | #include "gwpsan/base/metric.h" 19 | #include "gwpsan/base/vector.h" 20 | 21 | namespace gwpsan { 22 | 23 | SAN_INTERFACE void gwpsan_collect_metrics(void (*callback)(const gwpsan_metric*, 24 | void*), 25 | void* arg) { 26 | MallocVector values; 27 | // Copy metric data and call caller callback for every metric. 28 | CollectMetrics([&](const MetricRef& metric) { 29 | // Keep reusing the same values buffer for as long as we can. 30 | if (metric.size() > values.size()) 31 | values.resize(metric.size()); 32 | for (uptr i = 0; i < metric.size(); ++i) { 33 | values[i].value = metric.value(i); 34 | values[i].name = metric.name(i); 35 | } 36 | const gwpsan_metric export_metric = {.name = metric.name(), 37 | .desc = metric.desc(), 38 | .size = metric.size(), 39 | .values = values.data()}; 40 | callback(&export_metric, arg); 41 | }); 42 | } 43 | 44 | } // namespace gwpsan 45 | -------------------------------------------------------------------------------- /gwpsan/base/metric_collection.h: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef GWPSAN_BASE_METRIC_COLLECTION_H_ 16 | #define GWPSAN_BASE_METRIC_COLLECTION_H_ 17 | 18 | #include "gwpsan/base/common.h" 19 | #include "gwpsan/base/metric.h" 20 | 21 | SAN_DECLARE_SECTION_VARS(const gwpsan::MetricRef* const, gwpsan_metrics); 22 | 23 | namespace gwpsan SAN_LOCAL { 24 | 25 | using MetricCallback = FunctionRef; 26 | 27 | // Iterates through all metrics created via the DEFINE_METRIC*() macros. 28 | // 29 | // ENSURES: stable iteration order 30 | inline void CollectMetrics(MetricCallback callback) { 31 | for (const MetricRef* const* metric = __start_gwpsan_metrics; 32 | metric < __stop_gwpsan_metrics; ++metric) { 33 | callback(**metric); 34 | } 35 | } 36 | 37 | } // namespace gwpsan SAN_LOCAL 38 | 39 | extern "C" { 40 | // These structs are part of the gwpsan ABI, and should be representable in C as 41 | // well as by common foreign language FFIs. 42 | struct gwpsan_metric_value { 43 | gwpsan::s64 value; 44 | const char* name; 45 | }; 46 | struct gwpsan_metric { 47 | const char* name; 48 | const char* desc; 49 | gwpsan::uptr size; 50 | gwpsan_metric_value* values; 51 | }; 52 | 53 | // Helper to collect gwpsan metrics for external code. This is required where 54 | // external code wants to export gwpsan's metrics, but using CollectMetrics() 55 | // directly does not work for a variety of reasons: 56 | // 57 | // 1. The external code is in a shared library - in that case, it would not see 58 | // gwpsan's metric section. 59 | // 60 | // 2. The external code cannot depend on "external_interface". 61 | // 62 | // 3. The external code is just C code or some other language FFI. 63 | // 64 | // If it's possible to use CollectMetrics(), prefer that since it avoids 65 | // additional memory allocations. 66 | SAN_INTERFACE void gwpsan_collect_metrics(void (*callback)(const gwpsan_metric*, 67 | void*), 68 | void* arg); 69 | } // extern "C" 70 | 71 | #endif // GWPSAN_BASE_METRIC_COLLECTION_H_ 72 | -------------------------------------------------------------------------------- /gwpsan/base/module_list.h: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef GWPSAN_BASE_MODULE_LIST_H_ 16 | #define GWPSAN_BASE_MODULE_LIST_H_ 17 | 18 | #include "gwpsan/base/common.h" 19 | #include "gwpsan/base/optional.h" 20 | 21 | namespace gwpsan SAN_LOCAL { 22 | 23 | struct ModuleInfo { 24 | const char* name; 25 | uptr start_address; 26 | uptr end_address; 27 | uptr pc_offset; // what needs to be subtracted from PC to pass to addr2line 28 | }; 29 | 30 | bool InitModuleList(); 31 | bool IsVDSO(uptr pc); 32 | 33 | // Returns true if the address is part of the runtime which is loaded as a DSO. 34 | // If no value can be returned, then the runtime is statically linked into the 35 | // main binary. 36 | Optional IsRuntimeInDSO(uptr pc_or_addr); 37 | 38 | // Returns the main thread stack bounds. 39 | // Note: the start address is conservative and is based on the end of 40 | // the previous mapping (since the stack automatically grows down). 41 | Optional> GetStackBounds(); 42 | const ModuleInfo* FindModule(uptr pc); 43 | void ForEachModule(FunctionRef cb); 44 | 45 | namespace internal { 46 | 47 | struct ModuleInfoNode : ModuleInfo { 48 | const ModuleInfoNode* next; 49 | }; 50 | 51 | struct ModulesInfo { 52 | const ModuleInfoNode* list = nullptr; 53 | uptr stack_start = 0; 54 | uptr stack_end = 0; 55 | uptr vdso_start = 0; 56 | uptr vdso_end = 0; 57 | uptr own_start = 0; 58 | uptr own_end = 0; 59 | }; 60 | 61 | ModulesInfo ParseModuleList(char* buffer, uptr own_addr, uptr main_addr); 62 | 63 | } // namespace internal 64 | } // namespace gwpsan SAN_LOCAL 65 | 66 | #endif // GWPSAN_BASE_MODULE_LIST_H_ 67 | -------------------------------------------------------------------------------- /gwpsan/base/numeric.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "gwpsan/base/numeric.h" 16 | 17 | #include "gwpsan/base/common.h" 18 | #include "gwpsan/base/optional.h" 19 | 20 | namespace gwpsan { 21 | 22 | uptr ReverseBitsVal(uptr val) { 23 | uptr res = 0; 24 | for (uptr i = 0; i < kWordBits; i++) 25 | res |= ((val & (1ul << i)) >> i) << (kWordBits - i - 1); 26 | return res; 27 | } 28 | 29 | Optional Atoi(const char* str) { 30 | const char* c = str; 31 | // Skip leading spaces. 32 | for (; *c == ' '; ++c) {} 33 | // Compute sign. 34 | s64 sign = 1; 35 | for (; *c == '-'; ++c) 36 | sign *= -1; 37 | if (!*c) 38 | return {}; 39 | // Convert string to number. 40 | s64 num = 0; 41 | for (; *c >= '0' && *c <= '9'; ++c) { 42 | num *= 10; 43 | num += *c - '0'; 44 | } 45 | // Should have consumed whole string; allow spaces after number. 46 | if (*c && *c != ' ' && *c != '\n') 47 | return {}; 48 | return sign * num; 49 | } 50 | 51 | Rand::Rand() 52 | : state_(GetTid()) {} 53 | 54 | u32 Rand::Next() { 55 | // Numbers from: 56 | // https://en.wikipedia.org/wiki/Linear_congruential_generator#Parameters_in_common_use 57 | // Note: we intentionally don't use compare_exchange. 58 | // compare_exchange would be slower and we don't need strong and secure 59 | // random numbers for our sampling choices. 60 | u32 state = __atomic_load_n(&state_, __ATOMIC_RELAXED); 61 | state = (state + 12345) * 1103515245; 62 | __atomic_store_n(&state_, state, __ATOMIC_RELAXED); 63 | return state; 64 | } 65 | 66 | bool Rand::OneOf(uptr n) { 67 | // Don't crash in production due to division by 0. 68 | if (SAN_WARN(n == 0)) 69 | return false; 70 | return (Next() % n) == 0; 71 | } 72 | 73 | uptr Rand::Index(uptr n) { 74 | // Don't crash in production due to division by 0. 75 | if (SAN_WARN(n == 0)) 76 | return 0; 77 | return Next() % n; 78 | } 79 | 80 | } // namespace gwpsan 81 | -------------------------------------------------------------------------------- /gwpsan/base/numeric.h: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef GWPSAN_BASE_NUMERIC_H_ 16 | #define GWPSAN_BASE_NUMERIC_H_ 17 | 18 | #include "gwpsan/base/common.h" 19 | #include "gwpsan/base/optional.h" 20 | 21 | namespace gwpsan SAN_LOCAL { 22 | 23 | uptr ReverseBitsVal(uptr val); 24 | 25 | // Convert string to integer. 26 | Optional Atoi(const char* str); 27 | 28 | // A simple thread-safe pseudo-random generator. 29 | class Rand { 30 | public: 31 | Rand(); 32 | 33 | // Returns true in 1 out of n calls on average. 34 | // REQUIRES: n != 0 35 | bool OneOf(uptr n); 36 | 37 | // Returns a random number in the range [0, n). 38 | // REQUIRES: n != 0 39 | uptr Index(uptr n); 40 | 41 | private: 42 | u32 state_; 43 | 44 | u32 Next(); 45 | }; 46 | 47 | } // namespace gwpsan SAN_LOCAL 48 | 49 | #endif // GWPSAN_BASE_NUMERIC_H_ 50 | -------------------------------------------------------------------------------- /gwpsan/base/numeric_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "gwpsan/base/numeric.h" 16 | 17 | #include 18 | 19 | #include "gtest/gtest.h" 20 | #include "gwpsan/base/common.h" 21 | #include "gwpsan/base/printf.h" 22 | 23 | namespace gwpsan { 24 | namespace { 25 | using u64_limits = std::numeric_limits; 26 | using s64_limits = std::numeric_limits; 27 | 28 | TEST(Numeric, Atoi) { 29 | EXPECT_FALSE(Atoi("")); 30 | EXPECT_FALSE(Atoi(" ")); 31 | EXPECT_FALSE(Atoi("-")); 32 | EXPECT_FALSE(Atoi("foo")); 33 | EXPECT_FALSE(Atoi("-123-42")); 34 | EXPECT_FALSE(Atoi("123-42")); 35 | EXPECT_FALSE(Atoi("12342-")); 36 | EXPECT_FALSE(Atoi("123x42")); 37 | EXPECT_EQ(*Atoi("0"), 0); 38 | EXPECT_EQ(*Atoi("-0"), 0); 39 | EXPECT_EQ(*Atoi("--42"), 42); 40 | EXPECT_EQ(*Atoi("---42"), -42); 41 | EXPECT_EQ(*Atoi("42\n"), 42); 42 | 43 | Rand rand; 44 | for (int i = 0; i < 1000; i++) { 45 | const s64 val = rand.Index(u64_limits::max()) - s64_limits::max(); 46 | char buf[32]; 47 | SPrintf(buf, sizeof(buf), "%s%lld%s", rand.OneOf(2) ? " " : "", val, 48 | rand.OneOf(2) ? " " : ""); 49 | EXPECT_EQ(val, *Atoi(buf)); 50 | } 51 | } 52 | 53 | } // namespace 54 | } // namespace gwpsan 55 | -------------------------------------------------------------------------------- /gwpsan/base/os.h: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef GWPSAN_BASE_OS_H_ 16 | #define GWPSAN_BASE_OS_H_ 17 | 18 | #include "gwpsan/base/common.h" 19 | #include "gwpsan/base/optional.h" 20 | #include "gwpsan/base/span.h" 21 | #include "gwpsan/base/units.h" 22 | 23 | namespace gwpsan SAN_LOCAL { 24 | 25 | // Terminates the process. 26 | [[noreturn]] void Die(); 27 | 28 | // Returns the process ID of the calling process. 29 | // Returns 0 on failure. 30 | int GetPid(); 31 | 32 | // Create new virtual mapping in virtual address space of the calling process. 33 | // Returns nullptr on failure. 34 | char* Mmap(uptr size); 35 | 36 | // Unmap a virtual mapping in the virtual address space of the calling process. 37 | bool Munmap(void* addr, uptr size); 38 | 39 | // Suspends execution of the calling thread for at least `delay`. 40 | void Sleep(Nanoseconds delay); 41 | 42 | // Read the contents of a file into the provided buffer. 43 | Result ReadFile(const char* path, Span buf); 44 | 45 | using ReadFileMock = FunctionRef(const char*, Span)>; 46 | inline void SetReadFileMock(const Optional& cb) { 47 | extern OptionalBase readfile_mock; 48 | readfile_mock = cb; 49 | } 50 | 51 | // Read the environment variable into the provided buffer. 52 | bool GetEnv(const char* name, Span buf); 53 | 54 | // Read the value of a command-line argument with format: --arg=val. 55 | bool GetCommandLineArg(const char* arg, Span buf); 56 | 57 | // Read the process name into the provided buffer. 58 | bool ReadProcessName(Span buf); 59 | 60 | enum class ThreadState { 61 | kRunning, 62 | kSleeping, 63 | kDiskSleep, 64 | kStopped, 65 | kDead, 66 | kZombie, 67 | kParked, 68 | kIdle, 69 | }; 70 | // Get thread state of thread `tid`. 71 | Optional GetThreadState(int tid); 72 | 73 | // Return number of CPUs in the system. 74 | int GetNumCPUs(); 75 | 76 | // Iterates through this process's list of threads, and calls `callback` with 77 | // their IDs; if `callback` returns true continues iterating, otherwise stops. 78 | // There is no guarantee that the TIDs are still valid, e.g. if a thread exits. 79 | [[nodiscard]] bool ForEachTid(FunctionRef callback); 80 | 81 | extern bool pause_on_die; 82 | extern bool abort_on_die; 83 | extern int die_error_code; 84 | 85 | } // namespace gwpsan SAN_LOCAL 86 | 87 | #endif // GWPSAN_BASE_OS_H_ 88 | -------------------------------------------------------------------------------- /gwpsan/base/printf.h: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef GWPSAN_BASE_PRINTF_H_ 16 | #define GWPSAN_BASE_PRINTF_H_ 17 | 18 | #include 19 | 20 | #include "gwpsan/base/common.h" 21 | #include "gwpsan/base/optional.h" 22 | #include "gwpsan/base/span.h" 23 | 24 | namespace gwpsan SAN_LOCAL { 25 | 26 | // These functions always zero-terminate the buffer 27 | // and return the number of bytes written. 28 | void VPrintf(const char* format, va_list args); 29 | uptr SPrintf(char* buf, uptr len, const char* format, ...) SAN_FORMAT(3, 4); 30 | uptr VSPrintf(char* buf, uptr len, const char* format, va_list args); 31 | 32 | using PrintfCallback = FunctionRef&)>; 33 | // Set optional Printf() callback, called for every Printf() invocation. 34 | inline void SetPrintfCallback(const Optional& cb) { 35 | extern OptionalBase printf_callback; 36 | printf_callback = cb; 37 | } 38 | 39 | void PrintStackTrace(const Span& trace, const char* prefix = ""); 40 | void PrintCurrentStackTrace(); 41 | 42 | } // namespace gwpsan SAN_LOCAL 43 | 44 | #endif // GWPSAN_BASE_PRINTF_H_ 45 | -------------------------------------------------------------------------------- /gwpsan/base/printf_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "gwpsan/base/printf.h" 16 | 17 | #include 18 | 19 | #include "gtest/gtest.h" 20 | #include "gwpsan/base/common.h" 21 | #include "gwpsan/base/span.h" 22 | 23 | namespace gwpsan { 24 | namespace { 25 | 26 | TEST(Printf, Base) { 27 | std::string res; 28 | const auto printf_callback = [&](const Span& str) { 29 | res.append(str.data(), str.size()); 30 | }; 31 | SetPrintfCallback({printf_callback}); 32 | Printf("Hello, %s!\n", "world"); 33 | EXPECT_EQ(res, "Hello, world!\n"); 34 | res.clear(); 35 | Printf("aaa %5d bbb\n", -123); 36 | EXPECT_EQ(res, "aaa -123 bbb\n"); 37 | res.clear(); 38 | Printf("%p\n", reinterpret_cast(0x12345)); 39 | EXPECT_EQ(res, "0x000000012345\n"); 40 | res.clear(); 41 | Printf("%08X\n", 0x1A2B3C); 42 | EXPECT_EQ(res, "001A2B3C\n"); 43 | res.clear(); 44 | SetPrintfCallback({}); 45 | } 46 | 47 | } // namespace 48 | } // namespace gwpsan 49 | -------------------------------------------------------------------------------- /gwpsan/base/sanitizer.h: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef GWPSAN_BASE_SANITIZER_H_ 16 | #define GWPSAN_BASE_SANITIZER_H_ 17 | 18 | #include "gwpsan/base/common.h" 19 | 20 | // Provides ASAN_POISON_MEMORY_REGION and ASAN_UNPOISON_MEMORY_REGION. 21 | #include // IWYU pragma: export 22 | #if GWPSAN_INSTRUMENTED_MSAN 23 | #include // IWYU pragma: export 24 | #define MSAN_POISON_MEMORY_REGION(addr, size) \ 25 | __msan_allocated_memory((addr), (size)) 26 | #define MSAN_UNPOISON_MEMORY_REGION(addr, size) __msan_unpoison((addr), (size)) 27 | #define MSAN_CHECK_MEMORY_REGION(addr, size) \ 28 | __msan_check_mem_is_initialized((addr), (size)) 29 | #define MSAN_COPY_MEMORY_REGION(dst, src, size) \ 30 | __msan_copy_shadow((dst), (src), (size)) 31 | #else 32 | #define MSAN_POISON_MEMORY_REGION(addr, size) 33 | #define MSAN_UNPOISON_MEMORY_REGION(addr, size) 34 | #define MSAN_CHECK_MEMORY_REGION(addr, size) 35 | #define MSAN_COPY_MEMORY_REGION(dst, src, size) 36 | #endif 37 | 38 | namespace gwpsan { 39 | 40 | // Return true if `addr` is shadow memory of compiler-based sanitizer. 41 | inline bool IsSanitizerShadow(uptr addr) { 42 | #if GWPSAN_INSTRUMENTED_MSAN 43 | // Constants from MSan runtime in compiler-rt/lib/msan/msan.h. 44 | constexpr uptr kShadowRanges[][2] = 45 | #if GWPSAN_ARM64 // clang-format off 46 | { 47 | {0x0100000000000ULL, 0x0200000000000ULL}, 48 | {0x0300000000000ULL, 0x0400000000000ULL}, 49 | {0x0400000000000ULL, 0x0600000000000ULL}, 50 | {0x0600000000000ULL, 0x0800000000000ULL}, 51 | {0x0B00000000000ULL, 0x0C00000000000ULL}, 52 | {0x0C00000000000ULL, 0x0D00000000000ULL}, 53 | {0x0D00000000000ULL, 0x0E00000000000ULL}, 54 | }; 55 | #elif GWPSAN_X64 56 | { 57 | {0x010000000000ULL, 0x100000000000ULL}, 58 | {0x110000000000ULL, 0x200000000000ULL}, 59 | {0x200000000000ULL, 0x300000000000ULL}, 60 | {0x300000000000ULL, 0x400000000000ULL}, 61 | {0x500000000000ULL, 0x510000000000ULL}, 62 | {0x600000000000ULL, 0x610000000000ULL}, 63 | }; 64 | #else // clang-format on 65 | #error "Unsupported platform" 66 | #endif 67 | for (int i = 0; i < SAN_ARRAY_SIZE(kShadowRanges); i++) { 68 | if (addr >= kShadowRanges[i][0] && addr < kShadowRanges[i][1]) 69 | return true; 70 | } 71 | #endif // GWPSAN_INSTRUMENTED_MSAN 72 | 73 | return false; 74 | } 75 | 76 | } // namespace gwpsan 77 | 78 | #endif // GWPSAN_BASE_SANITIZER_H_ 79 | -------------------------------------------------------------------------------- /gwpsan/base/signal_test_foreign_interceptor.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Test that foreign interceptors, with or without sanitizers, will not 16 | // interfere with gwpsan's interception. Follows the protocol described in 17 | // compiler-rt: 18 | // 19 | // https://github.com/llvm/llvm-project/blob/a68c96875566/compiler-rt/lib/interception/interception.h#L86 20 | // 21 | 22 | // For this test, __interceptor_sigaction can be non-weak, because we only test 23 | // it in the presence of gwpsan as well. 24 | extern "C" int __interceptor_sigaction(int sig, const struct sigaction* act, 25 | struct sigaction* old); 26 | 27 | extern "C" int sigaction(int sig, const struct sigaction* act, 28 | struct sigaction* old) { 29 | return __interceptor_sigaction(sig, act, old); 30 | } 31 | -------------------------------------------------------------------------------- /gwpsan/base/stdlib_disallow_update.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Copyright 2024 The GWPSan Authors 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # https://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | set -ueo pipefail 18 | 19 | INPUT="stdlib.h" 20 | OUTPUT="stdlib_disallow.inc" 21 | 22 | cd "${0%/*}" 23 | 24 | # Finds the location of libc. 25 | libc=$(ldd "$(which nm)" | grep -m1 "libc\.so" | sed 's!^.*=> \([^ ]*\) .*$!\1!') 26 | echo "libc: $libc" 27 | 28 | ( 29 | echo "// AUTOMATICALLY GENERATED BY ${0##*/}" 30 | # Generate new list 31 | nm -D "$libc" | 32 | # Looks for text (or weak) symbols. 33 | grep ' [TW] ' | cut -d ' ' -f 3 | 34 | # Strips the version after @@. 35 | cut -d '@' -f 1 | 36 | # Filters out symbols with __ and capital letters. 37 | grep -v '__\|[A-Z]' | sort -u | 38 | # Filters out symbols we explicitly allow per stdlib.h. 39 | grep -Fxv "$(grep "^SAN_.*_FUNCTION(" "$INPUT" | sed 's/^.*(\([^)]*\)).*$/\1/')" | 40 | # Writes the remaining symbols to the out file. 41 | while read sym; do 42 | echo "SAN_DISALLOW_FUNCTION(${sym});" 43 | done 44 | ) > "$OUTPUT" 45 | -------------------------------------------------------------------------------- /gwpsan/base/string.h: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef GWPSAN_BASE_STRING_H_ 16 | #define GWPSAN_BASE_STRING_H_ 17 | 18 | #include "gwpsan/base/common.h" 19 | 20 | namespace gwpsan SAN_LOCAL { 21 | 22 | // Simple pattern matching. Without additional metacharacters matches if 23 | // `pattern` is a substring of `str`. 24 | // 25 | // Note: Metacharacters cannot be escaped, so only use this where the `pattern` 26 | // string should not contain literal characters that are also used as 27 | // metacharacters. 28 | // 29 | // Available metacharacters: 30 | // ^ : match beginning; 31 | // $ : match end; 32 | // | : or (match multiple patterns); 33 | bool MatchStr(const char* str, const char* pattern); 34 | 35 | // Extract the basename from a path. 36 | const char* Basename(const char* path); 37 | 38 | } // namespace gwpsan SAN_LOCAL 39 | 40 | #endif // GWPSAN_BASE_STRING_H_ 41 | -------------------------------------------------------------------------------- /gwpsan/base/synchronization.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "gwpsan/base/synchronization.h" 16 | 17 | #include 18 | 19 | #include "gwpsan/base/common.h" 20 | #include "gwpsan/base/syscall.h" 21 | 22 | namespace gwpsan { 23 | 24 | SAN_NOINLINE void Semaphore::Wait() { 25 | u32 count = __atomic_load_n(&state_, __ATOMIC_RELAXED); 26 | for (;;) { 27 | if (count == 0) { 28 | sys_futex(&state_, FUTEX_WAIT_PRIVATE, 0); 29 | count = __atomic_load_n(&state_, __ATOMIC_RELAXED); 30 | continue; 31 | } 32 | if (__atomic_compare_exchange_n(&state_, &count, count - 1, true, 33 | __ATOMIC_ACQUIRE, __ATOMIC_RELAXED)) 34 | break; 35 | } 36 | } 37 | 38 | SAN_NOINLINE void Semaphore::Post(u32 count) { 39 | SAN_CHECK_NE(count, 0); 40 | __atomic_fetch_add(&state_, count, __ATOMIC_RELEASE); 41 | sys_futex(&state_, FUTEX_WAKE_PRIVATE, count); 42 | } 43 | 44 | void OptionalSyncMultipleAccess::Emplace() { 45 | // Release visibility over object to the increment in Acquire(). 46 | SAN_CHECK_EQ(__atomic_exchange_n(&refs_, kInit, __ATOMIC_RELEASE), 0); 47 | } 48 | 49 | void OptionalSyncMultipleAccess::ResetBegin() { 50 | // Wait for all in-flight Optional::and_then_sync() users to finish. Because 51 | // `refs_` is below kInit, new users won't use the object. Pairs with the 52 | // decrement in Release(). 53 | if (SAN_UNLIKELY(__atomic_fetch_sub(&refs_, kInit, __ATOMIC_RELAXED) != 54 | kInit)) 55 | sem_.Wait(); 56 | } 57 | 58 | void OptionalSyncMultipleAccess::ResetEnd() { 59 | SAN_CHECK_EQ(__atomic_load_n(&refs_, __ATOMIC_RELAXED), 0); 60 | } 61 | 62 | } // namespace gwpsan 63 | -------------------------------------------------------------------------------- /gwpsan/base/test_flags.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | extern "C" const char* gwpsan_default_flags() { 16 | return "test_mode:" 17 | "must_init:" 18 | "log_failures:" 19 | "sample_interval_usec=1000:" 20 | "tsan_delay_usec=500:" 21 | "uar_check_every_nth_thread=1:"; 22 | } 23 | -------------------------------------------------------------------------------- /gwpsan/base/test_report_interceptor.h: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef GWPSAN_BASE_TEST_REPORT_INTERCEPTOR_H_ 16 | #define GWPSAN_BASE_TEST_REPORT_INTERCEPTOR_H_ 17 | 18 | #include 19 | #include 20 | 21 | #include "gwpsan/base/common.h" 22 | #include "gwpsan/base/printf.h" 23 | #include "gwpsan/base/span.h" 24 | #include "gwpsan/base/synchronization.h" 25 | 26 | namespace gwpsan SAN_LOCAL { 27 | 28 | std::string ReportRegexp(const char* report); 29 | void ExpectReport(std::function const& body, const char* report, 30 | int timeout_sec = 100); 31 | void ExpectDeathReport(std::function const& body, const char* report, 32 | int timeout_sec = 100); 33 | 34 | class ReportInterceptor { 35 | public: 36 | ReportInterceptor(); 37 | ~ReportInterceptor(); 38 | 39 | std::string output() const; 40 | bool Empty() const; 41 | void ExpectReport(const char* report); 42 | void ExpectPartial(const char* report); 43 | void ExpectRegexp(const char* re); 44 | 45 | private: 46 | mutable Mutex mtx_; 47 | std::string output_; 48 | bool has_output_ = false; 49 | 50 | void Expect(const char* report, bool partial); 51 | 52 | // Printf() callback. 53 | void operator()(const Span& str); 54 | friend PrintfCallback; 55 | 56 | ReportInterceptor(const ReportInterceptor&) = delete; 57 | ReportInterceptor& operator=(const ReportInterceptor&) = delete; 58 | }; 59 | 60 | } // namespace gwpsan SAN_LOCAL 61 | 62 | #endif 63 | -------------------------------------------------------------------------------- /gwpsan/base/test_report_interceptor_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "gwpsan/base/test_report_interceptor.h" 16 | 17 | #include 18 | 19 | #include "gtest/gtest.h" 20 | #include "gwpsan/base/common.h" 21 | 22 | namespace gwpsan { 23 | namespace { 24 | 25 | struct TestData { 26 | const char* re; 27 | const char* output; 28 | }; 29 | 30 | using ReportInterceptorTest = testing::TestWithParam; 31 | 32 | TEST_P(ReportInterceptorTest, Test) { 33 | ReportInterceptor interceptor; 34 | // Print char-by-char to work-around Printf buffer size limit. 35 | for (const char* pos = GetParam().output; *pos; pos++) 36 | Printf("%c", *pos); 37 | interceptor.ExpectReport(GetParam().re); 38 | Printf("\n"); 39 | } 40 | 41 | INSTANTIATE_TEST_SUITE_P( 42 | Test, ReportInterceptorTest, 43 | testing::ValuesIn(std::vector{ 44 | // clang-format off 45 | {"[[MODULE]] A", "(3363dae9_020007+0x137) ./gwp_sanitizers/base/printf.h:42 A"}, 46 | {"[[MODULE]] A", "(3363dae9_020007+0x137) /bin/../include/c++/v1/__functional/invoke.h:394 A"}, 47 | // clang-format on 48 | })); 49 | 50 | } // namespace 51 | } // namespace gwpsan 52 | -------------------------------------------------------------------------------- /gwpsan/base/test_signal_listener.h: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef GWPSAN_BASE_TEST_SIGNAL_LISTENER_H_ 16 | #define GWPSAN_BASE_TEST_SIGNAL_LISTENER_H_ 17 | 18 | #include 19 | #include 20 | 21 | #include "gwpsan/base/common.h" 22 | #include "gwpsan/base/optional.h" 23 | #include "gwpsan/base/signal.h" 24 | #include "gwpsan/base/synchronization.h" 25 | 26 | namespace gwpsan SAN_LOCAL { 27 | 28 | template 29 | class TestSignalListener { 30 | protected: 31 | ~TestSignalListener() { 32 | SignalReceiver::singleton().reset(); 33 | } 34 | 35 | // Resets OnSignalFunc, to avoid use-after-destruction by in-flight signals. 36 | class ScopedSignalHandler { 37 | public: 38 | ~ScopedSignalHandler() { 39 | // Will wait for in-flight signal handlers to finish. 40 | SignalReceiver::singleton()->on_signal().reset(); 41 | } 42 | 43 | private: 44 | ScopedSignalHandler() = default; 45 | ScopedSignalHandler(const ScopedSignalHandler&) = delete; 46 | ScopedSignalHandler& operator=(const ScopedSignalHandler&) = delete; 47 | friend class TestSignalListener; 48 | }; 49 | 50 | [[nodiscard]] ScopedSignalHandler set_on_signal( 51 | std::function on_signal) { 52 | SignalReceiver::singleton().emplace(std::move(on_signal)); 53 | return {}; 54 | } 55 | 56 | private: 57 | class SignalReceiver : public SignalListener, 58 | public SynchronizedSingleton { 59 | using OnSignalFunc = Optional, 60 | OptionalSyncMultipleAccess>; 61 | 62 | public: 63 | explicit SignalReceiver( 64 | std::function on_signal) { 65 | on_signal_.emplace(std::move(on_signal)); 66 | SAN_CHECK(this->InstallSignalHandler()); 67 | } 68 | 69 | bool OnSignal(int sig, siginfo_t* siginfo, void* uctx) { 70 | on_signal_.and_then_sync([=](auto& f) { f(*siginfo); }); 71 | return true; // Assume all signals handled by test. 72 | } 73 | 74 | OnSignalFunc& on_signal() { 75 | return on_signal_; 76 | } 77 | 78 | private: 79 | OnSignalFunc on_signal_; 80 | }; 81 | }; 82 | 83 | } // namespace gwpsan SAN_LOCAL 84 | 85 | #endif // GWPSAN_BASE_TEST_SIGNAL_LISTENER_H_ 86 | -------------------------------------------------------------------------------- /gwpsan/base/type_id.h: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef GWPSAN_BASE_TYPE_ID_H_ 16 | #define GWPSAN_BASE_TYPE_ID_H_ 17 | 18 | #include "gwpsan/base/common.h" 19 | 20 | namespace gwpsan SAN_LOCAL { 21 | 22 | // TypeId provides an efficient opaque unique identifier for different types. 23 | // Can be used as a more lightweight alternative to typeid() or dynamic casting 24 | // where RTTI is unavailable. 25 | // 26 | // Example: 27 | // 28 | // class Base : public TypeId { 29 | // public: 30 | // using TypeId::TypeId; 31 | // }; 32 | // 33 | // class Foo : public Base { 34 | // public: 35 | // Foo() : Base(this) {} 36 | // }; 37 | // 38 | // class Bar : public Base { 39 | // public: 40 | // Bar() : Base(this) {} 41 | // }; 42 | // 43 | // Foo foo; 44 | // Bar bar; 45 | // assert(foo.type_id() != bar.type_id()); 46 | // assert(foo.type_id() == GetTypeId()); 47 | // assert(bar.type_id() == GetTypeId()); 48 | // 49 | class TypeId { 50 | public: 51 | template 52 | explicit TypeId(const T*) 53 | : id_(GetInternalId()) {} 54 | 55 | bool operator==(const TypeId& rhs) const { 56 | return id_ == rhs.id_; 57 | } 58 | bool operator!=(const TypeId& rhs) const { 59 | return !(*this == rhs); 60 | } 61 | 62 | // Return TypeId for derived classes. 63 | const TypeId& type_id() const { 64 | return *this; 65 | } 66 | 67 | private: 68 | template 69 | static void* GetInternalId() { 70 | static char id; 71 | return &id; 72 | } 73 | 74 | void* const id_; 75 | }; 76 | 77 | template 78 | TypeId GetTypeId() { 79 | return TypeId(static_cast(nullptr)); 80 | } 81 | 82 | // dyn_cast<> may be used on classes derived from TypeId to safely cast between 83 | // base and derived classes. 84 | template 85 | To* dyn_cast(From* from) { 86 | return (from && from->type_id() == GetTypeId()) ? static_cast(from) 87 | : nullptr; 88 | } 89 | template 90 | const To* dyn_cast(const From* from) { 91 | return (from && from->type_id() == GetTypeId()) 92 | ? static_cast(from) 93 | : nullptr; 94 | } 95 | 96 | } // namespace gwpsan SAN_LOCAL 97 | 98 | #endif // GWPSAN_BASE_TYPE_ID_H_ 99 | -------------------------------------------------------------------------------- /gwpsan/base/units_test_printer.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | #include 17 | 18 | #include "gwpsan/base/units.h" 19 | 20 | namespace gwpsan { 21 | 22 | template <> 23 | std::ostream& operator<<(std::ostream& os, const BitSize& val) { 24 | return os << *val << "bits"; 25 | } 26 | 27 | template <> 28 | std::ostream& operator<<(std::ostream& os, const ByteSize& val) { 29 | return os << *val << "bytes"; 30 | } 31 | 32 | template <> 33 | std::ostream& operator<<(std::ostream& os, const WordSize& val) { 34 | return os << *val << "words"; 35 | } 36 | 37 | template <> 38 | std::ostream& operator<<(std::ostream& os, const Addr& val) { 39 | return os << std::hex << std::showbase << *val; 40 | } 41 | 42 | template <> 43 | std::ostream& operator<<(std::ostream& os, const Seconds& val) { 44 | return os << *val << "sec"; 45 | } 46 | 47 | template <> 48 | std::ostream& operator<<(std::ostream& os, const Milliseconds& val) { 49 | return os << *val << "msec"; 50 | } 51 | 52 | template <> 53 | std::ostream& operator<<(std::ostream& os, const Microseconds& val) { 54 | return os << *val << "usec"; 55 | } 56 | 57 | template <> 58 | std::ostream& operator<<(std::ostream& os, const Nanoseconds& val) { 59 | return os << *val << "nsec"; 60 | } 61 | 62 | } // namespace gwpsan 63 | -------------------------------------------------------------------------------- /gwpsan/base/unwind.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "gwpsan/base/unwind.h" 16 | 17 | #include "gwpsan/base/common.h" 18 | #include "gwpsan/base/env.h" 19 | #include "gwpsan/base/span.h" 20 | #include "gwpsan/base/units.h" 21 | #include "gwpsan/base/weak_imports.h" 22 | 23 | namespace gwpsan { 24 | // It may read stack redzones and uninits. 25 | SAN_NOINSTR uptr RawUnwindStack(Span storage, const void* fp) { 26 | struct Frame { 27 | Frame* next; 28 | void* pc; 29 | }; 30 | Frame f; 31 | uptr n = 0; 32 | // Strictly speaking we don't need kPageSize checks, since we use 33 | // non-failing loads, but since frame pointers realistically may be bogus 34 | // the additional check may speed up things. 35 | for (; reinterpret_cast(fp) > kPageSize && n < storage.size() && 36 | NonFailingLoad(fp, Sizeof(f), &f) && 37 | reinterpret_cast(f.pc) > kPageSize; 38 | fp = f.next) { 39 | storage[n++] = reinterpret_cast(__builtin_extract_return_addr(f.pc)); 40 | } 41 | return n; 42 | } 43 | 44 | Span RawUnwindStack(Span storage) { 45 | auto size = RawUnwindStack(storage, __builtin_frame_address(0)); 46 | return {storage.data(), size}; 47 | } 48 | 49 | void Symbolize(unsigned long pc, char* buf, int buf_size, bool add_src) { 50 | constexpr char kUnknown[] = "(unknown)"; 51 | if (!absl::Symbolize) { 52 | internal_strncpy(buf, kUnknown, buf_size); 53 | return; 54 | } 55 | if (!absl::Symbolize(reinterpret_cast(pc), buf, buf_size)) { 56 | internal_strncpy(buf, kUnknown, buf_size); 57 | return; 58 | } 59 | if (add_src) 60 | return; 61 | // Try to remove the file:line info, if present. 62 | // Note that the symbol name can contain both spaces 63 | // (in "anonymous namespace") and colons (as namespace separator). 64 | // For simplicity we currently assume that the file name does not contain 65 | // spaces, so the first space is the potential separator between file:line 66 | // and the symbol name. 67 | const char* src = buf; 68 | while (*src && *src != ' ') 69 | src++; 70 | if (!*src || src == buf || (src[-1] < '0' || src[-1] > '9')) 71 | return; 72 | src++; 73 | while ((*buf++ = *src++)) {} 74 | } 75 | } // namespace gwpsan 76 | -------------------------------------------------------------------------------- /gwpsan/base/unwind.h: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef GWPSAN_BASE_UNWIND_H_ 16 | #define GWPSAN_BASE_UNWIND_H_ 17 | 18 | #include "gwpsan/base/common.h" 19 | #include "gwpsan/base/span.h" 20 | 21 | namespace gwpsan SAN_LOCAL { 22 | 23 | // Unwinds the current stack w/o skipping our runtime frames. 24 | // Intended for debugging/bug reports in gwpsan itself. 25 | Span RawUnwindStack(Span storage); 26 | 27 | // Unwind the stack starting from the frame pointer 'fp'. 28 | uptr RawUnwindStack(Span storage, const void* fp); 29 | 30 | // Symbolizes pc and stores the result into the buf. 31 | // If add_src, may also add source:line info if available. 32 | void Symbolize(uptr pc, char* buf, int buf_size, bool add_src); 33 | 34 | } // namespace gwpsan SAN_LOCAL 35 | 36 | #endif // GWPSAN_BASE_UNWIND_H_ 37 | -------------------------------------------------------------------------------- /gwpsan/base/vector_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "gwpsan/base/vector.h" 16 | 17 | #include "gmock/gmock.h" 18 | #include "gtest/gtest.h" 19 | #include "gwpsan/base/common.h" 20 | 21 | namespace gwpsan { 22 | namespace { 23 | 24 | template 25 | void TestImpl() { 26 | VectorT v; 27 | ASSERT_TRUE(v.empty()); 28 | ASSERT_EQ(v.size(), 0); 29 | ASSERT_EQ(v.begin(), v.end()); 30 | for (uptr i = 0; i < 10; ++i) { 31 | v.emplace_back(static_cast(i + 1)); 32 | ASSERT_FALSE(v.empty()); 33 | ASSERT_EQ(v.size(), i + 1); 34 | ASSERT_LE(v.size(), v.capacity()); 35 | ASSERT_EQ(v.end() - v.begin(), i + 1); 36 | ASSERT_EQ(v.front(), 1); 37 | ASSERT_EQ(v.back(), i + 1); 38 | for (uptr j = 0; j <= i; ++j) { 39 | ASSERT_EQ(v[j], j + 1); 40 | ASSERT_EQ(v.at(j), j + 1); 41 | ASSERT_EQ(v.begin()[j], j + 1); 42 | } 43 | } 44 | EXPECT_DEATH(v.at(10), testing::HasSubstr("CHECK: n < size_ (10 < 10)")); 45 | for (uptr i = 0; i < 10; ++i) { 46 | ASSERT_EQ(v.pop_back(), 10 - i); 47 | ASSERT_EQ(v.size(), 10 - i - 1); 48 | } 49 | v.resize(10, 55); 50 | ASSERT_EQ(v.size(), 10); 51 | for (uptr i = 0; i < 10; ++i) { 52 | ASSERT_EQ(v.at(i), 55); 53 | } 54 | v.resize(0); 55 | ASSERT_EQ(v.size(), 0); 56 | } 57 | 58 | TEST(Vector, Basic) { 59 | TestImpl>(); 60 | TestImpl>(); 61 | TestImpl>(); 62 | } 63 | 64 | TEST(Vector, Move) { 65 | MallocVector v; 66 | v.resize(10, 55); 67 | void* orig_data = v.data(); 68 | MallocVector v2(move(v)); 69 | ASSERT_EQ(v.data(), nullptr); 70 | ASSERT_EQ(v.size(), 0); 71 | ASSERT_EQ(v2.data(), orig_data); 72 | ASSERT_EQ(v2.size(), 10); 73 | for (uptr i = 0; i < 10; ++i) 74 | ASSERT_EQ(v2.at(i), 55); 75 | MallocVector v3; 76 | v3 = move(v2); 77 | ASSERT_EQ(v2.data(), nullptr); 78 | ASSERT_EQ(v2.size(), 0); 79 | ASSERT_EQ(v3.data(), orig_data); 80 | ASSERT_EQ(v3.size(), 10); 81 | for (uptr i = 0; i < 10; ++i) 82 | ASSERT_EQ(v3.at(i), 55); 83 | } 84 | 85 | TEST(Vector, ArrayOverflow) { 86 | ArrayVector v; 87 | ASSERT_EQ(v.capacity(), 2); 88 | v.emplace_back(char{1}); 89 | v.emplace_back(char{2}); 90 | ASSERT_DEATH(v.emplace_back(char{3}), 91 | testing::HasSubstr("BUG: ArrayStorage grow")); 92 | } 93 | 94 | } // namespace 95 | } // namespace gwpsan 96 | -------------------------------------------------------------------------------- /gwpsan/base/weak_imports.h: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef GWPSAN_BASE_WEAK_IMPORTS_H_ 16 | #define GWPSAN_BASE_WEAK_IMPORTS_H_ 17 | 18 | #include "absl/base/config.h" 19 | #include "gwpsan/base/common.h" 20 | 21 | // Declare absl functions manually as weak because we don't want to depend on 22 | // nor link in absl library. We use them only if they are linked otherwise. 23 | namespace absl { 24 | ABSL_NAMESPACE_BEGIN 25 | SAN_WEAK_IMPORT bool Symbolize(const void* pc, char* out, int out_size); 26 | ABSL_NAMESPACE_END 27 | } // namespace absl 28 | 29 | #endif // GWPSAN_BASE_WEAK_IMPORTS_H_ 30 | -------------------------------------------------------------------------------- /gwpsan/base/weak_imports_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "gwpsan/base/weak_imports.h" 16 | 17 | #include "gtest/gtest.h" 18 | #include "absl/debugging/symbolize.h" 19 | 20 | namespace gwpsan { 21 | namespace { 22 | TEST(WeakImports, Test) { 23 | EXPECT_NE(absl::Symbolize, nullptr); 24 | } 25 | } // namespace 26 | } // namespace gwpsan 27 | -------------------------------------------------------------------------------- /gwpsan/base/weak_imports_use.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "absl/debugging/symbolize.h" 16 | #include "gwpsan/base/common.h" 17 | 18 | namespace gwpsan { 19 | 20 | // Force linking of optional dependencies, because if the only use is through 21 | // weak declarations, we are not guaranteed that they are actually linked. 22 | // 23 | // This should only be used in our standalone tests. 24 | SAN_USED void WeakImportsUse() { 25 | absl::Symbolize(nullptr, nullptr, 0); 26 | SAN_BUG("should never get here"); 27 | } 28 | 29 | } // namespace gwpsan 30 | -------------------------------------------------------------------------------- /gwpsan/core/breakmanager_perf_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | 17 | #include "absl/flags/flag.h" 18 | #include "gwpsan/base/common.h" 19 | #include "gwpsan/base/log.h" 20 | #include "gwpsan/base/optional.h" 21 | #include "gwpsan/base/os.h" 22 | #include "gwpsan/base/units.h" 23 | #include "gwpsan/core/breakmanager.h" 24 | #include "gwpsan/core/breakpoint.h" 25 | #include "gwpsan/core/init.h" 26 | 27 | // We need to define the flag so that the app flag parsing does not fail 28 | // on the unknown flag. But we parse it manually, since we need it much earlier. 29 | ABSL_FLAG(bool, gwpsan_arm_watchpoints, false, 30 | "Arm all watchpoints (but they will never fire)"); 31 | 32 | namespace gwpsan { 33 | namespace { 34 | 35 | // Poor man's command line parsing. 36 | // For some targets command line is the only way to pass arguments. 37 | void ParseCmdLine(bool& arm_watchpoints) { 38 | const uptr kBufSize = 64 << 20; 39 | char* buf = Mmap(kBufSize); 40 | SAN_CHECK(buf); 41 | auto res = ReadFile("/proc/self/cmdline", {buf, kBufSize}); 42 | SAN_CHECK(!!res); 43 | SAN_CHECK_LT(res.val(), kBufSize - 1); 44 | const char* kArmWatchpoints = "-gwpsan_arm_watchpoints"; 45 | if (memmem(buf, res.val(), kArmWatchpoints, strlen(kArmWatchpoints))) 46 | arm_watchpoints = true; 47 | Munmap(buf, kBufSize); 48 | } 49 | 50 | void ctor() { 51 | SAN_CHECK(Init()); 52 | bool arm_watchpoints = false; 53 | ParseCmdLine(arm_watchpoints); 54 | auto& mgr = *BreakManager::singleton().try_emplace(); 55 | if (arm_watchpoints) { 56 | static int watched[BreakManager::kMaxBreakpoints]; 57 | for (auto& w : watched) 58 | SAN_CHECK(mgr.Watch({Breakpoint::Type::kReadWrite, &w, Sizeof(w)})); 59 | } 60 | SAN_LOG("breakmanager perf test: arm_watchpoints=%d", arm_watchpoints); 61 | } 62 | 63 | __attribute__((section(".preinit_array"), used)) void (*preinit)(void) = ctor; 64 | 65 | } // namespace 66 | } // namespace gwpsan 67 | -------------------------------------------------------------------------------- /gwpsan/core/breakpoint_benchmark.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "benchmark/benchmark.h" 16 | #include "gwpsan/base/common.h" 17 | #include "gwpsan/base/units.h" 18 | #include "gwpsan/core/breakpoint.h" 19 | 20 | namespace gwpsan { 21 | namespace { 22 | 23 | template 24 | SAN_NOINLINE int EmptyFunc() { 25 | __atomic_signal_fence(__ATOMIC_SEQ_CST); 26 | return i; 27 | } 28 | 29 | // This benchmark aims to measure effects of breakpoints on execution when 30 | // they don't fire. Ideally the effect should be 0. 31 | // The breakpoints are setup in a way we expect them to be setup in prod. 32 | void BM_BreakpointEffect(benchmark::State& state) { 33 | const int kMaxBreakpoints = 4; 34 | Breakpoint bp[kMaxBreakpoints]; 35 | int (*breaks[])() = {EmptyFunc<0>, EmptyFunc<1>, EmptyFunc<2>, EmptyFunc<3>}; 36 | int watched[kMaxBreakpoints]; 37 | const int num_bps = state.range(0); 38 | const int num_wps = state.range(1); 39 | SAN_CHECK_LE(num_bps + num_wps, kMaxBreakpoints); 40 | for (int i = 0; i < num_bps; i++) { 41 | SAN_CHECK(bp[i].Init(Breakpoint::kModePerThread)); 42 | SAN_CHECK(!!bp[i].Enable({Breakpoint::Type::kCode, &breaks[i]})); 43 | } 44 | for (int i = num_bps; i < num_bps + num_wps; i++) { 45 | SAN_CHECK(bp[i].Init(0)); 46 | SAN_CHECK(!!bp[i].Enable( 47 | {Breakpoint::Type::kReadWrite, &watched[i], Sizeof(watched[i])})); 48 | } 49 | const int kDataSize = 64 << 10; 50 | volatile int data[kDataSize] = {}; 51 | for (auto s : state) { 52 | for (int i = 0; i < kDataSize; i++) 53 | data[i] = data[i] + 1; 54 | } 55 | } 56 | BENCHMARK(BM_BreakpointEffect) 57 | ->ArgPair(0, 0) 58 | ->ArgPair(1, 0) 59 | ->ArgPair(2, 0) 60 | ->ArgPair(3, 0) 61 | ->ArgPair(4, 0) 62 | ->ArgPair(0, 1) 63 | ->ArgPair(0, 2) 64 | ->ArgPair(0, 3) 65 | ->ArgPair(0, 4) 66 | ->ArgPair(1, 3) 67 | ->ArgPair(2, 2) 68 | ->ArgPair(3, 1); 69 | 70 | } // namespace 71 | } // namespace gwpsan 72 | -------------------------------------------------------------------------------- /gwpsan/core/context_benchmark.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | 17 | #include "benchmark/benchmark.h" 18 | #include "gwpsan/base/common.h" 19 | #include "gwpsan/core/arch.h" 20 | #include "gwpsan/core/context.h" 21 | #include "gwpsan/core/decode.h" 22 | #include "gwpsan/core/env.h" 23 | 24 | namespace gwpsan { 25 | namespace { 26 | 27 | void BM_Emulate(benchmark::State& state) { 28 | const uptr kBatch = 100; 29 | auto body = +[](void* arg) { 30 | SAN_UNUSED volatile uptr a = 0, b = 0; 31 | for (uptr i = 0; i < kBatch; ++i) 32 | a += b; 33 | }; 34 | int64_t instructions = 0; 35 | while (state.KeepRunningBatch(kBatch)) { 36 | CPUContext ctx; 37 | uptr stack[1024] = {}; 38 | ctx.SetupCall(body, nullptr, stack, sizeof(stack), nullptr); 39 | Env env(0); 40 | for (; ctx.reg(kPC).val; ++instructions) { 41 | ArchDecoder dec(ctx.reg(kPC).val); 42 | SAN_CHECK(dec.Decode()); 43 | ctx.Execute(env, dec); 44 | } 45 | } 46 | state.SetItemsProcessed(instructions); 47 | } 48 | BENCHMARK(BM_Emulate); 49 | 50 | } // namespace 51 | } // namespace gwpsan 52 | -------------------------------------------------------------------------------- /gwpsan/core/core_fwd.h: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef GWPSAN_CORE_CORE_FWD_H_ 16 | #define GWPSAN_CORE_CORE_FWD_H_ 17 | 18 | namespace gwpsan { 19 | 20 | class Arg; 21 | class Breakpoint; 22 | class CPUContext; 23 | class Env; 24 | class Instr; 25 | class InstrSequence; 26 | class InstructionOrigin; 27 | class Meta; 28 | class Operation; 29 | class Origin; 30 | class OriginChain; 31 | struct Word; 32 | struct MemAccess; 33 | using OpRef = const Operation&; 34 | 35 | } // namespace gwpsan 36 | 37 | #endif // GWPSAN_CORE_CORE_FWD_H_ 38 | -------------------------------------------------------------------------------- /gwpsan/core/decode.h: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef GWPSAN_CORE_DECODE_H_ 16 | #define GWPSAN_CORE_DECODE_H_ 17 | 18 | #include "gwpsan/base/common.h" 19 | 20 | #if GWPSAN_X64 21 | #include "gwpsan/core/decoder_x86.h" 22 | #elif GWPSAN_ARM64 23 | #include "gwpsan/core/decoder_arm64.h" 24 | #endif 25 | 26 | namespace gwpsan SAN_LOCAL { 27 | 28 | #if GWPSAN_X64 29 | using ArchDecoder = X86Decoder; 30 | #elif GWPSAN_ARM64 31 | using ArchDecoder = Arm64Decoder; 32 | #endif 33 | 34 | void DumpInstructions(); 35 | 36 | } // namespace gwpsan SAN_LOCAL 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /gwpsan/core/decoder_arm64.h: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef GWPSAN_CORE_DECODER_ARM64_H_ 16 | #define GWPSAN_CORE_DECODER_ARM64_H_ 17 | 18 | #include "gwpsan/core/decoder_dynamorio.h" 19 | 20 | namespace gwpsan SAN_LOCAL { 21 | 22 | class Arm64Decoder : public DynamoRIODecoder { 23 | public: 24 | Arm64Decoder(uptr pc, uptr pc_copy = 0); 25 | 26 | private: 27 | void DecodeArch() override; 28 | // Lots of instructions can have additional operands that encode shift 29 | // or extend of the preceeding operand. This function decodes these operands 30 | // starting from 'shift_arg_idx', applies shift/extend to 'arg' and returns 31 | // shifted/extended argument. 32 | Arg* ShiftExtend(Arg* arg, int shift_arg_idx); 33 | OpRef ShiftToOpRef(opnd_t opnd); 34 | RegIdx MapDRReg(reg_id_t reg, ByteSize& offset) override; 35 | Instr::Predicate MakePredicate(dr_pred_type_t pred) override; 36 | }; 37 | 38 | } // namespace gwpsan SAN_LOCAL 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /gwpsan/core/decoder_dynamorio.h: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef GWPSAN_CORE_DECODER_DYNAMORIO_H_ 16 | #define GWPSAN_CORE_DECODER_DYNAMORIO_H_ 17 | 18 | #include "gwpsan/import/drdecode/include/dr_api.h" 19 | #include "gwpsan/base/common.h" 20 | #include "gwpsan/base/type_id.h" 21 | #include "gwpsan/base/units.h" 22 | #include "gwpsan/base/vector.h" 23 | #include "gwpsan/core/arch.h" 24 | #include "gwpsan/core/decoder.h" 25 | #include "gwpsan/core/instruction.h" 26 | 27 | namespace gwpsan SAN_LOCAL { 28 | 29 | // Base class for instruction decoders based on DynamoRIO. 30 | class DynamoRIODecoder : public InstrDecoder { 31 | public: 32 | static bool Init(); 33 | static constexpr uptr kMaxOpcodes = OP_AFTER_LAST; 34 | static const char* OpcodeName(uptr opcode); 35 | 36 | protected: 37 | DynamoRIODecoder(uptr pc, uptr pc_copy); 38 | 39 | int opcode() { 40 | return instr_get_opcode(instr_); 41 | } 42 | 43 | int num_src() { 44 | return src_.size(); 45 | } 46 | 47 | int num_dst() { 48 | return dst_.size(); 49 | } 50 | 51 | Arg* src(int idx) { 52 | return idx < src_.size() ? src_[idx] : &imm_fallback_; 53 | } 54 | 55 | // Returns value of the immediate operand idx. 56 | uptr src_imm(int idx) { 57 | const auto* arg = dyn_cast(src(idx)); 58 | if (!arg) { 59 | DECODE_FAIL("immediate argument is not immediate"); 60 | return 0; 61 | } 62 | return arg->val(); 63 | } 64 | 65 | void set_src(int idx, Arg* arg) { 66 | src_.at(idx) = arg; 67 | } 68 | 69 | Arg* src_pop() { 70 | Arg* arg = src_.at(0); 71 | for (uptr i = 1; i < src_.size(); ++i) 72 | src_[i - 1] = src_[i]; 73 | src_.pop_back(); 74 | return arg; 75 | } 76 | 77 | Arg* dst(int idx) { 78 | return idx < dst_.size() ? dst_[idx] : &imm_fallback_; 79 | } 80 | 81 | uptr CurrentPC() { 82 | return reinterpret_cast(instr_get_app_pc(instr_)); 83 | } 84 | 85 | uptr NextPC() { 86 | return CurrentPC() + instr_length(GLOBAL_DCONTEXT, instr_); 87 | } 88 | 89 | instr_t* instr() { 90 | return instr_; 91 | } 92 | 93 | // Returns predicate associated with the instruction. 94 | Instr::Predicate Predicate(); 95 | // Returns predicate associated with the source argument idx. 96 | Instr::Predicate SrcPredicate(int idx); 97 | BitSize DecodeExtend(dr_extend_type_t ex, bool& sign); 98 | 99 | private: 100 | instr_noalloc_t noalloc_; 101 | instr_t* instr_ = nullptr; 102 | ArrayVector src_; 103 | ArrayVector dst_; 104 | 105 | void DecodeImpl() override; 106 | 107 | Arg* MakeArg(opnd_t opnd); 108 | Arg* MakeRegArg(opnd_t opnd); 109 | Arg* MakeImmArg(opnd_t opnd); 110 | Arg* MakeMemArg(opnd_t opnd); 111 | 112 | virtual void DecodeArch() = 0; 113 | virtual RegIdx MapDRReg(reg_id_t reg, ByteSize& offset) = 0; 114 | virtual Instr::Predicate MakePredicate(dr_pred_type_t pred) = 0; 115 | }; 116 | 117 | } // namespace gwpsan SAN_LOCAL 118 | 119 | #endif 120 | -------------------------------------------------------------------------------- /gwpsan/core/decoder_fuzzer.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | 17 | #include "gwpsan/base/allocator.h" 18 | #include "gwpsan/base/common.h" 19 | #include "gwpsan/core/context.h" 20 | #include "gwpsan/core/decoder_executor.h" 21 | 22 | namespace gwpsan { 23 | const char* DefaultFlags() { 24 | // LibFuzzer needs to catch SIGABRT to save the input. 25 | return "abort_on_error"; 26 | } 27 | 28 | extern "C" int LLVMFuzzerTestOneInput(const char* data, uptr size) { 29 | using IE = InstructionExecutor; 30 | // We don't fully implement xsavec32/64 semantics (don't model what exactly 31 | // they store to memory). 32 | // We emulate pushf correctly in most cases, however sometimes real execution 33 | // suddenly gets ID and NT flags (0x204000) set, and as the result 34 | // memory contents differ with what we predict. 35 | if (!IE::singleton()) 36 | IE::singleton().emplace(true, getenv("GWPSAN_OPCODES"), 37 | "xsavec32,xsavec64,pushf"); 38 | auto& exec = *IE::singleton(); 39 | HeapAllocatorLifetime alloc_lifetime; 40 | CPUContext ctx; 41 | auto code = IE::FuzzerDecode({reinterpret_cast(data), size}, ctx); 42 | exec.Execute(code, {}, ctx); 43 | return 0; 44 | } 45 | } // namespace gwpsan 46 | -------------------------------------------------------------------------------- /gwpsan/core/decoder_x86.h: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef GWPSAN_CORE_DECODER_X86_H_ 16 | #define GWPSAN_CORE_DECODER_X86_H_ 17 | 18 | #include "gwpsan/core/decoder_dynamorio.h" 19 | 20 | namespace gwpsan SAN_LOCAL { 21 | 22 | class X86Decoder : public DynamoRIODecoder { 23 | public: 24 | X86Decoder(uptr pc, uptr pc_copy = 0); 25 | 26 | private: 27 | void DecodeArch() override; 28 | // Zeros the upper (unoccupied by dst) part of YMM/ZMM register. 29 | void ZeroUpperXMMRegister(const Arg* dst); 30 | RegIdx MapDRReg(reg_id_t reg, ByteSize& offset) override; 31 | Instr::Predicate MakePredicate(dr_pred_type_t pred) override; 32 | }; 33 | 34 | } // namespace gwpsan SAN_LOCAL 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /gwpsan/core/disable_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "gwpsan/core/init.h" 16 | 17 | int main() { 18 | // Must fail since the target depends on the disable target. 19 | return gwpsan::Init(); 20 | } -------------------------------------------------------------------------------- /gwpsan/core/env.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "gwpsan/core/env.h" 16 | 17 | #include "gwpsan/base/common.h" 18 | #include "gwpsan/base/env.h" 19 | #include "gwpsan/base/log.h" 20 | #include "gwpsan/base/span.h" 21 | #include "gwpsan/base/units.h" 22 | #include "gwpsan/core/arch.h" 23 | #include "gwpsan/core/core_fwd.h" 24 | #include "gwpsan/core/meta.h" 25 | 26 | namespace gwpsan { 27 | 28 | Env::Env(Mode mode, Callback* cb) 29 | : mode_(mode) 30 | , cb_(cb) {} 31 | 32 | Word Env::Load(Addr addr, ByteSize size) { 33 | Word val; 34 | if (!(mode_ & kModeZero) && (!cb_ || cb_->FilterAccess(addr, size))) { 35 | if (!NonFailingLoad(addr, size, &val.val)) 36 | Exception(); 37 | } 38 | if (cb_) { 39 | val = cb_->Load(addr, size, val.val); 40 | if (current_instruction_) 41 | val.meta.Chain(current_instruction_); 42 | } 43 | SAN_LOG("load%zu 0x%zx->0x%zx[%zx]", *size, *addr, val.val, 44 | val.meta.shadow()); 45 | return val; 46 | } 47 | 48 | void Env::Store(Addr addr, ByteSize size, const Word& val) { 49 | SAN_LOG("store%zu 0x%zx<-0x%zx[%zx]", *size, *addr, val.val, 50 | val.meta.shadow()); 51 | if (cb_) 52 | cb_->Store(addr, size, val); 53 | if (!(mode_ & kModeImmutable) && (!cb_ || cb_->FilterAccess(addr, size))) { 54 | if (!NonFailingStore(addr, size, &val.val)) 55 | Exception(); 56 | } 57 | } 58 | 59 | void Env::Syscall(uptr nr, Span accesses) { 60 | if (cb_) 61 | cb_->Syscall(nr, accesses); 62 | } 63 | 64 | void Env::Exception() { 65 | SAN_WARN(exception_raised_); 66 | SAN_LOG("an exception is raised"); 67 | exception_raised_ = true; 68 | if (cb_) 69 | cb_->Exception(); 70 | } 71 | 72 | void Env::ReportUninit(const CPUContext& ctx, const OriginChain* origin, 73 | RegIdx reg, uptr flags) { 74 | if (cb_) 75 | cb_->ReportUninit(ctx, origin, reg, flags); 76 | } 77 | 78 | } // namespace gwpsan 79 | -------------------------------------------------------------------------------- /gwpsan/core/env.h: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef GWPSAN_CORE_ENV_H_ 16 | #define GWPSAN_CORE_ENV_H_ 17 | 18 | #include "gwpsan/base/common.h" 19 | #include "gwpsan/base/span.h" 20 | #include "gwpsan/base/units.h" 21 | #include "gwpsan/core/arch.h" 22 | #include "gwpsan/core/core_fwd.h" 23 | #include "gwpsan/core/meta.h" 24 | 25 | namespace gwpsan SAN_LOCAL { 26 | 27 | // Env stubs memory subsystem (loads and stores). 28 | class Env { 29 | public: 30 | using Mode = u32; 31 | // Don't do actual stores to memory. 32 | static constexpr Mode kModeImmutable = 1 << 0; 33 | // Don't load from memory and return zeros. 34 | static constexpr Mode kModeZero = 1 << 1; 35 | // Track uninitialized values and create origins in CPUContext. 36 | static constexpr Mode kUninitTracking = 1 << 2; 37 | 38 | class Callback { 39 | public: 40 | virtual Word Load(Addr addr, ByteSize size, uptr val) { 41 | return {val}; 42 | } 43 | virtual void Store(Addr addr, ByteSize size, const Word& val) {} 44 | // If FilterAccess returns false, the real memory access won't be done 45 | // (loads return 0, stores are ignored). 46 | virtual bool FilterAccess(Addr addr, ByteSize size) { 47 | return true; 48 | } 49 | virtual void Syscall(uptr nr, Span accesses) {} 50 | virtual void Exception() {} 51 | virtual void ReportUninit(const CPUContext& ctx, const OriginChain* origin, 52 | RegIdx reg, uptr flags) {} 53 | 54 | protected: 55 | ~Callback() = default; 56 | }; 57 | 58 | explicit Env(Mode mode, Callback* cb = nullptr); 59 | 60 | Word Load(Addr addr, ByteSize size); 61 | void Store(Addr addr, ByteSize size, const Word& val); 62 | void Syscall(uptr nr, Span accesses); 63 | void Exception(); 64 | 65 | // Called on use of uninitialized value. 66 | void ReportUninit(const CPUContext& ctx, const OriginChain* origin, 67 | RegIdx reg, uptr flags); 68 | 69 | void set_current_instruction(Origin* origin) { 70 | // Start of a new instruction execution. 71 | current_instruction_ = origin; 72 | exception_raised_ = false; 73 | } 74 | 75 | bool exception_raised() const { 76 | return exception_raised_; 77 | } 78 | 79 | bool uninit_tracking() const { 80 | return mode_ & kUninitTracking; 81 | } 82 | 83 | private: 84 | const Mode mode_; 85 | Callback* const cb_; 86 | Origin* current_instruction_ = nullptr; 87 | bool exception_raised_ = false; 88 | 89 | Env(const Env&) = delete; 90 | Env& operator=(const Env&) = delete; 91 | }; 92 | 93 | } // namespace gwpsan SAN_LOCAL 94 | 95 | #endif // GWPSAN_CORE_ENV_H_ 96 | -------------------------------------------------------------------------------- /gwpsan/core/env_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "gwpsan/base/env.h" 16 | 17 | #include 18 | 19 | #include "gtest/gtest.h" 20 | #include "gwpsan/base/common.h" 21 | #include "gwpsan/base/units.h" 22 | 23 | namespace gwpsan { 24 | namespace { 25 | 26 | TEST(NonFailing, Fails) { 27 | char* mem = static_cast( 28 | mmap(nullptr, 6 * kPageSize, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0)); 29 | ASSERT_NE(mem, MAP_FAILED); 30 | ASSERT_EQ(0, mprotect(mem + kPageSize, kPageSize, PROT_READ | PROT_WRITE)); 31 | ASSERT_EQ(0, mprotect(mem + 2 * kPageSize, kPageSize, PROT_READ)); 32 | ASSERT_EQ(0, mprotect(mem + 3 * kPageSize, kPageSize, PROT_WRITE)); 33 | ASSERT_EQ(0, mprotect(mem + 4 * kPageSize, kPageSize, PROT_READ | PROT_EXEC)); 34 | ASSERT_EQ(0, munmap(mem + 5 * kPageSize, kPageSize)); 35 | 36 | char tmp[100]; 37 | // Some clearly invalid addresses. 38 | EXPECT_FALSE(NonFailingLoad(nullptr, ByteSize(1), tmp)); 39 | EXPECT_FALSE(NonFailingStore(nullptr, ByteSize(2), tmp)); 40 | EXPECT_FALSE( 41 | NonFailingLoad(Addr(reinterpret_cast(-1)), ByteSize(1), tmp)); 42 | EXPECT_FALSE( 43 | NonFailingStore(Addr(reinterpret_cast(-1)), ByteSize(2), tmp)); 44 | EXPECT_FALSE(NonFailingLoad(Addr(reinterpret_cast(0x8181000000000000)), 45 | ByteSize(2), tmp)); 46 | EXPECT_FALSE(NonFailingStore( 47 | Addr(reinterpret_cast(0x8181000000000000)), ByteSize(3), tmp)); 48 | EXPECT_FALSE(NonFailingLoad(Addr(reinterpret_cast(0x5555000000000000)), 49 | ByteSize(4), tmp)); 50 | EXPECT_FALSE(NonFailingStore( 51 | Addr(reinterpret_cast(0x5555000000000000)), ByteSize(5), tmp)); 52 | // Our mapped pages. 53 | EXPECT_FALSE(NonFailingLoad(Addr(mem), ByteSize(6), tmp)); 54 | EXPECT_FALSE(NonFailingStore(Addr(mem), ByteSize(7), tmp)); 55 | EXPECT_TRUE(NonFailingLoad(Addr(mem + kPageSize), ByteSize(8), tmp)); 56 | EXPECT_TRUE(NonFailingStore(Addr(mem + kPageSize), ByteSize(9), tmp)); 57 | EXPECT_TRUE(NonFailingLoad(Addr(mem + 2 * kPageSize), ByteSize(8), tmp)); 58 | EXPECT_FALSE(NonFailingStore(Addr(mem + 2 * kPageSize), ByteSize(9), tmp)); 59 | // PROT_WRITE implies PROT_READ on both x86 and arm64. 60 | EXPECT_TRUE(NonFailingLoad(Addr(mem + 3 * kPageSize), ByteSize(4), tmp)); 61 | EXPECT_TRUE(NonFailingStore(Addr(mem + 3 * kPageSize), ByteSize(8), tmp)); 62 | EXPECT_TRUE(NonFailingLoad(Addr(mem + 4 * kPageSize), ByteSize(4), tmp)); 63 | EXPECT_FALSE(NonFailingStore(Addr(mem + 4 * kPageSize), ByteSize(8), tmp)); 64 | // Unmapped page. 65 | EXPECT_FALSE(NonFailingStore(Addr(mem + 5 * kPageSize), ByteSize(8), tmp)); 66 | 67 | munmap(mem, 5 * kPageSize); 68 | } 69 | 70 | } // namespace 71 | } // namespace gwpsan 72 | -------------------------------------------------------------------------------- /gwpsan/core/flags.h: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef GWPSAN_CORE_FLAGS_H_ 16 | #define GWPSAN_CORE_FLAGS_H_ 17 | 18 | #include "gwpsan/base/common.h" 19 | #include "gwpsan/base/units.h" 20 | 21 | namespace gwpsan SAN_LOCAL { 22 | 23 | struct Flags { 24 | // Runtime core 25 | bool help = false; 26 | bool& log; 27 | const char*& log_path; 28 | bool pause_on_start; 29 | bool& pause_on_die; 30 | bool log_failures = false; 31 | bool log_metrics = false; 32 | bool halt_on_error = false; 33 | int& error_code; 34 | bool& abort_on_error; 35 | bool origin_stacks = false; 36 | bool must_init = false; 37 | bool test_mode = false; 38 | const char* process_filter = nullptr; 39 | // TSan tool 40 | bool tsan = false; 41 | bool tsan_report_atomic_races = false; 42 | uptr tsan_delay_usec = 100; 43 | uptr tsan_skip_watch = 3; 44 | // UAR tool 45 | bool uar = false; 46 | uptr uar_check_every_nth_thread = 1; 47 | // LMSan tool 48 | bool lmsan = false; 49 | // Unified tool 50 | uptr sample_interval_usec = 0; 51 | bool sample_after_fork = true; 52 | uptr peek_instructions = 20; 53 | bool check_mem_funcs = true; 54 | bool check_syscalls = true; 55 | bool check_malloc = false; 56 | const char* dump = nullptr; 57 | }; 58 | 59 | bool InitFlags(); 60 | 61 | SAN_ALWAYS_INLINE const Flags& GetFlags() { 62 | extern Flags dont_use_flags; 63 | return dont_use_flags; 64 | } 65 | 66 | // ScopedTestFlagMutator can be used in tests to temporary change flags as: 67 | // 68 | // ScopedTestFlagMutator flags; 69 | // flags->tsan_report_atomic_races = false; 70 | // ... flags restores original values when goes out of scope 71 | // 72 | class ScopedTestFlagMutator { 73 | public: 74 | ScopedTestFlagMutator(); 75 | ~ScopedTestFlagMutator(); 76 | Flags* operator->(); 77 | void SetSampleInterval(Duration v); 78 | 79 | private: 80 | const Flags original_; 81 | 82 | ScopedTestFlagMutator(const ScopedTestFlagMutator&) = delete; 83 | void operator=(const ScopedTestFlagMutator&) = delete; 84 | }; 85 | 86 | } // namespace gwpsan SAN_LOCAL 87 | 88 | #endif 89 | -------------------------------------------------------------------------------- /gwpsan/core/init.h: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef GWPSAN_CORE_INIT_H_ 16 | #define GWPSAN_CORE_INIT_H_ 17 | 18 | #include "gwpsan/base/common.h" 19 | 20 | namespace gwpsan SAN_LOCAL { 21 | bool Init(); 22 | bool ForceInit(); // Skips all prerequisite checks. 23 | } // namespace gwpsan SAN_LOCAL 24 | 25 | #endif // GWPSAN_CORE_INIT_H_ 26 | -------------------------------------------------------------------------------- /gwpsan/core/init_for_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "gwpsan/base/common.h" 16 | #include "gwpsan/core/init.h" 17 | #include "gwpsan/core/semantic_metadata.h" 18 | 19 | namespace gwpsan { 20 | namespace { 21 | 22 | SAN_CONSTRUCTOR void TestInit() { 23 | SAN_CHECK(Init()); 24 | SAN_CHECK(InitSemanticMetadata(kSemanticAll)); 25 | } 26 | 27 | } // namespace 28 | } // namespace gwpsan 29 | -------------------------------------------------------------------------------- /gwpsan/core/known_functions.h: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef GWPSAN_CORE_KNOWN_FUNCTIONS_H_ 16 | #define GWPSAN_CORE_KNOWN_FUNCTIONS_H_ 17 | 18 | #include "gwpsan/base/common.h" 19 | #include "gwpsan/core/core_fwd.h" 20 | 21 | namespace gwpsan SAN_LOCAL { 22 | 23 | // Returns true if the current PC points to the beginning of a malloc-like 24 | // function (malloc, calloc, new, etc). Returns the requested object size 25 | // in 'size'; 'uninit' is set if the returned memory is uninitialized. 26 | bool IsMallocPC(const CPUContext& ctx, uptr& size, bool& uninit); 27 | 28 | // Returns true if the current PC points to the beginning of a free-like 29 | // function (free, delete, etc). Returns the freed pointer in 'ptr'. 30 | // If size of the object is known, it's returned in 'size'; 31 | // otherwise 'size' is set to 0. 32 | bool IsFreePC(const CPUContext& ctx, uptr& ptr, uptr& size); 33 | 34 | // Returns true if the current PC points to the beginning of a mem/str* 35 | // function (memset, strcmp, etc). 'cb' is called for each access 36 | // by the function memory range. 37 | bool IsMemAccessFunc(const CPUContext& ctx, 38 | const FunctionRef& cb); 39 | bool IsMemAccessFunc(uptr pc); 40 | 41 | template 42 | bool IsMemAccessFunc(const CPUContext& ctx, Vec& accesses) { 43 | return IsMemAccessFunc(ctx, [&](const MemAccess& a) { 44 | if (SAN_WARN(accesses.size() >= accesses.capacity())) 45 | return; 46 | accesses.emplace_back(a); 47 | }); 48 | } 49 | 50 | // Assuming ctx points to a syscall instruction, returns the syscall number 51 | // (SYS_*) and calls cb for every memory access done by the syscall. 52 | uptr ExtractSyscallAccesses(const CPUContext& ctx, 53 | const FunctionRef& cb); 54 | 55 | template 56 | uptr ExtractSyscallAccesses(const CPUContext& ctx, Vec& accesses) { 57 | return ExtractSyscallAccesses(ctx, [&](const MemAccess& a) { 58 | if (SAN_WARN(accesses.size() >= accesses.capacity())) 59 | return; 60 | accesses.emplace_back(a); 61 | }); 62 | } 63 | 64 | } // namespace gwpsan SAN_LOCAL 65 | 66 | #endif // GWPSAN_CORE_KNOWN_FUNCTIONS_H_ 67 | -------------------------------------------------------------------------------- /gwpsan/core/operation_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "gwpsan/core/operation.h" 16 | 17 | #include 18 | 19 | #include "gtest/gtest.h" 20 | #include "gwpsan/base/allocator.h" 21 | #include "gwpsan/base/common.h" 22 | #include "gwpsan/core/arch.h" 23 | #include "gwpsan/core/instruction.h" 24 | #include "gwpsan/core/meta.h" 25 | #include "gwpsan/core/origin.h" 26 | 27 | namespace gwpsan { 28 | namespace { 29 | 30 | TEST(Operation, TableTest) { 31 | HeapAllocatorLifetime alloc_lifetime; 32 | struct Test { 33 | OpRef op; 34 | uptr val0; 35 | uptr val1; 36 | uptr res; 37 | uptr shadow0; 38 | uptr shadow1; 39 | uptr shadow; 40 | uptr compute_flags; 41 | uptr expected_flags; 42 | }; 43 | // Remove when the crash is resolved: 44 | // https://github.com/llvm/llvm-project/issues/51767 45 | // clang-format off 46 | Test tests[] = { 47 | {OpOr, 0, 0, 0}, 48 | {OpOr, 0xab00, 0x00cd, 0xabcd}, 49 | {OpOr, 0x0f0f, 0x00ff, 0x0fff}, 50 | {OpOr, 0x0f0f, 0x00ff, 0x0fff, 0xffff, 0xffff, 0xffff}, 51 | {OpOr, 0x0f0f, 0x00ff, 0x0fff, 0x0000, 0xffff, 0xf0f0}, 52 | {OpOr, 0x0f0f, 0x00ff, 0x0fff, 0xffff, 0x0000, 0xff00}, 53 | 54 | {OpAnd, 0, 0xffff, 0}, 55 | {OpAnd, 0xab00, 0x00cd, 0}, 56 | {OpAnd, 0x0f0f, 0x00ff, 0x000f}, 57 | {OpAnd, 0x0f0f, 0x00ff, 0x000f, 0xffff, 0xffff, 0xffff}, 58 | {OpAnd, 0x0f0f, 0x00ff, 0x000f, 0x0000, 0xffff, 0x0f0f}, 59 | {OpAnd, 0x0f0f, 0x00ff, 0x000f, 0xffff, 0x0000, 0x00ff}, 60 | }; 61 | // clang-format on 62 | for (auto test : tests) { 63 | OpArgs src; 64 | src[0] = {test.val0, 65 | Meta().Set(test.shadow0, new OriginChain(new TaintOrigin( 66 | Origin::Type::kTainted, "src0")))}; 67 | src[1] = {test.val1, 68 | Meta().Set(test.shadow1, new OriginChain(new TaintOrigin( 69 | Origin::Type::kTainted, "src1")))}; 70 | Word res = test.op(src[0], src[1]); 71 | if (res.val != test.res || res.meta.shadow() != test.shadow) 72 | GTEST_FAIL() << std::hex << std::showbase << test.op.Name() << "(" 73 | << test.val0 << "[" << test.shadow0 << "], " << test.val1 74 | << "[" << test.shadow0 << "]) = " << res.val << "[" 75 | << res.meta.shadow() << "], expected " << test.res << "[" 76 | << test.shadow << "]"; 77 | if (!test.compute_flags) 78 | continue; 79 | RegArg dst(kSP); 80 | ImmArg arg0(test.val0); 81 | ImmArg arg1(test.val1); 82 | Instr instr(0, test.op, &dst, {&arg0, &arg1}); 83 | Instr::Flags flags{test.compute_flags}; 84 | uptr untainted = 0; 85 | uptr got_flags = test.op.ComputeFlags(instr, flags, untainted, res, src); 86 | if (got_flags != test.expected_flags) { 87 | GTEST_FAIL() << std::hex << std::showbase << test.op.Name() << "(" 88 | << test.val0 << ", " << test.val1 << ") = " << res.val 89 | << ", got flags " << got_flags << ", expected " 90 | << test.expected_flags; 91 | } 92 | } 93 | } 94 | 95 | } // namespace 96 | } // namespace gwpsan 97 | -------------------------------------------------------------------------------- /gwpsan/core/regset.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "gwpsan/core/regset.h" 16 | 17 | #include "gwpsan/base/common.h" 18 | #include "gwpsan/base/log.h" 19 | #include "gwpsan/core/arch.h" 20 | 21 | namespace gwpsan { 22 | 23 | RegSet::RegSet(RegIdx reg0, RegIdx reg1, RegIdx reg2, RegIdx reg3, RegIdx reg4, 24 | RegIdx reg5) { 25 | for (auto reg : (RegIdx[]){reg0, reg1, reg2, reg3, reg4, reg5}) { 26 | if (reg != kRZ) 27 | Set(reg); 28 | } 29 | } 30 | 31 | RegSet& RegSet::AddRange(RegIdx start, RegIdx end) { 32 | for (int reg = start; reg <= end; reg++) 33 | Set(reg); 34 | return *this; 35 | } 36 | 37 | RegSet& RegSet::Remove(RegIdx reg0, RegIdx reg1, RegIdx reg2, RegIdx reg3) { 38 | for (auto reg : (RegIdx[]){reg0, reg1, reg2, reg3}) { 39 | if (reg != kRZ) 40 | Reset(reg); 41 | } 42 | return *this; 43 | } 44 | 45 | void RegSet::Set(int reg) { 46 | SAN_CHECK_NE(reg, kUNDEF); 47 | set_[reg / kWordBits] |= 1ul << (reg % kWordBits); 48 | } 49 | 50 | void RegSet::Reset(int reg) { 51 | SAN_CHECK_NE(reg, kUNDEF); 52 | set_[reg / kWordBits] &= ~(1ul << (reg % kWordBits)); 53 | } 54 | 55 | RegSet::operator bool() const { 56 | for (auto elem : set_) { 57 | if (elem) 58 | return true; 59 | } 60 | return false; 61 | } 62 | 63 | bool RegSet::operator[](RegIdx reg) const { 64 | return set_[reg / kWordBits] & (1ul << (reg % kWordBits)); 65 | } 66 | 67 | RegSet& RegSet::operator|=(const RegSet& other) { 68 | for (uptr i = 0; i < kSize; i++) 69 | set_[i] |= other.set_[i]; 70 | return *this; 71 | } 72 | 73 | LogBuf RegSet::Dump() const { 74 | LogBuf buf; 75 | for (auto reg : *this) 76 | buf.Append("%s%s", buf.Empty() ? "" : ",", RegNames[reg]); 77 | return buf; 78 | } 79 | 80 | RegSet::iterator::iterator() 81 | : parent_() 82 | , pos_(kRegCount) {} 83 | 84 | RegSet::iterator::iterator(const RegSet& set) 85 | : parent_(&set) 86 | , pos_(0) { 87 | for (; !IsCurrentBitSet(); pos_++) {} 88 | } 89 | 90 | RegSet::iterator& RegSet::iterator::operator++() { 91 | for (pos_++; !IsCurrentBitSet(); pos_++) {} 92 | return *this; 93 | } 94 | 95 | bool RegSet::iterator::IsCurrentBitSet() const { 96 | if (pos_ == kRegCount) 97 | return true; 98 | return parent_->set_[pos_ / kWordBits] & (1ul << (pos_ % kWordBits)); 99 | } 100 | 101 | RegIdx RegSet::iterator::operator*() const { 102 | return static_cast(pos_); 103 | } 104 | 105 | bool RegSet::iterator::operator!=(const RegSet::iterator& other) const { 106 | return pos_ != other.pos_; 107 | } 108 | 109 | RegSet::iterator RegSet::begin() const { 110 | return iterator(*this); 111 | } 112 | 113 | RegSet::iterator RegSet::end() const { 114 | return iterator(); 115 | } 116 | 117 | } // namespace gwpsan 118 | -------------------------------------------------------------------------------- /gwpsan/core/regset.h: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef GWPSAN_CORE_REGSET_H_ 16 | #define GWPSAN_CORE_REGSET_H_ 17 | 18 | #include "gwpsan/base/array.h" 19 | #include "gwpsan/base/common.h" 20 | #include "gwpsan/base/log.h" 21 | #include "gwpsan/core/arch.h" 22 | 23 | namespace gwpsan SAN_LOCAL { 24 | 25 | // A set of registers. 26 | class RegSet { 27 | public: 28 | RegSet() = default; 29 | RegSet(RegIdx reg0, RegIdx reg1 = kRZ, RegIdx reg2 = kRZ, RegIdx reg3 = kRZ, 30 | RegIdx reg4 = kRZ, RegIdx reg5 = kRZ); 31 | RegSet& AddRange(RegIdx start, RegIdx end); 32 | RegSet& Remove(RegIdx reg0, RegIdx reg1 = kRZ, RegIdx reg2 = kRZ, 33 | RegIdx reg3 = kRZ); 34 | 35 | // Returns true if the set contains at least one register. 36 | operator bool() const; 37 | // Returns true if 'reg' is present in the set. 38 | bool operator[](RegIdx reg) const; 39 | // Merges 'other' into 'this'. 40 | RegSet& operator|=(const RegSet& other); 41 | 42 | LogBuf Dump() const; 43 | 44 | // Iterator over registers in the set. 45 | class iterator { 46 | public: 47 | iterator& operator++(); 48 | RegIdx operator*() const; 49 | bool operator!=(const iterator& other) const; 50 | 51 | private: 52 | const RegSet* parent_; 53 | uptr pos_; 54 | 55 | bool IsCurrentBitSet() const; 56 | iterator(); 57 | iterator(const RegSet& set); 58 | friend class RegSet; 59 | }; 60 | iterator begin() const; 61 | iterator end() const; 62 | 63 | private: 64 | static constexpr uptr kSize = 65 | RoundUpTo(kRegCount, kWordBits) / kWordBits; 66 | Array set_ = {}; 67 | 68 | void Set(int reg); 69 | void Reset(int reg); 70 | }; 71 | 72 | } // namespace gwpsan SAN_LOCAL 73 | 74 | #endif 75 | -------------------------------------------------------------------------------- /gwpsan/core/regset_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "gwpsan/core/regset.h" 16 | 17 | #include 18 | 19 | #include "gtest/gtest.h" 20 | #include "gwpsan/core/arch.h" 21 | 22 | namespace gwpsan { 23 | namespace { 24 | 25 | std::vector ToVec(const RegSet& set) { 26 | std::vector res; 27 | for (auto reg : set) 28 | res.push_back(reg); 29 | return res; 30 | } 31 | 32 | TEST(RegSet, Basic) { 33 | RegSet set; 34 | EXPECT_FALSE(set); 35 | EXPECT_STREQ(&set.Dump(), ""); 36 | EXPECT_EQ(ToVec(set), std::vector()); 37 | EXPECT_FALSE(set[kTEMP0]); 38 | 39 | set.AddRange(kTEMP0, kTEMP3); 40 | EXPECT_TRUE(set); 41 | EXPECT_STREQ(&set.Dump(), "TEMP0,TEMP1,TEMP2,TEMP3"); 42 | EXPECT_EQ(ToVec(set), (std::vector{kTEMP0, kTEMP1, kTEMP2, kTEMP3})); 43 | EXPECT_TRUE(set[kTEMP0]); 44 | EXPECT_TRUE(set[kTEMP1]); 45 | EXPECT_FALSE(set[kPC]); 46 | 47 | set.Remove(kTEMP1, kTEMP3); 48 | EXPECT_TRUE(set); 49 | EXPECT_STREQ(&set.Dump(), "TEMP0,TEMP2"); 50 | EXPECT_EQ(ToVec(set), (std::vector{kTEMP0, kTEMP2})); 51 | 52 | set.Remove(kTEMP0, kTEMP2); 53 | EXPECT_FALSE(set); 54 | EXPECT_STREQ(&set.Dump(), ""); 55 | EXPECT_EQ(ToVec(set), std::vector()); 56 | } 57 | 58 | TEST(RegSet, NonEmpty) { 59 | RegSet set(kTEMP3, kTEMPFLAGS, kTEMP2); 60 | EXPECT_TRUE(set); 61 | EXPECT_STREQ(&set.Dump(), "TEMP2,TEMP3,TFLAGS"); 62 | EXPECT_EQ(ToVec(set), (std::vector{kTEMP2, kTEMP3, kTEMPFLAGS})); 63 | 64 | set |= RegSet(kTEMP2, kTEMP1); 65 | EXPECT_STREQ(&set.Dump(), "TEMP1,TEMP2,TEMP3,TFLAGS"); 66 | } 67 | 68 | } // namespace 69 | } // namespace gwpsan 70 | -------------------------------------------------------------------------------- /gwpsan/core/report.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "gwpsan/core/report.h" 16 | 17 | #include "gwpsan/base/bazel.h" 18 | #include "gwpsan/base/common.h" 19 | #include "gwpsan/base/log.h" 20 | #include "gwpsan/base/metric.h" 21 | #include "gwpsan/base/optional.h" 22 | #include "gwpsan/base/os.h" 23 | #include "gwpsan/base/printf.h" 24 | #include "gwpsan/base/signal.h" 25 | #include "gwpsan/base/span.h" 26 | #include "gwpsan/base/unwind.h" 27 | #include "gwpsan/base/vector.h" 28 | #include "gwpsan/core/flags.h" 29 | #include "gwpsan/core/known_functions.h" 30 | #include "gwpsan/core/semantic_metadata.h" 31 | 32 | namespace gwpsan { 33 | 34 | DEFINE_METRIC(errors_detected, 0, "Total number of errors detected"); 35 | 36 | namespace { 37 | inline constexpr char kReportMarker[] = 38 | "=================================================================\n"; 39 | } // namespace 40 | 41 | ReportPrinter::ReportPrinter(const char* type, MetricRef& metric, 42 | const ucontext_t* uctx, uptr pc2) 43 | : uctx_(uctx) { 44 | metric_errors_detected.ExclusiveAdd(1); 45 | metric.ExclusiveAdd(0, 1); 46 | LogBuf summary; 47 | summary.Append("GWPSan: %s in ", type); 48 | if (uctx_) 49 | Symbolize(ExtractPC(*uctx_), summary.Remain().data(), 50 | summary.Remain().size(), false); 51 | if (pc2 && summary.Remain().size() > 8) { 52 | summary.Append(" / "); 53 | Symbolize(pc2, summary.Remain().data(), summary.Remain().size(), false); 54 | } 55 | BazelOnReport(&summary); 56 | Printf(kReportMarker); 57 | Printf("WARNING: %s (pid=%d)\n", &summary, GetPid()); 58 | } 59 | 60 | ReportPrinter::~ReportPrinter() { 61 | Printf(kReportMarker); 62 | if (GetFlags().halt_on_error) 63 | Die(); 64 | } 65 | 66 | void ReportPrinter::CurrentStack() { 67 | static OptionalBase> stack; 68 | stack.emplace(); 69 | UnwindStack(*stack, uctx_); 70 | PrintStackTrace(*stack, " "); 71 | } 72 | 73 | uptr UnwindStackSpan(Span storage, const ucontext_t* uctx) { 74 | SAN_CHECK(uctx); 75 | SAN_CHECK_GT(storage.size(), 2); 76 | uptr offset = 1; 77 | uptr pc = ExtractPC(*uctx); 78 | storage[0] = pc; 79 | // If we are on the first function PC, then we also need to add the caller PC 80 | // since the frame pointer does not yet point to it (it will be updated only 81 | // within next few instructions). 82 | // Note: we are not interested in all functions, only in the ones that we 83 | // check and that may appear in reports. Currently it's only UAR-checked 84 | // functions and memory access functions. 85 | if (IsFunctionStart(pc) || IsMemAccessFunc(pc)) 86 | storage[offset++] = ReturnPC(*uctx); 87 | return offset + RawUnwindStack({&storage[offset], storage.size() - offset}, 88 | reinterpret_cast(ExtractFP(*uctx))); 89 | } 90 | 91 | } // namespace gwpsan 92 | -------------------------------------------------------------------------------- /gwpsan/core/report.h: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef GWPSAN_CORE_REPORT_H_ 16 | #define GWPSAN_CORE_REPORT_H_ 17 | 18 | #include 19 | 20 | #include "gwpsan/base/common.h" 21 | #include "gwpsan/base/metric.h" 22 | #include "gwpsan/base/span.h" 23 | #include "gwpsan/base/vector.h" 24 | 25 | namespace gwpsan SAN_LOCAL { 26 | 27 | class ReportPrinter { 28 | public: 29 | ReportPrinter(const char* type, MetricRef& metric, 30 | const ucontext_t* uctx = nullptr, uptr pc2 = 0); 31 | ~ReportPrinter(); 32 | void CurrentStack(); 33 | 34 | private: 35 | const ucontext_t* uctx_; 36 | ReportPrinter(const ReportPrinter&) = delete; 37 | ReportPrinter& operator=(const ReportPrinter&) = delete; 38 | }; 39 | 40 | // Unwinds the current user stack for the signal context ``uctx``. 41 | // The stack is stored in the ``storage`` and the actual stack size is returned. 42 | [[nodiscard]] uptr UnwindStackSpan(Span storage, const ucontext_t* uctx); 43 | 44 | template 45 | SAN_ALWAYS_INLINE void UnwindStack(Vector& vec, 46 | const ucontext_t* uctx) { 47 | vec.resize(vec.capacity()); 48 | vec.resize(UnwindStackSpan(vec, uctx)); 49 | } 50 | 51 | } // namespace gwpsan SAN_LOCAL 52 | 53 | #endif 54 | -------------------------------------------------------------------------------- /gwpsan/core/report_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "gwpsan/core/report.h" 16 | 17 | #include 18 | 19 | #include "gtest/gtest.h" 20 | #include "gwpsan/base/array.h" 21 | #include "gwpsan/base/common.h" 22 | #include "gwpsan/base/printf.h" 23 | #include "gwpsan/base/test_report_interceptor.h" 24 | #include "gwpsan/base/units.h" 25 | #include "gwpsan/core/breakmanager.h" 26 | #include "gwpsan/core/breakpoint.h" 27 | #include "gwpsan/core/context.h" 28 | #include "gwpsan/core/report.h" 29 | 30 | namespace gwpsan { 31 | namespace { 32 | 33 | // Trickery to prevent tail calls, optimizations and ensure stable stacks 34 | // in debug and release builds. 35 | SAN_NOINLINE extern "C" void NoInline() { 36 | SAN_BARRIER(); 37 | } 38 | 39 | SAN_NOINLINE extern "C" void MyAccess(volatile int* data) { 40 | NoInline(); 41 | *data = 1; 42 | NoInline(); 43 | } 44 | 45 | SAN_NOINLINE extern "C" void MyCaller(volatile int* data) { 46 | NoInline(); 47 | MyAccess(data); 48 | NoInline(); 49 | } 50 | 51 | struct StackCapturer : BreakManager::Callback { 52 | Array stack; 53 | uptr size; 54 | uptr max_size; 55 | 56 | uptr OnEmulate(const CPUContext& ctx) override { 57 | size = UnwindStackSpan({stack.data(), max_size}, ctx.uctx()); 58 | return 0; 59 | } 60 | }; 61 | 62 | TEST(UnwindStack, Basic) { 63 | bool ok = true; 64 | ScopedBreakManagerSingleton<> mgr(ok); 65 | ASSERT_TRUE(ok); 66 | StackCapturer capturer; 67 | volatile int data = 0; 68 | for (bool main : {true, false}) { 69 | for (uptr max_size : {12, 64}) { 70 | capturer.size = 0; 71 | capturer.max_size = max_size; 72 | // TODO(dvyukov, elver): setup the breakpoint once before the loop 73 | // once Arm breakpoints are fixed. 74 | auto* bp = 75 | mgr->Watch({Breakpoint::Type::kReadWrite, &data, Sizeof(data)}); 76 | if (main) { 77 | MyCaller(&data); 78 | } else { 79 | std::thread th([&data]() { MyCaller(&data); }); 80 | th.join(); 81 | } 82 | mgr->Unwatch(bp); 83 | Printf("main=%d max_size=%zu size=%zu:\n", main, max_size, capturer.size); 84 | ReportInterceptor interceptor; 85 | PrintStackTrace({capturer.stack.data(), capturer.size}, " "); 86 | if (main) 87 | interceptor.ExpectReport( 88 | R"( #0: [[MODULE]] MyAccess 89 | #1: [[MODULE]] MyCaller 90 | #2: [[MODULE]] gwpsan::(anonymous namespace)::UnwindStack_Basic_Test::TestBody() 91 | #3: [[MODULE]] testing::.* 92 | [[SKIP-LINES]])" 93 | #if GWPSAN_OPTIMIZE >= 2 94 | // Only with max. optimizations will main be within first 10 frames. 95 | R"( #[[NUM]]: [[MODULE]] main 96 | )" 97 | #endif 98 | ); 99 | else 100 | // Depending on standard library, and optimizations we'll end up with 101 | // std::function internals (due to lack of tail call optimizations) or 102 | // other possibly unsymbolizable functions in the stack trace as well. 103 | // We are limited to testing the start of the trace looks as expected. 104 | interceptor.ExpectReport( 105 | R"( #0: [[MODULE]] MyAccess 106 | #1: [[MODULE]] MyCaller 107 | [[SKIP-LINES]])"); 108 | } 109 | } 110 | } 111 | 112 | } // namespace 113 | } // namespace gwpsan 114 | -------------------------------------------------------------------------------- /gwpsan/core/semantic_metadata.h: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef GWPSAN_CORE_SEMANTIC_METADATA_H_ 16 | #define GWPSAN_CORE_SEMANTIC_METADATA_H_ 17 | 18 | #include "gwpsan/base/common.h" 19 | #include "gwpsan/base/optional.h" 20 | 21 | namespace gwpsan SAN_LOCAL { 22 | 23 | using SemanticFlags = u32; 24 | 25 | // Instruction was lowered from an atomic operation per C11/C++11 memory model. 26 | inline constexpr SemanticFlags kSemanticAtomic = (1u << 0); 27 | 28 | // Function is suitable for use-after-return checking. 29 | inline constexpr SemanticFlags kSemanticUAR = (1u << 1); 30 | 31 | inline constexpr SemanticFlags kSemanticAll = ~0; 32 | 33 | // Prepares ``needed`` features for use. 34 | // Must be called before any other semantic metadata functions. 35 | bool InitSemanticMetadata(SemanticFlags needed); 36 | 37 | // Return true if we have compiler-provided semantic metadata of all types 38 | // specified in the ``mask``. 39 | bool HasSemanticMetadata(SemanticFlags mask); 40 | 41 | // Says if the pc is a start of a function (for which we have any metadata, 42 | // which guarantees it's compiled by clang and is reasonably well-behaving). 43 | bool IsFunctionStart(uptr pc); 44 | 45 | // If the function is not covered with the semantic metadata, 46 | // or if the metadata is not ready to be used, returns nothing. 47 | // Otherwise return if the instruction at ``pc`` is atomic. 48 | Optional IsAtomicPC(uptr pc); 49 | 50 | // Returns the size of stack arguments of the function if ``pc`` is a start of a 51 | // function suitable for use-after-return checking. 52 | Optional IsUARFunctionStart(uptr pc); 53 | 54 | // Print detailed metadata info. 55 | void DumpSemanticMetadata(); 56 | 57 | // To be initialized before fork(). 58 | class SemanticMetadataScopedFork { 59 | public: 60 | SemanticMetadataScopedFork(); 61 | ~SemanticMetadataScopedFork(); 62 | }; 63 | 64 | } // namespace gwpsan SAN_LOCAL 65 | 66 | #endif // GWPSAN_CORE_SEMANTIC_METADATA_H_ 67 | -------------------------------------------------------------------------------- /gwpsan/core/semantic_metadata_test_lib1.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | static volatile int var; 16 | 17 | // There seems to be a compiler issue with returning the label address with GCOV 18 | // enabled. Disable instrumentation - we don't need it here. 19 | __attribute__((no_profile_instrument_function)) extern "C" long no_atomics() { 20 | var = var + 1; 21 | var = var - 1; 22 | here: 23 | return reinterpret_cast(&&here) + 0; 24 | } 25 | 26 | __attribute__((no_profile_instrument_function)) extern "C" long atomics() { 27 | var = var + 1; 28 | __atomic_store_n(&var, 1, __ATOMIC_RELAXED); 29 | var = var - 1; 30 | here: 31 | return reinterpret_cast(&&here) + 0; 32 | } 33 | -------------------------------------------------------------------------------- /gwpsan/core/semantic_metadata_test_lib1_uncovered.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | static volatile int var; 16 | 17 | __attribute__((no_profile_instrument_function)) extern "C" long uncovered() { 18 | var = var + 1; 19 | var = var - 1; 20 | here: 21 | return reinterpret_cast(&&here) + 0; 22 | } 23 | -------------------------------------------------------------------------------- /gwpsan/core/semantic_metadata_test_lib2.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | static volatile int var; 16 | 17 | __attribute__((no_profile_instrument_function)) extern "C" long no_atomics() { 18 | var = var + 1; 19 | var = var - 1; 20 | here: 21 | return reinterpret_cast(&&here) + 0; 22 | } 23 | -------------------------------------------------------------------------------- /gwpsan/core/semantic_metadata_test_linkstatic.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | namespace gwpsan { 16 | namespace { 17 | volatile int var; 18 | } // namespace 19 | 20 | __attribute__((no_profile_instrument_function)) unsigned long 21 | ExternNoAtomicFunc() { 22 | var = var + 1; 23 | var = var - 1; 24 | here: 25 | return reinterpret_cast(&&here) + 0; 26 | } 27 | 28 | __attribute__((no_profile_instrument_function)) unsigned long 29 | ExternAtomicFunc() { 30 | var = var + 1; 31 | __atomic_store_n(&var, 1, __ATOMIC_RELAXED); 32 | var = var - 1; 33 | here: 34 | return reinterpret_cast(&&here) + 0; 35 | } 36 | 37 | } // namespace gwpsan 38 | -------------------------------------------------------------------------------- /gwpsan/core/store_buffer.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "gwpsan/core/store_buffer.h" 16 | 17 | #include "gwpsan/base/common.h" 18 | #include "gwpsan/base/units.h" 19 | 20 | namespace gwpsan { 21 | 22 | uptr StoreBuffer::Forward(Addr addr, ByteSize size, uptr val) { 23 | // Replay stores from the oldest one to the newest one and copy bits 24 | // that overlap with the loaded value. This gives us the right value 25 | // even with arbitrary overlapping stores. 26 | size = min(size, Sizeof(val)); 27 | for (uptr i = 0; i < buffer_.size(); i++) { 28 | const auto& store = buffer_[(pos_ + i) % buffer_.size()]; 29 | auto off = store.addr - addr; 30 | if (off >= size) 31 | continue; 32 | auto n = min(store.size, size - off); 33 | internal_memcpy(reinterpret_cast(&val) + Bytes(off), &store.val.val, 34 | Bytes(n)); 35 | } 36 | return val; 37 | } 38 | 39 | } // namespace gwpsan 40 | -------------------------------------------------------------------------------- /gwpsan/core/store_buffer.h: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef GWPSAN_CORE_STORE_BUFFER_H_ 16 | #define GWPSAN_CORE_STORE_BUFFER_H_ 17 | 18 | #include "gwpsan/base/array.h" 19 | #include "gwpsan/base/common.h" 20 | #include "gwpsan/base/units.h" 21 | #include "gwpsan/core/meta.h" 22 | 23 | namespace gwpsan SAN_LOCAL { 24 | 25 | // StoreBuffer holds past stores to satisfy subsequent loads during emulation. 26 | // It has fixed capacity and can produce incorrect results on overflow. 27 | class StoreBuffer { 28 | public: 29 | void Store(Addr addr, ByteSize size, uptr val) { 30 | size = min(size, Sizeof(val)); 31 | buffer_[pos_++ % buffer_.size()] = MemAccess{0, addr, size, {val}}; 32 | } 33 | 34 | // Forward updates and returns the value val loaded from addr/size 35 | // based on the previous stores. Since we emulated previous stores 36 | // the value in memory misses side-effects of these stores. 37 | // Forward returns the value what would be in memory if the stores 38 | // would actually happen. 39 | uptr Forward(Addr addr, ByteSize size, uptr val); 40 | 41 | private: 42 | // Since we have to use fixed space and cannot memorize all stores, 43 | // we use a ring buffer with the most recent stores only. 44 | Array buffer_; 45 | uptr pos_ = 0; 46 | }; 47 | 48 | } // namespace gwpsan SAN_LOCAL 49 | 50 | #endif 51 | -------------------------------------------------------------------------------- /gwpsan/core/store_buffer_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "gwpsan/core/store_buffer.h" 16 | 17 | #include 18 | #include 19 | 20 | #include "gtest/gtest.h" 21 | #include "gwpsan/base/common.h" 22 | #include "gwpsan/base/units.h" 23 | 24 | namespace gwpsan { 25 | namespace { 26 | 27 | TEST(StoreBuffer, Test) { 28 | struct Access { 29 | uptr addr; 30 | uptr size; 31 | uptr val; 32 | }; 33 | struct Test { 34 | uptr result; 35 | Access load; 36 | std::vector stores; 37 | }; 38 | // clang-format off 39 | Test tests[] = { 40 | { 41 | 0xaaaaaaaaaaaaaaaa, Access{0x100, 8, 0xaaaaaaaaaaaaaaaa}, 42 | }, 43 | { 44 | 0xaaaaaaaaaaaaaaaa, Access{0x100, 8, 0xaaaaaaaaaaaaaaaa}, 45 | {Access{0x200, 8, 0xbbbbbbbbbbbbbbbb}}, 46 | }, 47 | { 48 | 0xbbbbbbbbbbbbbbbb, Access{0x100, 8, 0xaaaaaaaaaaaaaaaa}, 49 | {Access{0x100, 8, 0xbbbbbbbbbbbbbbbb}}, 50 | }, 51 | { 52 | 0xaaaaaaaabbbbbbbb, Access{0x100, 8, 0xaaaaaaaaaaaaaaaa}, 53 | {Access{0x100, 4, 0xbbbbbbbbbbbbbbbb}}, 54 | }, 55 | { 56 | 0xbbbbbbbbaaaaaaaa, Access{0x100, 8, 0xaaaaaaaaaaaaaaaa}, 57 | {Access{0x104, 8, 0xbbbbbbbbbbbbbbbb}}, 58 | }, 59 | { 60 | 0xaaaabbbbbbbbaaaa, Access{0x100, 8, 0xaaaaaaaaaaaaaaaa}, 61 | {Access{0x102, 4, 0xbbbbbbbbbbbbbbbb}}, 62 | }, 63 | { 64 | 0xaaaaaaaa, Access{0x100, 4, 0xaaaaaaaa}, 65 | {Access{0x104, 8, 0xbbbbbbbbbbbbbbbb}}, 66 | }, 67 | { 68 | 0xaaaaaaaa, Access{0x104, 4, 0xaaaaaaaa}, 69 | {Access{0x100, 4, 0xbbbbbbbbbbbbbbbb}}, 70 | }, 71 | { 72 | 0xaaaaaaddddbbbbcc, Access{0x100, 8, 0xaaaaaaaaaaaaaaaa}, 73 | { 74 | Access{0x100, 4, 0xbbbbbbbbbbbbbbbb}, 75 | Access{0x100, 1, 0xcccccccccccccccc}, 76 | Access{0x103, 2, 0xdddddddddddddddd}, 77 | }, 78 | }, 79 | }; 80 | // clang-format on 81 | for (auto& test : tests) { 82 | StoreBuffer buffer; 83 | for (auto& store : test.stores) 84 | buffer.Store(Addr(store.addr), ByteSize(store.size), store.val); 85 | uptr result = buffer.Forward(Addr(test.load.addr), ByteSize(test.load.size), 86 | test.load.val); 87 | EXPECT_EQ(result, test.result) 88 | << std::showbase << "load " << std::hex << test.load.addr << "/" 89 | << test.load.size << "/" << std::hex << test.load.val << ": expect " 90 | << std::hex << test.result << ", got " << std::hex << result; 91 | } 92 | } 93 | 94 | } // namespace 95 | } // namespace gwpsan 96 | -------------------------------------------------------------------------------- /gwpsan/core/unwind_instruction.h: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef GWPSAN_CORE_UNWIND_INSTRUCTION_H_ 16 | #define GWPSAN_CORE_UNWIND_INSTRUCTION_H_ 17 | 18 | #include "gwpsan/base/common.h" 19 | #include "gwpsan/core/breakpoint.h" 20 | #include "gwpsan/core/core_fwd.h" 21 | 22 | namespace gwpsan SAN_LOCAL { 23 | 24 | // UnwindInstruction is used on x86 to work-around the following problem. 25 | // On x86 data watchpoints fire on the next instruction. 26 | // This is very unfortunate since we can't emulate the memory accessing 27 | // instruction and don't know what was loaded/stored and where it is now 28 | // (e.g. in what register). UnwindInstruction tries to find the previous 29 | // instruction and restore the context as it was before the previous 30 | // instruction executed. This is not possible to do reliaby, 31 | // UnwindInstruction return false if it fails. 32 | bool UnwindInstruction(CPUContext& ctx, Breakpoint::Info bpinfo); 33 | 34 | // Copies up to kMaxInstrLen bytes preceeding pc into buf and returns 35 | // number of bytes copied. May copy less if some of the bytes are located 36 | // on an inacessible page. In such case the copied bytes are located 37 | // at the end of the buf. 38 | uptr CopyPreceedingCode(uptr pc, u8* buf); 39 | 40 | } // namespace gwpsan SAN_LOCAL 41 | 42 | #endif // GWPSAN_CORE_UNWIND_INSTRUCTION_H_ 43 | -------------------------------------------------------------------------------- /gwpsan/core/unwind_instruction_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "gwpsan/core/unwind_instruction.h" 16 | 17 | #include 18 | #include 19 | 20 | #include "gtest/gtest.h" 21 | #include "gwpsan/base/common.h" 22 | #include "gwpsan/core/arch.h" 23 | 24 | namespace gwpsan { 25 | namespace { 26 | 27 | TEST(UnwindInstruction, CopyPreceedingCode) { 28 | // Map 3 pages, first is inaccessible. 29 | // This allows to test both boundary between accessible pages, 30 | // and accessible and inaccessible page. 31 | void* mem = mmap(nullptr, 3 * kPageSize, PROT_READ | PROT_WRITE, 32 | MAP_ANON | MAP_PRIVATE, -1, 0); 33 | ASSERT_NE(mem, MAP_FAILED); 34 | memset(mem, 0x42, 3 * kPageSize); 35 | ASSERT_EQ(0, mprotect(mem, kPageSize, PROT_NONE)); 36 | // Test boundary between accessible pages. 37 | // Here we must always copy kMaxInstrLen bytes. 38 | for (uptr off = 0; off < 2 * kMaxInstrLen; off++) { 39 | u8* code = static_cast(mem) + 2 * kPageSize - kMaxInstrLen - off; 40 | for (uptr i = 0; i < kMaxInstrLen; i++) 41 | code[i] = i + 1; 42 | u8 buf[kMaxInstrLen + 2] = {}; 43 | uptr copied = CopyPreceedingCode( 44 | reinterpret_cast(code) + kMaxInstrLen, buf + 1); 45 | ASSERT_EQ(copied, kMaxInstrLen); 46 | ASSERT_EQ(buf[0], 0); 47 | for (uptr i = 0; i < kMaxInstrLen; i++) 48 | ASSERT_EQ(buf[i + 1], i + 1); 49 | ASSERT_EQ(buf[kMaxInstrLen + 1], 0); 50 | } 51 | // Test boundary between accessible and inaccessible page. 52 | for (uptr off = 0; off < kMaxInstrLen; off++) { 53 | const uptr inaccessible = kMaxInstrLen - off; 54 | u8* code = static_cast(mem) + kPageSize - inaccessible; 55 | for (uptr i = 0; i < off; i++) 56 | code[inaccessible + i] = i + 1; 57 | u8 buf[kMaxInstrLen + 2] = {}; 58 | uptr copied = CopyPreceedingCode( 59 | reinterpret_cast(code) + kMaxInstrLen, buf + 1); 60 | ASSERT_EQ(copied, off); 61 | for (uptr i = 0; i < inaccessible + 1; i++) 62 | ASSERT_EQ(buf[i], 0); 63 | for (uptr i = 0; i < off; i++) 64 | ASSERT_EQ(buf[inaccessible + i + 1], i + 1); 65 | ASSERT_EQ(buf[kMaxInstrLen + 1], 0); 66 | } 67 | ASSERT_EQ(0, munmap(mem, 3 * kPageSize)); 68 | } 69 | 70 | } // namespace 71 | } // namespace gwpsan 72 | -------------------------------------------------------------------------------- /gwpsan/import/BUILD: -------------------------------------------------------------------------------- 1 | # Copyright 2024 The GWPSan Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # Third party libraries adjusted to work within the GWPSan runtime. Also 16 | # provide build rules for libraries that do not ship with Bazel build rules. 17 | 18 | load("//gwpsan/base:cc_implicit_output.bzl", "cc_implicit_output") 19 | load("//gwpsan/base:defs.bzl", "gwpsan_library") 20 | load("@rules_foreign_cc//foreign_cc:defs.bzl", "cmake") 21 | 22 | package( 23 | default_applicable_licenses = ["//:license"], 24 | default_visibility = ["//gwpsan:__subpackages__"], 25 | ) 26 | 27 | # Builtins implementations derived from LLVM/compiler-rt. 28 | gwpsan_library( 29 | name = "builtins", 30 | srcs = [ 31 | "udivmodti4.cpp", 32 | "udivti3.cpp", 33 | ], 34 | hdrs = [ 35 | "int_lib.h", 36 | ], 37 | deps = [ 38 | "//gwpsan/base", 39 | ], 40 | ) 41 | 42 | cmake( 43 | name = "drdecode", 44 | lib_source = "@dynamorio//:all_srcs", 45 | cache_entries = { 46 | "DISABLE_WARNINGS": "ON", 47 | "BUILD_TESTS": "OFF", 48 | "BUILD_DOCS": "OFF", 49 | "BUILD_SAMPLES": "OFF", 50 | "BUILD_EXT": "OFF", 51 | "BUILD_CLIENTS": "OFF", 52 | }, 53 | build_args = ["-j10"], 54 | targets = ["drdecode", "drlibc"], 55 | # Installation tries to install everything, not just the targets selected 56 | # above. Instead we then just have to selectively install what we need. 57 | install = 0, 58 | postfix_script = """ 59 | libs=( 60 | libdrdecode.a 61 | libdrlibc.a 62 | ) 63 | for x in "${libs[@]}"; do 64 | install -D lib64/$x $INSTALLDIR/lib/$x 65 | done 66 | for x in include/dr_*.h; do 67 | install -D $x $INSTALLDIR/$x 68 | done 69 | # Configure DynamoRIO: 70 | ( 71 | case "$(uname -m)" in 72 | x86*) arch=X86_64;; 73 | arm*|aarch*) arch=ARM_64;; 74 | *) echo "Unknown architecture"; exit 1;; 75 | esac 76 | echo "#define $arch" 77 | echo "#define LINUX" 78 | cat include/dr_defines.h 79 | ) > $INSTALLDIR/include/dr_defines.h 80 | """, 81 | out_static_libs = [ 82 | "libdrdecode.a", 83 | "libdrlibc.a", 84 | ], 85 | ) 86 | -------------------------------------------------------------------------------- /gwpsan/import/int_lib.h: -------------------------------------------------------------------------------- 1 | //===-- int_lib.h - configuration header for compiler-rt -----------------===// 2 | // 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 | // See https://llvm.org/LICENSE.txt for license information. 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 | // 7 | // Copyright 2024 The GWPSan Authors 8 | // 9 | // Licensed under the Apache License, Version 2.0 (the "License"); 10 | // you may not use this file except in compliance with the License. 11 | // You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | // Unless required by applicable law or agreed to in writing, software 16 | // distributed under the License is distributed on an "AS IS" BASIS, 17 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | // See the License for the specific language governing permissions and 19 | // limitations under the License. 20 | // 21 | //===----------------------------------------------------------------------===// 22 | // 23 | // This file is not part of the interface of this library. 24 | // 25 | // This file defines various standard types, most importantly a number of unions 26 | // used to access parts of larger types. 27 | // 28 | // This version is minimized to provide the bare minimum for the builtins 29 | // required by the GWPSan runtime. 30 | // 31 | //===----------------------------------------------------------------------===// 32 | 33 | #ifndef GWPSAN_IMPORT_INT_LIB_H_ 34 | #define GWPSAN_IMPORT_INT_LIB_H_ 35 | 36 | #include "gwpsan/base/common.h" 37 | 38 | static_assert(__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__); 39 | 40 | namespace gwpsan::third_party { 41 | 42 | typedef s32 si_int; 43 | typedef u32 su_int; 44 | typedef s64 di_int; 45 | typedef u64 du_int; 46 | typedef int ti_int __attribute__((mode(TI))); 47 | typedef unsigned tu_int __attribute__((mode(TI))); 48 | 49 | union utwords { 50 | tu_int all; 51 | struct { 52 | du_int low; 53 | du_int high; 54 | } s; 55 | }; 56 | 57 | extern "C" tu_int gwpsan_udivmodti4(tu_int a, tu_int b, tu_int* rem); 58 | 59 | } // namespace gwpsan::third_party 60 | 61 | #endif // GWPSAN_IMPORT_INT_LIB_H_ 62 | -------------------------------------------------------------------------------- /gwpsan/import/udivti3.cpp: -------------------------------------------------------------------------------- 1 | //===-- udivti3.c - Implement __udivti3 -----------------------------------===// 2 | // 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 | // See https://llvm.org/LICENSE.txt for license information. 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 | // 7 | // Copyright 2024 The GWPSan Authors 8 | // 9 | // Licensed under the Apache License, Version 2.0 (the "License"); 10 | // you may not use this file except in compliance with the License. 11 | // You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | // Unless required by applicable law or agreed to in writing, software 16 | // distributed under the License is distributed on an "AS IS" BASIS, 17 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | // See the License for the specific language governing permissions and 19 | // limitations under the License. 20 | // 21 | //===----------------------------------------------------------------------===// 22 | // 23 | // This file implements __udivti3 for the compiler_rt library. 24 | // 25 | //===----------------------------------------------------------------------===// 26 | 27 | #include "gwpsan/base/common.h" 28 | #include "gwpsan/import/int_lib.h" 29 | 30 | namespace gwpsan::third_party { 31 | extern "C" { 32 | 33 | // Returns: a / b 34 | 35 | SAN_LOCAL SAN_USED tu_int gwpsan_udivti3(tu_int a, tu_int b) { 36 | return gwpsan_udivmodti4(a, b, 0); 37 | } 38 | 39 | } // extern "C" 40 | } // namespace gwpsan::third_party 41 | -------------------------------------------------------------------------------- /gwpsan/lmsan/BUILD: -------------------------------------------------------------------------------- 1 | # Copyright 2024 The GWPSan Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | load("//gwpsan/base:defs.bzl", "gwpsan_library", "gwpsan_test", "gwpsan_test_library") 16 | 17 | package( 18 | default_applicable_licenses = ["//:license"], 19 | default_visibility = ["//gwpsan:__subpackages__"], 20 | ) 21 | 22 | gwpsan_library( 23 | name = "lmsan", 24 | srcs = ["lmsan.cpp"], 25 | deps = [ 26 | "//gwpsan/base", 27 | "//gwpsan/core", 28 | "//gwpsan/unified:tool", 29 | ], 30 | ) 31 | 32 | gwpsan_test_library( 33 | name = "test_lib", 34 | srcs = ["lmsan_test.cpp"], 35 | sanitize_metadata = 1, 36 | deps = [ 37 | "@googletest//:gtest_main", 38 | "//gwpsan/base:external_interface", 39 | "//gwpsan/base:test_utils", 40 | "//gwpsan/unified", 41 | ], 42 | ) 43 | 44 | gwpsan_test( 45 | name = "lmsan_test", 46 | deps = [":test_lib"], 47 | ) 48 | -------------------------------------------------------------------------------- /gwpsan/lmsan/lmsan_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "gtest/gtest.h" 16 | 17 | namespace gwpsan { 18 | 19 | const char* DefaultFlags() { 20 | return "lmsan"; 21 | } 22 | 23 | namespace { 24 | 25 | TEST(LMSanTest, Basic) {} 26 | 27 | } // namespace 28 | } // namespace gwpsan -------------------------------------------------------------------------------- /gwpsan/tsan/BUILD: -------------------------------------------------------------------------------- 1 | # Copyright 2024 The GWPSan Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | load("//gwpsan/base:defs.bzl", "gwpsan_library", "gwpsan_test", "gwpsan_test_library") 16 | 17 | package( 18 | default_applicable_licenses = ["//:license"], 19 | default_visibility = ["//gwpsan:__subpackages__"], 20 | ) 21 | 22 | gwpsan_library( 23 | name = "tsan", 24 | srcs = ["tsan.cpp"], 25 | hdrs = ["tsan.h"], 26 | deps = [ 27 | "//gwpsan/base", 28 | "//gwpsan/core", 29 | "//gwpsan/unified:tool", 30 | ], 31 | ) 32 | 33 | gwpsan_test_library( 34 | name = "tsan_test_lib", 35 | srcs = ["tsan_test.cpp"], 36 | sanitize_metadata = 1, 37 | deps = [ 38 | "@googletest//:gtest_main", 39 | "//gwpsan/base:external_interface", 40 | "//gwpsan/base:test_utils", 41 | "//gwpsan/core:external_interface", 42 | "//gwpsan/unified", 43 | ], 44 | ) 45 | 46 | gwpsan_test( 47 | name = "tsan_test", 48 | tags = [ 49 | "notap", 50 | ], 51 | deps = [":tsan_test_lib"], 52 | ) 53 | -------------------------------------------------------------------------------- /gwpsan/tsan/tsan.h: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef GWPSAN_TSAN_TSAN_H_ 16 | #define GWPSAN_TSAN_TSAN_H_ 17 | 18 | #include "gwpsan/base/common.h" 19 | #include "gwpsan/base/numeric.h" 20 | #include "gwpsan/base/span.h" 21 | #include "gwpsan/base/units.h" 22 | #include "gwpsan/base/vector.h" 23 | #include "gwpsan/core/core_fwd.h" 24 | #include "gwpsan/unified/tool.h" 25 | 26 | namespace gwpsan SAN_LOCAL { 27 | 28 | class RaceDetector final : public Tool { 29 | public: 30 | RaceDetector(bool& ok); 31 | 32 | private: 33 | bool IsInteresting(const CPUContext& ctx, const MemAccess& access) override; 34 | bool Check(const CPUContext& ctx, const MemAccess& access) override; 35 | void OnRace(const CPUContext& ctx); 36 | static void PrintThread(const MemAccess& ma, int tid, 37 | const Span& stack_trace); 38 | 39 | Rand rand_; 40 | uptr last_interesting_pc_ = 0; 41 | Addr last_interesting_addr_; 42 | Breakpoint* watched_ = nullptr; // guarded by mgr().mtx_ 43 | MemAccess sel_access_; // guarded by mgr().mtx_ 44 | MemAccess bp_access_; // guarded by mgr().mtx_ 45 | ArrayVector sel_stack_trace_; 46 | ArrayVector bp_stack_trace_; 47 | int bp_tid_ = -1; 48 | }; 49 | 50 | } // namespace gwpsan SAN_LOCAL 51 | 52 | #endif // GWPSAN_TSAN_TSAN_H_ 53 | -------------------------------------------------------------------------------- /gwpsan/uar/BUILD: -------------------------------------------------------------------------------- 1 | # Copyright 2024 The GWPSan Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | load("//gwpsan/base:defs.bzl", "gwpsan_library", "gwpsan_test", "gwpsan_test_library") 16 | 17 | package( 18 | default_applicable_licenses = ["//:license"], 19 | default_visibility = ["//gwpsan:__subpackages__"], 20 | ) 21 | 22 | gwpsan_library( 23 | name = "uar", 24 | srcs = [ 25 | "interceptors.cpp", 26 | "uar.cpp", 27 | ], 28 | hdrs = [ 29 | "uar.h", 30 | ], 31 | deps = [ 32 | "//gwpsan/base", 33 | "//gwpsan/core", 34 | "//gwpsan/unified:tool", 35 | ], 36 | ) 37 | 38 | gwpsan_test_library( 39 | name = "uar_test_lib", 40 | srcs = ["uar_test.cpp"], 41 | sanitize_metadata = 1, 42 | deps = [ 43 | "@googletest//:gtest", 44 | "//gwpsan/base:external_interface", 45 | "//gwpsan/base:test_utils", 46 | "//gwpsan/core:external_interface", 47 | "//gwpsan/unified", 48 | ], 49 | ) 50 | 51 | gwpsan_test( 52 | name = "uar_test", 53 | deps = [":uar_test_lib"], 54 | ) 55 | 56 | gwpsan_test( 57 | name = "uar_benchmark", 58 | srcs = ["uar_benchmark.cpp"], 59 | sanitize_metadata = 1, 60 | deps = [ 61 | "@google_benchmark//:benchmark_main", 62 | "//gwpsan/base:external_interface", 63 | "//gwpsan/base:test_utils", 64 | "//gwpsan/unified", 65 | ], 66 | ) 67 | -------------------------------------------------------------------------------- /gwpsan/uar/uar_benchmark.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | 17 | #include 18 | 19 | #include "benchmark/benchmark.h" 20 | 21 | namespace gwpsan { 22 | 23 | const char* DefaultFlags() { 24 | return "uar"; 25 | } 26 | 27 | namespace { 28 | 29 | void BM_ThreadCreation(benchmark::State& state) { 30 | for (auto _ : state) { 31 | std::thread th([]() { 32 | // Make comparison more apples-to-apples by triggerring 33 | // per-thread malloc initialization. 34 | void* volatile p = malloc(1); 35 | free(p); 36 | }); 37 | th.join(); 38 | } 39 | } 40 | BENCHMARK(BM_ThreadCreation); 41 | 42 | } // namespace 43 | } // namespace gwpsan 44 | -------------------------------------------------------------------------------- /gwpsan/unified/init.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "gwpsan/core/init.h" 16 | 17 | #include 18 | 19 | #include "gwpsan/base/common.h" 20 | #include "gwpsan/base/log.h" 21 | #include "gwpsan/base/optional.h" 22 | #include "gwpsan/base/os.h" 23 | #include "gwpsan/core/breakmanager.h" 24 | #include "gwpsan/core/decode.h" 25 | #include "gwpsan/core/flags.h" 26 | #include "gwpsan/core/semantic_metadata.h" 27 | #include "gwpsan/unified/unified.h" 28 | 29 | SAN_WEAK_IMPORT extern "C" int __fork(); 30 | SAN_DECLARE_INTERCEPTOR(int, fork); 31 | 32 | namespace gwpsan { 33 | namespace { 34 | 35 | constinit OptionalBase unified; 36 | 37 | bool GwpsanInit() { 38 | if (!Init()) 39 | return false; 40 | if (GetFlags().dump) { 41 | if (!InitSemanticMetadata(kSemanticAll)) { 42 | Printf("failed to initialize semantic metadata\n"); 43 | Die(); 44 | } 45 | if (!internal_strcmp(GetFlags().dump, "metadata")) 46 | DumpSemanticMetadata(); 47 | else if (!internal_strcmp(GetFlags().dump, "instructions")) 48 | DumpInstructions(); 49 | else 50 | Printf("supported dump values: metadata, instructions\n"); 51 | Die(); 52 | } 53 | 54 | const auto cfg = UnifiedTool::GetBreakManagerConfig(); 55 | return BreakManager::singleton().try_emplace(cfg) && unified.try_emplace(); 56 | } 57 | 58 | SAN_PUSH_DIAG("-Wglobal-constructors"); 59 | SAN_CONSTRUCTOR void GwpsanInitCtor() { 60 | if (!GwpsanInit()) { 61 | SAN_LOG("failed to initialize"); 62 | SAN_CHECK(!GetFlags().must_init); 63 | return; 64 | } 65 | SAN_LOG("started"); 66 | } 67 | SAN_POP_DIAG(); 68 | 69 | auto* resolve_fork() { 70 | if (___interceptor_fork) 71 | return ___interceptor_fork; 72 | auto fn = reinterpret_cast(dlsym(RTLD_NEXT, "fork")); 73 | if (fn) 74 | return fn; 75 | if (__fork) 76 | return __fork; 77 | SAN_BUG("dlsym(\"fork\") failed (%s)", dlerror()); 78 | } 79 | 80 | } // namespace 81 | 82 | SAN_INTERFACE int __interceptor_fork() { 83 | static auto real_fork = resolve_fork(); 84 | if (!unified || !GetFlags().sample_after_fork) 85 | return real_fork(); 86 | 87 | SemanticMetadataScopedFork semantic_scoped_fork; 88 | 89 | BreakManager::CallbackDisableContext disable_ctx; 90 | BreakManager::singleton()->BeginFork(disable_ctx); 91 | 92 | const int pid = real_fork(); 93 | 94 | BreakManager::singleton()->EndFork(pid, disable_ctx); 95 | unified->EndFork(pid); 96 | 97 | SAN_LOG("intercepted fork()=%d", pid); 98 | return pid; 99 | } 100 | 101 | // vfork() does not need to be intercepted, as stated in the man pages, what is 102 | // allowed by the child after a vfork() is very limited: 103 | // 104 | // "The vfork() function has the same effect as fork(2), except that the 105 | // behavior is undefined if the process created by vfork() either modifies any 106 | // data other than a variable of type pid_t used to store the return value 107 | // from vfork(), or returns from the function in which vfork() was called, or 108 | // calls any other function before successfully calling _exit(2) or one of the 109 | // exec(3) family of functions." 110 | // 111 | // Since the child "shares all memory with its parent, including the stack", it 112 | // would be foolish to attempt to change any GWPSan state after a vfork(). 113 | 114 | } // namespace gwpsan 115 | -------------------------------------------------------------------------------- /gwpsan/unified/redefined.syms: -------------------------------------------------------------------------------- 1 | memset gwpsan_memset 2 | memcpy gwpsan_memcpy 3 | gwpsan_real_memset memset 4 | gwpsan_real_memcpy memcpy 5 | __udivmoddi4 gwpsan_udivmoddi4 6 | __udivmodsi4 gwpsan_udivmodsi4 7 | __udivmodti4 gwpsan_udivmodti4 8 | __udivdi3 gwpsan_udivdi3 9 | __udivsi3 gwpsan_udivsi3 10 | __udivti3 gwpsan_udivti3 11 | -------------------------------------------------------------------------------- /gwpsan/unified/tool.h: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef GWPSAN_UNIFIED_TOOL_H_ 16 | #define GWPSAN_UNIFIED_TOOL_H_ 17 | 18 | #include "gwpsan/base/common.h" 19 | #include "gwpsan/base/memory.h" 20 | #include "gwpsan/base/metric.h" 21 | #include "gwpsan/core/breakmanager.h" 22 | #include "gwpsan/core/core_fwd.h" 23 | #include "gwpsan/core/flags.h" 24 | #include "gwpsan/core/semantic_metadata.h" 25 | 26 | namespace gwpsan SAN_LOCAL { 27 | 28 | // Tool interface for the UnifiedTool. 29 | class Tool { 30 | public: 31 | // IsInteresting says if the given context or memory access is interesting 32 | // for the tool. The tool must not yet do any actual checking in this method. 33 | // If the tool says yes, a matching Check call is expected to follow. 34 | virtual bool IsInteresting(const CPUContext& ctx) { 35 | return false; 36 | } 37 | virtual bool IsInteresting(const CPUContext& ctx, const MemAccess& access) { 38 | return false; 39 | } 40 | 41 | // Check does actual checking of the context or memory access. 42 | // The tool must return true if it did any expensive checking or 43 | // unlocked the break manager mutex. 44 | // The tool may change the context and the underlying ucontext_t 45 | // for checking purposes, then execution will resume with the changed context. 46 | virtual bool Check(CPUContext& ctx) { 47 | return false; 48 | } 49 | virtual bool Check(const CPUContext& ctx, const MemAccess& access) { 50 | return false; 51 | } 52 | 53 | virtual void OnMalloc(const CPUContext& ctx, uptr ptr, uptr size, 54 | bool uninit) {} 55 | 56 | virtual void OnThreadExit() {} 57 | 58 | virtual ~Tool() = default; 59 | 60 | const char* const name; 61 | 62 | protected: 63 | Tool(const char* name) 64 | : name(name) {} 65 | 66 | BreakManager& mgr() { 67 | return BreakManager::singleton().value(); 68 | } 69 | 70 | Tool(const Tool&) = delete; 71 | Tool& operator=(const Tool&) = delete; 72 | }; 73 | 74 | // Meta information about a tool. 75 | struct ToolDesc { 76 | const char* name; 77 | bool Flags::*enabled; 78 | MetricRef& init_ok; 79 | MetricRef& init_fail; 80 | SemanticFlags semantic_flags; 81 | BreakManager::Config config; 82 | UniquePtr (*make_unique)(); 83 | 84 | bool Enabled() const { 85 | return GetFlags().*enabled; 86 | } 87 | }; 88 | 89 | extern const ToolDesc kTsanTool; 90 | extern const ToolDesc kUarTool; 91 | extern const ToolDesc kLmsanTool; 92 | 93 | inline constexpr const ToolDesc* kAllTools[] = {&kTsanTool, &kUarTool, 94 | &kLmsanTool}; 95 | inline constexpr uptr kToolCount = SAN_ARRAY_SIZE(kAllTools); 96 | 97 | } // namespace gwpsan SAN_LOCAL 98 | 99 | #endif // GWPSAN_UNIFIED_TOOL_H_ 100 | -------------------------------------------------------------------------------- /gwpsan/unified/unified.h: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef GWPSAN_UNIFIED_UNIFIED_H_ 16 | #define GWPSAN_UNIFIED_UNIFIED_H_ 17 | 18 | #include "gwpsan/base/common.h" 19 | #include "gwpsan/base/memory.h" 20 | #include "gwpsan/base/vector.h" 21 | #include "gwpsan/core/breakmanager.h" 22 | #include "gwpsan/core/breakpoint.h" 23 | #include "gwpsan/core/context.h" 24 | #include "gwpsan/unified/tool.h" 25 | 26 | namespace gwpsan SAN_LOCAL { 27 | 28 | // The top-level tool that dispatches callbacks to each individual tool. 29 | class UnifiedTool final : protected BreakManager::Callback { 30 | public: 31 | UnifiedTool(bool& ok); 32 | ~UnifiedTool(); 33 | 34 | // Reinitialize state after fork(). 35 | void EndFork(int pid); 36 | 37 | // Return ideal break manager configuration based on the requested tools. 38 | static BreakManager::Config GetBreakManagerConfig(); 39 | 40 | private: 41 | using ThreadID = const void*; 42 | static constexpr ThreadID kNoThread = nullptr; 43 | static ThreadID CurrentThread(); 44 | 45 | bool OnTimer() override; 46 | bool OnBreak(const Breakpoint::Info& bpinfo, uptr hit_count) override; 47 | uptr OnEmulate(const CPUContext& ctx) override; 48 | void OnThreadExit() override; 49 | 50 | ArrayVector, kToolCount> tools_; 51 | // Needed for tools that unlock the break manager mutex and want to handle 52 | // nested events (tsan). Current tool is set for the outer event, 53 | // then if it's set we ignore timer events and direct all other events 54 | // (watchpoints) to this tool only. 55 | Tool* current_tool_ = nullptr; 56 | // Tool that has done expensive checking that aborts emulation last 57 | // (e.g. tsan stalls). This tool will be skipped during the next timer 58 | // sample to achieve some notion of fairness between tools. 59 | Tool* throttled_tool_ = nullptr; 60 | // Use to "resume" emulation after we returned non-0 PC from OnEmulate. 61 | // When we get the next OnEmulate we can check that we received it 62 | // on the expected thread/pc. 63 | uptr resume_pc_ = 0; 64 | ThreadID resume_thread_ = kNoThread; 65 | uptr peek_instructions_ = 0; 66 | uptr emulate_seq_ = 0; 67 | uptr malloc_size_ = 0; 68 | bool malloc_uninit_ = false; 69 | }; 70 | 71 | } // namespace gwpsan SAN_LOCAL 72 | 73 | #endif // GWPSAN_UNIFIED_UNIFIED_H_ 74 | -------------------------------------------------------------------------------- /gwpsan/unified/unified_notool_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The GWPSan Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | 17 | #include "gtest/gtest.h" 18 | #include "gwpsan/base/metric.h" 19 | 20 | namespace gwpsan { 21 | 22 | DECLARE_METRIC(timer_samples); 23 | DECLARE_METRIC(gwpsan_tools); 24 | 25 | namespace { 26 | 27 | // Simple test that checks that if no tool is enabled we don't crash; also try 28 | // to receive some signals by waiting a bit. 29 | TEST(UnifiedTool, NoToolEnabled) { 30 | ASSERT_EQ(metric_gwpsan_tools.value(), 0); 31 | using std::chrono::high_resolution_clock; 32 | const auto start = high_resolution_clock::now(); 33 | while (metric_timer_samples.value() < 2 && 34 | high_resolution_clock::now() - start < std::chrono::seconds(10)) {} 35 | EXPECT_GE(metric_timer_samples.value(), 2); 36 | } 37 | 38 | } // namespace 39 | } // namespace gwpsan 40 | --------------------------------------------------------------------------------