Fix bad probe on span assignment operator.

Empty spans with data pointers one past the end of an object are
allowed, but we can't allow UnownedPtr<T> to probe for validity of
this location during an assignment for these spans.

See e.g. https://pdfium-review.googlesource.com/c/pdfium/+/96971
for an example of how this goes wrong.

Change-Id: I7ab60800a4d1584aeeec8343362337cb6c137672
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/97031
Reviewed-by: Lei Zhang <thestig@chromium.org>
Commit-Queue: Tom Sepez <tsepez@chromium.org>
diff --git a/core/fxcrt/span_util_unittest.cpp b/core/fxcrt/span_util_unittest.cpp
index e304e91..2ceab6a 100644
--- a/core/fxcrt/span_util_unittest.cpp
+++ b/core/fxcrt/span_util_unittest.cpp
@@ -81,3 +81,11 @@
   EXPECT_EQ(dst[2], 'A');
   EXPECT_EQ(dst[3], 'B');
 }
+
+TEST(Span, AssignOverOnePastEnd) {
+  std::vector<char> src(2, 'A');
+  pdfium::span<char> span = pdfium::make_span(src);
+  span = span.subspan(2);
+  span = pdfium::make_span(src);
+  EXPECT_EQ(span.size(), 2u);
+}
diff --git a/third_party/base/span.h b/third_party/base/span.h
index e45dcec..ec9f990 100644
--- a/third_party/base/span.h
+++ b/third_party/base/span.h
@@ -209,14 +209,15 @@
   // seamlessly used as a span<const T>, but not the other way around.
   template <typename U, typename = internal::EnableIfLegalSpanConversion<U, T>>
   constexpr span(const span<U>& other) : span(other.data(), other.size()) {}
-  span& operator=(const span& other) noexcept = default;
-  ~span() noexcept {
-    if (!size_) {
-      // Empty spans might point to byte N+1 of a N-byte object, legal for
-      // C pointers but not UnownedPtrs.
-      data_.ReleaseBadPointer();
+  span& operator=(const span& other) noexcept {
+    if (this != &other) {
+      ReleaseEmptySpan();
+      data_ = other.data_;
+      size_ = other.size_;
     }
+    return *this;
   }
+  ~span() noexcept { ReleaseEmptySpan(); }
 
   // [span.sub], span subviews
   const span first(size_t count) const {
@@ -281,6 +282,13 @@
   }
 
  private:
+  void ReleaseEmptySpan() noexcept {
+    // Empty spans might point to byte N+1 of a N-byte object, legal for
+    // C pointers but not UnownedPtrs.
+    if (!size_)
+      data_.ReleaseBadPointer();
+  }
+
   UnownedPtr<T> data_;
   size_t size_;
 };