Securely clear memory containing key information / passwords before freeing.

The previously used "memset(ptr, 0, size)" can get optimized away by compilers
if "ptr" is not used afterwards.

A new class "ZeroOnFreeBuffer" is introduced that can hold sensitive data and
that automatically clears underlying memory when it's no longer used.

Bug: webrtc:8806, webrtc:8897, webrtc:8905
Change-Id: Iedddddf80790f9af0addaab3346ec5bff102917d
Reviewed-on: https://webrtc-review.googlesource.com/41941
Commit-Queue: Joachim Bauch <jbauch@webrtc.org>
Reviewed-by: Karl Wiberg <kwiberg@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#22334}
diff --git a/rtc_base/buffer_unittest.cc b/rtc_base/buffer_unittest.cc
index 5cd1889..e3b7d46 100644
--- a/rtc_base/buffer_unittest.cc
+++ b/rtc_base/buffer_unittest.cc
@@ -434,4 +434,81 @@
   EXPECT_EQ(kObsidian, buf[2].stone);
 }
 
+TEST(ZeroOnFreeBufferTest, TestZeroOnSetData) {
+  ZeroOnFreeBuffer<uint8_t> buf(kTestData, 7);
+  const uint8_t* old_data = buf.data();
+  const size_t old_capacity = buf.capacity();
+  const size_t old_size = buf.size();
+  constexpr size_t offset = 1;
+  buf.SetData(kTestData + offset, 2);
+  // Sanity checks to make sure the underlying heap memory was not reallocated.
+  EXPECT_EQ(old_data, buf.data());
+  EXPECT_EQ(old_capacity, buf.capacity());
+  // The first two elements have been overwritten, and the remaining five have
+  // been zeroed.
+  EXPECT_EQ(kTestData[offset], buf[0]);
+  EXPECT_EQ(kTestData[offset + 1], buf[1]);
+  for (size_t i = 2; i < old_size; i++) {
+    EXPECT_EQ(0, old_data[i]);
+  }
+}
+
+TEST(ZeroOnFreeBufferTest, TestZeroOnSetDataFromSetter) {
+  static constexpr size_t offset = 1;
+  const auto setter = [](rtc::ArrayView<uint8_t> av) {
+    for (int i = 0; i != 2; ++i)
+      av[i] = kTestData[offset + i];
+    return 2;
+  };
+
+  ZeroOnFreeBuffer<uint8_t> buf(kTestData, 7);
+  const uint8_t* old_data = buf.data();
+  const size_t old_capacity = buf.capacity();
+  const size_t old_size = buf.size();
+  buf.SetData(2, setter);
+  // Sanity checks to make sure the underlying heap memory was not reallocated.
+  EXPECT_EQ(old_data, buf.data());
+  EXPECT_EQ(old_capacity, buf.capacity());
+  // The first two elements have been overwritten, and the remaining five have
+  // been zeroed.
+  EXPECT_EQ(kTestData[offset], buf[0]);
+  EXPECT_EQ(kTestData[offset + 1], buf[1]);
+  for (size_t i = 2; i < old_size; i++) {
+    EXPECT_EQ(0, old_data[i]);
+  }
+}
+
+TEST(ZeroOnFreeBufferTest, TestZeroOnSetSize) {
+  ZeroOnFreeBuffer<uint8_t> buf(kTestData, 7);
+  const uint8_t* old_data = buf.data();
+  const size_t old_capacity = buf.capacity();
+  const size_t old_size = buf.size();
+  buf.SetSize(2);
+  // Sanity checks to make sure the underlying heap memory was not reallocated.
+  EXPECT_EQ(old_data, buf.data());
+  EXPECT_EQ(old_capacity, buf.capacity());
+  // The first two elements have not been modified and the remaining five have
+  // been zeroed.
+  EXPECT_EQ(kTestData[0], buf[0]);
+  EXPECT_EQ(kTestData[1], buf[1]);
+  for (size_t i = 2; i < old_size; i++) {
+    EXPECT_EQ(0, old_data[i]);
+  }
+}
+
+TEST(ZeroOnFreeBufferTest, TestZeroOnClear) {
+  ZeroOnFreeBuffer<uint8_t> buf(kTestData, 7);
+  const uint8_t* old_data = buf.data();
+  const size_t old_capacity = buf.capacity();
+  const size_t old_size = buf.size();
+  buf.Clear();
+  // Sanity checks to make sure the underlying heap memory was not reallocated.
+  EXPECT_EQ(old_data, buf.data());
+  EXPECT_EQ(old_capacity, buf.capacity());
+  // The underlying memory was not released but cleared.
+  for (size_t i = 0; i < old_size; i++) {
+    EXPECT_EQ(0, old_data[i]);
+  }
+}
+
 }  // namespace rtc