diff options
| author | Mehmet Samet Duman <yongdohyun@projecttick.org> | 2026-04-02 18:42:50 +0300 |
|---|---|---|
| committer | Mehmet Samet Duman <yongdohyun@projecttick.org> | 2026-04-02 18:42:50 +0300 |
| commit | 5fad10f89c485cfdc7b99011f07609f8871160d4 (patch) | |
| tree | 1860b39753b652dfe54d3cbbc80c875f40198d1f /json4cpp/tests/thirdparty/Fuzzer/FuzzerTraceState.cpp | |
| parent | 292baed7ac0cf84263263966ed32ed113cae857f (diff) | |
| parent | 9a737481aed085fd289f82dff1fa8c3c66627a7e (diff) | |
| download | Project-Tick-5fad10f89c485cfdc7b99011f07609f8871160d4.tar.gz Project-Tick-5fad10f89c485cfdc7b99011f07609f8871160d4.zip | |
Add 'json4cpp/' from commit '9a737481aed085fd289f82dff1fa8c3c66627a7e'
git-subtree-dir: json4cpp
git-subtree-mainline: 292baed7ac0cf84263263966ed32ed113cae857f
git-subtree-split: 9a737481aed085fd289f82dff1fa8c3c66627a7e
Diffstat (limited to 'json4cpp/tests/thirdparty/Fuzzer/FuzzerTraceState.cpp')
| -rw-r--r-- | json4cpp/tests/thirdparty/Fuzzer/FuzzerTraceState.cpp | 325 |
1 files changed, 325 insertions, 0 deletions
diff --git a/json4cpp/tests/thirdparty/Fuzzer/FuzzerTraceState.cpp b/json4cpp/tests/thirdparty/Fuzzer/FuzzerTraceState.cpp new file mode 100644 index 0000000000..be62a6624b --- /dev/null +++ b/json4cpp/tests/thirdparty/Fuzzer/FuzzerTraceState.cpp @@ -0,0 +1,325 @@ +//===- FuzzerTraceState.cpp - Trace-based fuzzer mutator ------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Data tracing. +//===----------------------------------------------------------------------===// + +#include "FuzzerDictionary.h" +#include "FuzzerInternal.h" +#include "FuzzerIO.h" +#include "FuzzerMutate.h" +#include "FuzzerRandom.h" +#include "FuzzerTracePC.h" +#include <algorithm> +#include <cstring> +#include <map> +#include <set> +#include <thread> + +namespace fuzzer { + +// For now, very simple: put Size bytes of Data at position Pos. +struct TraceBasedMutation { + uint32_t Pos; + Word W; +}; + +// Declared as static globals for faster checks inside the hooks. +static bool RecordingMemcmp = false; +static bool RecordingMemmem = false; +static bool DoingMyOwnMemmem = false; + +ScopedDoingMyOwnMemmem::ScopedDoingMyOwnMemmem() { DoingMyOwnMemmem = true; } +ScopedDoingMyOwnMemmem::~ScopedDoingMyOwnMemmem() { DoingMyOwnMemmem = false; } + +class TraceState { +public: + TraceState(MutationDispatcher &MD, const FuzzingOptions &Options, + const Fuzzer *F) + : MD(MD), Options(Options), F(F) {} + + 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.UseMemcmp) + return; + RecordingMemcmp = Options.UseMemcmp; + RecordingMemmem = Options.UseMemmem; + NumMutations = 0; + InterestingWords.clear(); + MD.ClearAutoDictionary(); + } + + void StopTraceRecording() { + if (!RecordingMemcmp) + return; + RecordingMemcmp = false; + for (size_t i = 0; i < NumMutations; i++) { + auto &M = Mutations[i]; + if (Options.Verbosity >= 2) { + AutoDictUnitCounts[M.W]++; + AutoDictAdds++; + if ((AutoDictAdds & (AutoDictAdds - 1)) == 0) { + typedef std::pair<size_t, Word> CU; + std::vector<CU> CountedUnits; + for (auto &I : AutoDictUnitCounts) + CountedUnits.push_back(std::make_pair(I.second, I.first)); + std::sort(CountedUnits.begin(), CountedUnits.end(), + [](const CU &a, const CU &b) { return a.first > b.first; }); + Printf("AutoDict:\n"); + for (auto &I : CountedUnits) { + Printf(" %zd ", I.first); + PrintASCII(I.second.data(), I.second.size()); + Printf("\n"); + } + } + } + MD.AddWordToAutoDictionary({M.W, M.Pos}); + } + for (auto &W : InterestingWords) + MD.AddWordToAutoDictionary({W}); + } + + void AddMutation(uint32_t Pos, uint32_t Size, const uint8_t *Data) { + if (NumMutations >= kMaxMutations) return; + auto &M = Mutations[NumMutations++]; + M.Pos = Pos; + M.W.Set(Data, Size); + } + + void AddMutation(uint32_t Pos, uint32_t Size, uint64_t Data) { + assert(Size <= sizeof(Data)); + AddMutation(Pos, Size, reinterpret_cast<uint8_t*>(&Data)); + } + + void AddInterestingWord(const uint8_t *Data, size_t Size) { + if (!RecordingMemmem || !F->InFuzzingThread()) return; + if (Size <= 1) return; + Size = std::min(Size, Word::GetMaxSize()); + Word W(Data, Size); + InterestingWords.insert(W); + } + + private: + bool IsTwoByteData(uint64_t Data) { + int64_t Signed = static_cast<int64_t>(Data); + Signed >>= 16; + return Signed == 0 || Signed == -1L; + } + + // We don't want to create too many trace-based mutations as it is both + // expensive and useless. So after some number of mutations is collected, + // start rejecting some of them. The more there are mutations the more we + // reject. + bool WantToHandleOneMoreMutation() { + const size_t FirstN = 64; + // Gladly handle first N mutations. + if (NumMutations <= FirstN) return true; + size_t Diff = NumMutations - FirstN; + size_t DiffLog = sizeof(long) * 8 - __builtin_clzl((long)Diff); + assert(DiffLog > 0 && DiffLog < 64); + bool WantThisOne = MD.GetRand()(1 << DiffLog) == 0; // 1 out of DiffLog. + return WantThisOne; + } + + static const size_t kMaxMutations = 1 << 16; + size_t NumMutations; + TraceBasedMutation Mutations[kMaxMutations]; + // TODO: std::set is too inefficient, need to have a custom DS here. + std::set<Word> InterestingWords; + MutationDispatcher &MD; + const FuzzingOptions Options; + const Fuzzer *F; + std::map<Word, size_t> AutoDictUnitCounts; + size_t AutoDictAdds = 0; +}; + +int TraceState::TryToAddDesiredData(uint64_t PresentData, uint64_t DesiredData, + size_t DataSize) { + if (NumMutations >= kMaxMutations || !WantToHandleOneMoreMutation()) return 0; + ScopedDoingMyOwnMemmem scoped_doing_my_own_memmem; + const uint8_t *UnitData; + auto UnitSize = F->GetCurrentUnitInFuzzingThead(&UnitData); + int Res = 0; + const uint8_t *Beg = UnitData; + const uint8_t *End = Beg + UnitSize; + for (const uint8_t *Cur = Beg; Cur < End; Cur++) { + Cur = (uint8_t *)SearchMemory(Cur, End - Cur, &PresentData, DataSize); + if (!Cur) + break; + size_t Pos = Cur - Beg; + assert(Pos < UnitSize); + AddMutation(Pos, DataSize, DesiredData); + AddMutation(Pos, DataSize, DesiredData + 1); + AddMutation(Pos, DataSize, DesiredData - 1); + Res++; + } + return Res; +} + +int TraceState::TryToAddDesiredData(const uint8_t *PresentData, + const uint8_t *DesiredData, + size_t DataSize) { + if (NumMutations >= kMaxMutations || !WantToHandleOneMoreMutation()) return 0; + ScopedDoingMyOwnMemmem scoped_doing_my_own_memmem; + const uint8_t *UnitData; + auto UnitSize = F->GetCurrentUnitInFuzzingThead(&UnitData); + int Res = 0; + const uint8_t *Beg = UnitData; + const uint8_t *End = Beg + UnitSize; + for (const uint8_t *Cur = Beg; Cur < End; Cur++) { + Cur = (uint8_t *)SearchMemory(Cur, End - Cur, PresentData, DataSize); + if (!Cur) + break; + size_t Pos = Cur - Beg; + assert(Pos < UnitSize); + AddMutation(Pos, DataSize, DesiredData); + Res++; + } + return Res; +} + +void TraceState::TraceMemcmpCallback(size_t CmpSize, const uint8_t *Data1, + const uint8_t *Data2) { + if (!RecordingMemcmp || !F->InFuzzingThread()) return; + CmpSize = std::min(CmpSize, Word::GetMaxSize()); + int Added2 = TryToAddDesiredData(Data1, Data2, CmpSize); + int Added1 = TryToAddDesiredData(Data2, Data1, CmpSize); + if ((Added1 || Added2) && Options.Verbosity >= 3) { + Printf("MemCmp Added %d%d: ", Added1, Added2); + if (Added1) PrintASCII(Data1, CmpSize); + if (Added2) PrintASCII(Data2, CmpSize); + Printf("\n"); + } +} + +void TraceState::TraceSwitchCallback(uintptr_t PC, size_t ValSizeInBits, + uint64_t Val, size_t NumCases, + uint64_t *Cases) { + if (F->InFuzzingThread()) return; + size_t ValSize = ValSizeInBits / 8; + bool TryShort = IsTwoByteData(Val); + for (size_t i = 0; i < NumCases; i++) + TryShort &= IsTwoByteData(Cases[i]); + + if (Options.Verbosity >= 3) + Printf("TraceSwitch: %p %zd # %zd; TryShort %d\n", PC, Val, NumCases, + TryShort); + + for (size_t i = 0; i < NumCases; i++) { + TryToAddDesiredData(Val, Cases[i], ValSize); + if (TryShort) + TryToAddDesiredData(Val, Cases[i], 2); + } +} + +static TraceState *TS; + +void Fuzzer::StartTraceRecording() { + if (!TS) return; + TS->StartTraceRecording(); +} + +void Fuzzer::StopTraceRecording() { + if (!TS) return; + TS->StopTraceRecording(); +} + +void Fuzzer::InitializeTraceState() { + if (!Options.UseMemcmp) return; + TS = new TraceState(MD, Options, this); +} + +static size_t InternalStrnlen(const char *S, size_t MaxLen) { + size_t Len = 0; + for (; Len < MaxLen && S[Len]; Len++) {} + return Len; +} + +} // namespace fuzzer + +using fuzzer::TS; +using fuzzer::RecordingMemcmp; + +extern "C" { + +// We may need to avoid defining weak hooks to stay compatible with older clang. +#ifndef LLVM_FUZZER_DEFINES_SANITIZER_WEAK_HOOOKS +# define LLVM_FUZZER_DEFINES_SANITIZER_WEAK_HOOOKS 1 +#endif + +#if LLVM_FUZZER_DEFINES_SANITIZER_WEAK_HOOOKS +void __sanitizer_weak_hook_memcmp(void *caller_pc, const void *s1, + const void *s2, size_t n, int result) { + fuzzer::TPC.AddValueForMemcmp(caller_pc, s1, s2, n); + if (!RecordingMemcmp) return; + if (result == 0) return; // No reason to mutate. + if (n <= 1) return; // Not interesting. + TS->TraceMemcmpCallback(n, reinterpret_cast<const uint8_t *>(s1), + reinterpret_cast<const uint8_t *>(s2)); +} + +void __sanitizer_weak_hook_strncmp(void *caller_pc, const char *s1, + const char *s2, size_t n, int result) { + fuzzer::TPC.AddValueForStrcmp(caller_pc, s1, s2, n); + if (!RecordingMemcmp) return; + if (result == 0) return; // No reason to mutate. + 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. + TS->TraceMemcmpCallback(n, reinterpret_cast<const uint8_t *>(s1), + reinterpret_cast<const uint8_t *>(s2)); +} + +void __sanitizer_weak_hook_strcmp(void *caller_pc, const char *s1, + const char *s2, int result) { + fuzzer::TPC.AddValueForStrcmp(caller_pc, s1, s2, 64); + if (!RecordingMemcmp) return; + if (result == 0) return; // No reason to mutate. + size_t Len1 = strlen(s1); + size_t Len2 = strlen(s2); + size_t N = std::min(Len1, Len2); + if (N <= 1) return; // Not interesting. + TS->TraceMemcmpCallback(N, reinterpret_cast<const uint8_t *>(s1), + reinterpret_cast<const uint8_t *>(s2)); +} + +void __sanitizer_weak_hook_strncasecmp(void *called_pc, const char *s1, + const char *s2, size_t n, int result) { + return __sanitizer_weak_hook_strncmp(called_pc, s1, s2, n, result); +} +void __sanitizer_weak_hook_strcasecmp(void *called_pc, const char *s1, + const char *s2, int result) { + return __sanitizer_weak_hook_strcmp(called_pc, s1, s2, result); +} +void __sanitizer_weak_hook_strstr(void *called_pc, const char *s1, + const char *s2, char *result) { + TS->AddInterestingWord(reinterpret_cast<const uint8_t *>(s2), strlen(s2)); +} +void __sanitizer_weak_hook_strcasestr(void *called_pc, const char *s1, + const char *s2, char *result) { + TS->AddInterestingWord(reinterpret_cast<const uint8_t *>(s2), strlen(s2)); +} +void __sanitizer_weak_hook_memmem(void *called_pc, const void *s1, size_t len1, + const void *s2, size_t len2, void *result) { + if (fuzzer::DoingMyOwnMemmem) return; + TS->AddInterestingWord(reinterpret_cast<const uint8_t *>(s2), len2); +} + +#endif // LLVM_FUZZER_DEFINES_SANITIZER_WEAK_HOOOKS +} // extern "C" |
