Make Rust/C++ callable manager calls pass CFI in both directions. PiperOrigin-RevId: 999035084
diff --git a/absl/base/attributes.h b/absl/base/attributes.h index 1e4c7c3..601be76 100644 --- a/absl/base/attributes.h +++ b/absl/base/attributes.h
@@ -85,16 +85,16 @@ // Note: As the GCC manual states, "[s]ince non-static C++ methods // have an implicit 'this' argument, the arguments of such methods // should be counted from two, not one." -#if ABSL_HAVE_ATTRIBUTE(format) || (defined(__GNUC__) && !defined(__clang__)) -#define ABSL_PRINTF_ATTRIBUTE(string_index, first_to_check) \ - __attribute__((__format__(__printf__, string_index, first_to_check))) #if ABSL_HAVE_CPP_ATTRIBUTE(gnu::format) +#define ABSL_PRINTF_ATTRIBUTE(string_index, first_to_check) \ + [[gnu::format(printf, string_index, first_to_check)]] #define ABSL_SCANF_ATTRIBUTE(string_index, first_to_check) \ [[gnu::format(scanf, string_index, first_to_check)]] -#else +#elif ABSL_HAVE_ATTRIBUTE(format) || (defined(__GNUC__) && !defined(__clang__)) +#define ABSL_PRINTF_ATTRIBUTE(string_index, first_to_check) \ + __attribute__((__format__(__printf__, string_index, first_to_check))) #define ABSL_SCANF_ATTRIBUTE(string_index, first_to_check) \ __attribute__((__format__(__scanf__, string_index, first_to_check))) -#endif #else #define ABSL_PRINTF_ATTRIBUTE(string_index, first_to_check) #define ABSL_SCANF_ATTRIBUTE(string_index, first_to_check) @@ -1117,4 +1117,52 @@ #define ABSL_ATTRIBUTE_WARN_UNUSED #endif +// ABSL_UNSAFE_BUFFERS() +// +// Wraps code that performs unsafe buffer accesses or pointer arithmetic. Used +// to suppress warnings when `-Wunsafe-buffer-usage` is enabled. +// +// **WARNING**: This macro should be used when unavoidable. Applying it means +// the compiler is unable to prove that your code avoids out-of-bounds reads or +// writes, forcing reliance on manual review. Over time, code evolves and +// undocumented assumptions can silently break, introducing vulnerabilities. +// Always prioritize refactoring to safer alternatives (e.g. std::array, +// absl::Span, or bounds-checked containers) over using this suppression. +// +// When using this macro is unavoidable, restrict its scope to the smallest +// possible expression or statement. You must also add a `// SAFETY: ...` +// comment explaining why the operation is actually safe. +// Justifications include: +// * Explicit bounds checks (e.g., `CHECK()`) immediately preceding the access. +// * Invariants strictly enforced by surrounding types or functions. +// * Preconditions required of the caller (though this is heavily discouraged). +// +// Example (expression): +// +// // SAFETY: `buf` is NUL-terminated so `strdup` won't read past the end. +// return ABSL_UNSAFE_BUFFERS(strdup(buf)); +// +// Example (single-line statement): +// +// // SAFETY: `offset` is bounds-checked above to be within `buffer`. +// ABSL_UNSAFE_BUFFERS(buffer += offset;) +// +// Example (multi-line statement): +// +// // SAFETY: `offset` is bounds-checked above to be within `buffer`. +// ABSL_UNSAFE_BUFFERS( +// char* p = buffer + offset; +// p[0] = 'a'; +// ) +// +// Note: When wrapping a multi-line statement, placing a semicolon after the +// closing parenthesis may produce an extra semicolon warning. +#if defined(__clang__) +#define ABSL_UNSAFE_BUFFERS(...) \ + _Pragma("clang unsafe_buffer_usage begin") \ + __VA_ARGS__ _Pragma("clang unsafe_buffer_usage end") +#else +#define ABSL_UNSAFE_BUFFERS(...) __VA_ARGS__ +#endif + #endif // ABSL_BASE_ATTRIBUTES_H_
diff --git a/absl/base/attributes_test.cc b/absl/base/attributes_test.cc index 058d1be..e87b66e 100644 --- a/absl/base/attributes_test.cc +++ b/absl/base/attributes_test.cc
@@ -54,4 +54,22 @@ Agg good1 [[maybe_unused]] = {ImplicitlyConstructible("hello")}; } +TEST(Attributes, UnsafeBuffers) { + int buffer[10] = {0}; + int offset = 1; + + // Expression example + int* p1 = ABSL_UNSAFE_BUFFERS(buffer + offset); + EXPECT_EQ(p1, &buffer[1]); + + // Single-line statement example + int* p2 = buffer; + ABSL_UNSAFE_BUFFERS(p2 += offset;) + EXPECT_EQ(p2, &buffer[1]); + + // Multi-line statement example + ABSL_UNSAFE_BUFFERS(int* p3 = buffer + offset; p3[0] = 1;) + EXPECT_EQ(buffer[1], 1); +} + } // namespace
diff --git a/absl/crc/internal/crc_x86_arm_combined.cc b/absl/crc/internal/crc_x86_arm_combined.cc index 95221e4..d6f42d9 100644 --- a/absl/crc/internal/crc_x86_arm_combined.cc +++ b/absl/crc/internal/crc_x86_arm_combined.cc
@@ -1084,6 +1084,7 @@ 3, 1, PclmulStreamType::PCLMUL>(); case CpuType::kAmdMilan: case CpuType::kAmdGenoa: + case CpuType::kAmdRyzenV3000: case CpuType::kAmdSiena: case CpuType::kAmdTurin: case CpuType::kAmdVenice:
diff --git a/absl/functional/BUILD.bazel b/absl/functional/BUILD.bazel index d1b46c7..0cad083 100644 --- a/absl/functional/BUILD.bazel +++ b/absl/functional/BUILD.bazel
@@ -124,6 +124,7 @@ linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":bind_front", + "//absl/base:config", "//absl/memory", "@googletest//:gtest", "@googletest//:gtest_main",
diff --git a/absl/functional/CMakeLists.txt b/absl/functional/CMakeLists.txt index 1ddfa29..231c9dd 100644 --- a/absl/functional/CMakeLists.txt +++ b/absl/functional/CMakeLists.txt
@@ -105,6 +105,7 @@ ${ABSL_DEFAULT_COPTS} DEPS absl::bind_front + absl::config absl::memory GTest::gmock_main )
diff --git a/absl/functional/bind_front.h b/absl/functional/bind_front.h index 45ca4fc..06b6e52 100644 --- a/absl/functional/bind_front.h +++ b/absl/functional/bind_front.h
@@ -21,10 +21,11 @@ // it does not require the use of argument placeholders. The simpler syntax of // `absl::bind_front()` allows you to avoid known misuses with `std::bind()`. // -// `absl::bind_front()` is meant as a drop-in replacement for C++20's upcoming -// `std::bind_front()`, which similarly resolves these issues with -// `std::bind()`. Both `bind_front()` alternatives, unlike `std::bind()`, allow -// partial function application. (See +// `absl::bind_front()` is meant as a drop-in replacement for C++20's +// `std::bind_front(func, args...)` and C++26's +// `std::bind_front<ConstFn>(args...)`, which similarly resolve these issues +// with `std::bind()`. Both `bind_front()` alternatives, unlike `std::bind()`, +// allow partial function application. (See // https://en.wikipedia.org/wiki/Partial_application). #ifndef ABSL_FUNCTIONAL_BIND_FRONT_H_ @@ -39,17 +40,40 @@ #include <functional> // For std::bind_front. #endif // defined(__cpp_lib_bind_front) && __cpp_lib_bind_front >= 201907L +#include <type_traits> #include <utility> #include "absl/functional/internal/front_binder.h" #include "absl/utility/utility.h" +// ABSL_INTERNAL_HAVE_STD_NTTP_BIND_FRONT +// +// Defined if the standard library implements C++26 `std::bind_front<ConstFn>`, +// which `absl::bind_front<ConstFn>` then aliases. +// +// libc++ 17 through 22 define `__cpp_lib_bind_front` as 202306L in C++26 mode, +// but `std::bind_front<ConstFn>` was first implemented in libc++ 23: +// https://github.com/llvm/llvm-project/pull/165096#issuecomment-4487517016 +#ifdef ABSL_INTERNAL_HAVE_STD_NTTP_BIND_FRONT +#error ABSL_INTERNAL_HAVE_STD_NTTP_BIND_FRONT cannot be directly set +#elif defined(__cpp_lib_bind_front) && __cpp_lib_bind_front >= 202306L && \ + (!defined(_LIBCPP_VERSION) || _LIBCPP_VERSION >= 230000) +#define ABSL_INTERNAL_HAVE_STD_NTTP_BIND_FRONT 1 +#endif + namespace absl { ABSL_NAMESPACE_BEGIN // bind_front() // // Binds the first N arguments of an invocable object and stores them by value. +// The target invocable can be passed either as a runtime argument +// `absl::bind_front(func, args...)` (C++20 `std::bind_front`) or as a +// compile-time non-type template parameter `absl::bind_front<ConstFn>(args...)` +// (C++26 `std::bind_front`). +// +// Passing `ConstFn` as a non-type template parameter avoids storing a function +// pointer or pointer-to-member at runtime. // // Like `std::bind()`, `absl::bind_front()` is implicitly convertible to // `std::function`. In particular, it may be used as a simpler replacement for @@ -57,7 +81,8 @@ // specified. More importantly, it provides more reliable correctness guarantees // than `std::bind()`; while `std::bind()` will silently ignore passing more // parameters than expected, for example, `absl::bind_front()` will report such -// mis-uses as errors. In C++20, `absl::bind_front` is replaced by +// mis-uses as errors. In C++20 (for runtime callables) and C++26 (for +// non-type template parameter callables), `absl::bind_front` aliases // `std::bind_front`. // // absl::bind_front(a...) can be seen as storing the results of @@ -70,6 +95,8 @@ // assert(absl::bind_front(Minus)(3, 2) == 3 - 2); // assert(absl::bind_front(Minus, 3)(2) == 3 - 2); // assert(absl::bind_front(Minus, 3, 2)() == 3 - 2); +// assert(absl::bind_front<&Minus>()(3, 2) == 3 - 2); +// assert(absl::bind_front<&Minus>(3)(2) == 3 - 2); // // Example: Binding a member function. // @@ -80,8 +107,10 @@ // Math math; // // assert(absl::bind_front(&Math::Double)(&math, 3) == 2 * 3); +// assert(absl::bind_front<&Math::Double>()(&math, 3) == 2 * 3); // // Stores a pointer to math inside the functor. // assert(absl::bind_front(&Math::Double, &math)(3) == 2 * 3); +// assert(absl::bind_front<&Math::Double>(&math)(3) == 2 * 3); // // Stores a copy of math inside the functor. // assert(absl::bind_front(&Math::Double, math)(3) == 2 * 3); // // Stores std::unique_ptr<Math> inside the functor. @@ -193,7 +222,23 @@ } #endif // defined(__cpp_lib_bind_front) && __cpp_lib_bind_front >= 201907L +#ifndef ABSL_INTERNAL_HAVE_STD_NTTP_BIND_FRONT +template <auto ConstFn, class... BoundArgs> +constexpr auto bind_front(BoundArgs&&... args) { + using F = decltype(ConstFn); + if constexpr (std::is_pointer_v<F> || std::is_member_pointer_v<F>) { + static_assert(ConstFn != nullptr, + "bind_front: ConstFn cannot be equal to nullptr"); + } + // The runtime `bind_front` checks the requirements on `BoundArgs`. + return absl::bind_front(functional_internal::ConstFnInvoker<ConstFn>{}, + std::forward<BoundArgs>(args)...); +} +#endif // ABSL_INTERNAL_HAVE_STD_NTTP_BIND_FRONT + ABSL_NAMESPACE_END } // namespace absl +#undef ABSL_INTERNAL_HAVE_STD_NTTP_BIND_FRONT + #endif // ABSL_FUNCTIONAL_BIND_FRONT_H_
diff --git a/absl/functional/bind_front_test.cc b/absl/functional/bind_front_test.cc index 6e87d43..300466c 100644 --- a/absl/functional/bind_front_test.cc +++ b/absl/functional/bind_front_test.cc
@@ -19,15 +19,17 @@ #include <functional> #include <memory> #include <string> +#include <type_traits> #include <utility> #include "gmock/gmock.h" #include "gtest/gtest.h" +#include "absl/base/config.h" #include "absl/memory/memory.h" namespace { -char CharAt(const char* s, size_t index) { return s[index]; } +constexpr char CharAt(const char* s, size_t index) { return s[index]; } TEST(BindTest, Basics) { EXPECT_EQ('C', absl::bind_front(CharAt)("ABC", 2)); @@ -200,7 +202,7 @@ EXPECT_EQ(42, std::move(f)()); } -int Plus(int a, int b) { return a + b; } +constexpr int Plus(int a, int b) { return a + b; } TEST(BindTest, ConstExpr) { constexpr auto f = absl::bind_front(CharAt); @@ -223,4 +225,224 @@ absl::bind_front(ManglingCall{}, 1, 3.3)("A"); } +struct Math { + constexpr int Add(int a, int b) const noexcept { return base + a + b; } + int base = 10; +}; + +TEST(BindConstFnTest, Basics) { + EXPECT_EQ('C', absl::bind_front<&CharAt>()("ABC", 2)); + EXPECT_EQ('C', absl::bind_front<&CharAt>("ABC")(2)); + EXPECT_EQ('C', absl::bind_front<&CharAt>("ABC", 2)()); +} + +TEST(BindConstFnTest, MemberPointers) { + Math m{10}; + auto bound_ptr = absl::bind_front<&Math::Add>(&m); + EXPECT_EQ(15, bound_ptr(2, 3)); + + auto bound_ptr_and_arg = absl::bind_front<&Math::Add>(&m, 2); + EXPECT_EQ(15, bound_ptr_and_arg(3)); + + auto bound_zero_args = absl::bind_front<&Math::Add>(); + EXPECT_EQ(15, bound_zero_args(&m, 2, 3)); + EXPECT_EQ(15, bound_zero_args(m, 2, 3)); +} + +TEST(BindConstFnTest, StoreByCopyRefAndPointer) { + Struct s = {"hello"}; + auto by_copy = absl::bind_front<&Struct::value>(s); + EXPECT_EQ("hello", by_copy()); + EXPECT_NE(&s.value, &by_copy()); + + NonCopyable nc("hello"); + auto by_ref = absl::bind_front<&GetNonCopyableValue>(std::ref(nc)); + EXPECT_EQ("hello", by_ref()); + EXPECT_EQ(&nc.value, &by_ref()); + + auto by_cref = absl::bind_front<&GetNonCopyableValue>(std::cref(nc)); + EXPECT_EQ("hello", by_cref()); + EXPECT_EQ(&nc.value, &by_cref()); + + auto by_ptr = absl::bind_front<&NonCopyable::value>(&nc); + EXPECT_EQ("hello", by_ptr()); + EXPECT_EQ(&nc.value, &by_ptr()); + nc.value = "goodbye"; + EXPECT_EQ("goodbye", by_ref()); + EXPECT_EQ("goodbye", by_cref()); + EXPECT_EQ("goodbye", by_ptr()); +} + +TEST(BindConstFnTest, MoveOnlyArgsAndResult) { + EXPECT_EQ(42, absl::bind_front<&Sink>()(std::make_unique<int>(42))); + EXPECT_EQ(42, absl::bind_front<&Sink>(std::make_unique<int>(42))()); + EXPECT_THAT(absl::bind_front<&Factory>()(42), ::testing::Pointee(42)); + EXPECT_THAT(absl::bind_front<&Factory>(42)(), ::testing::Pointee(42)); + + FalseCopyable<std::unique_ptr<int>> x; + x.m = std::make_unique<int>(42); + auto f = absl::bind_front<&GetMember>(std::move(x)); + EXPECT_EQ(42, std::move(f)()); +} + +TEST(BindConstFnTest, ConstExpr) { + constexpr auto f = absl::bind_front<&CharAt>(); + EXPECT_EQ(f("ABC", 1), 'B'); + + constexpr auto plus5 = absl::bind_front<&Plus>(5); + EXPECT_EQ(plus5(1), 6); + + constexpr auto plus5_1 = absl::bind_front<&Plus>(5, 1); + EXPECT_EQ(plus5_1(), 6); + + constexpr Math m{10}; + constexpr auto add_m = absl::bind_front<&Math::Add>(m, 2); + EXPECT_EQ(add_m(3), 15); + + // Invoking the results is `constexpr` only where the runtime `bind_front` + // that produces them is `std::bind_front` (C++20). The pre-C++20 fallback's + // call operators and `std::invoke` are not `constexpr`. +#if defined(__cpp_lib_bind_front) && __cpp_lib_bind_front >= 201907L + static_assert(f("ABC", 1) == 'B'); + static_assert(plus5(1) == 6); + static_assert(plus5_1() == 6); + static_assert(add_m(3) == 15); +#endif // __cpp_lib_bind_front >= 201907L +} + +// The results come from the runtime `bind_front`, which propagates `noexcept` +// where it is `std::bind_front` (C++20). +#if defined(__cpp_lib_bind_front) && __cpp_lib_bind_front >= 201907L +int NoexceptPlus(int a, int b) noexcept { return a + b; } +int PotentiallyThrowingPlus(int a, int b) { return a + b; } + +TEST(BindConstFnTest, NoexceptPropagation) { + auto noexcept_0 = absl::bind_front<&NoexceptPlus>(); + static_assert(noexcept(noexcept_0(1, 2))); + + auto throwing_0 = absl::bind_front<&PotentiallyThrowingPlus>(); + static_assert(!noexcept(throwing_0(1, 2))); + + Math m{10}; + auto math_0 = absl::bind_front<&Math::Add>(); + static_assert(noexcept(math_0(m, 1, 2))); +} +#endif // __cpp_lib_bind_front >= 201907L + +// Invalid calls must be substitution failures rather than hard errors, so that +// `std::is_invocable` works on the result. +TEST(BindConstFnTest, SfinaeFriendly) { + using PlusBinder = decltype(absl::bind_front<&Plus>()); + static_assert(std::is_invocable_v<PlusBinder, int, int>); + static_assert(!std::is_invocable_v<PlusBinder, std::string, int>); + static_assert(!std::is_invocable_v<PlusBinder, int>); + + using Plus1Binder = decltype(absl::bind_front<&Plus>(1)); + static_assert(std::is_invocable_v<Plus1Binder, int>); + static_assert(!std::is_invocable_v<Plus1Binder, std::string>); +} + +// These tests pass class-type objects as template arguments (P0732R2, +// P1907R1), and `StatelessLambdaNTTP` puts a lambda in a template argument +// (P0315R4, which has no feature-test macro). +// +// We check the language version rather than +// `__cpp_nontype_template_args >= 201911L` because Clang (through at least +// 24) still defines it as 201411L: its P1907R1 support is incomplete, though +// everything used here works. Checking the feature macro would silently skip +// these tests on Clang. If a toolchain claims C++20 but can't compile these +// tests, exclude it here. +#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L +TEST(BindConstFnTest, StatelessLambdaNTTP) { + constexpr auto f = absl::bind_front<[](int x, int y) { return x + y; }>(); + EXPECT_EQ(f(2, 3), 5); + + constexpr auto f2 = absl::bind_front<[](int x, int y) { return x + y; }>(2); + EXPECT_EQ(f2(3), 5); +} + +struct Adder { + constexpr int operator()(int x) const& { return base + x; } + // `ConstFn` names a template parameter object, which is a `const` lvalue + // ([temp.param]), so it must never be invoked as an rvalue. + int operator()(int) const&& = delete; + + int base; +}; + +TEST(BindConstFnTest, StatefulObjectNTTP) { + constexpr auto add5 = absl::bind_front<Adder{5}>(); + EXPECT_EQ(add5(1), 6); + + constexpr auto add5_1 = absl::bind_front<Adder{5}>(1); + EXPECT_EQ(add5_1(), 6); +} + +struct DescribeCall { + template <class... Args> + std::string operator()(Args&&...) & { + return "&"; + } + template <class... Args> + std::string operator()(Args&&...) const& { + return "const&"; + } + template <class... Args> + std::string operator()(Args&&...) && { + return "&&"; + } + template <class... Args> + std::string operator()(Args&&...) const&& { + return "const&&"; + } +}; + +// Since `ConstFn` is a `const` lvalue, it is invoked through its `const&` +// overload whatever the value category of the result. Both zero and nonzero +// bound arguments are tested, since the standard library's +// `std::bind_front<ConstFn>` may implement them differently. +TEST(BindConstFnTest, InvokesConstFnAsConstLvalue) { + auto f0 = absl::bind_front<DescribeCall{}>(); + const auto& cf0 = f0; + EXPECT_EQ("const&", f0()); + EXPECT_EQ("const&", cf0()); + EXPECT_EQ("const&", std::move(f0)()); + EXPECT_EQ("const&", std::move(cf0)()); + + auto f1 = absl::bind_front<DescribeCall{}>(1); + const auto& cf1 = f1; + EXPECT_EQ("const&", f1()); + EXPECT_EQ("const&", cf1()); + EXPECT_EQ("const&", std::move(f1)()); + EXPECT_EQ("const&", std::move(cf1)()); +} + +struct DeletedConstLvalueCall { + template <class... Args> + void operator()(Args&&...) &; + template <class... Args> + void operator()(Args&&...) const& = delete; + template <class... Args> + void operator()(Args&&...) &&; + template <class... Args> + void operator()(Args&&...) const&&; +}; + +// If `ConstFn`'s `const&` overload is deleted, the result isn't invocable, +// rather than falling back to another overload. +TEST(BindConstFnTest, NotInvocableIfConstLvalueCallDeleted) { + using F0 = decltype(absl::bind_front<DeletedConstLvalueCall{}>()); + static_assert(!std::is_invocable_v<F0&>); + static_assert(!std::is_invocable_v<const F0&>); + static_assert(!std::is_invocable_v<F0>); + static_assert(!std::is_invocable_v<const F0>); + + using F1 = decltype(absl::bind_front<DeletedConstLvalueCall{}>(1)); + static_assert(!std::is_invocable_v<F1&>); + static_assert(!std::is_invocable_v<const F1&>); + static_assert(!std::is_invocable_v<F1>); + static_assert(!std::is_invocable_v<const F1>); +} +#endif // ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L + } // namespace
diff --git a/absl/functional/internal/front_binder.h b/absl/functional/internal/front_binder.h index fcada9c..33d4fb4 100644 --- a/absl/functional/internal/front_binder.h +++ b/absl/functional/internal/front_binder.h
@@ -18,6 +18,7 @@ #define ABSL_FUNCTIONAL_INTERNAL_FRONT_BINDER_H_ #include <cstddef> +#include <functional> #include <type_traits> #include <utility> @@ -87,6 +88,34 @@ template <class F, class... BoundArgs> using bind_front_t = FrontBinder<std::decay_t<F>, std::decay_t<BoundArgs>...>; +// Stateless invoker for the compile-time constant target `ConstFn` of +// `absl::bind_front<ConstFn>(args...)` (C++26 [func.bind.front]). +// +// `operator()` has a trailing `decltype(std::invoke(ConstFn, ...))` return type +// rather than `decltype(auto)` so that an invalid call is a substitution +// failure instead of a hard error. Deducing a `decltype(auto)` return type +// instantiates the body, so merely checking whether a call is valid would fail +// to compile. That would break `std::is_invocable` and overload resolution, +// including among the call operators of a `bind_front` result wrapping the +// invoker: e.g. calling such a result as an rvalue fails if it holds a +// move-only bound argument, because its `const&` overload is also considered. +// +// The return type is `decltype(std::invoke(ConstFn, ...))` rather than +// `std::invoke_result_t<decltype(ConstFn), ...>` because for C++20 class-type +// non-type template parameters, `decltype(ConstFn)` is the unqualified type `F` +// (which `std::invoke_result_t` treats as an rvalue `F&&`), whereas the +// template parameter expression `ConstFn` is a `const F` lvalue as specified by +// [func.bind.front]. +template <auto ConstFn> +struct ConstFnInvoker { + template <class... FreeArgs> + constexpr auto operator()(FreeArgs&&... free_args) const noexcept( + noexcept(std::invoke(ConstFn, std::forward<FreeArgs>(free_args)...))) + -> decltype(std::invoke(ConstFn, std::forward<FreeArgs>(free_args)...)) { + return std::invoke(ConstFn, std::forward<FreeArgs>(free_args)...); + } +}; + } // namespace functional_internal ABSL_NAMESPACE_END } // namespace absl