[libcxx-commits] [libcxx] [libc++] Guard call_once against operator hijacking. (PR #128054)

Mark de Wever via libcxx-commits libcxx-commits at lists.llvm.org
Fri Feb 21 08:49:31 PST 2025


https://github.com/mordante updated https://github.com/llvm/llvm-project/pull/128054

>From e7e5f74786d3fe8875a5bedf444572207712be85 Mon Sep 17 00:00:00 2001
From: Mark de Wever <koraq at xs4all.nl>
Date: Tue, 18 Feb 2025 21:40:50 +0100
Subject: [PATCH] [libc++] Guard call_once against operator hijacking.

---
 libcxx/include/__mutex/once_flag.h               |  7 ++++---
 .../thread.once.callonce/call_once.pass.cpp      | 16 +++++++++++++++-
 libcxx/test/support/operator_hijacker.h          |  1 +
 libcxx/test/support/test_macros.h                |  4 ++++
 4 files changed, 24 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/__mutex/once_flag.h b/libcxx/include/__mutex/once_flag.h
index 08ff54bf99265..2b0ba7781faa4 100644
--- a/libcxx/include/__mutex/once_flag.h
+++ b/libcxx/include/__mutex/once_flag.h
@@ -11,6 +11,7 @@
 
 #include <__config>
 #include <__functional/invoke.h>
+#include <__memory/addressof.h>
 #include <__memory/shared_count.h> // __libcpp_acquire_load
 #include <__tuple/tuple_indices.h>
 #include <__tuple/tuple_size.h>
@@ -128,7 +129,7 @@ inline _LIBCPP_HIDE_FROM_ABI void call_once(once_flag& __flag, _Callable&& __fun
     typedef tuple<_Callable&&, _Args&&...> _Gp;
     _Gp __f(std::forward<_Callable>(__func), std::forward<_Args>(__args)...);
     __call_once_param<_Gp> __p(__f);
-    std::__call_once(__flag.__state_, &__p, &__call_once_proxy<_Gp>);
+    std::__call_once(__flag.__state_, std::addressof(__p), std::addressof(__call_once_proxy<_Gp>));
   }
 }
 
@@ -138,7 +139,7 @@ template <class _Callable>
 inline _LIBCPP_HIDE_FROM_ABI void call_once(once_flag& __flag, _Callable& __func) {
   if (__libcpp_acquire_load(&__flag.__state_) != once_flag::_Complete) {
     __call_once_param<_Callable> __p(__func);
-    std::__call_once(__flag.__state_, &__p, &__call_once_proxy<_Callable>);
+    std::__call_once(__flag.__state_, std::addressof(__p), std::addressof(__call_once_proxy<_Callable>));
   }
 }
 
@@ -146,7 +147,7 @@ template <class _Callable>
 inline _LIBCPP_HIDE_FROM_ABI void call_once(once_flag& __flag, const _Callable& __func) {
   if (__libcpp_acquire_load(&__flag.__state_) != once_flag::_Complete) {
     __call_once_param<const _Callable> __p(__func);
-    std::__call_once(__flag.__state_, &__p, &__call_once_proxy<const _Callable>);
+    std::__call_once(__flag.__state_, std::addressof(__p), std::addressof(__call_once_proxy<const _Callable>));
   }
 }
 
diff --git a/libcxx/test/std/thread/thread.mutex/thread.once/thread.once.callonce/call_once.pass.cpp b/libcxx/test/std/thread/thread.mutex/thread.once/thread.once.callonce/call_once.pass.cpp
index 7708efcb54c7f..f27395927e793 100644
--- a/libcxx/test/std/thread/thread.mutex/thread.once/thread.once.callonce/call_once.pass.cpp
+++ b/libcxx/test/std/thread/thread.mutex/thread.once/thread.once.callonce/call_once.pass.cpp
@@ -21,6 +21,7 @@
 
 #include "make_test_thread.h"
 #include "test_macros.h"
+#include "operator_hijacker.h"
 
 typedef std::chrono::milliseconds ms;
 
@@ -253,7 +254,20 @@ int main(int, char**)
         std::call_once(f2, std::move(rq));
         assert(rq.rv_called == 1);
     }
+    {
+      std::once_flag flag;
+      auto f = [](const operator_hijacker&) {};
+      std::call_once(flag, f, operator_hijacker{});
+    }
+
 #endif // TEST_STD_VER >= 11
 
-  return 0;
+#ifndef TEST_USE_FROZEN_CXX03_HEADERS
+    {
+      std::once_flag flag;
+      operator_hijacker f;
+      std::call_once(flag, f);
+    }
+#endif // TEST_USE_FROZEN_CXX03_HEADERS
+    return 0;
 }
diff --git a/libcxx/test/support/operator_hijacker.h b/libcxx/test/support/operator_hijacker.h
index fbd8b58ab0f83..ee76e312eba1c 100644
--- a/libcxx/test/support/operator_hijacker.h
+++ b/libcxx/test/support/operator_hijacker.h
@@ -23,6 +23,7 @@
 struct operator_hijacker {
   TEST_CONSTEXPR bool operator<(const operator_hijacker&) const { return true; }
   TEST_CONSTEXPR bool operator==(const operator_hijacker&) const { return true; }
+  TEST_CONSTEXPR void operator()() const {}
 
   template <typename T>
   friend void operator&(T&&) = delete;
diff --git a/libcxx/test/support/test_macros.h b/libcxx/test/support/test_macros.h
index 858bbaa2d6d83..f27c58ced8b6e 100644
--- a/libcxx/test/support/test_macros.h
+++ b/libcxx/test/support/test_macros.h
@@ -272,6 +272,10 @@
 #  define TEST_HAS_FROM_CHARS_FLOATING_POINT
 #endif
 
+#ifdef _LIBCPP_USE_FROZEN_CXX03_HEADERS
+#  define TEST_USE_FROZEN_CXX03_HEADERS
+#endif
+
 namespace test_macros_detail {
 template <class T, class U>
 struct is_same { enum { value = 0};} ;



More information about the libcxx-commits mailing list