├── .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 |
--------------------------------------------------------------------------------