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/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__)