1 | //===- Sanitizers.cpp - C Language Family Language Options ----------------===// |
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 | //===----------------------------------------------------------------------===// |
8 | // |
9 | // This file defines the classes from Sanitizers.h |
10 | // |
11 | //===----------------------------------------------------------------------===// |
12 | |
13 | #include "clang/Basic/Sanitizers.h" |
14 | #include "llvm/ADT/Hashing.h" |
15 | #include "llvm/ADT/StringSwitch.h" |
16 | #include "llvm/Support/Format.h" |
17 | #include "llvm/Support/raw_ostream.h" |
18 | #include <algorithm> |
19 | #include <cmath> |
20 | #include <optional> |
21 | |
22 | using namespace clang; |
23 | |
24 | static const double SanitizerMaskCutoffsEps = 0.000000001f; |
25 | |
26 | void SanitizerMaskCutoffs::set(SanitizerMask K, double V) { |
27 | if (V < SanitizerMaskCutoffsEps && Cutoffs.empty()) |
28 | return; |
29 | for (unsigned int i = 0; i < SanitizerKind::SO_Count; ++i) |
30 | if (K & SanitizerMask::bitPosToMask(Pos: i)) { |
31 | Cutoffs.resize(new_size: SanitizerKind::SO_Count); |
32 | Cutoffs[i] = V; |
33 | } |
34 | } |
35 | |
36 | std::optional<double> SanitizerMaskCutoffs::operator[](unsigned Kind) const { |
37 | if (Cutoffs.empty() || Cutoffs[Kind] < SanitizerMaskCutoffsEps) |
38 | return std::nullopt; |
39 | |
40 | return Cutoffs[Kind]; |
41 | } |
42 | |
43 | void SanitizerMaskCutoffs::clear(SanitizerMask K) { set(K, V: 0); } |
44 | |
45 | std::optional<std::vector<unsigned>> |
46 | SanitizerMaskCutoffs::getAllScaled(unsigned ScalingFactor) const { |
47 | std::vector<unsigned> ScaledCutoffs; |
48 | |
49 | bool AnyCutoff = false; |
50 | for (unsigned int i = 0; i < SanitizerKind::SO_Count; ++i) { |
51 | auto C = (*this)[i]; |
52 | if (C.has_value()) { |
53 | ScaledCutoffs.push_back(x: lround(x: std::clamp(val: *C, lo: 0.0, hi: 1.0) * ScalingFactor)); |
54 | AnyCutoff = true; |
55 | } else { |
56 | ScaledCutoffs.push_back(x: 0); |
57 | } |
58 | } |
59 | |
60 | if (AnyCutoff) |
61 | return ScaledCutoffs; |
62 | |
63 | return std::nullopt; |
64 | } |
65 | |
66 | // Once LLVM switches to C++17, the constexpr variables can be inline and we |
67 | // won't need this. |
68 | #define SANITIZER(NAME, ID) constexpr SanitizerMask SanitizerKind::ID; |
69 | #define SANITIZER_GROUP(NAME, ID, ALIAS) \ |
70 | constexpr SanitizerMask SanitizerKind::ID; \ |
71 | constexpr SanitizerMask SanitizerKind::ID##Group; |
72 | #include "clang/Basic/Sanitizers.def" |
73 | |
74 | SanitizerMask clang::parseSanitizerValue(StringRef Value, bool AllowGroups) { |
75 | SanitizerMask ParsedKind = llvm::StringSwitch<SanitizerMask>(Value) |
76 | #define SANITIZER(NAME, ID) .Case(NAME, SanitizerKind::ID) |
77 | #define SANITIZER_GROUP(NAME, ID, ALIAS) \ |
78 | .Case(NAME, AllowGroups ? SanitizerKind::ID##Group : SanitizerMask()) |
79 | #include "clang/Basic/Sanitizers.def" |
80 | .Default(Value: SanitizerMask()); |
81 | return ParsedKind; |
82 | } |
83 | |
84 | bool clang::parseSanitizerWeightedValue(StringRef Value, bool AllowGroups, |
85 | SanitizerMaskCutoffs &Cutoffs) { |
86 | SanitizerMask ParsedKind = llvm::StringSwitch<SanitizerMask>(Value) |
87 | #define SANITIZER(NAME, ID) .StartsWith(NAME "=", SanitizerKind::ID) |
88 | #define SANITIZER_GROUP(NAME, ID, ALIAS) \ |
89 | .StartsWith(NAME "=", \ |
90 | AllowGroups ? SanitizerKind::ID##Group : SanitizerMask()) |
91 | #include "clang/Basic/Sanitizers.def" |
92 | .Default(Value: SanitizerMask()); |
93 | |
94 | if (!ParsedKind) |
95 | return false; |
96 | auto [N, W] = Value.split(Separator: '='); |
97 | double A; |
98 | if (W.getAsDouble(Result&: A)) |
99 | return false; |
100 | A = std::clamp(val: A, lo: 0.0, hi: 1.0); |
101 | // AllowGroups is already taken into account for ParsedKind, |
102 | // hence we unconditionally expandSanitizerGroups. |
103 | Cutoffs.set(K: expandSanitizerGroups(Kinds: ParsedKind), V: A); |
104 | return true; |
105 | } |
106 | |
107 | void clang::serializeSanitizerSet(SanitizerSet Set, |
108 | SmallVectorImpl<StringRef> &Values) { |
109 | #define SANITIZER(NAME, ID) \ |
110 | if (Set.has(SanitizerKind::ID)) \ |
111 | Values.push_back(NAME); |
112 | #include "clang/Basic/Sanitizers.def" |
113 | } |
114 | |
115 | void clang::serializeSanitizerMaskCutoffs( |
116 | const SanitizerMaskCutoffs &Cutoffs, SmallVectorImpl<std::string> &Values) { |
117 | #define SANITIZER(NAME, ID) \ |
118 | if (auto C = Cutoffs[SanitizerKind::SO_##ID]) { \ |
119 | std::string Str; \ |
120 | llvm::raw_string_ostream OS(Str); \ |
121 | OS << NAME "=" << llvm::format("%.8f", *C); \ |
122 | Values.emplace_back(StringRef(Str).rtrim('0')); \ |
123 | } |
124 | #include "clang/Basic/Sanitizers.def" |
125 | } |
126 | |
127 | SanitizerMask clang::expandSanitizerGroups(SanitizerMask Kinds) { |
128 | #define SANITIZER(NAME, ID) |
129 | #define SANITIZER_GROUP(NAME, ID, ALIAS) \ |
130 | if (Kinds & SanitizerKind::ID##Group) \ |
131 | Kinds |= SanitizerKind::ID; |
132 | #include "clang/Basic/Sanitizers.def" |
133 | return Kinds; |
134 | } |
135 | |
136 | llvm::hash_code SanitizerMask::hash_value() const { |
137 | return llvm::hash_combine_range(first: &maskLoToHigh[0], last: &maskLoToHigh[kNumElem]); |
138 | } |
139 | |
140 | namespace clang { |
141 | unsigned SanitizerMask::countPopulation() const { |
142 | unsigned total = 0; |
143 | for (const auto &Val : maskLoToHigh) |
144 | total += llvm::popcount(Value: Val); |
145 | return total; |
146 | } |
147 | |
148 | llvm::hash_code hash_value(const clang::SanitizerMask &Arg) { |
149 | return Arg.hash_value(); |
150 | } |
151 | |
152 | StringRef AsanDtorKindToString(llvm::AsanDtorKind kind) { |
153 | switch (kind) { |
154 | case llvm::AsanDtorKind::None: |
155 | return "none" ; |
156 | case llvm::AsanDtorKind::Global: |
157 | return "global" ; |
158 | case llvm::AsanDtorKind::Invalid: |
159 | return "invalid" ; |
160 | } |
161 | return "invalid" ; |
162 | } |
163 | |
164 | llvm::AsanDtorKind AsanDtorKindFromString(StringRef kindStr) { |
165 | return llvm::StringSwitch<llvm::AsanDtorKind>(kindStr) |
166 | .Case(S: "none" , Value: llvm::AsanDtorKind::None) |
167 | .Case(S: "global" , Value: llvm::AsanDtorKind::Global) |
168 | .Default(Value: llvm::AsanDtorKind::Invalid); |
169 | } |
170 | |
171 | StringRef AsanDetectStackUseAfterReturnModeToString( |
172 | llvm::AsanDetectStackUseAfterReturnMode mode) { |
173 | switch (mode) { |
174 | case llvm::AsanDetectStackUseAfterReturnMode::Always: |
175 | return "always" ; |
176 | case llvm::AsanDetectStackUseAfterReturnMode::Runtime: |
177 | return "runtime" ; |
178 | case llvm::AsanDetectStackUseAfterReturnMode::Never: |
179 | return "never" ; |
180 | case llvm::AsanDetectStackUseAfterReturnMode::Invalid: |
181 | return "invalid" ; |
182 | } |
183 | return "invalid" ; |
184 | } |
185 | |
186 | llvm::AsanDetectStackUseAfterReturnMode |
187 | AsanDetectStackUseAfterReturnModeFromString(StringRef modeStr) { |
188 | return llvm::StringSwitch<llvm::AsanDetectStackUseAfterReturnMode>(modeStr) |
189 | .Case(S: "always" , Value: llvm::AsanDetectStackUseAfterReturnMode::Always) |
190 | .Case(S: "runtime" , Value: llvm::AsanDetectStackUseAfterReturnMode::Runtime) |
191 | .Case(S: "never" , Value: llvm::AsanDetectStackUseAfterReturnMode::Never) |
192 | .Default(Value: llvm::AsanDetectStackUseAfterReturnMode::Invalid); |
193 | } |
194 | |
195 | } // namespace clang |
196 | |