Bounds-check bssl::Array and bssl::Vector

These are internal types, and we actually don't even index into them
that much, but we should aim to be a bit safer.

Change-Id: Ifad9a4382d63d135c2fdd65fc04ffb0dfca07b7b
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/71829
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/ssl/internal.h b/ssl/internal.h
index d4c1c27..a0d4221 100644
--- a/ssl/internal.h
+++ b/ssl/internal.h
@@ -289,8 +289,14 @@
   size_t size() const { return size_; }
   bool empty() const { return size_ == 0; }
 
-  const T &operator[](size_t i) const { return data_[i]; }
-  T &operator[](size_t i) { return data_[i]; }
+  const T &operator[](size_t i) const {
+    BSSL_CHECK(i < size_);
+    return data_[i];
+  }
+  T &operator[](size_t i) {
+    BSSL_CHECK(i < size_);
+    return data_[i];
+  }
 
   T *begin() { return data_; }
   const T *begin() const { return data_; }
@@ -399,8 +405,14 @@
   size_t size() const { return size_; }
   bool empty() const { return size_ == 0; }
 
-  const T &operator[](size_t i) const { return data_[i]; }
-  T &operator[](size_t i) { return data_[i]; }
+  const T &operator[](size_t i) const {
+    BSSL_CHECK(i < size_);
+    return data_[i];
+  }
+  T &operator[](size_t i) {
+    BSSL_CHECK(i < size_);
+    return data_[i];
+  }
 
   T *begin() { return data_; }
   const T *begin() const { return data_; }
diff --git a/ssl/span_test.cc b/ssl/span_test.cc
index 481b0fc..84cb5a3 100644
--- a/ssl/span_test.cc
+++ b/ssl/span_test.cc
@@ -98,5 +98,22 @@
   static_assert(span2[0] == 1, "wrong value");
 }
 
+TEST(SpanDeathTest, BoundsChecks) {
+  // Make an array that's larger than we need, so that a failure to bounds check
+  // won't crash.
+  const int v[] = {1, 2, 3, 4};
+  Span<const int> span(v, 3);
+  // Out of bounds access.
+  EXPECT_DEATH_IF_SUPPORTED(span[3], "");
+  EXPECT_DEATH_IF_SUPPORTED(span.subspan(4), "");
+  EXPECT_DEATH_IF_SUPPORTED(span.first(4), "");
+  EXPECT_DEATH_IF_SUPPORTED(span.last(4), "");
+  // Accessing an empty span.
+  Span<const int> empty(v, 0);
+  EXPECT_DEATH_IF_SUPPORTED(empty[0], "");
+  EXPECT_DEATH_IF_SUPPORTED(empty.front(), "");
+  EXPECT_DEATH_IF_SUPPORTED(empty.back(), "");
+}
+
 }  // namespace
 BSSL_NAMESPACE_END
diff --git a/ssl/ssl_test.cc b/ssl/ssl_test.cc
index b21c798..a51b594 100644
--- a/ssl/ssl_test.cc
+++ b/ssl/ssl_test.cc
@@ -567,6 +567,13 @@
   return true;
 }
 
+TEST(ArrayDeathTest, BoundsChecks) {
+  Array<int> array;
+  const int v[] = {1, 2, 3, 4};
+  ASSERT_TRUE(array.CopyFrom(v));
+  EXPECT_DEATH_IF_SUPPORTED(array[4], "");
+}
+
 TEST(VectorTest, Resize) {
   Vector<size_t> vec;
   ASSERT_TRUE(vec.empty());
@@ -663,6 +670,15 @@
   EXPECT_EQ(3u, vec[3].array.size());
 }
 
+TEST(VectorDeathTest, BoundsChecks) {
+  Vector<int> vec;
+  ASSERT_TRUE(vec.Push(1));
+  // Within bounds of the capacity, but not the vector.
+  EXPECT_DEATH_IF_SUPPORTED(vec[1], "");
+  // Not within bounds of the capacity either.
+  EXPECT_DEATH_IF_SUPPORTED(vec[10000], "");
+}
+
 TEST(ReconstructSeqnumTest, Increment) {
   // Test simple cases from the beginning of an epoch with both 8- and 16-bit
   // wire sequence numbers.