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
set(SanitizerMask K,double V)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(i)) {
31 Cutoffs.resize(SanitizerKind::SO_Count);
32 Cutoffs[i] = V;
33 }
34 }
35
operator [](unsigned Kind) const36 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
clear(SanitizerMask K)43 void SanitizerMaskCutoffs::clear(SanitizerMask K) { set(K, 0); }
44
45 std::optional<std::vector<unsigned>>
getAllScaled(unsigned ScalingFactor) const46 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(lround(std::clamp(*C, 0.0, 1.0) * ScalingFactor));
54 AnyCutoff = true;
55 } else {
56 ScaledCutoffs.push_back(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
parseSanitizerValue(StringRef Value,bool AllowGroups)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(SanitizerMask());
81 return ParsedKind;
82 }
83
parseSanitizerWeightedValue(StringRef Value,bool AllowGroups,SanitizerMaskCutoffs & Cutoffs)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(SanitizerMask());
93
94 if (!ParsedKind)
95 return false;
96 auto [N, W] = Value.split('=');
97 double A;
98 if (W.getAsDouble(A) || A < 0.0 || A > 1.0)
99 return false;
100 // AllowGroups is already taken into account for ParsedKind,
101 // hence we unconditionally expandSanitizerGroups.
102 Cutoffs.set(expandSanitizerGroups(ParsedKind), A);
103 return true;
104 }
105
serializeSanitizerSet(SanitizerSet Set,SmallVectorImpl<StringRef> & Values)106 void clang::serializeSanitizerSet(SanitizerSet Set,
107 SmallVectorImpl<StringRef> &Values) {
108 #define SANITIZER(NAME, ID) \
109 if (Set.has(SanitizerKind::ID)) \
110 Values.push_back(NAME);
111 #include "clang/Basic/Sanitizers.def"
112 }
113
serializeSanitizerMaskCutoffs(const SanitizerMaskCutoffs & Cutoffs,SmallVectorImpl<std::string> & Values)114 void clang::serializeSanitizerMaskCutoffs(
115 const SanitizerMaskCutoffs &Cutoffs, SmallVectorImpl<std::string> &Values) {
116 #define SANITIZER(NAME, ID) \
117 if (auto C = Cutoffs[SanitizerKind::SO_##ID]) { \
118 std::string Str; \
119 llvm::raw_string_ostream OS(Str); \
120 OS << NAME "=" << llvm::format("%.8f", *C); \
121 Values.emplace_back(StringRef(Str).rtrim('0')); \
122 }
123 #include "clang/Basic/Sanitizers.def"
124 }
125
expandSanitizerGroups(SanitizerMask Kinds)126 SanitizerMask clang::expandSanitizerGroups(SanitizerMask Kinds) {
127 #define SANITIZER(NAME, ID)
128 #define SANITIZER_GROUP(NAME, ID, ALIAS) \
129 if (Kinds & SanitizerKind::ID##Group) \
130 Kinds |= SanitizerKind::ID;
131 #include "clang/Basic/Sanitizers.def"
132 return Kinds;
133 }
134
hash_value() const135 llvm::hash_code SanitizerMask::hash_value() const {
136 return llvm::hash_combine_range(&maskLoToHigh[0], &maskLoToHigh[kNumElem]);
137 }
138
139 namespace clang {
countPopulation() const140 unsigned SanitizerMask::countPopulation() const {
141 unsigned total = 0;
142 for (const auto &Val : maskLoToHigh)
143 total += llvm::popcount(Val);
144 return total;
145 }
146
hash_value(const clang::SanitizerMask & Arg)147 llvm::hash_code hash_value(const clang::SanitizerMask &Arg) {
148 return Arg.hash_value();
149 }
150
AsanDtorKindToString(llvm::AsanDtorKind kind)151 StringRef AsanDtorKindToString(llvm::AsanDtorKind kind) {
152 switch (kind) {
153 case llvm::AsanDtorKind::None:
154 return "none";
155 case llvm::AsanDtorKind::Global:
156 return "global";
157 case llvm::AsanDtorKind::Invalid:
158 return "invalid";
159 }
160 return "invalid";
161 }
162
AsanDtorKindFromString(StringRef kindStr)163 llvm::AsanDtorKind AsanDtorKindFromString(StringRef kindStr) {
164 return llvm::StringSwitch<llvm::AsanDtorKind>(kindStr)
165 .Case("none", llvm::AsanDtorKind::None)
166 .Case("global", llvm::AsanDtorKind::Global)
167 .Default(llvm::AsanDtorKind::Invalid);
168 }
169
AsanDetectStackUseAfterReturnModeToString(llvm::AsanDetectStackUseAfterReturnMode mode)170 StringRef AsanDetectStackUseAfterReturnModeToString(
171 llvm::AsanDetectStackUseAfterReturnMode mode) {
172 switch (mode) {
173 case llvm::AsanDetectStackUseAfterReturnMode::Always:
174 return "always";
175 case llvm::AsanDetectStackUseAfterReturnMode::Runtime:
176 return "runtime";
177 case llvm::AsanDetectStackUseAfterReturnMode::Never:
178 return "never";
179 case llvm::AsanDetectStackUseAfterReturnMode::Invalid:
180 return "invalid";
181 }
182 return "invalid";
183 }
184
185 llvm::AsanDetectStackUseAfterReturnMode
AsanDetectStackUseAfterReturnModeFromString(StringRef modeStr)186 AsanDetectStackUseAfterReturnModeFromString(StringRef modeStr) {
187 return llvm::StringSwitch<llvm::AsanDetectStackUseAfterReturnMode>(modeStr)
188 .Case("always", llvm::AsanDetectStackUseAfterReturnMode::Always)
189 .Case("runtime", llvm::AsanDetectStackUseAfterReturnMode::Runtime)
190 .Case("never", llvm::AsanDetectStackUseAfterReturnMode::Never)
191 .Default(llvm::AsanDetectStackUseAfterReturnMode::Invalid);
192 }
193
194 } // namespace clang
195