From: Kostya Serebryany Date: Sat, 9 Jan 2016 01:39:55 +0000 (+0000) Subject: [libFuzzer] don't limit memcmp tracing with 8 bytes X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=295ef47f9e9c1a771954b55a6c4e3794e618cecd;p=oota-llvm.git [libFuzzer] don't limit memcmp tracing with 8 bytes git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@257245 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/Fuzzer/FuzzerTraceState.cpp b/lib/Fuzzer/FuzzerTraceState.cpp index 1bfe6cefc83..1dc93df1f35 100644 --- a/lib/Fuzzer/FuzzerTraceState.cpp +++ b/lib/Fuzzer/FuzzerTraceState.cpp @@ -172,6 +172,8 @@ struct TraceBasedMutation { uint8_t Data[kMaxSize]; }; +const size_t TraceBasedMutation::kMaxSize; + class TraceState { public: TraceState(const Fuzzer::FuzzingOptions &Options, const Unit &CurrentUnit) @@ -186,15 +188,22 @@ class TraceState { void DFSanCmpCallback(uintptr_t PC, size_t CmpSize, size_t CmpType, uint64_t Arg1, uint64_t Arg2, dfsan_label L1, dfsan_label L2); + void DFSanMemcmpCallback(size_t CmpSize, const uint8_t *Data1, + const uint8_t *Data2, dfsan_label L1, + dfsan_label L2); void DFSanSwitchCallback(uint64_t PC, size_t ValSizeInBits, uint64_t Val, size_t NumCases, uint64_t *Cases, dfsan_label L); void TraceCmpCallback(uintptr_t PC, size_t CmpSize, size_t CmpType, uint64_t Arg1, uint64_t Arg2); + void TraceMemcmpCallback(size_t CmpSize, const uint8_t *Data1, + const uint8_t *Data2); void TraceSwitchCallback(uintptr_t PC, size_t ValSizeInBits, uint64_t Val, size_t NumCases, uint64_t *Cases); int TryToAddDesiredData(uint64_t PresentData, uint64_t DesiredData, size_t DataSize); + int TryToAddDesiredData(const uint8_t *PresentData, + const uint8_t *DesiredData, size_t DataSize); void StartTraceRecording() { if (!Options.UseTraces) return; @@ -209,12 +218,18 @@ class TraceState { void ApplyTraceBasedMutation(size_t Idx, fuzzer::Unit *U); - void AddMutation(uint32_t Pos, uint32_t Size, uint64_t Data) { + void AddMutation(uint32_t Pos, uint32_t Size, const uint8_t *Data) { if (NumMutations >= kMaxMutations) return; + assert(Size <= TraceBasedMutation::kMaxSize); auto &M = Mutations[NumMutations++]; M.Pos = Pos; M.Size = Size; - memcpy(M.Data, &Data, sizeof(Data)); + memcpy(M.Data, Data, Size); + } + + void AddMutation(uint32_t Pos, uint32_t Size, uint64_t Data) { + assert(Size <= sizeof(Data)); + AddMutation(Pos, Size, reinterpret_cast(&Data)); } private: @@ -287,6 +302,26 @@ void TraceState::DFSanCmpCallback(uintptr_t PC, size_t CmpSize, size_t CmpType, PC, CmpSize, CmpType, Arg1, Arg2, Res, L1, L2, NumMutations); } +void TraceState::DFSanMemcmpCallback(size_t CmpSize, const uint8_t *Data1, + const uint8_t *Data2, dfsan_label L1, + dfsan_label L2) { + + assert(ReallyHaveDFSan()); + if (!RecordingTraces || !IsMyThread) return; + if (L1 == 0 && L2 == 0) + return; // Not actionable. + if (L1 != 0 && L2 != 0) + return; // Probably still actionable. + + const uint8_t *Data = L1 ? Data2 : Data1; + LabelRange LR = L1 ? GetLabelRange(L1) : GetLabelRange(L2); + for (size_t Pos = LR.Beg; Pos + CmpSize <= LR.End; Pos++) { + AddMutation(Pos, CmpSize, Data); + if (Options.Verbosity >= 3) + Printf("DFSanMemcmpCallback: Pos %d Size %d\n", Pos, CmpSize); + } +} + void TraceState::DFSanSwitchCallback(uint64_t PC, size_t ValSizeInBits, uint64_t Val, size_t NumCases, uint64_t *Cases, dfsan_label L) { @@ -333,6 +368,24 @@ int TraceState::TryToAddDesiredData(uint64_t PresentData, uint64_t DesiredData, return Res; } +int TraceState::TryToAddDesiredData(const uint8_t *PresentData, + const uint8_t *DesiredData, + size_t DataSize) { + int Res = 0; + const uint8_t *Beg = CurrentUnit.data(); + const uint8_t *End = Beg + CurrentUnit.size(); + for (const uint8_t *Cur = Beg; Cur < End; Cur++) { + Cur = (uint8_t *)memmem(Cur, End - Cur, PresentData, DataSize); + if (!Cur) + break; + size_t Pos = Cur - Beg; + assert(Pos < CurrentUnit.size()); + AddMutation(Pos, DataSize, DesiredData); + Res++; + } + return Res; +} + void TraceState::TraceCmpCallback(uintptr_t PC, size_t CmpSize, size_t CmpType, uint64_t Arg1, uint64_t Arg2) { if (!RecordingTraces || !IsMyThread) return; @@ -347,6 +400,14 @@ void TraceState::TraceCmpCallback(uintptr_t PC, size_t CmpSize, size_t CmpType, } } +void TraceState::TraceMemcmpCallback(size_t CmpSize, const uint8_t *Data1, + const uint8_t *Data2) { + if (!RecordingTraces || !IsMyThread) return; + CmpSize = std::min(CmpSize, TraceBasedMutation::kMaxSize); + TryToAddDesiredData(Data1, Data2, CmpSize); + TryToAddDesiredData(Data2, Data1, CmpSize); +} + void TraceState::TraceSwitchCallback(uintptr_t PC, size_t ValSizeInBits, uint64_t Val, size_t NumCases, uint64_t *Cases) { @@ -436,89 +497,66 @@ void dfsan_weak_hook_memcmp(void *caller_pc, const void *s1, const void *s2, size_t n, dfsan_label s1_label, dfsan_label s2_label, dfsan_label n_label) { if (!TS) return; - uintptr_t PC = reinterpret_cast(caller_pc); - uint64_t S1 = 0, S2 = 0; - // Simplification: handle only first 8 bytes. - memcpy(&S1, s1, std::min(n, sizeof(S1))); - memcpy(&S2, s2, std::min(n, sizeof(S2))); dfsan_label L1 = dfsan_read_label(s1, n); dfsan_label L2 = dfsan_read_label(s2, n); - TS->DFSanCmpCallback(PC, n, fuzzer::ICMP_EQ, S1, S2, L1, L2); + TS->DFSanMemcmpCallback(n, reinterpret_cast(s1), + reinterpret_cast(s2), L1, L2); } void dfsan_weak_hook_strncmp(void *caller_pc, const char *s1, const char *s2, size_t n, dfsan_label s1_label, dfsan_label s2_label, dfsan_label n_label) { if (!TS) return; - uintptr_t PC = reinterpret_cast(caller_pc); - uint64_t S1 = 0, S2 = 0; n = std::min(n, fuzzer::InternalStrnlen(s1, n)); n = std::min(n, fuzzer::InternalStrnlen(s2, n)); - // Simplification: handle only first 8 bytes. - memcpy(&S1, s1, std::min(n, sizeof(S1))); - memcpy(&S2, s2, std::min(n, sizeof(S2))); dfsan_label L1 = dfsan_read_label(s1, n); dfsan_label L2 = dfsan_read_label(s2, n); - TS->DFSanCmpCallback(PC, n, fuzzer::ICMP_EQ, S1, S2, L1, L2); + TS->DFSanMemcmpCallback(n, reinterpret_cast(s1), + reinterpret_cast(s2), L1, L2); } void dfsan_weak_hook_strcmp(void *caller_pc, const char *s1, const char *s2, dfsan_label s1_label, dfsan_label s2_label) { if (!TS) return; - uintptr_t PC = reinterpret_cast(caller_pc); - uint64_t S1 = 0, S2 = 0; size_t Len1 = strlen(s1); size_t Len2 = strlen(s2); size_t N = std::min(Len1, Len2); if (N <= 1) return; // Not interesting. - // Simplification: handle only first 8 bytes. - memcpy(&S1, s1, std::min(N, sizeof(S1))); - memcpy(&S2, s2, std::min(N, sizeof(S2))); dfsan_label L1 = dfsan_read_label(s1, Len1); dfsan_label L2 = dfsan_read_label(s2, Len2); - TS->DFSanCmpCallback(PC, N, fuzzer::ICMP_EQ, S1, S2, L1, L2); + TS->DFSanMemcmpCallback(N, reinterpret_cast(s1), + reinterpret_cast(s2), L1, L2); } void __sanitizer_weak_hook_memcmp(void *caller_pc, const void *s1, const void *s2, size_t n) { if (!TS) return; - uintptr_t PC = reinterpret_cast(caller_pc); - uint64_t S1 = 0, S2 = 0; - // Simplification: handle only first 8 bytes. - memcpy(&S1, s1, std::min(n, sizeof(S1))); - memcpy(&S2, s2, std::min(n, sizeof(S2))); - TS->TraceCmpCallback(PC, n, fuzzer::ICMP_EQ, S1, S2); + if (n <= 1) return; // Not interesting. + TS->TraceMemcmpCallback(n, reinterpret_cast(s1), + reinterpret_cast(s2)); } void __sanitizer_weak_hook_strncmp(void *caller_pc, const char *s1, const char *s2, size_t n) { if (!TS) return; - uintptr_t PC = reinterpret_cast(caller_pc); - uint64_t S1 = 0, S2 = 0; size_t Len1 = fuzzer::InternalStrnlen(s1, n); size_t Len2 = fuzzer::InternalStrnlen(s2, n); n = std::min(n, Len1); n = std::min(n, Len2); if (n <= 1) return; // Not interesting. - // Simplification: handle only first 8 bytes. - memcpy(&S1, s1, std::min(n, sizeof(S1))); - memcpy(&S2, s2, std::min(n, sizeof(S2))); - TS->TraceCmpCallback(PC, n, fuzzer::ICMP_EQ, S1, S2); + TS->TraceMemcmpCallback(n, reinterpret_cast(s1), + reinterpret_cast(s2)); } void __sanitizer_weak_hook_strcmp(void *caller_pc, const char *s1, const char *s2) { if (!TS) return; - uintptr_t PC = reinterpret_cast(caller_pc); - uint64_t S1 = 0, S2 = 0; size_t Len1 = strlen(s1); size_t Len2 = strlen(s2); size_t N = std::min(Len1, Len2); if (N <= 1) return; // Not interesting. - // Simplification: handle only first 8 bytes. - memcpy(&S1, s1, std::min(N, sizeof(S1))); - memcpy(&S2, s2, std::min(N, sizeof(S2))); - TS->TraceCmpCallback(PC, N, fuzzer::ICMP_EQ, S1, S2); + TS->TraceMemcmpCallback(N, reinterpret_cast(s1), + reinterpret_cast(s2)); } __attribute__((visibility("default"))) diff --git a/lib/Fuzzer/test/MemcmpTest.cpp b/lib/Fuzzer/test/MemcmpTest.cpp index 47ce59e0d8f..c19c95717bb 100644 --- a/lib/Fuzzer/test/MemcmpTest.cpp +++ b/lib/Fuzzer/test/MemcmpTest.cpp @@ -10,8 +10,16 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { if (Size >= 12 && memcmp(Data + 8, "ABCD", 4) == 0) { if (Size >= 14 && memcmp(Data + 12, "XY", 2) == 0) { if (Size >= 16 && memcmp(Data + 14, "KLM", 3) == 0) { - fprintf(stderr, "BINGO\n"); - exit(1); + if (Size >= 27 && memcmp(Data + 17, "ABCDE-GHIJ", 10) == 0){ + fprintf(stderr, "BINGO %zd\n", Size); + for (size_t i = 0; i < Size; i++) { + uint8_t C = Data[i]; + if (C >= 32 && C < 127) + fprintf(stderr, "%c", C); + } + fprintf(stderr, "\n"); + exit(1); + } } } } diff --git a/lib/Fuzzer/test/fuzzer-dfsan.test b/lib/Fuzzer/test/fuzzer-dfsan.test index 982f143669d..567086ed65a 100644 --- a/lib/Fuzzer/test/fuzzer-dfsan.test +++ b/lib/Fuzzer/test/fuzzer-dfsan.test @@ -5,18 +5,19 @@ CHECK4: BINGO CHECK_DFSanCmpCallback: DFSanCmpCallback: PC CHECK_DFSanSwitchCallback: DFSanSwitchCallback: PC +CHECK_DFSanMemcmpCallback: DFSanMemcmpCallback: Pos RUN: not LLVMFuzzer-SimpleCmpTest-DFSan -use_traces=1 -seed=1 -runs=1000000 -timeout=5 2>&1 | FileCheck %s --check-prefix=CHECK1 RUN: LLVMFuzzer-SimpleCmpTest-DFSan -use_traces=1 -seed=1 -runs=100 -timeout=5 -verbosity=3 2>&1 | FileCheck %s -check-prefix=CHECK_DFSanCmpCallback RUN: not LLVMFuzzer-MemcmpTest-DFSan -use_traces=1 -seed=1 -runs=10000 -timeout=5 2>&1 | FileCheck %s --check-prefix=CHECK2 -RUN: LLVMFuzzer-MemcmpTest-DFSan -use_traces=1 -seed=1 -runs=2 -timeout=5 -verbosity=3 2>&1 | FileCheck %s -check-prefix=CHECK_DFSanCmpCallback +RUN: LLVMFuzzer-MemcmpTest-DFSan -use_traces=1 -seed=1 -runs=2 -timeout=5 -verbosity=3 2>&1 | FileCheck %s -check-prefix=CHECK_DFSanMemcmpCallback RUN: not LLVMFuzzer-StrncmpTest-DFSan -use_traces=1 -seed=1 -runs=10000 -timeout=5 2>&1 | FileCheck %s --check-prefix=CHECK3 -RUN: LLVMFuzzer-StrncmpTest-DFSan -use_traces=1 -seed=1 -runs=2 -timeout=5 -verbosity=3 2>&1 | FileCheck %s -check-prefix=CHECK_DFSanCmpCallback +RUN: LLVMFuzzer-StrncmpTest-DFSan -use_traces=1 -seed=1 -runs=2 -timeout=5 -verbosity=3 2>&1 | FileCheck %s -check-prefix=CHECK_DFSanMemcmpCallback RUN: not LLVMFuzzer-StrcmpTest-DFSan -use_traces=1 -seed=1 -runs=10000 -timeout=5 2>&1 | FileCheck %s --check-prefix=CHECK3 -RUN: LLVMFuzzer-StrcmpTest-DFSan -use_traces=1 -seed=1 -runs=2 -timeout=5 -verbosity=3 2>&1 | FileCheck %s -check-prefix=CHECK_DFSanCmpCallback +RUN: LLVMFuzzer-StrcmpTest-DFSan -use_traces=1 -seed=1 -runs=2 -timeout=5 -verbosity=3 2>&1 | FileCheck %s -check-prefix=CHECK_DFSanMemcmpCallback RUN: not LLVMFuzzer-SwitchTest-DFSan -use_traces=1 -seed=1 -runs=100000 -timeout=5 2>&1 | FileCheck %s --check-prefix=CHECK4 RUN: LLVMFuzzer-SwitchTest-DFSan -use_traces=1 -seed=1 -runs=2 -timeout=5 -verbosity=3 2>&1 | FileCheck %s -check-prefix=CHECK_DFSanSwitchCallback