Add various tests for memory BIOs.

Our test coverage for BIOs isn't great. Fill in missing memory BIO
tests, in preparation for reworking it a bit to be size_t-clean.

Change-Id: I77aeab93d6d9275c65e998d517463f4cc10efcf3
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/58545
Commit-Queue: Bob Beck <bbe@google.com>
Auto-Submit: David Benjamin <davidben@google.com>
Reviewed-by: Bob Beck <bbe@google.com>
diff --git a/crypto/bio/bio_test.cc b/crypto/bio/bio_test.cc
index 342325c..28b9c6d 100644
--- a/crypto/bio/bio_test.cc
+++ b/crypto/bio/bio_test.cc
@@ -219,6 +219,217 @@
   }
 }
 
+TEST(BIOTest, MemReadOnly) {
+  // A memory BIO created from |BIO_new_mem_buf| is a read-only buffer.
+  static const char kData[] = "abcdefghijklmno";
+  bssl::UniquePtr<BIO> bio(BIO_new_mem_buf(kData, strlen(kData)));
+  ASSERT_TRUE(bio);
+
+  // Writing to read-only buffers should fail.
+  EXPECT_EQ(BIO_write(bio.get(), kData, strlen(kData)), -1);
+
+  const uint8_t *contents;
+  size_t len;
+  ASSERT_TRUE(BIO_mem_contents(bio.get(), &contents, &len));
+  EXPECT_EQ(Bytes(contents, len), Bytes(kData));
+  EXPECT_EQ(BIO_eof(bio.get()), 0);
+
+  // Read less than the whole buffer.
+  char buf[6];
+  int ret = BIO_read(bio.get(), buf, sizeof(buf));
+  ASSERT_GT(ret, 0);
+  EXPECT_EQ(Bytes(buf, ret), Bytes("abcdef"));
+
+  ASSERT_TRUE(BIO_mem_contents(bio.get(), &contents, &len));
+  EXPECT_EQ(Bytes(contents, len), Bytes("ghijklmno"));
+  EXPECT_EQ(BIO_eof(bio.get()), 0);
+
+  ret = BIO_read(bio.get(), buf, sizeof(buf));
+  ASSERT_GT(ret, 0);
+  EXPECT_EQ(Bytes(buf, ret), Bytes("ghijkl"));
+
+  ASSERT_TRUE(BIO_mem_contents(bio.get(), &contents, &len));
+  EXPECT_EQ(Bytes(contents, len), Bytes("mno"));
+  EXPECT_EQ(BIO_eof(bio.get()), 0);
+
+  // Read the remainder of the buffer.
+  ret = BIO_read(bio.get(), buf, sizeof(buf));
+  ASSERT_GT(ret, 0);
+  EXPECT_EQ(Bytes(buf, ret), Bytes("mno"));
+
+  ASSERT_TRUE(BIO_mem_contents(bio.get(), &contents, &len));
+  EXPECT_EQ(Bytes(contents, len), Bytes(""));
+  EXPECT_EQ(BIO_eof(bio.get()), 1);
+
+  // By default, reading from a consumed read-only buffer returns EOF.
+  EXPECT_EQ(BIO_read(bio.get(), buf, sizeof(buf)), 0);
+  EXPECT_FALSE(BIO_should_read(bio.get()));
+
+  // A memory BIO can be configured to return an error instead of EOF. This is
+  // error is returned as retryable. (This is not especially useful here. It
+  // makes more sense for a writable BIO.)
+  EXPECT_EQ(BIO_set_mem_eof_return(bio.get(), -1), 1);
+  EXPECT_EQ(BIO_read(bio.get(), buf, sizeof(buf)), -1);
+  EXPECT_TRUE(BIO_should_read(bio.get()));
+
+  // Read exactly the right number of bytes, to test the boundary condition is
+  // correct.
+  bio.reset(BIO_new_mem_buf("abc", 3));
+  ASSERT_TRUE(bio);
+  ret = BIO_read(bio.get(), buf, 3);
+  ASSERT_GT(ret, 0);
+  EXPECT_EQ(Bytes(buf, ret), Bytes("abc"));
+  EXPECT_EQ(BIO_eof(bio.get()), 1);
+}
+
+TEST(BIOTest, MemWritable) {
+  // A memory BIO created from |BIO_new| is writable.
+  bssl::UniquePtr<BIO> bio(BIO_new(BIO_s_mem()));
+  ASSERT_TRUE(bio);
+
+  // It is initially empty.
+  const uint8_t *contents;
+  size_t len;
+  ASSERT_TRUE(BIO_mem_contents(bio.get(), &contents, &len));
+  EXPECT_EQ(Bytes(contents, len), Bytes(""));
+  EXPECT_EQ(BIO_eof(bio.get()), 1);
+
+  // Reading from it should default to returning a retryable error.
+  char buf[32];
+  EXPECT_EQ(BIO_read(bio.get(), buf, sizeof(buf)), -1);
+  EXPECT_TRUE(BIO_should_read(bio.get()));
+
+  // This can be configured to return an EOF.
+  EXPECT_EQ(BIO_set_mem_eof_return(bio.get(), 0), 1);
+  EXPECT_EQ(BIO_read(bio.get(), buf, sizeof(buf)), 0);
+  EXPECT_FALSE(BIO_should_read(bio.get()));
+
+  // Restore the default. A writable memory |BIO| is typically used in this mode
+  // so additional data can be written when exhausted.
+  EXPECT_EQ(BIO_set_mem_eof_return(bio.get(), -1), 1);
+
+  // Writes append to the buffer.
+  ASSERT_EQ(BIO_write(bio.get(), "abcdef", 6), 6);
+  ASSERT_TRUE(BIO_mem_contents(bio.get(), &contents, &len));
+  EXPECT_EQ(Bytes(contents, len), Bytes("abcdef"));
+  EXPECT_EQ(BIO_eof(bio.get()), 0);
+
+  // Writes can include embedded NULs.
+  ASSERT_EQ(BIO_write(bio.get(), "\0ghijk", 6), 6);
+  ASSERT_TRUE(BIO_mem_contents(bio.get(), &contents, &len));
+  EXPECT_EQ(Bytes(contents, len), Bytes("abcdef\0ghijk", 12));
+  EXPECT_EQ(BIO_eof(bio.get()), 0);
+
+  // Do a partial read.
+  int ret = BIO_read(bio.get(), buf, 4);
+  ASSERT_GT(ret, 0);
+  EXPECT_EQ(Bytes(buf, ret), Bytes("abcd"));
+  ASSERT_TRUE(BIO_mem_contents(bio.get(), &contents, &len));
+  EXPECT_EQ(Bytes(contents, len), Bytes("ef\0ghijk", 8));
+  EXPECT_EQ(BIO_eof(bio.get()), 0);
+
+  // Reads and writes may alternate.
+  ASSERT_EQ(BIO_write(bio.get(), "lmnopq", 6), 6);
+  ASSERT_TRUE(BIO_mem_contents(bio.get(), &contents, &len));
+  EXPECT_EQ(Bytes(contents, len), Bytes("ef\0ghijklmnopq", 14));
+  EXPECT_EQ(BIO_eof(bio.get()), 0);
+
+  // Reads may consume embedded NULs.
+  ret = BIO_read(bio.get(), buf, 4);
+  ASSERT_GT(ret, 0);
+  EXPECT_EQ(Bytes(buf, ret), Bytes("ef\0g", 4));
+  ASSERT_TRUE(BIO_mem_contents(bio.get(), &contents, &len));
+  EXPECT_EQ(Bytes(contents, len), Bytes("hijklmnopq"));
+  EXPECT_EQ(BIO_eof(bio.get()), 0);
+
+  // The read buffer exceeds the |BIO|, so we consume everything.
+  ret = BIO_read(bio.get(), buf, sizeof(buf));
+  ASSERT_GT(ret, 0);
+  EXPECT_EQ(Bytes(buf, ret), Bytes("hijklmnopq"));
+  ASSERT_TRUE(BIO_mem_contents(bio.get(), &contents, &len));
+  EXPECT_EQ(Bytes(contents, len), Bytes(""));
+  EXPECT_EQ(BIO_eof(bio.get()), 1);
+
+  // The |BIO| is now empty.
+  EXPECT_EQ(BIO_read(bio.get(), buf, sizeof(buf)), -1);
+  EXPECT_TRUE(BIO_should_read(bio.get()));
+
+  // Repeat the above, reading exactly the right number of bytes, to test the
+  // boundary condition is correct.
+  ASSERT_EQ(BIO_write(bio.get(), "abc", 3), 3);
+  ret = BIO_read(bio.get(), buf, 3);
+  EXPECT_EQ(Bytes(buf, ret), Bytes("abc"));
+  EXPECT_EQ(BIO_read(bio.get(), buf, sizeof(buf)), -1);
+  EXPECT_TRUE(BIO_should_read(bio.get()));
+  EXPECT_EQ(BIO_eof(bio.get()), 1);
+}
+
+TEST(BIOTest, MemGets) {
+  const struct {
+    std::string bio;
+    int gets_len;
+    std::string gets_result;
+  } kGetsTests[] = {
+      // BIO_gets should stop at the first newline. If the buffer is too small,
+      // stop there instead. Note the buffer size
+      // includes a trailing NUL.
+      {"123456789\n123456789", 5, "1234"},
+      {"123456789\n123456789", 9, "12345678"},
+      {"123456789\n123456789", 10, "123456789"},
+      {"123456789\n123456789", 11, "123456789\n"},
+      {"123456789\n123456789", 12, "123456789\n"},
+      {"123456789\n123456789", 256, "123456789\n"},
+
+      // If we run out of buffer, read the whole buffer.
+      {"12345", 5, "1234"},
+      {"12345", 6, "12345"},
+      {"12345", 10, "12345"},
+
+      // NUL bytes do not terminate gets.
+      // TODO(davidben): File BIOs don't get this right. It's unclear if it's
+      // even possible to use fgets correctly here.
+      {std::string("abc\0def\nghi", 11), 256, std::string("abc\0def\n", 8)},
+
+      // An output size of one means we cannot read any bytes. Only the trailing
+      // NUL is included.
+      {"12345", 1, ""},
+
+      // Empty line.
+      {"\nabcdef", 256, "\n"},
+      // Empty BIO.
+      {"", 256, ""},
+  };
+  for (const auto& t : kGetsTests) {
+    SCOPED_TRACE(t.bio);
+    SCOPED_TRACE(t.gets_len);
+
+    bssl::UniquePtr<BIO> bio(BIO_new_mem_buf(t.bio.data(), t.bio.size()));
+    ASSERT_TRUE(bio);
+
+    std::vector<char> buf(t.gets_len, 'a');
+    int ret = BIO_gets(bio.get(), buf.data(), t.gets_len);
+    ASSERT_GE(ret, 0);
+    // |BIO_gets| should write a NUL terminator, not counted in the return
+    // value.
+    EXPECT_EQ(Bytes(buf.data(), ret + 1),
+              Bytes(t.gets_result.data(), t.gets_result.size() + 1));
+
+    // The remaining data should still be in the BIO.
+    const uint8_t *contents;
+    size_t len;
+    ASSERT_TRUE(BIO_mem_contents(bio.get(), &contents, &len));
+    EXPECT_EQ(Bytes(contents, len), Bytes(t.bio.substr(ret)));
+  }
+
+  // Negative and zero lengths should not output anything, even a trailing NUL.
+  bssl::UniquePtr<BIO> bio(BIO_new_mem_buf("12345", -1));
+  ASSERT_TRUE(bio);
+  char c = 'a';
+  EXPECT_EQ(0, BIO_gets(bio.get(), &c, -1));
+  EXPECT_EQ(0, BIO_gets(bio.get(), &c, 0));
+  EXPECT_EQ(c, 'a');
+}
+
 // Run through the tests twice, swapping |bio1| and |bio2|, for symmetry.
 class BIOPairTest : public testing::TestWithParam<bool> {};