mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-26 04:32:44 +01:00
[libFuzzer] extend -print_coverage to also print uncovered lines, functions, and files.
Example of output: COVERAGE: COVERED: in DSO2(int) /pathto/DSO2.cpp:6 COVERED: in DSO2(int) /pathto/DSO2.cpp:8 COVERED: in DSO1(int) /pathto/DSO1.cpp:6 COVERED: in DSO1(int) /pathto/DSO1.cpp:8 COVERED: in LLVMFuzzerTestOneInput /pathto/DSOTestMain.cpp:16 COVERED: in LLVMFuzzerTestOneInput /pathto/DSOTestMain.cpp:19 COVERED: in LLVMFuzzerTestOneInput /pathto/DSOTestMain.cpp:25 COVERED: in LLVMFuzzerTestOneInput /pathto/DSOTestMain.cpp:26 MODULE_WITH_COVERAGE: /pathto/libLLVMFuzzer-DSO1.so UNCOVERED_LINE: in DSO1(int) /pathto/DSO1.cpp:9 UNCOVERED_FUNC: in Uncovered1() MODULE_WITH_COVERAGE: /pathto/libLLVMFuzzer-DSO2.so UNCOVERED_LINE: in DSO2(int) /pathto/DSO2.cpp:9 UNCOVERED_FUNC: in Uncovered2() MODULE_WITH_COVERAGE: /pathto/LLVMFuzzer-DSOTest UNCOVERED_LINE: in LLVMFuzzerTestOneInput /pathto/DSOTestMain.cpp:21 UNCOVERED_LINE: in LLVMFuzzerTestOneInput /pathto/DSOTestMain.cpp:27 UNCOVERED_FILE: /pathto/DSOTestExtra.cpp Several things are not perfect here: * we are using objdump+awk instead of sancov because sancov does not support DSOs yet. * this breaks in the presence of ASAN_OPTIONS=strip_path_prefix=... (need to implement another API to get the module name by PC) llvm-svn: 284554
This commit is contained in:
parent
53f2da3ddf
commit
988580974c
@ -93,6 +93,8 @@ void SetSigIntHandler();
|
|||||||
void SetSigTermHandler();
|
void SetSigTermHandler();
|
||||||
std::string Base64(const Unit &U);
|
std::string Base64(const Unit &U);
|
||||||
int ExecuteCommand(const std::string &Command);
|
int ExecuteCommand(const std::string &Command);
|
||||||
|
bool ExecuteCommandAndReadOutput(const std::string &Command, std::string *Out);
|
||||||
|
|
||||||
size_t GetPeakRSSMb();
|
size_t GetPeakRSSMb();
|
||||||
|
|
||||||
// Private copy of SHA1 implementation.
|
// Private copy of SHA1 implementation.
|
||||||
|
@ -12,9 +12,14 @@
|
|||||||
//
|
//
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <set>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
#include "FuzzerCorpus.h"
|
#include "FuzzerCorpus.h"
|
||||||
#include "FuzzerDefs.h"
|
#include "FuzzerDefs.h"
|
||||||
#include "FuzzerDictionary.h"
|
#include "FuzzerDictionary.h"
|
||||||
|
#include "FuzzerExtFunctions.h"
|
||||||
#include "FuzzerTracePC.h"
|
#include "FuzzerTracePC.h"
|
||||||
#include "FuzzerValueBitMap.h"
|
#include "FuzzerValueBitMap.h"
|
||||||
|
|
||||||
@ -112,11 +117,100 @@ void TracePC::HandleCallerCallee(uintptr_t Caller, uintptr_t Callee) {
|
|||||||
HandleValueProfile(Idx);
|
HandleValueProfile(Idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool IsInterestingCoverageFile(std::string &File) {
|
||||||
|
if (File.find("compiler-rt/lib/") != std::string::npos)
|
||||||
|
return false; // sanitizer internal.
|
||||||
|
if (File.find("/usr/lib/") != std::string::npos)
|
||||||
|
return false;
|
||||||
|
if (File.find("/usr/include/") != std::string::npos)
|
||||||
|
return false;
|
||||||
|
if (File == "<null>")
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void TracePC::PrintCoverage() {
|
void TracePC::PrintCoverage() {
|
||||||
|
if (!EF->__sanitizer_symbolize_pc) {
|
||||||
|
Printf("INFO: __sanitizer_symbolize_pc is not available,"
|
||||||
|
" not printing coverage\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::map<std::string, std::vector<uintptr_t>> CoveredPCsPerModule;
|
||||||
|
std::map<std::string, uintptr_t> ModuleOffsets;
|
||||||
|
std::set<std::string> CoveredFiles, CoveredFunctions, CoveredLines;
|
||||||
Printf("COVERAGE:\n");
|
Printf("COVERAGE:\n");
|
||||||
for (size_t i = 0; i < Min(NumGuards + 1, kNumPCs); i++) {
|
for (size_t i = 0; i < Min(NumGuards + 1, kNumPCs); i++) {
|
||||||
if (PCs[i])
|
if (!PCs[i]) continue;
|
||||||
PrintPC("COVERED: %p %F %L\n", "COVERED: %p\n", PCs[i]);
|
std::string FileStr = DescribePC("%s", PCs[i]);
|
||||||
|
if (!IsInterestingCoverageFile(FileStr)) continue;
|
||||||
|
std::string FixedPCStr = DescribePC("%p", PCs[i]);
|
||||||
|
std::string FunctionStr = DescribePC("%F", PCs[i]);
|
||||||
|
std::string LineStr = DescribePC("%l", PCs[i]);
|
||||||
|
// TODO(kcc): get the module using some other way since this
|
||||||
|
// does not work with ASAN_OPTIONS=strip_path_prefix=something.
|
||||||
|
std::string Module = DescribePC("%m", PCs[i]);
|
||||||
|
std::string OffsetStr = DescribePC("%o", PCs[i]);
|
||||||
|
uintptr_t FixedPC = std::stol(FixedPCStr, 0, 16);
|
||||||
|
uintptr_t PcOffset = std::stol(OffsetStr, 0, 16);
|
||||||
|
ModuleOffsets[Module] = FixedPC - PcOffset;
|
||||||
|
CoveredPCsPerModule[Module].push_back(PcOffset);
|
||||||
|
CoveredFunctions.insert(FunctionStr);
|
||||||
|
CoveredFiles.insert(FileStr);
|
||||||
|
if (!CoveredLines.insert(FileStr + ":" + LineStr).second)
|
||||||
|
continue;
|
||||||
|
Printf("COVERED: %s %s:%s\n", FunctionStr.c_str(),
|
||||||
|
FileStr.c_str(), LineStr.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &M : CoveredPCsPerModule) {
|
||||||
|
std::set<std::string> UncoveredFiles, UncoveredFunctions;
|
||||||
|
std::map<std::string, std::set<int> > UncoveredLines; // Func+File => lines
|
||||||
|
auto &ModuleName = M.first;
|
||||||
|
auto &CoveredOffsets = M.second;
|
||||||
|
uintptr_t ModuleOffset = ModuleOffsets[ModuleName];
|
||||||
|
std::sort(CoveredOffsets.begin(), CoveredOffsets.end());
|
||||||
|
Printf("MODULE_WITH_COVERAGE: %s\n", ModuleName.c_str());
|
||||||
|
// sancov does not yet fully support DSOs.
|
||||||
|
// std::string Cmd = "sancov -print-coverage-pcs " + ModuleName;
|
||||||
|
std::string Cmd = "objdump -d " + ModuleName +
|
||||||
|
" | grep 'call.*__sanitizer_cov_trace_pc_guard' | awk -F: '{print $1}'";
|
||||||
|
std::string SanCovOutput;
|
||||||
|
if (!ExecuteCommandAndReadOutput(Cmd, &SanCovOutput)) {
|
||||||
|
Printf("INFO: Command failed: %s\n", Cmd.c_str());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
std::istringstream ISS(SanCovOutput);
|
||||||
|
std::string S;
|
||||||
|
while (std::getline(ISS, S, '\n')) {
|
||||||
|
uintptr_t PcOffset = std::stol(S, 0, 16);
|
||||||
|
if (!std::binary_search(CoveredOffsets.begin(), CoveredOffsets.end(),
|
||||||
|
PcOffset)) {
|
||||||
|
uintptr_t PC = ModuleOffset + PcOffset;
|
||||||
|
auto FileStr = DescribePC("%s", PC);
|
||||||
|
if (!IsInterestingCoverageFile(FileStr)) continue;
|
||||||
|
if (CoveredFiles.count(FileStr) == 0) {
|
||||||
|
UncoveredFiles.insert(FileStr);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
auto FunctionStr = DescribePC("%F", PC);
|
||||||
|
if (CoveredFunctions.count(FunctionStr) == 0) {
|
||||||
|
UncoveredFunctions.insert(FunctionStr);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
std::string LineStr = DescribePC("%l", PC);
|
||||||
|
uintptr_t Line = std::stoi(LineStr);
|
||||||
|
std::string FileLineStr = FileStr + ":" + LineStr;
|
||||||
|
if (CoveredLines.count(FileLineStr) == 0)
|
||||||
|
UncoveredLines[FunctionStr + " " + FileStr].insert(Line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (auto &FileLine: UncoveredLines)
|
||||||
|
for (int Line : FileLine.second)
|
||||||
|
Printf("UNCOVERED_LINE: %s:%d\n", FileLine.first.c_str(), Line);
|
||||||
|
for (auto &Func : UncoveredFunctions)
|
||||||
|
Printf("UNCOVERED_FUNC: %s\n", Func.c_str());
|
||||||
|
for (auto &File : UncoveredFiles)
|
||||||
|
Printf("UNCOVERED_FILE: %s\n", File.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <stdio.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
@ -306,4 +307,14 @@ void PrintPC(const char *SymbolizedFMT, const char *FallbackFMT, uintptr_t PC) {
|
|||||||
Printf(FallbackFMT, PC);
|
Printf(FallbackFMT, PC);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ExecuteCommandAndReadOutput(const std::string &Command, std::string *Out) {
|
||||||
|
FILE *Pipe = popen(Command.c_str(), "r");
|
||||||
|
if (!Pipe) return false;
|
||||||
|
char Buff[1024];
|
||||||
|
size_t N;
|
||||||
|
while ((N = fread(Buff, 1, sizeof(Buff), Pipe)) > 0)
|
||||||
|
Out->append(Buff, N);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace fuzzer
|
} // namespace fuzzer
|
||||||
|
@ -9,3 +9,4 @@ int DSO1(int a) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Uncovered1() { }
|
||||||
|
@ -9,3 +9,4 @@ int DSO2(int a) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Uncovered2() {}
|
||||||
|
@ -11,17 +11,21 @@ extern int DSO1(int a);
|
|||||||
extern int DSO2(int a);
|
extern int DSO2(int a);
|
||||||
extern int DSOTestExtra(int a);
|
extern int DSOTestExtra(int a);
|
||||||
|
|
||||||
|
static volatile int *nil = 0;
|
||||||
|
|
||||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||||
if (Size < sizeof(int) * 3) return 0;
|
|
||||||
int x, y, z;
|
int x, y, z;
|
||||||
memcpy(&x, Data + 0 * sizeof(int), sizeof(int));
|
if (Size < sizeof(int) * 3) {
|
||||||
memcpy(&y, Data + 1 * sizeof(int), sizeof(int));
|
x = y = z = 0;
|
||||||
memcpy(&z, Data + 2 * sizeof(int), sizeof(int));
|
} else {
|
||||||
int sum = DSO1(x) + DSO2(y) + DSOTestExtra(z);
|
memcpy(&x, Data + 0 * sizeof(int), sizeof(int));
|
||||||
|
memcpy(&y, Data + 1 * sizeof(int), sizeof(int));
|
||||||
|
memcpy(&z, Data + 2 * sizeof(int), sizeof(int));
|
||||||
|
}
|
||||||
|
int sum = DSO1(x) + DSO2(y) + (z ? DSOTestExtra(z) : 0);
|
||||||
if (sum == 3) {
|
if (sum == 3) {
|
||||||
fprintf(stderr, "BINGO %d %d %d\n", x, y, z);
|
fprintf(stderr, "BINGO %d %d %d\n", x, y, z);
|
||||||
exit(1);
|
*nil = 0;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -4,3 +4,15 @@ CHECK-DAG: COVERED: {{.*}}in LLVMFuzzerTestOneInput {{.*}}NullDerefTest.cpp:14
|
|||||||
CHECK-DAG: COVERED: {{.*}}in LLVMFuzzerTestOneInput {{.*}}NullDerefTest.cpp:16
|
CHECK-DAG: COVERED: {{.*}}in LLVMFuzzerTestOneInput {{.*}}NullDerefTest.cpp:16
|
||||||
CHECK-DAG: COVERED: {{.*}}in LLVMFuzzerTestOneInput {{.*}}NullDerefTest.cpp:19
|
CHECK-DAG: COVERED: {{.*}}in LLVMFuzzerTestOneInput {{.*}}NullDerefTest.cpp:19
|
||||||
RUN: not LLVMFuzzer-NullDerefTest-TracePC -print_coverage=1 2>&1 | FileCheck %s
|
RUN: not LLVMFuzzer-NullDerefTest-TracePC -print_coverage=1 2>&1 | FileCheck %s
|
||||||
|
|
||||||
|
RUN: LLVMFuzzer-DSOTest -print_coverage=1 -runs=0 2>&1 | FileCheck %s --check-prefix=DSO
|
||||||
|
DSO: COVERAGE:
|
||||||
|
DSO-DAG: COVERED:{{.*}}DSO1{{.*}}DSO1.cpp
|
||||||
|
DSO-DAG: COVERED:{{.*}}DSO2{{.*}}DSO2.cpp
|
||||||
|
DSO-DAG: COVERED:{{.*}}LLVMFuzzerTestOneInput{{.*}}DSOTestMain
|
||||||
|
DSO-DAG: UNCOVERED_LINE:{{.*}}DSO1{{.*}}DSO1.cpp
|
||||||
|
DSO-DAG: UNCOVERED_LINE:{{.*}}DSO2{{.*}}DSO2.cpp
|
||||||
|
DSO-DAG: UNCOVERED_FUNC: in Uncovered1
|
||||||
|
DSO-DAG: UNCOVERED_FUNC: in Uncovered2
|
||||||
|
DSO-DAG: UNCOVERED_LINE: in LLVMFuzzerTestOneInput
|
||||||
|
DSO-DAG: UNCOVERED_FILE:{{.*}}DSOTestExtra.cpp
|
||||||
|
Loading…
Reference in New Issue
Block a user