// // Clang compile-time and run-time tests for Bionic's FORTIFY. //
// This file is compiled in two configurations to give us reasonable coverage of clang's // FORTIFY implementation: // // 1. For compile-time checks, we use clang's diagnostic consumer // (https://clang.llvm.org/doxygen/classclang_1_1VerifyDiagnosticConsumer.html#details) // to check diagnostics (e.g. the expected-* comments everywhere). // // 2. For run-time checks, we build and run as regular gtests (as described in // bionic/README.md).
// Note that these tests do things like leaking memory. That's WAI.
// // Configuration for the compile-time checks. (These comments have side effects!) // // 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}} // // And finally, all explicitly-unavailable-here complaints from headers are // uninteresting // expected-note@* 0+{{has been explicitly marked unavailable here}} // // Note that some of these diagnostics come from clang itself, while others come from // `diagnose_if`s sprinkled throughout Bionic.
#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(...) #pragma clang diagnostic ignored "-Wfortify-source"
// SOMETIMES_CONST allows clang to emit eager diagnostics when we're doing compilation tests, but // blocks them otherwise. This is needed for diagnostics emitted with __enable_if. #define SOMETIMES_CONST volatile #else #define SOMETIMES_CONST const #endif
template <typename Fn>
__attribute__((noreturn)) staticvoid 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
FORTIFY_TEST(strlen) { auto run_strlen_with_contents = [&](std::array<char, 3> contents) { // A lot of cruft is necessary to make this test DTRT. LLVM and Clang love to fold/optimize // strlen calls, and that's the opposite of what we want to happen.
// Loop to convince LLVM that `contents` can never be known (since `xor volatile_value` can flip // any bit in each elem of `contents`). volatilechar always_zero = 0; for (char& c : contents) {
c ^= always_zero;
}
android::base::DoNotOptimize(strlen(&contents.front()));
};
FORTIFY_TEST(fcntl) { constchar target[] = "/dev/null"; int dirfd = 0;
// These all emit hard errors without diagnose_if, so running them is a bit // more involved. #ifdef COMPILATION_TESTS // expected-error@+1{{too many arguments}}
open("/", 0, 0, 0); // expected-error@+1{{too many arguments}}
open64("/", 0, 0, 0); // expected-error@+1{{too many arguments}}
openat(0, "/", 0, 0, 0); // expected-error@+1{{too many arguments}}
openat64(0, "/", 0, 0, 0); #endif
// 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();
}
{ 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*>(); #if0 // expected-error@+2{{ignoring return value}} #endif // expected-warning@+1{{format specifies type 'int'}}
asprintf(&retval, "%d", unsigned_value); #if0 // expected-error@+2{{ignoring return value}} #endif // expected-warning@+1{{format string is not a string literal}}
asprintf(&retval, unknown_string, unsigned_value); #if0 // 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.
}
}
staticvoid testStdlib() { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wnonnull" char path_buffer[PATH_MAX - 1]; // expected-warning@+2{{ignoring return value of function}} // expected-error@+1{{must be NULL or a pointer to a buffer with >= PATH_MAX bytes}}
realpath("/", path_buffer); // expected-warning@+1{{ignoring return value of function}}
realpath("/", nullptr);
// expected-warning@+2{{ignoring return value of function}} // expected-error@+1{{flipped arguments?}}
realpath(nullptr, path_buffer);
// expected-warning@+2{{ignoring return value of function}} // expected-error@+1{{flipped arguments?}}
realpath(nullptr, nullptr); #pragma clang diagnostic pop
}
} // 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));
}
#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));
}
}
// expected-error@+1{{format string will always overflow}}
EXPECT_FORTIFY_DEATH(sprintf(small_buffer, format_string));
}
// 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. #if0 // 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)); #if0 // 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)); #if0 // 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)); #if0 // 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)); #if0 // 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)); #if0 // 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)); #if0 // 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)); #if0 // 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));
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.