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); }