diff --git a/third_party/blink/renderer/platform/wtf/vector_test.cc b/third_party/blink/renderer/platform/wtf/vector_test.cc index b1239ccc0d74c..57e8a564697f6 100644 --- a/third_party/blink/renderer/platform/wtf/vector_test.cc +++ b/third_party/blink/renderer/platform/wtf/vector_test.cc @@ -25,6 +25,8 @@ #include "third_party/blink/renderer/platform/wtf/vector.h" +#include + #include #include @@ -890,6 +892,50 @@ TEST(VectorTest, EraseDuringIteration) { } #endif +// POC: WTF::Vector::Vector(base::span) passes the span's size_t +// extent into VectorBuffer(wtf_size_t) and assigns it to wtf_size_t size_ +// without a checked_cast (unlike the std::initializer_list ctor / operator= +// which both use base::checked_cast()). For a span larger than +// 2^32 elements the backing store is sized to (N mod 2^32) but +// UninitializedCopy()/Copy() still memcpy the full N bytes from the source +// span, producing a wild heap-buffer-overflow write. +TEST(VectorTest, SpanCtorSizeTruncationOverflow) { + // 4 GiB + 256 bytes: truncates to 0x100 when narrowed to uint32_t. + constexpr size_t kBig = 0x100000100ull; + static_assert(static_cast(kBig) == 0x100u); + + // Reserve a >4 GiB readable source region. MAP_NORESERVE / anonymous zero + // pages keep this cheap; ASan's memcpy interceptor reports the destination + // overflow before any of this is actually faulted in. + void* src = mmap(nullptr, kBig, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + ASSERT_NE(src, MAP_FAILED); + + // SAFETY: |src| points to |kBig| readable bytes per the mmap above. + base::span huge = + UNSAFE_BUFFERS(base::span(static_cast(src), kBig)); + ASSERT_EQ(huge.size(), kBig); + + // Vector(huge): + // Base(other.size()) -> VectorBuffer(wtf_size_t = 0x100), 256 B alloc. + // size_ = other.size() -> 0x100. + // UninitializedCopy(huge, base::span(*this), kConstruction) + // -> Copy(huge.begin(), huge.end(), data()) + // -> memcpy(dst /*256 B*/, src, 0x1_0000_0100). + // ASAN: heap-buffer-overflow WRITE. + fprintf(stderr, + "[POC] constructing Vector(span of 0x%zx bytes); " + "expect heap-buffer-overflow WRITE into a 0x%x-byte buffer\n", + huge.size(), static_cast(huge.size())); + Vector v(huge); + + // Not reached under ASAN. If we do get here (e.g. in a hardened build with + // a future fix), make sure the size wasn't silently truncated. + EXPECT_EQ(static_cast(v.size()), kBig) + << "WTF::Vector(base::span) silently truncated the span size"; + + munmap(src, kBig); +} + } // anonymous namespace } // namespace blink