diff options
author | Abseil Team <absl-team@google.com> | 2023-12-18 12:37:32 -0800 |
---|---|---|
committer | Copybara-Service <copybara-worker@google.com> | 2023-12-18 12:38:18 -0800 |
commit | 8900d7c496afb0fb0e0a737d001a46828bc446dd (patch) | |
tree | c89237017baee1e83f54a7f09ff7c4ddf9bbe664 | |
parent | d22aa4df3f530872d4e11e3906c9c6e002deefca (diff) |
Mutex: Fix Condition pointer-to-member cast to respect const qualifier
Previously, `absl::Condition` incorrectly used the same (non-`const`)
pointer-to-method type when wrapping both `const` and non-`const` methods.
Unfortunately, this is undefined behavior according to `[expr.reinterpret.cast]`
in the C++ standard:
> The effect of calling a function through a pointer to a function type that is
> not the same as the type used in the definition of the function is undefined.
This fixes the UB.
PiperOrigin-RevId: 591981682
Change-Id: Iaca955346699417232383d3a1800ea9b82ea5761
-rw-r--r-- | absl/synchronization/mutex.h | 14 | ||||
-rw-r--r-- | absl/synchronization/mutex_test.cc | 9 |
2 files changed, 16 insertions, 7 deletions
diff --git a/absl/synchronization/mutex.h b/absl/synchronization/mutex.h index 157b7050..85e8d0e0 100644 --- a/absl/synchronization/mutex.h +++ b/absl/synchronization/mutex.h @@ -848,7 +848,7 @@ class Condition { static bool CallVoidPtrFunction(const Condition*); template <typename T> static bool CastAndCallFunction(const Condition* c); - template <typename T> + template <typename T, typename ConditionMethodPtr> static bool CastAndCallMethod(const Condition* c); // Helper methods for storing, validating, and reading callback arguments. @@ -1080,12 +1080,12 @@ inline void Mutex::Dtor() {} inline CondVar::CondVar() : cv_(0) {} // static -template <typename T> +template <typename T, typename ConditionMethodPtr> bool Condition::CastAndCallMethod(const Condition* c) { T* object = static_cast<T*>(c->arg_); - bool (T::*method_pointer)(); - c->ReadCallback(&method_pointer); - return (object->*method_pointer)(); + ConditionMethodPtr condition_method_pointer; + c->ReadCallback(&condition_method_pointer); + return (object->*condition_method_pointer)(); } // static @@ -1115,7 +1115,7 @@ inline Condition::Condition(bool (*func)(T*), template <typename T> inline Condition::Condition(T* object, bool (absl::internal::identity<T>::type::*method)()) - : eval_(&CastAndCallMethod<T>), arg_(object) { + : eval_(&CastAndCallMethod<T, decltype(method)>), arg_(object) { static_assert(sizeof(&method) <= sizeof(callback_), "An overlarge method pointer was passed to Condition."); StoreCallback(method); @@ -1125,7 +1125,7 @@ template <typename T> inline Condition::Condition(const T* object, bool (absl::internal::identity<T>::type::*method)() const) - : eval_(&CastAndCallMethod<T>), + : eval_(&CastAndCallMethod<const T, decltype(method)>), arg_(reinterpret_cast<void*>(const_cast<T*>(object))) { StoreCallback(method); } diff --git a/absl/synchronization/mutex_test.cc b/absl/synchronization/mutex_test.cc index 35b43334..a8d75827 100644 --- a/absl/synchronization/mutex_test.cc +++ b/absl/synchronization/mutex_test.cc @@ -977,6 +977,15 @@ TEST(Mutex, FunctionPointerConditionWithDerivedToBaseConversion) { const Derived *>::value)); } +struct Constable { + bool WotsAllThisThen() const { return true; } +}; + +TEST(Mutex, FunctionPointerConditionWithConstMethod) { + const Constable chapman; + EXPECT_TRUE(absl::Condition(&chapman, &Constable::WotsAllThisThen).Eval()); +} + struct True { template <class... Args> bool operator()(Args...) const { |