From 76c35b626fce1d10a08d604fe4075c968775d361 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Fri, 23 Oct 2026 17:08:09 -0700 Subject: [PATCH] Add ABSL_ATTRIBUTE_UNSAFE_BUFFER_USAGE_IN_CONTAINER and apply it to absl::Span. Clang's `-Wunsafe-buffer-usage-in-container` flags two-parameter `std::span` constructions whose arguments are not provably safe. The new macro wraps `[[clang::unsafe_buffer_usage_in_container]]`, which opts other container and view types into the same check, and expands to nothing on compilers that do not support the attribute. Apply it to the (pointer, length) constructor of `absl::Span` and the (pointer, length) and (begin, end) overloads of `absl::MakeSpan()` and `absl::MakeConstSpan()`, so that calls such as `absl::Span(p, n)` are diagnosed while `absl::Span(v.data(), v.size())` is not. PiperOrigin-RevId: 1005970571 --- absl/base/attributes.h | 37 ++++++++++++++++++++++++++++++++++++ absl/base/attributes_test.cc | 15 +++++++++++++++ absl/types/span.h | 21 +++++++++++--------- 3 files changed, 64 insertions(+), 9 deletions(-) diff --git a/absl/base/attributes.h b/absl/base/attributes.h index f236f6f7030..f5fdcfeb926 100644 --- a/absl/base/attributes.h +++ b/absl/base/attributes.h @@ -1161,4 +1161,41 @@ struct AbslInternal_YouForgotToExplicitlyInitializeAField { #define ABSL_UNSAFE_BUFFERS(...) __VA_ARGS__ #endif +// ABSL_ATTRIBUTE_UNSAFE_BUFFER_USAGE_IN_CONTAINER +// +// Marks a two-parameter constructor or factory function of a container or view +// type that takes a (pointer, size) or (begin, end) pair, such as +// `absl::Span(T* ptr, size_t size)`. When Clang's +// `-Wunsafe-buffer-usage-in-container` diagnostic is enabled, calls to an +// annotated function emit a warning unless the arguments are provably safe, +// e.g. `c.data(), c.size()` or `c.begin(), c.end()` on the same container `c`, +// a constant-size array and its size, or when the size evaluates to zero. +// +// Example: +// +// template +// class MySpan { +// public: +// ABSL_ATTRIBUTE_UNSAFE_BUFFER_USAGE_IN_CONTAINER +// MySpan(T* ptr, size_t size); +// }; +// +// void F(int* p, size_t n, std::vector& v) { +// MySpan s1(p, n); // Warning +// MySpan s2(v.data(), v.size()); // OK +// } +// +// Unavoidable unsafe calls can be suppressed with `ABSL_UNSAFE_BUFFERS()`. +// Note that, as with `[[clang::unsafe_buffer_usage]]`, Clang does not check the +// body of an annotated function for unsafe buffer usage. +// +// See the upstream documentation: +// https://clang.llvm.org/docs/AttributeReference.html#unsafe-buffer-usage +#if ABSL_HAVE_CPP_ATTRIBUTE(clang::unsafe_buffer_usage_in_container) +#define ABSL_ATTRIBUTE_UNSAFE_BUFFER_USAGE_IN_CONTAINER \ + [[clang::unsafe_buffer_usage_in_container]] +#else +#define ABSL_ATTRIBUTE_UNSAFE_BUFFER_USAGE_IN_CONTAINER +#endif + #endif // ABSL_BASE_ATTRIBUTES_H_ diff --git a/absl/base/attributes_test.cc b/absl/base/attributes_test.cc index e87b66e542f..f4070501d21 100644 --- a/absl/base/attributes_test.cc +++ b/absl/base/attributes_test.cc @@ -14,6 +14,7 @@ #include "absl/base/attributes.h" +#include #include #include "gtest/gtest.h" @@ -72,4 +73,18 @@ TEST(Attributes, UnsafeBuffers) { EXPECT_EQ(buffer[1], 1); } +TEST(Attributes, UnsafeBufferUsageInContainer) { + struct IntSpan { + ABSL_ATTRIBUTE_UNSAFE_BUFFER_USAGE_IN_CONTAINER + IntSpan(int* p, size_t n) : ptr(p), size(n) {} + + int* ptr; + size_t size; + }; + int buffer[3] = {0}; + IntSpan span(buffer, 3); + EXPECT_EQ(span.ptr, buffer); + EXPECT_EQ(span.size, 3u); +} + } // namespace diff --git a/absl/types/span.h b/absl/types/span.h index 41aba0aea5b..e3a230f5a51 100644 --- a/absl/types/span.h +++ b/absl/types/span.h @@ -219,6 +219,7 @@ class ABSL_ATTRIBUTE_VIEW Span { static constexpr size_type npos = static_cast(-1); constexpr Span() noexcept : Span(nullptr, 0) {} + ABSL_ATTRIBUTE_UNSAFE_BUFFER_USAGE_IN_CONTAINER constexpr Span(pointer array ABSL_ATTRIBUTE_LIFETIME_BOUND, size_type length) noexcept : ptr_(array), len_(length) {} @@ -716,14 +717,15 @@ constexpr bool operator>=(Span a, const U& b) { // or pass the container directly instead of using `&v[0]` or `&v[v.size()]`. // template -constexpr Span MakeSpan(T* absl_nullable ptr ABSL_ATTRIBUTE_LIFETIME_BOUND, - size_t size) noexcept { +ABSL_ATTRIBUTE_UNSAFE_BUFFER_USAGE_IN_CONTAINER constexpr Span MakeSpan( + T* absl_nullable ptr ABSL_ATTRIBUTE_LIFETIME_BOUND, size_t size) noexcept { return Span(ptr, size); } template -Span MakeSpan(T* absl_nullable begin ABSL_ATTRIBUTE_LIFETIME_BOUND, - T* absl_nullable end) noexcept { +ABSL_ATTRIBUTE_UNSAFE_BUFFER_USAGE_IN_CONTAINER Span MakeSpan( + T* absl_nullable begin ABSL_ATTRIBUTE_LIFETIME_BOUND, + T* absl_nullable end) noexcept { absl::base_internal::HardeningAssertLE(begin, end); return Span(begin, static_cast(end - begin)); } @@ -776,15 +778,16 @@ constexpr Span MakeSpan( // ProcessInts(absl::MakeConstSpan(std::vector{ 0, 0, 0 })); // template -constexpr Span MakeConstSpan( - T* absl_nullable ptr ABSL_ATTRIBUTE_LIFETIME_BOUND, size_t size) noexcept { +ABSL_ATTRIBUTE_UNSAFE_BUFFER_USAGE_IN_CONTAINER constexpr Span +MakeConstSpan(T* absl_nullable ptr ABSL_ATTRIBUTE_LIFETIME_BOUND, + size_t size) noexcept { return Span(ptr, size); } template -Span MakeConstSpan(T* absl_nullable begin - ABSL_ATTRIBUTE_LIFETIME_BOUND, - T* absl_nullable end) noexcept { +ABSL_ATTRIBUTE_UNSAFE_BUFFER_USAGE_IN_CONTAINER Span MakeConstSpan( + T* absl_nullable begin ABSL_ATTRIBUTE_LIFETIME_BOUND, + T* absl_nullable end) noexcept { absl::base_internal::HardeningAssertLE(begin, end); return Span(begin, end - begin); }