Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(243)

Side by Side Diff: webrtc/base/optional.h

Issue 2289383002: rtc::Optional: Tell sanitizers that unset values aren't OK to access (Closed)
Patch Set: Review comment Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 /* 1 /*
2 * Copyright 2015 The WebRTC Project Authors. All rights reserved. 2 * Copyright 2015 The WebRTC Project Authors. All rights reserved.
3 * 3 *
4 * Use of this source code is governed by a BSD-style license 4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source 5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found 6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may 7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree. 8 * be found in the AUTHORS file in the root of the source tree.
9 */ 9 */
10 10
11 #ifndef WEBRTC_BASE_OPTIONAL_H_ 11 #ifndef WEBRTC_BASE_OPTIONAL_H_
12 #define WEBRTC_BASE_OPTIONAL_H_ 12 #define WEBRTC_BASE_OPTIONAL_H_
13 13
14 #include <algorithm> 14 #include <algorithm>
15 #include <memory> 15 #include <memory>
16 #include <utility> 16 #include <utility>
17 17
18 #include "webrtc/base/array_view.h"
18 #include "webrtc/base/checks.h" 19 #include "webrtc/base/checks.h"
20 #include "webrtc/base/sanitizer.h"
19 21
20 namespace rtc { 22 namespace rtc {
21 23
24 namespace internal {
25
26 #if RTC_HAS_ASAN
27
28 // This is a non-inlined function. The optimizer can't see inside it.
29 void* FunctionThatDoesNothingImpl(void*);
ossu 2016/09/05 08:27:21 I'm a bit cautious about this one. Since we can't
kwiberg-webrtc 2016/09/05 09:11:30 No, I can't put it in rtc::Optional, because that'
ossu 2016/09/08 08:49:03 Ah, that's true. Yeah, optional_internal is fine b
30
31 template <typename T>
32 inline T* FunctionThatDoesNothing(T* x) {
33 return reinterpret_cast<T*>(
34 FunctionThatDoesNothingImpl(reinterpret_cast<void*>(x)));
35 }
36
37 #else
38
39 template <typename T>
40 inline T* FunctionThatDoesNothing(T* x) { return x; }
41
42 #endif
43
44 } // namespace internal
45
22 // Simple std::optional-wannabe. It either contains a T or not. 46 // Simple std::optional-wannabe. It either contains a T or not.
23 // 47 //
24 // A moved-from Optional<T> may only be destroyed, and assigned to if T allows 48 // A moved-from Optional<T> may only be destroyed, and assigned to if T allows
25 // being assigned to after having been moved from. Specifically, you may not 49 // being assigned to after having been moved from. Specifically, you may not
26 // assume that it just doesn't contain a value anymore. 50 // assume that it just doesn't contain a value anymore.
27 // 51 //
28 // Examples of good places to use Optional: 52 // Examples of good places to use Optional:
29 // 53 //
30 // - As a class or struct member, when the member doesn't always have a value: 54 // - As a class or struct member, when the member doesn't always have a value:
31 // struct Prisoner { 55 // struct Prisoner {
(...skipping 20 matching lines...) Expand all
52 // Optional<double> when parsing a single number as in the example above 76 // Optional<double> when parsing a single number as in the example above
53 // might make sense, but any larger parse job is probably going to need to 77 // might make sense, but any larger parse job is probably going to need to
54 // tell the caller what the problem was, not just that there was one. 78 // tell the caller what the problem was, not just that there was one.
55 // 79 //
56 // TODO(kwiberg): Get rid of this class when the standard library has 80 // TODO(kwiberg): Get rid of this class when the standard library has
57 // std::optional (and we're allowed to use it). 81 // std::optional (and we're allowed to use it).
58 template <typename T> 82 template <typename T>
59 class Optional final { 83 class Optional final {
60 public: 84 public:
61 // Construct an empty Optional. 85 // Construct an empty Optional.
62 Optional() : has_value_(false), empty_('\0') {} 86 Optional() : has_value_(false), empty_('\0') {
87 PoisonValue();
88 }
63 89
64 // Construct an Optional that contains a value. 90 // Construct an Optional that contains a value.
65 explicit Optional(const T& value) : has_value_(true) { 91 explicit Optional(const T& value) : has_value_(true) {
66 new (&value_) T(value); 92 new (&value_) T(value);
67 } 93 }
68 explicit Optional(T&& value) : has_value_(true) { 94 explicit Optional(T&& value) : has_value_(true) {
69 new (&value_) T(std::move(value)); 95 new (&value_) T(std::move(value));
70 } 96 }
71 97
72 // Copy constructor: copies the value from m if it has one. 98 // Copy constructor: copies the value from m if it has one.
73 Optional(const Optional& m) : has_value_(m.has_value_) { 99 Optional(const Optional& m) : has_value_(m.has_value_) {
74 if (has_value_) 100 if (has_value_)
75 new (&value_) T(m.value_); 101 new (&value_) T(m.value_);
102 else
103 PoisonValue();
76 } 104 }
77 105
78 // Move constructor: if m has a value, moves the value from m, leaving m 106 // Move constructor: if m has a value, moves the value from m, leaving m
79 // still in a state where it has a value, but a moved-from one (the 107 // still in a state where it has a value, but a moved-from one (the
80 // properties of which depends on T; the only general guarantee is that we 108 // properties of which depends on T; the only general guarantee is that we
81 // can destroy m). 109 // can destroy m).
82 Optional(Optional&& m) : has_value_(m.has_value_) { 110 Optional(Optional&& m) : has_value_(m.has_value_) {
83 if (has_value_) 111 if (has_value_)
84 new (&value_) T(std::move(m.value_)); 112 new (&value_) T(std::move(m.value_));
113 else
114 PoisonValue();
85 } 115 }
86 116
87 ~Optional() { 117 ~Optional() {
88 if (has_value_) 118 if (has_value_)
89 value_.~T(); 119 value_.~T();
120 else
121 UnpoisonValue();
90 } 122 }
91 123
92 // Copy assignment. Uses T's copy assignment if both sides have a value, T's 124 // Copy assignment. Uses T's copy assignment if both sides have a value, T's
93 // copy constructor if only the right-hand side has a value. 125 // copy constructor if only the right-hand side has a value.
94 Optional& operator=(const Optional& m) { 126 Optional& operator=(const Optional& m) {
95 if (m.has_value_) { 127 if (m.has_value_) {
96 if (has_value_) { 128 if (has_value_) {
97 value_ = m.value_; // T's copy assignment. 129 value_ = m.value_; // T's copy assignment.
98 } else { 130 } else {
131 UnpoisonValue();
99 new (&value_) T(m.value_); // T's copy constructor. 132 new (&value_) T(m.value_); // T's copy constructor.
100 has_value_ = true; 133 has_value_ = true;
101 } 134 }
102 } else if (has_value_) { 135 } else if (has_value_) {
103 value_.~T(); 136 value_.~T();
104 has_value_ = false; 137 has_value_ = false;
138 PoisonValue();
105 } 139 }
106 return *this; 140 return *this;
107 } 141 }
108 142
109 // Move assignment. Uses T's move assignment if both sides have a value, T's 143 // Move assignment. Uses T's move assignment if both sides have a value, T's
110 // move constructor if only the right-hand side has a value. The state of m 144 // move constructor if only the right-hand side has a value. The state of m
111 // after it's been moved from is as for the move constructor. 145 // after it's been moved from is as for the move constructor.
112 Optional& operator=(Optional&& m) { 146 Optional& operator=(Optional&& m) {
113 if (m.has_value_) { 147 if (m.has_value_) {
114 if (has_value_) { 148 if (has_value_) {
115 value_ = std::move(m.value_); // T's move assignment. 149 value_ = std::move(m.value_); // T's move assignment.
116 } else { 150 } else {
151 UnpoisonValue();
117 new (&value_) T(std::move(m.value_)); // T's move constructor. 152 new (&value_) T(std::move(m.value_)); // T's move constructor.
118 has_value_ = true; 153 has_value_ = true;
119 } 154 }
120 } else if (has_value_) { 155 } else if (has_value_) {
121 value_.~T(); 156 value_.~T();
122 has_value_ = false; 157 has_value_ = false;
158 PoisonValue();
123 } 159 }
124 return *this; 160 return *this;
125 } 161 }
126 162
127 // Swap the values if both m1 and m2 have values; move the value if only one 163 // Swap the values if both m1 and m2 have values; move the value if only one
128 // of them has one. 164 // of them has one.
129 friend void swap(Optional& m1, Optional& m2) { 165 friend void swap(Optional& m1, Optional& m2) {
130 if (m1.has_value_) { 166 if (m1.has_value_) {
131 if (m2.has_value_) { 167 if (m2.has_value_) {
132 // Both have values: swap. 168 // Both have values: swap.
133 using std::swap; 169 using std::swap;
134 swap(m1.value_, m2.value_); 170 swap(m1.value_, m2.value_);
135 } else { 171 } else {
136 // Only m1 has a value: move it to m2. 172 // Only m1 has a value: move it to m2.
173 m2.UnpoisonValue();
137 new (&m2.value_) T(std::move(m1.value_)); 174 new (&m2.value_) T(std::move(m1.value_));
138 m1.value_.~T(); // Destroy the moved-from value. 175 m1.value_.~T(); // Destroy the moved-from value.
139 m1.has_value_ = false; 176 m1.has_value_ = false;
140 m2.has_value_ = true; 177 m2.has_value_ = true;
178 m1.PoisonValue();
141 } 179 }
142 } else if (m2.has_value_) { 180 } else if (m2.has_value_) {
143 // Only m2 has a value: move it to m1. 181 // Only m2 has a value: move it to m1.
182 m1.UnpoisonValue();
144 new (&m1.value_) T(std::move(m2.value_)); 183 new (&m1.value_) T(std::move(m2.value_));
145 m2.value_.~T(); // Destroy the moved-from value. 184 m2.value_.~T(); // Destroy the moved-from value.
146 m1.has_value_ = true; 185 m1.has_value_ = true;
147 m2.has_value_ = false; 186 m2.has_value_ = false;
187 m2.PoisonValue();
148 } 188 }
149 } 189 }
150 190
151 // Conversion to bool to test if we have a value. 191 // Conversion to bool to test if we have a value.
152 explicit operator bool() const { return has_value_; } 192 explicit operator bool() const { return has_value_; }
153 193
154 // Dereferencing. Only allowed if we have a value. 194 // Dereferencing. Only allowed if we have a value.
155 const T* operator->() const { 195 const T* operator->() const {
156 RTC_DCHECK(has_value_); 196 RTC_DCHECK(has_value_);
157 return &value_; 197 return &value_;
158 } 198 }
159 T* operator->() { 199 T* operator->() {
160 RTC_DCHECK(has_value_); 200 RTC_DCHECK(has_value_);
161 return &value_; 201 return &value_;
162 } 202 }
163 const T& operator*() const { 203 const T& operator*() const {
164 RTC_DCHECK(has_value_); 204 RTC_DCHECK(has_value_);
165 return value_; 205 return value_;
166 } 206 }
167 T& operator*() { 207 T& operator*() {
168 RTC_DCHECK(has_value_); 208 RTC_DCHECK(has_value_);
169 return value_; 209 return value_;
170 } 210 }
171 211
172 // Dereference with a default value in case we don't have a value. 212 // Dereference with a default value in case we don't have a value.
173 const T& value_or(const T& default_val) const { 213 const T& value_or(const T& default_val) const {
174 return has_value_ ? value_ : default_val; 214 // The no-op call prevents the compiler from generating optimized code that
215 // reads value_ even if !has_value_, but only if FunctionThatDoesNothing is
216 // not completely inlined; see its declaration.).
217 return has_value_ ? *internal::FunctionThatDoesNothing(&value_)
ossu 2016/09/05 09:38:43 If this is a problem for us here, how do we preven
kwiberg-webrtc 2016/09/05 12:41:21 Yeah, it isn't safe in the general case, in that t
ossu 2016/09/08 08:49:03 Hmm... alright, let's put this in for now and revi
218 : default_val;
175 } 219 }
176 220
177 // Equality tests. Two Optionals are equal if they contain equivalent values, 221 // Equality tests. Two Optionals are equal if they contain equivalent values,
178 // or 222 // or
179 // if they're both empty. 223 // if they're both empty.
180 friend bool operator==(const Optional& m1, const Optional& m2) { 224 friend bool operator==(const Optional& m1, const Optional& m2) {
181 return m1.has_value_ && m2.has_value_ ? m1.value_ == m2.value_ 225 return m1.has_value_ && m2.has_value_ ? m1.value_ == m2.value_
182 : m1.has_value_ == m2.has_value_; 226 : m1.has_value_ == m2.has_value_;
183 } 227 }
184 friend bool operator!=(const Optional& m1, const Optional& m2) { 228 friend bool operator!=(const Optional& m1, const Optional& m2) {
185 return m1.has_value_ && m2.has_value_ ? m1.value_ != m2.value_ 229 return m1.has_value_ && m2.has_value_ ? m1.value_ != m2.value_
186 : m1.has_value_ != m2.has_value_; 230 : m1.has_value_ != m2.has_value_;
187 } 231 }
188 232
189 private: 233 private:
234 // Tell sanitizers that value_ shouldn't be touched.
235 void PoisonValue() {
236 rtc::AsanPoison(rtc::MakeArrayView(&value_, 1));
237 rtc::MsanMarkUninitialized(rtc::MakeArrayView(&value_, 1));
238 }
239
240 // Tell sanitizers that value_ is OK to touch again.
241 void UnpoisonValue() {
242 rtc::AsanUnpoison(rtc::MakeArrayView(&value_, 1));
243 }
244
190 bool has_value_; // True iff value_ contains a live value. 245 bool has_value_; // True iff value_ contains a live value.
191 union { 246 union {
192 // empty_ exists only to make it possible to initialize the union, even when 247 // empty_ exists only to make it possible to initialize the union, even when
193 // it doesn't contain any data. If the union goes uninitialized, it may 248 // it doesn't contain any data. If the union goes uninitialized, it may
194 // trigger compiler warnings. 249 // trigger compiler warnings.
195 char empty_; 250 char empty_;
196 // By placing value_ in a union, we get to manage its construction and 251 // By placing value_ in a union, we get to manage its construction and
197 // destruction manually: the Optional constructors won't automatically 252 // destruction manually: the Optional constructors won't automatically
198 // construct it, and the Optional destructor won't automatically destroy 253 // construct it, and the Optional destructor won't automatically destroy
199 // it. Basically, this just allocates a properly sized and aligned block of 254 // it. Basically, this just allocates a properly sized and aligned block of
200 // memory in which we can manually put a T with placement new. 255 // memory in which we can manually put a T with placement new.
201 T value_; 256 T value_;
202 }; 257 };
203 }; 258 };
204 259
205 } // namespace rtc 260 } // namespace rtc
206 261
207 #endif // WEBRTC_BASE_OPTIONAL_H_ 262 #endif // WEBRTC_BASE_OPTIONAL_H_
OLDNEW
« no previous file with comments | « webrtc/base/base.gyp ('k') | webrtc/base/optional.cc » ('j') | webrtc/base/optional.cc » ('J')

Powered by Google App Engine
This is Rietveld 408576698