mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-26 12:43:36 +01:00
[libFuzzer] do mutations based on memcmp/strcmp interceptors under a separate flag (-use_memcmp, default=1)
llvm-svn: 257873
This commit is contained in:
parent
4a20fbee68
commit
b40c61f46c
@ -270,6 +270,7 @@ int FuzzerDriver(const std::vector<std::string> &Args,
|
|||||||
Options.UseCounters = Flags.use_counters;
|
Options.UseCounters = Flags.use_counters;
|
||||||
Options.UseIndirCalls = Flags.use_indir_calls;
|
Options.UseIndirCalls = Flags.use_indir_calls;
|
||||||
Options.UseTraces = Flags.use_traces;
|
Options.UseTraces = Flags.use_traces;
|
||||||
|
Options.UseMemcmp = Flags.use_memcmp;
|
||||||
Options.ShuffleAtStartUp = Flags.shuffle;
|
Options.ShuffleAtStartUp = Flags.shuffle;
|
||||||
Options.PreferSmallDuringInitialShuffle =
|
Options.PreferSmallDuringInitialShuffle =
|
||||||
Flags.prefer_small_during_initial_shuffle;
|
Flags.prefer_small_during_initial_shuffle;
|
||||||
|
@ -38,6 +38,8 @@ FUZZER_FLAG_INT(merge, 0, "If 1, the 2-nd, 3-rd, etc corpora will be "
|
|||||||
FUZZER_FLAG_INT(use_counters, 1, "Use coverage counters")
|
FUZZER_FLAG_INT(use_counters, 1, "Use coverage counters")
|
||||||
FUZZER_FLAG_INT(use_indir_calls, 1, "Use indirect caller-callee counters")
|
FUZZER_FLAG_INT(use_indir_calls, 1, "Use indirect caller-callee counters")
|
||||||
FUZZER_FLAG_INT(use_traces, 0, "Experimental: use instruction traces")
|
FUZZER_FLAG_INT(use_traces, 0, "Experimental: use instruction traces")
|
||||||
|
FUZZER_FLAG_INT(use_memcmp, 1,
|
||||||
|
"Use hints from intercepting memcmp, strcmp, etc")
|
||||||
FUZZER_FLAG_INT(jobs, 0, "Number of jobs to run. If jobs >= 1 we spawn"
|
FUZZER_FLAG_INT(jobs, 0, "Number of jobs to run. If jobs >= 1 we spawn"
|
||||||
" this number of jobs in separate worker processes"
|
" this number of jobs in separate worker processes"
|
||||||
" with stdout/stderr redirected to fuzz-JOB.log.")
|
" with stdout/stderr redirected to fuzz-JOB.log.")
|
||||||
|
@ -83,6 +83,7 @@ class Fuzzer {
|
|||||||
bool UseCounters = false;
|
bool UseCounters = false;
|
||||||
bool UseIndirCalls = true;
|
bool UseIndirCalls = true;
|
||||||
bool UseTraces = false;
|
bool UseTraces = false;
|
||||||
|
bool UseMemcmp = true;
|
||||||
bool UseFullCoverageSet = false;
|
bool UseFullCoverageSet = false;
|
||||||
bool Reload = true;
|
bool Reload = true;
|
||||||
bool ShuffleAtStartUp = true;
|
bool ShuffleAtStartUp = true;
|
||||||
|
@ -172,6 +172,10 @@ struct TraceBasedMutation {
|
|||||||
|
|
||||||
const size_t TraceBasedMutation::kMaxSize;
|
const size_t TraceBasedMutation::kMaxSize;
|
||||||
|
|
||||||
|
// Declared as static globals for faster checks inside the hooks.
|
||||||
|
static bool RecordingTraces = false;
|
||||||
|
static bool RecordingMemcmp = false;
|
||||||
|
|
||||||
class TraceState {
|
class TraceState {
|
||||||
public:
|
public:
|
||||||
TraceState(UserSuppliedFuzzer &USF, const Fuzzer::FuzzingOptions &Options,
|
TraceState(UserSuppliedFuzzer &USF, const Fuzzer::FuzzingOptions &Options,
|
||||||
@ -206,15 +210,17 @@ class TraceState {
|
|||||||
const uint8_t *DesiredData, size_t DataSize);
|
const uint8_t *DesiredData, size_t DataSize);
|
||||||
|
|
||||||
void StartTraceRecording() {
|
void StartTraceRecording() {
|
||||||
if (!Options.UseTraces) return;
|
if (!Options.UseTraces && !Options.UseMemcmp) return;
|
||||||
RecordingTraces = true;
|
RecordingTraces = Options.UseTraces;
|
||||||
|
RecordingMemcmp = Options.UseMemcmp;
|
||||||
NumMutations = 0;
|
NumMutations = 0;
|
||||||
USF.GetMD().ClearAutoDictionary();
|
USF.GetMD().ClearAutoDictionary();
|
||||||
}
|
}
|
||||||
|
|
||||||
void StopTraceRecording() {
|
void StopTraceRecording() {
|
||||||
if (!RecordingTraces) return;
|
if (!RecordingTraces && !RecordingMemcmp) return;
|
||||||
RecordingTraces = false;
|
RecordingTraces = false;
|
||||||
|
RecordingMemcmp = false;
|
||||||
for (size_t i = 0; i < NumMutations; i++) {
|
for (size_t i = 0; i < NumMutations; i++) {
|
||||||
auto &M = Mutations[i];
|
auto &M = Mutations[i];
|
||||||
Unit U(M.Data, M.Data + M.Size);
|
Unit U(M.Data, M.Data + M.Size);
|
||||||
@ -260,7 +266,6 @@ class TraceState {
|
|||||||
Signed >>= 16;
|
Signed >>= 16;
|
||||||
return Signed == 0 || Signed == -1L;
|
return Signed == 0 || Signed == -1L;
|
||||||
}
|
}
|
||||||
bool RecordingTraces = false;
|
|
||||||
static const size_t kMaxMutations = 1 << 16;
|
static const size_t kMaxMutations = 1 << 16;
|
||||||
size_t NumMutations;
|
size_t NumMutations;
|
||||||
TraceBasedMutation Mutations[kMaxMutations];
|
TraceBasedMutation Mutations[kMaxMutations];
|
||||||
@ -320,7 +325,7 @@ void TraceState::DFSanMemcmpCallback(size_t CmpSize, const uint8_t *Data1,
|
|||||||
dfsan_label L2) {
|
dfsan_label L2) {
|
||||||
|
|
||||||
assert(ReallyHaveDFSan());
|
assert(ReallyHaveDFSan());
|
||||||
if (!RecordingTraces || !IsMyThread) return;
|
if (!RecordingMemcmp || !IsMyThread) return;
|
||||||
if (L1 == 0 && L2 == 0)
|
if (L1 == 0 && L2 == 0)
|
||||||
return; // Not actionable.
|
return; // Not actionable.
|
||||||
if (L1 != 0 && L2 != 0)
|
if (L1 != 0 && L2 != 0)
|
||||||
@ -417,7 +422,7 @@ void TraceState::TraceCmpCallback(uintptr_t PC, size_t CmpSize, size_t CmpType,
|
|||||||
|
|
||||||
void TraceState::TraceMemcmpCallback(size_t CmpSize, const uint8_t *Data1,
|
void TraceState::TraceMemcmpCallback(size_t CmpSize, const uint8_t *Data1,
|
||||||
const uint8_t *Data2) {
|
const uint8_t *Data2) {
|
||||||
if (!RecordingTraces || !IsMyThread) return;
|
if (!RecordingMemcmp || !IsMyThread) return;
|
||||||
CmpSize = std::min(CmpSize, TraceBasedMutation::kMaxSize);
|
CmpSize = std::min(CmpSize, TraceBasedMutation::kMaxSize);
|
||||||
int Added2 = TryToAddDesiredData(Data1, Data2, CmpSize);
|
int Added2 = TryToAddDesiredData(Data1, Data2, CmpSize);
|
||||||
int Added1 = TryToAddDesiredData(Data2, Data1, CmpSize);
|
int Added1 = TryToAddDesiredData(Data2, Data1, CmpSize);
|
||||||
@ -462,14 +467,14 @@ void Fuzzer::StopTraceRecording() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Fuzzer::AssignTaintLabels(uint8_t *Data, size_t Size) {
|
void Fuzzer::AssignTaintLabels(uint8_t *Data, size_t Size) {
|
||||||
if (!Options.UseTraces) return;
|
if (!Options.UseTraces && !Options.UseMemcmp) return;
|
||||||
if (!ReallyHaveDFSan()) return;
|
if (!ReallyHaveDFSan()) return;
|
||||||
for (size_t i = 0; i < Size; i++)
|
for (size_t i = 0; i < Size; i++)
|
||||||
dfsan_set_label(i + 1, &Data[i], 1);
|
dfsan_set_label(i + 1, &Data[i], 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Fuzzer::InitializeTraceState() {
|
void Fuzzer::InitializeTraceState() {
|
||||||
if (!Options.UseTraces) return;
|
if (!Options.UseTraces && !Options.UseMemcmp) return;
|
||||||
TS = new TraceState(USF, Options, &CurrentUnitData, &CurrentUnitSize);
|
TS = new TraceState(USF, Options, &CurrentUnitData, &CurrentUnitSize);
|
||||||
if (ReallyHaveDFSan()) {
|
if (ReallyHaveDFSan()) {
|
||||||
for (size_t i = 0; i < static_cast<size_t>(Options.MaxLen); i++) {
|
for (size_t i = 0; i < static_cast<size_t>(Options.MaxLen); i++) {
|
||||||
@ -492,12 +497,14 @@ static size_t InternalStrnlen(const char *S, size_t MaxLen) {
|
|||||||
} // namespace fuzzer
|
} // namespace fuzzer
|
||||||
|
|
||||||
using fuzzer::TS;
|
using fuzzer::TS;
|
||||||
|
using fuzzer::RecordingTraces;
|
||||||
|
using fuzzer::RecordingMemcmp;
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
void __dfsw___sanitizer_cov_trace_cmp(uint64_t SizeAndType, uint64_t Arg1,
|
void __dfsw___sanitizer_cov_trace_cmp(uint64_t SizeAndType, uint64_t Arg1,
|
||||||
uint64_t Arg2, dfsan_label L0,
|
uint64_t Arg2, dfsan_label L0,
|
||||||
dfsan_label L1, dfsan_label L2) {
|
dfsan_label L1, dfsan_label L2) {
|
||||||
if (!TS) return;
|
if (!RecordingTraces) return;
|
||||||
assert(L0 == 0);
|
assert(L0 == 0);
|
||||||
uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
|
uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
|
||||||
uint64_t CmpSize = (SizeAndType >> 32) / 8;
|
uint64_t CmpSize = (SizeAndType >> 32) / 8;
|
||||||
@ -507,7 +514,7 @@ void __dfsw___sanitizer_cov_trace_cmp(uint64_t SizeAndType, uint64_t Arg1,
|
|||||||
|
|
||||||
void __dfsw___sanitizer_cov_trace_switch(uint64_t Val, uint64_t *Cases,
|
void __dfsw___sanitizer_cov_trace_switch(uint64_t Val, uint64_t *Cases,
|
||||||
dfsan_label L1, dfsan_label L2) {
|
dfsan_label L1, dfsan_label L2) {
|
||||||
if (!TS) return;
|
if (!RecordingTraces) return;
|
||||||
uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
|
uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
|
||||||
TS->DFSanSwitchCallback(PC, Cases[1], Val, Cases[0], Cases+2, L1);
|
TS->DFSanSwitchCallback(PC, Cases[1], Val, Cases[0], Cases+2, L1);
|
||||||
}
|
}
|
||||||
@ -515,7 +522,7 @@ void __dfsw___sanitizer_cov_trace_switch(uint64_t Val, uint64_t *Cases,
|
|||||||
void dfsan_weak_hook_memcmp(void *caller_pc, const void *s1, const void *s2,
|
void dfsan_weak_hook_memcmp(void *caller_pc, const void *s1, const void *s2,
|
||||||
size_t n, dfsan_label s1_label,
|
size_t n, dfsan_label s1_label,
|
||||||
dfsan_label s2_label, dfsan_label n_label) {
|
dfsan_label s2_label, dfsan_label n_label) {
|
||||||
if (!TS) return;
|
if (!RecordingMemcmp) return;
|
||||||
dfsan_label L1 = dfsan_read_label(s1, n);
|
dfsan_label L1 = dfsan_read_label(s1, n);
|
||||||
dfsan_label L2 = dfsan_read_label(s2, n);
|
dfsan_label L2 = dfsan_read_label(s2, n);
|
||||||
TS->DFSanMemcmpCallback(n, reinterpret_cast<const uint8_t *>(s1),
|
TS->DFSanMemcmpCallback(n, reinterpret_cast<const uint8_t *>(s1),
|
||||||
@ -525,7 +532,7 @@ void dfsan_weak_hook_memcmp(void *caller_pc, const void *s1, const void *s2,
|
|||||||
void dfsan_weak_hook_strncmp(void *caller_pc, const char *s1, const char *s2,
|
void dfsan_weak_hook_strncmp(void *caller_pc, const char *s1, const char *s2,
|
||||||
size_t n, dfsan_label s1_label,
|
size_t n, dfsan_label s1_label,
|
||||||
dfsan_label s2_label, dfsan_label n_label) {
|
dfsan_label s2_label, dfsan_label n_label) {
|
||||||
if (!TS) return;
|
if (!RecordingMemcmp) return;
|
||||||
n = std::min(n, fuzzer::InternalStrnlen(s1, n));
|
n = std::min(n, fuzzer::InternalStrnlen(s1, n));
|
||||||
n = std::min(n, fuzzer::InternalStrnlen(s2, n));
|
n = std::min(n, fuzzer::InternalStrnlen(s2, n));
|
||||||
dfsan_label L1 = dfsan_read_label(s1, n);
|
dfsan_label L1 = dfsan_read_label(s1, n);
|
||||||
@ -536,7 +543,7 @@ void dfsan_weak_hook_strncmp(void *caller_pc, const char *s1, const char *s2,
|
|||||||
|
|
||||||
void dfsan_weak_hook_strcmp(void *caller_pc, const char *s1, const char *s2,
|
void dfsan_weak_hook_strcmp(void *caller_pc, const char *s1, const char *s2,
|
||||||
dfsan_label s1_label, dfsan_label s2_label) {
|
dfsan_label s1_label, dfsan_label s2_label) {
|
||||||
if (!TS) return;
|
if (!RecordingMemcmp) return;
|
||||||
size_t Len1 = strlen(s1);
|
size_t Len1 = strlen(s1);
|
||||||
size_t Len2 = strlen(s2);
|
size_t Len2 = strlen(s2);
|
||||||
size_t N = std::min(Len1, Len2);
|
size_t N = std::min(Len1, Len2);
|
||||||
@ -555,7 +562,7 @@ void dfsan_weak_hook_strcmp(void *caller_pc, const char *s1, const char *s2,
|
|||||||
#if LLVM_FUZZER_DEFINES_SANITIZER_WEAK_HOOOKS
|
#if LLVM_FUZZER_DEFINES_SANITIZER_WEAK_HOOOKS
|
||||||
void __sanitizer_weak_hook_memcmp(void *caller_pc, const void *s1,
|
void __sanitizer_weak_hook_memcmp(void *caller_pc, const void *s1,
|
||||||
const void *s2, size_t n, int result) {
|
const void *s2, size_t n, int result) {
|
||||||
if (!TS) return;
|
if (!RecordingMemcmp) return;
|
||||||
if (result == 0) return; // No reason to mutate.
|
if (result == 0) return; // No reason to mutate.
|
||||||
if (n <= 1) return; // Not interesting.
|
if (n <= 1) return; // Not interesting.
|
||||||
TS->TraceMemcmpCallback(n, reinterpret_cast<const uint8_t *>(s1),
|
TS->TraceMemcmpCallback(n, reinterpret_cast<const uint8_t *>(s1),
|
||||||
@ -564,7 +571,7 @@ void __sanitizer_weak_hook_memcmp(void *caller_pc, const void *s1,
|
|||||||
|
|
||||||
void __sanitizer_weak_hook_strncmp(void *caller_pc, const char *s1,
|
void __sanitizer_weak_hook_strncmp(void *caller_pc, const char *s1,
|
||||||
const char *s2, size_t n, int result) {
|
const char *s2, size_t n, int result) {
|
||||||
if (!TS) return;
|
if (!RecordingMemcmp) return;
|
||||||
if (result == 0) return; // No reason to mutate.
|
if (result == 0) return; // No reason to mutate.
|
||||||
size_t Len1 = fuzzer::InternalStrnlen(s1, n);
|
size_t Len1 = fuzzer::InternalStrnlen(s1, n);
|
||||||
size_t Len2 = fuzzer::InternalStrnlen(s2, n);
|
size_t Len2 = fuzzer::InternalStrnlen(s2, n);
|
||||||
@ -577,7 +584,7 @@ void __sanitizer_weak_hook_strncmp(void *caller_pc, const char *s1,
|
|||||||
|
|
||||||
void __sanitizer_weak_hook_strcmp(void *caller_pc, const char *s1,
|
void __sanitizer_weak_hook_strcmp(void *caller_pc, const char *s1,
|
||||||
const char *s2, int result) {
|
const char *s2, int result) {
|
||||||
if (!TS) return;
|
if (!RecordingMemcmp) return;
|
||||||
if (result == 0) return; // No reason to mutate.
|
if (result == 0) return; // No reason to mutate.
|
||||||
size_t Len1 = strlen(s1);
|
size_t Len1 = strlen(s1);
|
||||||
size_t Len2 = strlen(s2);
|
size_t Len2 = strlen(s2);
|
||||||
@ -592,7 +599,7 @@ void __sanitizer_weak_hook_strcmp(void *caller_pc, const char *s1,
|
|||||||
__attribute__((visibility("default")))
|
__attribute__((visibility("default")))
|
||||||
void __sanitizer_cov_trace_cmp(uint64_t SizeAndType, uint64_t Arg1,
|
void __sanitizer_cov_trace_cmp(uint64_t SizeAndType, uint64_t Arg1,
|
||||||
uint64_t Arg2) {
|
uint64_t Arg2) {
|
||||||
if (!TS) return;
|
if (!RecordingTraces) return;
|
||||||
uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
|
uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
|
||||||
uint64_t CmpSize = (SizeAndType >> 32) / 8;
|
uint64_t CmpSize = (SizeAndType >> 32) / 8;
|
||||||
uint64_t Type = (SizeAndType << 32) >> 32;
|
uint64_t Type = (SizeAndType << 32) >> 32;
|
||||||
@ -601,7 +608,7 @@ void __sanitizer_cov_trace_cmp(uint64_t SizeAndType, uint64_t Arg1,
|
|||||||
|
|
||||||
__attribute__((visibility("default")))
|
__attribute__((visibility("default")))
|
||||||
void __sanitizer_cov_trace_switch(uint64_t Val, uint64_t *Cases) {
|
void __sanitizer_cov_trace_switch(uint64_t Val, uint64_t *Cases) {
|
||||||
if (!TS) return;
|
if (!RecordingTraces) return;
|
||||||
uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
|
uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
|
||||||
TS->TraceSwitchCallback(PC, Cases[1], Val, Cases[0], Cases + 2);
|
TS->TraceSwitchCallback(PC, Cases[1], Val, Cases[0], Cases + 2);
|
||||||
}
|
}
|
||||||
|
@ -4,14 +4,14 @@ Done10000000: Done 10000000 runs in
|
|||||||
|
|
||||||
RUN: not LLVMFuzzer-SimpleCmpTest -use_traces=1 -seed=1 -runs=10000001 2>&1 | FileCheck %s
|
RUN: not LLVMFuzzer-SimpleCmpTest -use_traces=1 -seed=1 -runs=10000001 2>&1 | FileCheck %s
|
||||||
|
|
||||||
RUN: not LLVMFuzzer-MemcmpTest -use_traces=1 -seed=4294967295 -runs=100000 2>&1 | FileCheck %s
|
RUN: not LLVMFuzzer-MemcmpTest -seed=4294967295 -runs=100000 2>&1 | FileCheck %s
|
||||||
RUN: LLVMFuzzer-MemcmpTest -seed=4294967295 -runs=1000000 2>&1 | FileCheck %s --check-prefix=Done1000000
|
RUN: LLVMFuzzer-MemcmpTest -use_memcmp=0 -seed=4294967295 -runs=1000000 2>&1 | FileCheck %s --check-prefix=Done1000000
|
||||||
|
|
||||||
RUN: not LLVMFuzzer-StrncmpTest -use_traces=1 -seed=1 -runs=100000 2>&1 | FileCheck %s
|
RUN: not LLVMFuzzer-StrncmpTest -seed=1 -runs=100000 2>&1 | FileCheck %s
|
||||||
RUN: LLVMFuzzer-StrncmpTest -seed=1 -runs=1000000 2>&1 | FileCheck %s --check-prefix=Done1000000
|
RUN: LLVMFuzzer-StrncmpTest -use_memcmp=0 -seed=1 -runs=1000000 2>&1 | FileCheck %s --check-prefix=Done1000000
|
||||||
|
|
||||||
RUN: not LLVMFuzzer-StrcmpTest -use_traces=1 -seed=1 -runs=200000 2>&1 | FileCheck %s
|
RUN: not LLVMFuzzer-StrcmpTest -seed=1 -runs=200000 2>&1 | FileCheck %s
|
||||||
RUN: LLVMFuzzer-StrcmpTest -seed=1 -runs=1000000 2>&1 | FileCheck %s --check-prefix=Done1000000
|
RUN: LLVMFuzzer-StrcmpTest -use_memcmp=0 -seed=1 -runs=1000000 2>&1 | FileCheck %s --check-prefix=Done1000000
|
||||||
|
|
||||||
RUN: not LLVMFuzzer-SwitchTest -use_traces=1 -seed=1 -runs=1000002 2>&1 | FileCheck %s
|
RUN: not LLVMFuzzer-SwitchTest -use_traces=1 -seed=1 -runs=1000002 2>&1 | FileCheck %s
|
||||||
RUN: LLVMFuzzer-SwitchTest -seed=1 -runs=1000000 2>&1 | FileCheck %s --check-prefix=Done1000000
|
RUN: LLVMFuzzer-SwitchTest -seed=1 -runs=1000000 2>&1 | FileCheck %s --check-prefix=Done1000000
|
||||||
@ -19,7 +19,7 @@ RUN: LLVMFuzzer-SwitchTest -seed=1 -runs=1000000 2>&1 | FileC
|
|||||||
RUN: not LLVMFuzzer-SimpleHashTest -use_traces=1 -seed=1 -runs=10000000 2>&1 | FileCheck %s
|
RUN: not LLVMFuzzer-SimpleHashTest -use_traces=1 -seed=1 -runs=10000000 2>&1 | FileCheck %s
|
||||||
RUN: LLVMFuzzer-SimpleHashTest -seed=1 -runs=10000000 2>&1 | FileCheck %s --check-prefix=Done10000000
|
RUN: LLVMFuzzer-SimpleHashTest -seed=1 -runs=10000000 2>&1 | FileCheck %s --check-prefix=Done10000000
|
||||||
|
|
||||||
RUN: LLVMFuzzer-RepeatedMemcmp -use_traces=1 -seed=1 -runs=100000 2>&1 | FileCheck %s --check-prefix=RECOMMENDED_DICT
|
RUN: LLVMFuzzer-RepeatedMemcmp -seed=1 -runs=100000 2>&1 | FileCheck %s --check-prefix=RECOMMENDED_DICT
|
||||||
RECOMMENDED_DICT:###### Recommended dictionary. ######
|
RECOMMENDED_DICT:###### Recommended dictionary. ######
|
||||||
RECOMMENDED_DICT-DAG: "foo"
|
RECOMMENDED_DICT-DAG: "foo"
|
||||||
RECOMMENDED_DICT-DAG: "bar"
|
RECOMMENDED_DICT-DAG: "bar"
|
||||||
|
Loading…
Reference in New Issue
Block a user