fortify: import tests from Chrome OS

Chrome OS has a fairly extensive FORTIFY test suite for both
compile-time and run-time diagnostics. It covers tons of edge cases, and
conveniently centralizes diagnostic and death testing.

A fair amount of it has been ifdef'ed out, since Bionic doesn't yet
diagnose (or crash on) some of these things. The intent is to explicitly
declare defeat on the things we don't care to FORTIFY, and slowly fix
the rest in easier-to-digest CLs.

Once that's done, we might be able to look into retiring some of the
FORTIFY testing that we don't share with the CrOS folks.

Bug: 131861088
Test: mma + bionic-unit-tests on blueline

Change-Id: I16734ea0769e03cf658ef10532d64f28fdb36a89
diff --git a/tests/Android.bp b/tests/Android.bp
index e0faa5f..a55d08b 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -281,6 +281,16 @@
 // -----------------------------------------------------------------------------
 
 cc_defaults {
+    name: "bionic_clang_fortify_tests_w_flags",
+    cflags: [
+        "-Wno-builtin-memcpy-chk-size",
+        "-Wno-format-zero-length",
+        "-Wno-memset-transposed-args",
+        "-Wno-strncat-size",
+    ],
+}
+
+cc_defaults {
     name: "bionic_fortify_tests_defaults",
     cflags: [
         "-U_FORTIFY_SOURCE",
@@ -299,6 +309,9 @@
 // unused.
 cc_test_library {
     name: "fortify_disabled_for_asan",
+    defaults: [
+        "bionic_clang_fortify_tests_w_flags",
+    ],
     cflags: [
         "-Werror",
         "-D_FORTIFY_SOURCE=2",
@@ -306,11 +319,10 @@
         // enabled. Since the intent is just to build this, we can get away with
         // passing this flag on its own.
         "-fsanitize=address",
-        "-Wno-memset-transposed-args",
     ],
     // Ignore that we don't have ASAN symbols linked in.
     allow_undefined_symbols: true,
-    srcs: ["fortify_filecheck_diagnostics_test.cpp"],
+    srcs: ["clang_fortify_tests.cpp"],
 }
 
 // Ensure we don't use FORTIFY'ed functions with the static analyzer/clang-tidy:
@@ -319,13 +331,15 @@
 // enabled. The library that results from building this is meant to be unused.
 cc_test_library {
     name: "fortify_disabled_for_tidy",
+    defaults: [
+        "bionic_clang_fortify_tests_w_flags",
+    ],
     cflags: [
         "-Werror",
         "-D_FORTIFY_SOURCE=2",
         "-D__clang_analyzer__",
-        "-Wno-memset-transposed-args",
     ],
-    srcs: ["fortify_filecheck_diagnostics_test.cpp"],
+    srcs: ["clang_fortify_tests.cpp"],
 }
 
 cc_test_library {
@@ -358,6 +372,53 @@
     },
 }
 
+cc_defaults {
+    name: "bionic_new_fortify_tests_defaults",
+    defaults: [
+        "bionic_clang_fortify_tests_w_flags",
+    ],
+    cflags: [
+        "-U_FORTIFY_SOURCE",
+    ],
+    srcs: ["clang_fortify_tests.cpp"],
+    target: {
+        host: {
+            clang_cflags: ["-D__clang__"],
+        },
+    },
+}
+
+cc_test_library {
+    name: "libfortify1-new-tests-clang",
+    defaults: [
+        "bionic_new_fortify_tests_defaults",
+        "bionic_tests_defaults",
+    ],
+    cflags: [
+        "-D_FORTIFY_SOURCE=1",
+        "-DTEST_NAME=Fortify1_clang_new",
+    ],
+    shared: {
+        enabled: false,
+    },
+}
+
+cc_test_library {
+    name: "libfortify2-new-tests-clang",
+    defaults: [
+        "bionic_new_fortify_tests_defaults",
+        "bionic_tests_defaults",
+    ],
+    cflags: [
+        "-D_FORTIFY_SOURCE=2",
+        "-DTEST_NAME=Fortify2_clang_new",
+    ],
+    shared: {
+        enabled: false,
+    },
+}
+
+
 // -----------------------------------------------------------------------------
 // Library of all tests (excluding the dynamic linker tests).
 // -----------------------------------------------------------------------------
@@ -368,7 +429,9 @@
         "libBionicStandardTests",
         "libBionicElfTlsTests",
         "libfortify1-tests-clang",
+        "libfortify1-new-tests-clang",
         "libfortify2-tests-clang",
+        "libfortify2-new-tests-clang",
     ],
     shared: {
         enabled: false,
diff --git a/tests/Android.mk b/tests/Android.mk
index 848d291..b5571e3 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -59,23 +59,11 @@
 # Compile time tests.
 # -----------------------------------------------------------------------------
 
-# Some of these are intentionally using = instead of := since we need access to
-# some variables not initialtized until we're in the build system.
+FORTIFY_LEVEL := 1
+include $(LOCAL_PATH)/make_fortify_compile_test.mk
 
-include $(CLEAR_VARS)
-LOCAL_ADDITIONAL_DEPENDENCIES := \
-    $(LOCAL_PATH)/Android.mk \
-    $(LOCAL_PATH)/touch-obj-on-success
-
-LOCAL_CXX := $(LOCAL_PATH)/touch-obj-on-success \
-    $(LLVM_PREBUILTS_PATH)/clang++ \
-
-LOCAL_CLANG := true
-LOCAL_MODULE := bionic-compile-time-tests-clang++
-LOCAL_CPPFLAGS := -Wall -Wno-error
-LOCAL_CPPFLAGS += -fno-color-diagnostics -ferror-limit=10000 -Xclang -verify
-LOCAL_SRC_FILES := fortify_filecheck_diagnostics_test.cpp
-include $(BUILD_STATIC_LIBRARY)
+FORTIFY_LEVEL := 2
+include $(LOCAL_PATH)/make_fortify_compile_test.mk
 
 endif # linux-x86
 
diff --git a/tests/clang_fortify_tests.cpp b/tests/clang_fortify_tests.cpp
new file mode 100644
index 0000000..948faf3
--- /dev/null
+++ b/tests/clang_fortify_tests.cpp
@@ -0,0 +1,611 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __clang__
+#error "Non-clang isn't supported"
+#endif
+
+// Clang compile-time and run-time tests for Bionic's FORTIFY.
+//
+// This file is compiled in two configurations ways to give us a sane set of tests for clang's
+// FORTIFY implementation.
+//
+// One configuration uses clang's diagnostic consumer
+// (https://clang.llvm.org/doxygen/classclang_1_1VerifyDiagnosticConsumer.html#details)
+// to check diagnostics (e.g. the expected-* comments everywhere).
+//
+// Please note that this test does things like leaking memory. That's WAI.
+
+// Silence all "from 'diagnose_if'" `note`s from anywhere, including headers; they're uninteresting
+// for this test case, and their line numbers may change over time.
+// expected-note@* 0+{{from 'diagnose_if'}}
+//
+// Similarly, there are a few overload tricks we have to emit errors. Ignore any notes from those.
+// expected-note@* 0+{{candidate function}}
+
+#ifndef _FORTIFY_SOURCE
+#error "_FORTIFY_SOURCE must be defined"
+#endif
+
+#include <sys/cdefs.h>
+
+// This is a test specifically of bionic's FORTIFY machinery. Other stdlibs need not apply.
+#ifndef __BIONIC__
+// expected-no-diagnostics
+#else
+
+// As alluded to above, we're going to be doing some obviously very broken things in this file.
+// FORTIFY helpfully flags a lot of it at compile-time, but we want it to *actually* crash, too. So
+// let's wipe out any build-time errors.
+#ifndef COMPILATION_TESTS
+#undef __clang_error_if
+#define __clang_error_if(...)
+#undef __clang_warning_if
+#define __clang_warning_if(...)
+#endif
+
+#include <err.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <wchar.h>
+
+#ifndef COMPILATION_TESTS
+#include <gtest/gtest.h>
+#include "BionicDeathTest.h"
+
+#define CONCAT2(x, y) x##y
+#define CONCAT(x, y) CONCAT2(x, y)
+#define FORTIFY_TEST_NAME CONCAT(clang_fortify_test_, _FORTIFY_SOURCE)
+
+namespace {
+struct FORTIFY_TEST_NAME : BionicDeathTest {
+ protected:
+  void SetUp() override {
+    stdin_saved = dup(STDIN_FILENO);
+    if (stdin_saved < 0) err(1, "failed to dup stdin");
+
+    int devnull = open("/dev/null", O_RDONLY);
+    if (devnull < 0) err(1, "failed to open /dev/null");
+
+    if (!dup2(devnull, STDIN_FILENO)) err(1, "failed to overwrite stdin");
+    static_cast<void>(close(devnull));
+
+    BionicDeathTest::SetUp();
+  }
+
+  void TearDown() override {
+    if (stdin_saved == -1) return;
+    if (!dup2(stdin_saved, STDIN_FILENO)) warn("failed to restore stdin");
+
+    static_cast<void>(close(stdin_saved));
+
+    BionicDeathTest::TearDown();
+  }
+
+ private:
+  int stdin_saved = -1;
+};
+}  // namespace
+
+template <typename Fn>
+__attribute__((noreturn)) static void ExitAfter(Fn&& f) {
+  f();
+  // No need to tear things down; our parent process should handle that.
+  _exit(0);
+}
+
+// In any case (including failing tests), we always want to die after this.
+#define DIE_WITH(expr, cond, regex) EXPECT_EXIT(ExitAfter([&] { (expr); }), cond, regex)
+
+// EXPECT_NO_DEATH forks so that the test remains alive on a bug, and so that the environment
+// doesn't get modified on no bug. (Environment modification is especially tricky to deal with given
+// the *_STRUCT variants below.)
+#define EXPECT_NO_DEATH(expr) DIE_WITH(expr, testing::ExitedWithCode(0), "")
+#define EXPECT_FORTIFY_DEATH(expr) DIE_WITH(expr, testing::KilledBySignal(SIGABRT), "FORTIFY")
+// Expecting death, but only if we're doing a "strict" struct-checking mode.
+#if _FORTIFY_SOURCE > 1
+#define EXPECT_FORTIFY_DEATH_STRUCT EXPECT_FORTIFY_DEATH
+#else
+#define EXPECT_FORTIFY_DEATH_STRUCT EXPECT_NO_DEATH
+#endif
+
+#define FORTIFY_TEST(test_name) TEST(FORTIFY_TEST_NAME, test_name)
+
+#else  // defined(COMPILATION_TESTS)
+
+#define EXPECT_NO_DEATH(expr) expr
+#define EXPECT_FORTIFY_DEATH(expr) expr
+#define EXPECT_FORTIFY_DEATH_STRUCT EXPECT_FORTIFY_DEATH
+#define FORTIFY_TEST(test_name) void test_name()
+#endif
+
+const static int kBogusFD = -1;
+
+FORTIFY_TEST(string) {
+  char small_buffer[8] = {};
+
+  {
+    char large_buffer[sizeof(small_buffer) + 1] = {};
+    // expected-error@+1{{size bigger than buffer}}
+    EXPECT_FORTIFY_DEATH(memcpy(small_buffer, large_buffer, sizeof(large_buffer)));
+    // expected-error@+1{{size bigger than buffer}}
+    EXPECT_FORTIFY_DEATH(memmove(small_buffer, large_buffer, sizeof(large_buffer)));
+    // FIXME: this should be EXPECT_FORTIFY_DEATH
+#if 0
+    // expected-error@+1{{called with bigger length than the destination}}
+#endif
+    EXPECT_NO_DEATH(mempcpy(small_buffer, large_buffer, sizeof(large_buffer)));
+    // expected-error@+1{{size bigger than buffer}}
+    EXPECT_FORTIFY_DEATH(memset(small_buffer, 0, sizeof(large_buffer)));
+    // expected-warning@+1{{arguments got flipped?}}
+    EXPECT_NO_DEATH(memset(small_buffer, sizeof(small_buffer), 0));
+    // FIXME: Should these be warnings?
+    // expected-warning@+1{{will always overflow}}
+    EXPECT_FORTIFY_DEATH(bcopy(large_buffer, small_buffer, sizeof(large_buffer)));
+    // expected-warning@+1{{will always overflow}}
+    EXPECT_FORTIFY_DEATH(bzero(small_buffer, sizeof(large_buffer)));
+  }
+
+  {
+    const char large_string[] = "Hello!!!";
+    static_assert(sizeof(large_string) > sizeof(small_buffer), "");
+
+    // expected-error@+1{{string bigger than buffer}}
+    EXPECT_FORTIFY_DEATH(strcpy(small_buffer, large_string));
+    // expected-error@+1{{string bigger than buffer}}
+    EXPECT_FORTIFY_DEATH(stpcpy(small_buffer, large_string));
+#if 0
+    // expected-error@+1{{called with bigger length than the destination}}
+#endif
+    EXPECT_FORTIFY_DEATH(strncpy(small_buffer, large_string, sizeof(large_string)));
+#if 0
+    // expected-error@+1{{called with bigger length than the destination}}
+#endif
+    EXPECT_FORTIFY_DEATH(stpncpy(small_buffer, large_string, sizeof(large_string)));
+#if 0
+    // expected-error@+1{{destination buffer will always be overflown}}
+#endif
+    EXPECT_FORTIFY_DEATH(strcat(small_buffer, large_string));
+#if 0
+    // expected-error@+1{{destination buffer will always be overflown}}
+#endif
+    EXPECT_FORTIFY_DEATH(strncat(small_buffer, large_string, sizeof(large_string)));
+  }
+
+  {
+    struct {
+      char tiny_buffer[4];
+      char tiny_buffer2[4];
+    } split = {};
+
+    EXPECT_NO_DEATH(memcpy(split.tiny_buffer, &split, sizeof(split)));
+    EXPECT_NO_DEATH(memcpy(split.tiny_buffer, &split, sizeof(split)));
+    EXPECT_NO_DEATH(memmove(split.tiny_buffer, &split, sizeof(split)));
+    EXPECT_NO_DEATH(mempcpy(split.tiny_buffer, &split, sizeof(split)));
+    EXPECT_NO_DEATH(memset(split.tiny_buffer, 0, sizeof(split)));
+
+    EXPECT_NO_DEATH(bcopy(&split, split.tiny_buffer, sizeof(split)));
+    EXPECT_NO_DEATH(bzero(split.tiny_buffer, sizeof(split)));
+
+    const char small_string[] = "Hi!!";
+    static_assert(sizeof(small_string) > sizeof(split.tiny_buffer), "");
+
+#if _FORTIFY_SOURCE > 1
+    // expected-error@+2{{string bigger than buffer}}
+#endif
+    EXPECT_FORTIFY_DEATH_STRUCT(strcpy(split.tiny_buffer, small_string));
+
+#if _FORTIFY_SOURCE > 1
+    // expected-error@+2{{string bigger than buffer}}
+#endif
+    EXPECT_FORTIFY_DEATH_STRUCT(stpcpy(split.tiny_buffer, small_string));
+
+#if _FORTIFY_SOURCE > 1
+#if 0
+    // expected-error@+2{{called with bigger length than the destination}}
+#endif
+#endif
+    EXPECT_FORTIFY_DEATH_STRUCT(strncpy(split.tiny_buffer, small_string, sizeof(small_string)));
+
+#if _FORTIFY_SOURCE > 1
+#if 0
+    // expected-error@+2{{called with bigger length than the destination}}
+#endif
+#endif
+    EXPECT_FORTIFY_DEATH_STRUCT(stpncpy(split.tiny_buffer, small_string, sizeof(small_string)));
+
+#if _FORTIFY_SOURCE > 1
+#if 0
+    // expected-error@+2{{destination buffer will always be overflown}}
+#endif
+#endif
+    EXPECT_FORTIFY_DEATH_STRUCT(strcat(split.tiny_buffer, small_string));
+
+#if _FORTIFY_SOURCE > 1
+#if 0
+    // expected-error@+2{{destination buffer will always be overflown}}
+#endif
+#endif
+    EXPECT_FORTIFY_DEATH_STRUCT(strncat(split.tiny_buffer, small_string, sizeof(small_string)));
+  }
+}
+
+// Since these emit hard errors, it's sort of hard to run them...
+#ifdef COMPILATION_TESTS
+namespace compilation_tests {
+template <typename T>
+static T declval() {
+  __builtin_unreachable();
+}
+
+static void testFcntl() {
+  // expected-error@+1{{too many arguments}}
+  open("/", 0, 0, 0);
+#if 0
+  // expected-error@+1{{either with 2 or 3 arguments, not more}}
+#endif
+  open64("/", 0, 0, 0);
+  // expected-error@+1{{too many arguments}}
+  openat(0, "/", 0, 0, 0);
+#if 0
+  // expected-error@+1{{either with 3 or 4 arguments, not more}}
+#endif
+  openat64(0, "/", 0, 0, 0);
+
+  // expected-error@+1{{missing mode}}
+  open("/", O_CREAT);
+  // expected-error@+1{{missing mode}}
+  open("/", O_TMPFILE);
+#if 0
+  // expected-error@+1{{needs 3 arguments}}
+#endif
+  open64("/", O_CREAT);
+#if 0
+  // expected-error@+1{{needs 3 arguments}}
+#endif
+  open64("/", O_TMPFILE);
+  // expected-error@+1{{missing mode}}
+  openat(0, "/", O_CREAT);
+  // expected-error@+1{{missing mode}}
+  openat(0, "/", O_TMPFILE);
+#if 0
+  // expected-error@+1{{needs 4 arguments}}
+#endif
+  openat64(0, "/", O_CREAT);
+#if 0
+  // expected-error@+1{{needs 4 arguments}}
+#endif
+  openat64(0, "/", O_TMPFILE);
+
+  // Superfluous modes are sometimes bugs, but not often enough to complain
+  // about, apparently.
+}
+
+static void testFormatStrings() {
+  const auto unsigned_value = declval<unsigned long long>();
+  const auto* unknown_string = declval<const char*>();
+  const auto va = declval<va_list>();
+
+  {
+    auto some_fd = declval<int>();
+    // expected-warning@+1{{format specifies type 'int'}}
+    dprintf(some_fd, "%d", unsigned_value);
+    // expected-warning@+1{{format string is not a string literal}}
+    dprintf(some_fd, unknown_string, unsigned_value);
+    // expected-warning@+1{{format string is not a string literal}}
+    vdprintf(1, unknown_string, va);
+  }
+
+  {
+    auto* retval = declval<char*>();
+#if 0
+    // expected-error@+2{{ignoring return value}}
+#endif
+    // expected-warning@+1{{format specifies type 'int'}}
+    asprintf(&retval, "%d", unsigned_value);
+#if 0
+    // expected-error@+2{{ignoring return value}}
+#endif
+    // expected-warning@+1{{format string is not a string literal}}
+    asprintf(&retval, unknown_string, unsigned_value);
+#if 0
+    // expected-error@+2{{ignoring return value}}
+#endif
+    // expected-warning@+1{{format string is not a string literal}}
+    vasprintf(&retval, unknown_string, va);
+  }
+
+  // expected-warning@+1{{format specifies type 'int'}}
+  syslog(0, "%d", unsigned_value);
+  // expected-warning@+1{{format string is not a string literal}}
+  syslog(0, unknown_string, unsigned_value);
+  // expected-warning@+1{{format string is not a string literal}}
+  vsyslog(0, unknown_string, va);
+
+  {
+    auto* file = declval<FILE*>();
+    // expected-warning@+1{{format specifies type 'int'}}
+    fprintf(file, "%d", unsigned_value);
+    // expected-warning@+1{{format string is not a string literal}}
+    fprintf(file, unknown_string, unsigned_value);
+    // expected-warning@+1{{format string is not a string literal}}
+    vfprintf(file, unknown_string, va);
+  }
+
+  // expected-warning@+1{{format specifies type 'int'}}
+  printf("%d", unsigned_value);
+  // expected-warning@+1{{format string is not a string literal}}
+  printf(unknown_string, unsigned_value);
+  // expected-warning@+1{{format string is not a string literal}}
+  vprintf(unknown_string, va);
+
+  {
+    char buf[128];
+    // expected-warning@+1{{format specifies type 'int'}}
+    sprintf(buf, "%d", unsigned_value);
+    // expected-warning@+1{{format string is not a string literal}}
+    sprintf(buf, unknown_string, unsigned_value);
+    // expected-warning@+1{{format string is not a string literal}}
+    sprintf(buf, unknown_string, va);
+
+    // expected-warning@+1{{format specifies type 'int'}}
+    snprintf(buf, sizeof(buf), "%d", unsigned_value);
+    // expected-warning@+1{{format string is not a string literal}}
+    snprintf(buf, sizeof(buf), unknown_string, unsigned_value);
+    // expected-warning@+1{{format string is not a string literal}}
+    vsnprintf(buf, sizeof(buf), unknown_string, va);
+  }
+
+  // FIXME: below are general format string cases where clang should probably try to warn.
+  {
+    char buf[4];
+    sprintf(buf, "%s", "1234");
+    sprintf(buf, "1%s4", "23");
+    sprintf(buf, "%d", 1234);
+
+    // Similar thoughts for strncpy, etc.
+  }
+}
+
+static void testStdlib() {
+  char path_buffer[PATH_MAX - 1];
+#if 0
+  // expected-error@+2{{ignoring return value of function}}
+#endif
+  // expected-error@+1{{must be NULL or a pointer to a buffer with >= PATH_MAX bytes}}
+  realpath("/", path_buffer);
+#if 0
+    // expected-error@+1{{ignoring return value of function}}
+#endif
+  realpath("/", nullptr);
+
+  // FIXME: This should complain about flipped arguments, instead of objectsize.
+  // expected-error@+1{{must be NULL or a pointer to a buffer with >= PATH_MAX bytes}}
+  realpath(nullptr, path_buffer);
+
+  // expected-error@+1{{flipped arguments?}}
+  realpath(nullptr, nullptr);
+}
+}  // namespace compilation_tests
+#endif
+
+FORTIFY_TEST(poll) {
+  int pipe_fds[2];
+  if (pipe(pipe_fds)) err(1, "pipe failed");
+
+  // after this, pipe_fds[0] should always report RDHUP
+  if (close(pipe_fds[1])) err(1, "close failed");
+
+  struct pollfd poll_fd = { pipe_fds[0], POLLRDHUP, 0 };
+  {
+    struct pollfd few_fds[] = { poll_fd, poll_fd };
+    // expected-error@+1{{fd_count is larger than the given buffer}}
+    EXPECT_FORTIFY_DEATH(poll(few_fds, 3, 0));
+    // expected-error@+1{{fd_count is larger than the given buffer}}
+    EXPECT_FORTIFY_DEATH(ppoll(few_fds, 3, 0, 0));
+    // expected-error@+1{{fd_count is larger than the given buffer}}
+    EXPECT_FORTIFY_DEATH(ppoll64(few_fds, 3, 0, nullptr));
+  }
+
+  {
+    struct {
+      struct pollfd few[2];
+      struct pollfd extra[1];
+    } fds = { { poll_fd, poll_fd }, { poll_fd } };
+    static_assert(sizeof(fds) >= sizeof(struct pollfd) * 3, "");
+
+#if _FORTIFY_SOURCE > 1
+    // expected-error@+2{{fd_count is larger than the given buffer}}
+#endif
+    EXPECT_FORTIFY_DEATH_STRUCT(poll(fds.few, 3, 0));
+
+    struct timespec timeout = {};
+#if _FORTIFY_SOURCE > 1
+    // expected-error@+2{{fd_count is larger than the given buffer}}
+#endif
+    EXPECT_FORTIFY_DEATH_STRUCT(ppoll(fds.few, 3, &timeout, 0));
+
+#if _FORTIFY_SOURCE > 1
+    // expected-error@+2{{fd_count is larger than the given buffer}}
+#endif
+    EXPECT_FORTIFY_DEATH_STRUCT(ppoll64(fds.few, 3, 0, nullptr));
+  }
+}
+
+FORTIFY_TEST(socket) {
+  {
+    char small_buffer[8];
+    // expected-error@+1{{size bigger than buffer}}
+    EXPECT_FORTIFY_DEATH(recv(kBogusFD, small_buffer, sizeof(small_buffer) + 1, 0));
+    // expected-error@+1{{size bigger than buffer}}
+    EXPECT_FORTIFY_DEATH(recvfrom(kBogusFD, small_buffer, sizeof(small_buffer) + 1, 0, 0, 0));
+
+    // expected-error@+1{{size bigger than buffer}}
+    EXPECT_FORTIFY_DEATH(send(kBogusFD, small_buffer, sizeof(small_buffer) + 1, 0));
+    // expected-error@+1{{size bigger than buffer}}
+    EXPECT_FORTIFY_DEATH(sendto(kBogusFD, small_buffer, sizeof(small_buffer) + 1, 0, 0, 0));
+  }
+
+  {
+    struct {
+      char tiny_buffer[4];
+      char tiny_buffer2;
+    } split = {};
+
+    EXPECT_NO_DEATH(recv(kBogusFD, split.tiny_buffer, sizeof(split), 0));
+    EXPECT_NO_DEATH(recvfrom(kBogusFD, split.tiny_buffer, sizeof(split), 0, 0, 0));
+  }
+}
+
+FORTIFY_TEST(sys_stat) {
+  // expected-error@+1{{'umask' called with invalid mode}}
+  EXPECT_FORTIFY_DEATH(umask(01777));
+}
+
+FORTIFY_TEST(stdio) {
+  char small_buffer[8] = {};
+  {
+#if 0
+    // expected-error@+1{{may overflow the destination buffer}}
+#endif
+    EXPECT_FORTIFY_DEATH(snprintf(small_buffer, sizeof(small_buffer) + 1, ""));
+
+    va_list va;
+#if 0
+    // expected-error@+1{{may overflow the destination buffer}}
+#endif
+    // expected-warning@+1{{format string is empty}}
+    EXPECT_FORTIFY_DEATH(vsnprintf(small_buffer, sizeof(small_buffer) + 1, "", va));
+  }
+
+  // expected-error@+1{{size should not be negative}}
+  EXPECT_FORTIFY_DEATH(fgets(small_buffer, -1, stdin));
+  // expected-error@+1{{size is larger than the destination buffer}}
+  EXPECT_FORTIFY_DEATH(fgets(small_buffer, sizeof(small_buffer) + 1, stdin));
+
+  // expected-error@+1{{size * count overflows}}
+  EXPECT_NO_DEATH(fread(small_buffer, 2, (size_t)-1, stdin));
+  // expected-error@+1{{size * count is too large for the given buffer}}
+  EXPECT_FORTIFY_DEATH(fread(small_buffer, 1, sizeof(small_buffer) + 1, stdin));
+
+  // expected-error@+1{{size * count overflows}}
+  EXPECT_NO_DEATH(fwrite(small_buffer, 2, (size_t)-1, stdout));
+  // expected-error@+1{{size * count is too large for the given buffer}}
+  EXPECT_FORTIFY_DEATH(fwrite(small_buffer, 1, sizeof(small_buffer) + 1, stdout));
+}
+
+FORTIFY_TEST(unistd) {
+  char small_buffer[8];
+
+  // Return value warnings are (sort of) a part of FORTIFY, so we don't ignore them.
+#if 0
+  // expected-error@+2{{ignoring return value of function}}
+#endif
+  // expected-error@+1{{bytes overflows the given object}}
+  EXPECT_FORTIFY_DEATH(read(kBogusFD, small_buffer, sizeof(small_buffer) + 1));
+#if 0
+  // expected-error@+2{{ignoring return value of function}}
+#endif
+  // expected-error@+1{{bytes overflows the given object}}
+  EXPECT_FORTIFY_DEATH(pread(kBogusFD, small_buffer, sizeof(small_buffer) + 1, 0));
+#if 0
+  // expected-error@+2{{ignoring return value of function}}
+#endif
+  // expected-error@+1{{bytes overflows the given object}}
+  EXPECT_FORTIFY_DEATH(pread64(kBogusFD, small_buffer, sizeof(small_buffer) + 1, 0));
+#if 0
+  // expected-error@+2{{ignoring return value of function}}
+#endif
+  // expected-error@+1{{bytes overflows the given object}}
+  EXPECT_FORTIFY_DEATH(write(kBogusFD, small_buffer, sizeof(small_buffer) + 1));
+#if 0
+  // expected-error@+2{{ignoring return value of function}}
+#endif
+  // expected-error@+1{{bytes overflows the given object}}
+  EXPECT_FORTIFY_DEATH(pwrite(kBogusFD, small_buffer, sizeof(small_buffer) + 1, 0));
+#if 0
+  // expected-error@+2{{ignoring return value of function}}
+#endif
+  // expected-error@+1{{bytes overflows the given object}}
+  EXPECT_FORTIFY_DEATH(pwrite64(kBogusFD, small_buffer, sizeof(small_buffer) + 1, 0));
+#if 0
+  // expected-error@+2{{ignoring return value of function}}
+#endif
+  // expected-error@+1{{bytes overflows the given object}}
+  EXPECT_FORTIFY_DEATH(readlink("/", small_buffer, sizeof(small_buffer) + 1));
+#if 0
+  // expected-error@+2{{ignoring return value of function}}
+#endif
+  // expected-error@+1{{bytes overflows the given object}}
+  EXPECT_FORTIFY_DEATH(getcwd(small_buffer, sizeof(small_buffer) + 1));
+
+  // getcwd allocates and returns a buffer if you pass null to getcwd
+  EXPECT_NO_DEATH(getcwd(nullptr, 0));
+  EXPECT_NO_DEATH(getcwd(nullptr, 4096));
+
+  struct {
+    char tiny_buffer[4];
+    char tiny_buffer2[4];
+  } split;
+
+  EXPECT_NO_DEATH(read(kBogusFD, split.tiny_buffer, sizeof(split)));
+  EXPECT_NO_DEATH(pread(kBogusFD, split.tiny_buffer, sizeof(split), 0));
+  EXPECT_NO_DEATH(pread64(kBogusFD, split.tiny_buffer, sizeof(split), 0));
+  EXPECT_NO_DEATH(write(kBogusFD, split.tiny_buffer, sizeof(split)));
+  EXPECT_NO_DEATH(pwrite(kBogusFD, split.tiny_buffer, sizeof(split), 0));
+  EXPECT_NO_DEATH(pwrite64(kBogusFD, split.tiny_buffer, sizeof(split), 0));
+
+#if _FORTIFY_SOURCE > 1
+  // expected-error@+2{{bytes overflows the given object}}
+#endif
+  EXPECT_FORTIFY_DEATH_STRUCT(readlink("/", split.tiny_buffer, sizeof(split)));
+#if _FORTIFY_SOURCE > 1
+  // expected-error@+2{{bytes overflows the given object}}
+#endif
+  EXPECT_FORTIFY_DEATH_STRUCT(getcwd(split.tiny_buffer, sizeof(split)));
+
+  {
+    // FIXME: These should all die in FORTIFY. Headers are bugged.
+#ifdef COMPILATION_TESTS
+    char* volatile unknown = small_buffer;
+    const size_t count = static_cast<size_t>(SSIZE_MAX) + 1;
+    // expected-error@+1{{'count' must be <= SSIZE_MAX}}
+    EXPECT_FORTIFY_DEATH(read(kBogusFD, unknown, count));
+    // expected-error@+1{{'count' must be <= SSIZE_MAX}}
+    EXPECT_FORTIFY_DEATH(pread(kBogusFD, unknown, count, 0));
+    // expected-error@+1{{'count' must be <= SSIZE_MAX}}
+    EXPECT_FORTIFY_DEATH(pread64(kBogusFD, unknown, count, 0));
+    // expected-error@+1{{'count' must be <= SSIZE_MAX}}
+    EXPECT_FORTIFY_DEATH(write(kBogusFD, unknown, count));
+    // expected-error@+1{{'count' must be <= SSIZE_MAX}}
+    EXPECT_FORTIFY_DEATH(pwrite(kBogusFD, unknown, count, 0));
+    // expected-error@+1{{'count' must be <= SSIZE_MAX}}
+    EXPECT_FORTIFY_DEATH(pwrite64(kBogusFD, unknown, count, 0));
+#endif
+  }
+}
+
+#endif  // defined(__BIONIC__)
diff --git a/tests/fortify_filecheck_diagnostics_test.cpp b/tests/fortify_filecheck_diagnostics_test.cpp
deleted file mode 100644
index ac9853c..0000000
--- a/tests/fortify_filecheck_diagnostics_test.cpp
+++ /dev/null
@@ -1,323 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/*
- * Silence all notes about enable_if-related 'candidates'; they're nice to know
- * about for users, but this test doesn't care.
- */
-// expected-note@* 0+{{candidate function}}
-
-/* Similarly, ignore all "from 'diagnose_if'"s. */
-// expected-note@* 0+{{from 'diagnose_if'}}
-
-
-#undef _FORTIFY_SOURCE
-#define _FORTIFY_SOURCE 2
-#include <fcntl.h>
-#include <netinet/in.h>
-#include <poll.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <time.h>
-#include <unistd.h>
-
-#if !defined(__BIONIC__)
-#  error "This only works with Bionic."
-#endif
-
-void test_sprintf() {
-  char buf[4];
-
-  // NOLINTNEXTLINE(whitespace/line_length)
-  // expected-error@+1{{call to unavailable function 'sprintf': format string will always overflow destination buffer}}
-  sprintf(buf, "foobar");  // NOLINT(runtime/printf)
-
-  // TODO: clang should emit a warning, but doesn't
-  sprintf(buf, "%s", "foobar");  // NOLINT(runtime/printf)
-}
-
-void test_snprintf() {
-  char buf[4];
-
-  // NOLINTNEXTLINE(whitespace/line_length)
-  // expected-error@+1{{call to unavailable function 'snprintf': format string will always overflow destination buffer}}
-  snprintf(buf, 5, "foobar");  // NOLINT(runtime/printf)
-
-  // TODO: clang should emit a warning, but doesn't
-  snprintf(buf, 5, "%s", "foobar");  // NOLINT(runtime/printf)
-
-  // TODO: clang should emit a warning, but doesn't
-  snprintf(buf, 5, " %s ", "foobar");  // NOLINT(runtime/printf)
-
-  // TODO: clang should emit a warning, but doesn't
-  snprintf(buf, 5, "%d", 100000);  // NOLINT(runtime/printf)
-}
-
-void test_memcpy() {
-  char buf[4];
-
-  // expected-error@+1{{'memcpy' called with size bigger than buffer}}
-  memcpy(buf, "foobar", sizeof("foobar") + 100);
-}
-
-void test_memmove() {
-  char buf[4];
-
-  // expected-error@+1{{'memmove' called with size bigger than buffer}}
-  memmove(buf, "foobar", sizeof("foobar"));
-}
-
-void test_memset() {
-  char buf[4];
-
-  // expected-error@+1{{'memset' called with size bigger than buffer}}
-  memset(buf, 0, 6);
-}
-
-void test_strcpy() {
-  char buf[4];
-
-  // expected-error@+1{{'strcpy' called with string bigger than buffer}}
-  strcpy(buf, "foobar");  // NOLINT(runtime/printf)
-
-  // expected-error@+1{{'strcpy' called with string bigger than buffer}}
-  strcpy(buf, "quux");
-}
-
-void test_stpcpy() {
-  char buf[4];
-
-  // expected-error@+1{{'stpcpy' called with string bigger than buffer}}
-  stpcpy(buf, "foobar");
-
-  // expected-error@+1{{'stpcpy' called with string bigger than buffer}}
-  stpcpy(buf, "quux");
-}
-
-void test_strncpy() {
-  char buf[4];
-
-  // TODO: clang should emit a warning, but doesn't
-  strncpy(buf, "foobar", sizeof("foobar"));
-}
-
-void test_strcat() {
-  char buf[4] = "";
-
-  // TODO: clang should emit a warning, but doesn't
-  strcat(buf, "foobar");  // NOLINT(runtime/printf)
-}
-
-void test_strncat() {
-  char buf[4] = "";
-
-  // TODO: clang should emit a warning, but doesn't
-  strncat(buf, "foobar", sizeof("foobar"));
-}
-
-void test_vsprintf(const char* fmt, ...) {
-  va_list va;
-  char buf[4];
-  va_start(va, fmt);
-
-  // clang should emit a warning, but doesn't
-  vsprintf(buf, "foobar", va);
-  va_end(va);
-}
-
-void test_vsnprintf(const char* fmt, ...) {
-  va_list va;
-  char buf[4];
-  va_start(va, fmt);
-
-  // clang should emit a warning, but doesn't
-  vsnprintf(buf, 5, "foobar", va);  // NOLINT(runtime/printf)
-
-  va_end(va);
-}
-
-void test_fgets() {
-  char buf[4];
-
-  // expected-error@+1{{in call to 'fgets', size should not be negative}}
-  fgets(buf, -1, stdin);
-
-  // expected-error@+1{{in call to 'fgets', size is larger than the destination buffer}}
-  fgets(buf, 6, stdin);
-}
-
-void test_recvfrom() {
-  char buf[4];
-  sockaddr_in addr;
-
-  // expected-error@+1{{'recvfrom' called with size bigger than buffer}}
-  recvfrom(0, buf, 6, 0, reinterpret_cast<sockaddr*>(&addr), nullptr);
-}
-
-void test_recv() {
-  char buf[4] = {0};
-
-  // expected-error@+1{{'recv' called with size bigger than buffer}}
-  recv(0, buf, 6, 0);
-}
-
-void test_umask() {
-  // expected-error@+1{{'umask' called with invalid mode}}
-  umask(01777);
-}
-
-void test_read() {
-  char buf[4];
-  // expected-error@+1{{in call to 'read', 'count' bytes overflows the given object}}
-  read(0, buf, 6);
-}
-
-void test_open() {
-  // expected-error@+1{{'open' called with O_CREAT or O_TMPFILE, but missing mode}}
-  open("/dev/null", O_CREAT);
-
-  // expected-error@+1{{'open' called with O_CREAT or O_TMPFILE, but missing mode}}
-  open("/dev/null", O_TMPFILE);
-
-  // expected-error@+1{{call to unavailable function 'open': too many arguments}}
-  open("/dev/null", O_CREAT, 0, 0);
-
-  // expected-error@+1{{call to unavailable function 'open': too many arguments}}
-  open("/dev/null", O_TMPFILE, 0, 0);
-
-  // expected-warning@+1{{'open' has superfluous mode bits; missing O_CREAT?}}
-  open("/dev/null", O_RDONLY, 0644);
-
-  // expected-warning@+1{{'open' has superfluous mode bits; missing O_CREAT?}}
-  open("/dev/null", O_DIRECTORY, 0644);
-}
-
-void test_poll() {
-  pollfd fds[1];
-  // expected-error@+1{{in call to 'poll', fd_count is larger than the given buffer}}
-  poll(fds, 2, 0);
-}
-
-void test_ppoll() {
-  pollfd fds[1];
-  timespec timeout;
-  // expected-error@+1{{in call to 'ppoll', fd_count is larger than the given buffer}}
-  ppoll(fds, 2, &timeout, nullptr);
-}
-
-void test_ppoll64() {
-  pollfd fds[1];
-  timespec timeout;
-  // NOLINTNEXTLINE(whitespace/line_length)
-  // expected-error@+1{{in call to 'ppoll64', fd_count is larger than the given buffer}}
-  ppoll64(fds, 2, &timeout, nullptr);
-}
-
-void test_fread_overflow() {
-  char buf[4];
-  // expected-error@+1{{in call to 'fread', size * count overflows}}
-  fread(buf, 2, (size_t)-1, stdin);
-}
-
-void test_fread_too_big() {
-  char buf[4];
-  // NOLINTNEXTLINE(whitespace/line_length)
-  // expected-error@+1{{in call to 'fread', size * count is too large for the given buffer}}
-  fread(buf, 1, 5, stdin);
-}
-
-void test_fwrite_overflow() {
-  char buf[4] = {0};
-  // expected-error@+1{{in call to 'fwrite', size * count overflows}}
-  fwrite(buf, 2, (size_t)-1, stdout);
-}
-
-void test_fwrite_too_big() {
-  char buf[4] = {0};
-  // NOLINTNEXTLINE(whitespace/line_length)
-  // expected-error@+1{{in call to 'fwrite', size * count is too large for the given buffer}}
-  fwrite(buf, 1, 5, stdout);
-}
-
-void test_getcwd() {
-  char buf[4];
-  // expected-error@+1{{in call to 'getcwd', 'size' bytes overflows the given object}}
-  getcwd(buf, 5);
-}
-
-void test_pwrite64_size() {
-  char buf[4] = {0};
-  // expected-error@+1{{in call to 'pwrite64', 'count' bytes overflows the given object}}
-  pwrite64(STDOUT_FILENO, buf, 5, 0);
-}
-
-void test_pwrite64_too_big_malloc() {
-  void *buf = calloc(atoi("5"), 1);
-  // expected-error@+1{{in call to 'pwrite64', 'count' must be <= SSIZE_MAX}}
-  pwrite64(STDOUT_FILENO, buf, SIZE_MAX, 0);
-}
-
-void test_pwrite64_too_big() {
-  char buf[4] = {0};
-  // expected-error@+1{{in call to 'pwrite64', 'count' must be <= SSIZE_MAX}}
-  pwrite64(STDOUT_FILENO, buf, SIZE_MAX, 0);
-}
-
-void test_write_size() {
-  char buf[4] = {0};
-  // expected-error@+1{{in call to 'write', 'count' bytes overflows the given object}}
-  write(STDOUT_FILENO, buf, 5);
-}
-
-void test_memset_args_flipped() {
-  char from[4] = {0};
-  // NOLINTNEXTLINE(whitespace/line_length)
-  // expected-warning@+1{{'memset' will set 0 bytes; maybe the arguments got flipped?}}
-  memset(from, sizeof(from), 0);
-}
-
-void test_sendto() {
-  char buf[4] = {0};
-  sockaddr_in addr;
-
-  // expected-error@+1{{'sendto' called with size bigger than buffer}}
-  sendto(0, buf, 6, 0, reinterpret_cast<sockaddr*>(&addr), sizeof(sockaddr_in));
-}
-
-void test_send() {
-  char buf[4] = {0};
-
-  // expected-error@+1{{'send' called with size bigger than buffer}}
-  send(0, buf, 6, 0);
-}
-
-void test_realpath() {
-  char buf[4] = {0};
-  // NOLINTNEXTLINE(whitespace/line_length)
-  // expected-error@+1{{'realpath' output parameter must be NULL or a pointer to a buffer with >= PATH_MAX bytes}}
-  realpath(".", buf);
-
-  // This is fine.
-  realpath(".", nullptr);
-
-  char bigbuf[PATH_MAX];
-  // expected-error@+1{{'realpath': NULL path is never correct; flipped arguments?}}
-  realpath(nullptr, bigbuf);
-}
diff --git a/tests/make_fortify_compile_test.mk b/tests/make_fortify_compile_test.mk
new file mode 100644
index 0000000..8270f29
--- /dev/null
+++ b/tests/make_fortify_compile_test.mk
@@ -0,0 +1,20 @@
+include $(CLEAR_VARS)
+
+LOCAL_ADDITIONAL_DEPENDENCIES := \
+    $(LOCAL_PATH)/Android.mk \
+    $(LOCAL_PATH)/touch-obj-on-success
+
+LOCAL_CXX := $(LOCAL_PATH)/touch-obj-on-success \
+    $(LLVM_PREBUILTS_PATH)/clang++ \
+
+LOCAL_CLANG := true
+LOCAL_MODULE := bionic-compile-time-tests$(FORTIFY_LEVEL)-clang++
+LOCAL_CPPFLAGS := -Wall -Wno-error
+LOCAL_CPPFLAGS += -fno-color-diagnostics -ferror-limit=10000 -Xclang -verify
+LOCAL_CPPFLAGS += -DCOMPILATION_TESTS=1 -Wformat-nonliteral
+LOCAL_CPPFLAGS += -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=$(FORTIFY_LEVEL)
+LOCAL_SRC_FILES := clang_fortify_tests.cpp
+
+include $(BUILD_STATIC_LIBRARY)
+
+FORTIFY_LEVEL :=