add fortified implementations of fread/fwrite

A __size_mul_overflow utility is used to take advantage of the checked
overflow intrinsics in Clang and GCC (>= 5). The fallback for older
compilers is the optimized but less than ideal overflow checking pattern
used in OpenBSD.

Change-Id: Ibb0d4fd9b5acb67983e6a9f46844c2fd444f7e69
diff --git a/tests/fortify_compilation_test.cpp b/tests/fortify_compilation_test.cpp
index 537b341..166e8d9 100644
--- a/tests/fortify_compilation_test.cpp
+++ b/tests/fortify_compilation_test.cpp
@@ -230,3 +230,35 @@
   // clang should emit a warning, but doesn't
   ppoll(fds, 2, &timeout, NULL);
 }
+
+void test_fread_overflow() {
+  char buf[4];
+  // NOLINTNEXTLINE(whitespace/line_length)
+  // GCC: error: call to '__fread_overflow' declared with attribute error: fread called with overflowing size * count
+  // clang should emit a warning, but doesn't
+  fread(buf, 2, (size_t)-1, stdin);
+}
+
+void test_fread_too_big() {
+  char buf[4];
+  // NOLINTNEXTLINE(whitespace/line_length)
+  // GCC: error: call to '__fread_too_big_error' declared with attribute error: fread called with size * count bigger than buffer
+  // clang should emit a warning, but doesn't
+  fread(buf, 1, 5, stdin);
+}
+
+void test_fwrite_overflow() {
+  char buf[4];
+  // NOLINTNEXTLINE(whitespace/line_length)
+  // GCC: error: call to '__fwrite_overflow' declared with attribute error: fwrite called with overflowing size * count
+  // clang should emit a warning, but doesn't
+  fwrite(buf, 2, (size_t)-1, stdout);
+}
+
+void test_fwrite_too_big() {
+  char buf[4] = {0};
+  // NOLINTNEXTLINE(whitespace/line_length)
+  // GCC: error: call to '__fwrite_too_big_error' declared with attribute error: fwrite called with size * count bigger than buffer
+  // clang should emit a warning, but doesn't
+  fwrite(buf, 1, 5, stdout);
+}
diff --git a/tests/fortify_test.cpp b/tests/fortify_test.cpp
index 4faccb4..664e057 100644
--- a/tests/fortify_test.cpp
+++ b/tests/fortify_test.cpp
@@ -647,6 +647,22 @@
   close(fd);
 }
 
+TEST_F(DEATHTEST, fread_fortified) {
+  char buf[1];
+  size_t ct = atoi("2"); // prevent optimizations
+  FILE* fp = fopen("/dev/null", "r");
+  ASSERT_FORTIFY(fread(buf, 1, ct, fp));
+  fclose(fp);
+}
+
+TEST_F(DEATHTEST, fwrite_fortified) {
+  char buf[1] = {0};
+  size_t ct = atoi("2"); // prevent optimizations
+  FILE* fp = fopen("/dev/null", "w");
+  ASSERT_FORTIFY(fwrite(buf, 1, ct, fp));
+  fclose(fp);
+}
+
 TEST_F(DEATHTEST, readlink_fortified) {
   char buf[1];
   size_t ct = atoi("2"); // prevent optimizations