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

Unified 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 side-by-side diff with in-line comments
Download patch
Index: webrtc/base/optional.h
diff --git a/webrtc/base/optional.h b/webrtc/base/optional.h
index e98ab2e3ce7e9765386bcb8cbb47c92f4a7b42fa..8a5ce99af3a2c10e9367128bf20eb5b0d0e8eaad 100644
--- a/webrtc/base/optional.h
+++ b/webrtc/base/optional.h
@@ -15,10 +15,34 @@
#include <memory>
#include <utility>
+#include "webrtc/base/array_view.h"
#include "webrtc/base/checks.h"
+#include "webrtc/base/sanitizer.h"
namespace rtc {
+namespace internal {
+
+#if RTC_HAS_ASAN
+
+// This is a non-inlined function. The optimizer can't see inside it.
+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
+
+template <typename T>
+inline T* FunctionThatDoesNothing(T* x) {
+ return reinterpret_cast<T*>(
+ FunctionThatDoesNothingImpl(reinterpret_cast<void*>(x)));
+}
+
+#else
+
+template <typename T>
+inline T* FunctionThatDoesNothing(T* x) { return x; }
+
+#endif
+
+} // namespace internal
+
// Simple std::optional-wannabe. It either contains a T or not.
//
// A moved-from Optional<T> may only be destroyed, and assigned to if T allows
@@ -59,7 +83,9 @@ template <typename T>
class Optional final {
public:
// Construct an empty Optional.
- Optional() : has_value_(false), empty_('\0') {}
+ Optional() : has_value_(false), empty_('\0') {
+ PoisonValue();
+ }
// Construct an Optional that contains a value.
explicit Optional(const T& value) : has_value_(true) {
@@ -73,6 +99,8 @@ class Optional final {
Optional(const Optional& m) : has_value_(m.has_value_) {
if (has_value_)
new (&value_) T(m.value_);
+ else
+ PoisonValue();
}
// Move constructor: if m has a value, moves the value from m, leaving m
@@ -82,11 +110,15 @@ class Optional final {
Optional(Optional&& m) : has_value_(m.has_value_) {
if (has_value_)
new (&value_) T(std::move(m.value_));
+ else
+ PoisonValue();
}
~Optional() {
if (has_value_)
value_.~T();
+ else
+ UnpoisonValue();
}
// Copy assignment. Uses T's copy assignment if both sides have a value, T's
@@ -96,12 +128,14 @@ class Optional final {
if (has_value_) {
value_ = m.value_; // T's copy assignment.
} else {
+ UnpoisonValue();
new (&value_) T(m.value_); // T's copy constructor.
has_value_ = true;
}
} else if (has_value_) {
value_.~T();
has_value_ = false;
+ PoisonValue();
}
return *this;
}
@@ -114,12 +148,14 @@ class Optional final {
if (has_value_) {
value_ = std::move(m.value_); // T's move assignment.
} else {
+ UnpoisonValue();
new (&value_) T(std::move(m.value_)); // T's move constructor.
has_value_ = true;
}
} else if (has_value_) {
value_.~T();
has_value_ = false;
+ PoisonValue();
}
return *this;
}
@@ -134,17 +170,21 @@ class Optional final {
swap(m1.value_, m2.value_);
} else {
// Only m1 has a value: move it to m2.
+ m2.UnpoisonValue();
new (&m2.value_) T(std::move(m1.value_));
m1.value_.~T(); // Destroy the moved-from value.
m1.has_value_ = false;
m2.has_value_ = true;
+ m1.PoisonValue();
}
} else if (m2.has_value_) {
// Only m2 has a value: move it to m1.
+ m1.UnpoisonValue();
new (&m1.value_) T(std::move(m2.value_));
m2.value_.~T(); // Destroy the moved-from value.
m1.has_value_ = true;
m2.has_value_ = false;
+ m2.PoisonValue();
}
}
@@ -171,7 +211,11 @@ class Optional final {
// Dereference with a default value in case we don't have a value.
const T& value_or(const T& default_val) const {
- return has_value_ ? value_ : default_val;
+ // The no-op call prevents the compiler from generating optimized code that
+ // reads value_ even if !has_value_, but only if FunctionThatDoesNothing is
+ // not completely inlined; see its declaration.).
+ 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
+ : default_val;
}
// Equality tests. Two Optionals are equal if they contain equivalent values,
@@ -187,6 +231,17 @@ class Optional final {
}
private:
+ // Tell sanitizers that value_ shouldn't be touched.
+ void PoisonValue() {
+ rtc::AsanPoison(rtc::MakeArrayView(&value_, 1));
+ rtc::MsanMarkUninitialized(rtc::MakeArrayView(&value_, 1));
+ }
+
+ // Tell sanitizers that value_ is OK to touch again.
+ void UnpoisonValue() {
+ rtc::AsanUnpoison(rtc::MakeArrayView(&value_, 1));
+ }
+
bool has_value_; // True iff value_ contains a live value.
union {
// empty_ exists only to make it possible to initialize the union, even when
« 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