forked from OSchip/llvm-project
[libFuzzer] Port to Windows
Summary: Port libFuzzer to windows-msvc. This patch allows libFuzzer targets to be built and run on Windows, using -fsanitize=fuzzer and/or fsanitize=fuzzer-no-link. It allows these forms of coverage instrumentation to work on Windows as well. It does not fix all issues, such as those with -fsanitize-coverage=stack-depth, which is not usable on Windows as of this patch. It also does not fix any libFuzzer integration tests. Nearly all of them fail to compile, fixing them will come in a later patch, so libFuzzer tests are disabled on Windows until them. Patch By: metzman Reviewers: morehouse, rnk Reviewed By: morehouse, rnk Subscribers: #sanitizers, delcypher, morehouse, kcc, eraman Differential Revision: https://reviews.llvm.org/D51022 llvm-svn: 341082
This commit is contained in:
parent
a733d08db2
commit
7e042bb1d1
|
@ -365,6 +365,17 @@ void visualstudio::Linker::ConstructJob(Compilation &C, const JobAction &JA,
|
|||
CmdArgs.push_back(Args.MakeArgString(std::string("-implib:") + ImplibName));
|
||||
}
|
||||
|
||||
if (TC.getSanitizerArgs().needsFuzzer()) {
|
||||
if (!Args.hasArg(options::OPT_shared))
|
||||
CmdArgs.push_back(
|
||||
Args.MakeArgString(std::string("-wholearchive:") +
|
||||
TC.getCompilerRTArgString(Args, "fuzzer", false)));
|
||||
CmdArgs.push_back(Args.MakeArgString("-debug"));
|
||||
// Prevent the linker from padding sections we use for instrumentation
|
||||
// arrays.
|
||||
CmdArgs.push_back(Args.MakeArgString("-incremental:no"));
|
||||
}
|
||||
|
||||
if (TC.getSanitizerArgs().needsAsanRt()) {
|
||||
CmdArgs.push_back(Args.MakeArgString("-debug"));
|
||||
CmdArgs.push_back(Args.MakeArgString("-incremental:no"));
|
||||
|
@ -1298,6 +1309,8 @@ MSVCToolChain::ComputeEffectiveClangTriple(const ArgList &Args,
|
|||
SanitizerMask MSVCToolChain::getSupportedSanitizers() const {
|
||||
SanitizerMask Res = ToolChain::getSupportedSanitizers();
|
||||
Res |= SanitizerKind::Address;
|
||||
Res |= SanitizerKind::Fuzzer;
|
||||
Res |= SanitizerKind::FuzzerNoLink;
|
||||
Res &= ~SanitizerKind::CFIMFCall;
|
||||
return Res;
|
||||
}
|
||||
|
|
|
@ -619,7 +619,10 @@ else()
|
|||
endif()
|
||||
|
||||
if (COMPILER_RT_HAS_SANITIZER_COMMON AND FUZZER_SUPPORTED_ARCH AND
|
||||
OS_NAME MATCHES "Android|Darwin|Linux|NetBSD|FreeBSD|OpenBSD|Fuchsia")
|
||||
OS_NAME MATCHES "Android|Darwin|Linux|NetBSD|FreeBSD|OpenBSD|Fuchsia|Windows" AND
|
||||
# TODO: Support builds with MSVC.
|
||||
NOT "${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC" AND
|
||||
NOT "${CMAKE_C_COMPILER_ID}" STREQUAL "MSVC")
|
||||
set(COMPILER_RT_HAS_FUZZER TRUE)
|
||||
else()
|
||||
set(COMPILER_RT_HAS_FUZZER FALSE)
|
||||
|
|
|
@ -129,8 +129,15 @@
|
|||
|
||||
#if LIBFUZZER_WINDOWS
|
||||
#define ATTRIBUTE_INTERFACE __declspec(dllexport)
|
||||
// This is used for __sancov_lowest_stack which is needed for
|
||||
// -fsanitize-coverage=stack-depth. That feature is not yet available on
|
||||
// Windows, so make the symbol static to avoid linking errors.
|
||||
#define ATTRIBUTES_INTERFACE_TLS_INITIAL_EXEC \
|
||||
__attribute__((tls_model("initial-exec"))) thread_local static
|
||||
#else
|
||||
#define ATTRIBUTE_INTERFACE __attribute__((visibility("default")))
|
||||
#define ATTRIBUTES_INTERFACE_TLS_INITIAL_EXEC \
|
||||
ATTRIBUTE_INTERFACE __attribute__((tls_model("initial-exec"))) thread_local
|
||||
#endif
|
||||
|
||||
namespace fuzzer {
|
||||
|
|
|
@ -100,14 +100,6 @@ std::string DirPlusFile(const std::string &DirPath,
|
|||
return DirPath + GetSeparator() + FileName;
|
||||
}
|
||||
|
||||
std::string Basename(const std::string &Path, char Separator) {
|
||||
size_t Pos = Path.rfind(Separator);
|
||||
if (Pos == std::string::npos)
|
||||
return Path;
|
||||
assert(Pos < Path.size());
|
||||
return Path.substr(Pos + 1);
|
||||
}
|
||||
|
||||
void DupAndCloseStderr() {
|
||||
int OutputFd = DuplicateFile(2);
|
||||
if (OutputFd > 0) {
|
||||
|
|
|
@ -68,7 +68,7 @@ void GetSizedFilesFromDir(const std::string &Dir, Vector<SizedFile> *V);
|
|||
|
||||
char GetSeparator();
|
||||
// Similar to the basename utility: returns the file name w/o the dir prefix.
|
||||
std::string Basename(const std::string &Path, char Separator = GetSeparator());
|
||||
std::string Basename(const std::string &Path);
|
||||
|
||||
FILE* OpenFile(int Fd, const char *Mode);
|
||||
|
||||
|
|
|
@ -46,6 +46,13 @@ size_t FileSize(const std::string &Path) {
|
|||
return St.st_size;
|
||||
}
|
||||
|
||||
std::string Basename(const std::string &Path) {
|
||||
size_t Pos = Path.rfind(GetSeparator());
|
||||
if (Pos == std::string::npos) return Path;
|
||||
assert(Pos < Path.size());
|
||||
return Path.substr(Pos + 1);
|
||||
}
|
||||
|
||||
void ListFilesInDirRecursive(const std::string &Dir, long *Epoch,
|
||||
Vector<std::string> *V, bool TopDir) {
|
||||
auto E = GetEpoch(Dir);
|
||||
|
|
|
@ -72,6 +72,26 @@ bool IsFile(const std::string &Path) {
|
|||
return IsFile(Path, Att);
|
||||
}
|
||||
|
||||
std::string Basename(const std::string &Path) {
|
||||
size_t Pos = Path.find_last_of("/\\");
|
||||
if (Pos == std::string::npos) return Path;
|
||||
assert(Pos < Path.size());
|
||||
return Path.substr(Pos + 1);
|
||||
}
|
||||
|
||||
size_t FileSize(const std::string &Path) {
|
||||
WIN32_FILE_ATTRIBUTE_DATA attr;
|
||||
if (!GetFileAttributesExA(Path.c_str(), GetFileExInfoStandard, &attr)) {
|
||||
Printf("GetFileAttributesExA() failed for \"%s\" (Error code: %lu).\n",
|
||||
Path.c_str(), GetLastError());
|
||||
return 0;
|
||||
}
|
||||
ULARGE_INTEGER size;
|
||||
size.HighPart = attr.nFileSizeHigh;
|
||||
size.LowPart = attr.nFileSizeLow;
|
||||
return size.QuadPart;
|
||||
}
|
||||
|
||||
void ListFilesInDirRecursive(const std::string &Dir, long *Epoch,
|
||||
Vector<std::string> *V, bool TopDir) {
|
||||
auto E = GetEpoch(Dir);
|
||||
|
|
|
@ -32,8 +32,7 @@ ATTRIBUTE_INTERFACE
|
|||
uintptr_t __sancov_trace_pc_pcs[fuzzer::TracePC::kNumPCs];
|
||||
|
||||
// Used by -fsanitize-coverage=stack-depth to track stack depth
|
||||
ATTRIBUTE_INTERFACE __attribute__((tls_model("initial-exec")))
|
||||
thread_local uintptr_t __sancov_lowest_stack;
|
||||
ATTRIBUTES_INTERFACE_TLS_INITIAL_EXEC uintptr_t __sancov_lowest_stack;
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
|
|
|
@ -179,7 +179,9 @@ const void *SearchMemory(const void *Data, size_t DataLen, const void *Patt,
|
|||
}
|
||||
|
||||
std::string DisassembleCmd(const std::string &FileName) {
|
||||
if (ExecuteCommand("dumpbin /summary > nul") == 0)
|
||||
Vector<std::string> command_vector;
|
||||
command_vector.push_back("dumpbin /summary > nul");
|
||||
if (ExecuteCommand(Command(command_vector)) == 0)
|
||||
return "dumpbin /disasm " + FileName;
|
||||
Printf("libFuzzer: couldn't find tool to disassemble (dumpbin)\n");
|
||||
exit(1);
|
||||
|
|
|
@ -17,6 +17,8 @@ list(APPEND LIBFUZZER_UNITTEST_LINK_FLAGS --driver-mode=g++)
|
|||
|
||||
if(APPLE OR CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
|
||||
list(APPEND LIBFUZZER_UNITTEST_LINK_FLAGS -lc++ -lpthread)
|
||||
elseif(WIN32)
|
||||
list(APPEND LIBFUZZER_UNITTEST_LINK_FLAGS -lstdc++)
|
||||
else()
|
||||
list(APPEND LIBFUZZER_UNITTEST_LINK_FLAGS -lstdc++ -lpthread)
|
||||
endif()
|
||||
|
|
|
@ -34,6 +34,13 @@ TEST(Fuzzer, Basename) {
|
|||
EXPECT_EQ(Basename("/bar"), "bar");
|
||||
EXPECT_EQ(Basename("foo/x"), "x");
|
||||
EXPECT_EQ(Basename("foo/"), "");
|
||||
#if LIBFUZZER_WINDOWS
|
||||
EXPECT_EQ(Basename("foo\\bar"), "bar");
|
||||
EXPECT_EQ(Basename("foo\\bar/baz"), "baz");
|
||||
EXPECT_EQ(Basename("\\bar"), "bar");
|
||||
EXPECT_EQ(Basename("foo\\x"), "x");
|
||||
EXPECT_EQ(Basename("foo\\"), "");
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(Fuzzer, CrossOver) {
|
||||
|
|
|
@ -7,16 +7,57 @@
|
|||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file defines delimiters for Sanitizer Coverage's section.
|
||||
// This file defines delimiters for Sanitizer Coverage's section. It contains
|
||||
// Windows specific tricks to coax the linker into giving us the start and stop
|
||||
// addresses of a section, as ELF linkers can do, to get the size of certain
|
||||
// arrays. According to https://msdn.microsoft.com/en-us/library/7977wcck.aspx
|
||||
// sections with the same name before "$" are sorted alphabetically by the
|
||||
// string that comes after "$" and merged into one section. We take advantage
|
||||
// of this by putting data we want the size of into the middle (M) of a section,
|
||||
// by using the letter "M" after "$". We get the start of this data (ie:
|
||||
// __start_section_name) by making the start variable come at the start of the
|
||||
// section (using the letter A after "$"). We do the same to get the end of the
|
||||
// data by using the letter "Z" after "$" to make the end variable come after
|
||||
// the data. Note that because of our technique the address of the start
|
||||
// variable is actually the address of data that comes before our middle
|
||||
// section. We also need to prevent the linker from adding any padding. Each
|
||||
// technique we use for this is explained in the comments below.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "sanitizer_platform.h"
|
||||
#if SANITIZER_WINDOWS
|
||||
#include <stdint.h>
|
||||
#pragma section(".SCOV$A", read, write) // NOLINT
|
||||
#pragma section(".SCOV$Z", read, write) // NOLINT
|
||||
extern "C" {
|
||||
__declspec(allocate(".SCOV$A")) uint32_t __start___sancov_guards = 0;
|
||||
__declspec(allocate(".SCOV$Z")) uint32_t __stop___sancov_guards = 0;
|
||||
// The Guard array and counter array should both be merged into the .data
|
||||
// section to reduce the number of PE sections However, because PCTable is
|
||||
// constant it should be merged with the .rdata section.
|
||||
#pragma section(".SCOV$GA", read, write) // NOLINT
|
||||
// Use align(1) to avoid adding any padding that will mess up clients trying to
|
||||
// determine the start and end of the array.
|
||||
__declspec(allocate(".SCOV$GA")) __declspec(align(1)) uint64_t
|
||||
__start___sancov_guards = 0;
|
||||
#pragma section(".SCOV$GZ", read, write) // NOLINT
|
||||
__declspec(allocate(".SCOV$GZ")) __declspec(align(1)) uint64_t
|
||||
__stop___sancov_guards = 0;
|
||||
|
||||
#pragma section(".SCOV$CA", read, write) // NOLINT
|
||||
__declspec(allocate(".SCOV$CA")) __declspec(align(1)) uint64_t
|
||||
__start___sancov_cntrs = 0;
|
||||
#pragma section(".SCOV$CZ", read, write) // NOLINT
|
||||
__declspec(allocate(".SCOV$CZ")) __declspec(align(1)) uint64_t
|
||||
__stop___sancov_cntrs = 0;
|
||||
|
||||
#pragma comment(linker, "/MERGE:.SCOV=.data")
|
||||
|
||||
// Use uint64_t so there won't be any issues if the linker tries to word align
|
||||
// the pc array.
|
||||
#pragma section(".SCOVP$A", read) // NOLINT
|
||||
__declspec(allocate(".SCOVP$A")) __declspec(align(1)) uint64_t
|
||||
__start___sancov_pcs = 0;
|
||||
#pragma section(".SCOVP$Z", read) // NOLINT
|
||||
__declspec(allocate(".SCOVP$Z")) __declspec(align(1)) uint64_t
|
||||
__stop___sancov_pcs = 0;
|
||||
|
||||
#pragma comment(linker, "/MERGE:.SCOVP=.rdata")
|
||||
}
|
||||
#endif // SANITIZER_WINDOWS
|
||||
#endif // SANITIZER_WINDOWS
|
||||
|
|
|
@ -62,7 +62,10 @@ if(COMPILER_RT_CAN_EXECUTE_TESTS)
|
|||
compiler_rt_test_runtime(sanitizer_common)
|
||||
|
||||
# OpenBSD not supporting asan, cannot run the tests
|
||||
if(COMPILER_RT_BUILD_LIBFUZZER AND NOT "${CMAKE_SYSTEM_NAME}" STREQUAL "OpenBSD" AND NOT ANDROID)
|
||||
# Tests are broken for now on Windows
|
||||
if(COMPILER_RT_BUILD_LIBFUZZER
|
||||
AND NOT "${CMAKE_SYSTEM_NAME}" STREQUAL "OpenBSD"
|
||||
AND NOT "${CMAKE_SYSTEM_NAME}" STREQUAL "Windows" AND NOT ANDROID)
|
||||
compiler_rt_test_runtime(fuzzer)
|
||||
endif()
|
||||
|
||||
|
|
|
@ -273,9 +273,20 @@ Function *SanitizerCoverageModule::CreateInitCallsForSections(
|
|||
auto SecStart = SecStartEnd.first;
|
||||
auto SecEnd = SecStartEnd.second;
|
||||
Function *CtorFunc;
|
||||
Value *SecStartPtr = nullptr;
|
||||
// Account for the fact that on windows-msvc __start_* symbols actually
|
||||
// point to a uint64_t before the start of the array.
|
||||
if (TargetTriple.getObjectFormat() == Triple::COFF) {
|
||||
auto SecStartI8Ptr = IRB.CreatePointerCast(SecStart, Int8PtrTy);
|
||||
auto GEP = IRB.CreateGEP(SecStartI8Ptr,
|
||||
ConstantInt::get(IntptrTy, sizeof(uint64_t)));
|
||||
SecStartPtr = IRB.CreatePointerCast(GEP, Ty);
|
||||
} else {
|
||||
SecStartPtr = IRB.CreatePointerCast(SecStart, Ty);
|
||||
}
|
||||
std::tie(CtorFunc, std::ignore) = createSanitizerCtorAndInitFunctions(
|
||||
M, SanCovModuleCtorName, InitFunctionName, {Ty, Ty},
|
||||
{IRB.CreatePointerCast(SecStart, Ty), IRB.CreatePointerCast(SecEnd, Ty)});
|
||||
{SecStartPtr, IRB.CreatePointerCast(SecEnd, Ty)});
|
||||
|
||||
if (TargetTriple.supportsCOMDAT()) {
|
||||
// Use comdat to dedup CtorFunc.
|
||||
|
@ -397,9 +408,20 @@ bool SanitizerCoverageModule::runOnModule(Module &M) {
|
|||
Function *InitFunction = declareSanitizerInitFunction(
|
||||
M, SanCovPCsInitName, {IntptrPtrTy, IntptrPtrTy});
|
||||
IRBuilder<> IRBCtor(Ctor->getEntryBlock().getTerminator());
|
||||
IRBCtor.CreateCall(InitFunction,
|
||||
{IRB.CreatePointerCast(SecStartEnd.first, IntptrPtrTy),
|
||||
IRB.CreatePointerCast(SecStartEnd.second, IntptrPtrTy)});
|
||||
Value *SecStartPtr = nullptr;
|
||||
// Account for the fact that on windows-msvc __start_pc_table actually
|
||||
// points to a uint64_t before the start of the PC table.
|
||||
if (TargetTriple.getObjectFormat() == Triple::COFF) {
|
||||
auto SecStartI8Ptr = IRB.CreatePointerCast(SecStartEnd.first, Int8PtrTy);
|
||||
auto GEP = IRB.CreateGEP(SecStartI8Ptr,
|
||||
ConstantInt::get(IntptrTy, sizeof(uint64_t)));
|
||||
SecStartPtr = IRB.CreatePointerCast(GEP, IntptrPtrTy);
|
||||
} else {
|
||||
SecStartPtr = IRB.CreatePointerCast(SecStartEnd.first, IntptrPtrTy);
|
||||
}
|
||||
IRBCtor.CreateCall(
|
||||
InitFunction,
|
||||
{SecStartPtr, IRB.CreatePointerCast(SecStartEnd.second, IntptrPtrTy)});
|
||||
}
|
||||
// We don't reference these arrays directly in any of our runtime functions,
|
||||
// so we need to prevent them from being dead stripped.
|
||||
|
@ -809,8 +831,13 @@ void SanitizerCoverageModule::InjectCoverageAtBlock(Function &F, BasicBlock &BB,
|
|||
|
||||
std::string
|
||||
SanitizerCoverageModule::getSectionName(const std::string &Section) const {
|
||||
if (TargetTriple.getObjectFormat() == Triple::COFF)
|
||||
return ".SCOV$M";
|
||||
if (TargetTriple.getObjectFormat() == Triple::COFF) {
|
||||
if (Section == SanCovCountersSectionName)
|
||||
return ".SCOV$CM";
|
||||
if (Section == SanCovPCsSectionName)
|
||||
return ".SCOVP$M";
|
||||
return ".SCOV$GM"; // For SanCovGuardsSectionName.
|
||||
}
|
||||
if (TargetTriple.isOSBinFormatMachO())
|
||||
return "__DATA,__" + Section;
|
||||
return "__" + Section;
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
; Checks that the PC and 8-bit Counter Arrays are placed in their own sections in COFF binaries.
|
||||
; RUN: opt < %s -sancov -sanitizer-coverage-level=1 -sanitizer-coverage-inline-8bit-counters=1 -sanitizer-coverage-pc-table=1 -S | FileCheck %s
|
||||
target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128"
|
||||
target triple = "x86_64-pc-windows-msvc19.14.26433"
|
||||
|
||||
define void @foo() {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; CHECK-DAG: section ".SCOV{{\$}}CM",
|
||||
; CHECK-DAG: section ".SCOVP{{\$}}M",
|
Loading…
Reference in New Issue