diff options
| author | Dmitry Vyukov <dvyukov@google.com> | 2017-10-20 11:30:59 +0200 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2017-10-23 09:59:39 +0200 |
| commit | b71450d9fbb6e1d07d83b01d2f2fe4b41c5cdefb (patch) | |
| tree | 1cfe7f7330e15879d6cfcb58e44a318c5b36e5d2 /executor/executor.h | |
| parent | 3c6fe803958431367baa476f5e591cf6d749640a (diff) | |
executor: prevent executor from messing with output region
When comparisons are enabled fuzzer somehow manages to discover
the output region and corrupt it. It seems to fetch the address
from some memory operations (mmap/munmap).
Don't leak the output region address.
Diffstat (limited to 'executor/executor.h')
| -rw-r--r-- | executor/executor.h | 38 |
1 files changed, 22 insertions, 16 deletions
diff --git a/executor/executor.h b/executor/executor.h index e4bafc21e..669851e90 100644 --- a/executor/executor.h +++ b/executor/executor.h @@ -26,6 +26,7 @@ const int kOutPipeFd = 251; // remapped from stdout const int kMaxInput = 2 << 20; const int kMaxOutput = 16 << 20; +const int kCoverSize = 64 << 10; const int kMaxArgs = 9; const int kMaxThreads = 16; const int kMaxCommands = 16 << 10; @@ -164,9 +165,8 @@ struct kcov_comparison_t { uint64_t arg2; uint64_t pc; - // Writes the structure using the write_one function for each field. - // Inspired by write_output() function. - void write(uint32_t* (*write_one)(uint32_t)); + bool ignore() const; + void write(); bool operator==(const struct kcov_comparison_t& other) const; bool operator<(const struct kcov_comparison_t& other) const; }; @@ -520,13 +520,19 @@ void handle_completion(thread_t* th) if (flag_collect_comps) { // Collect only the comparisons - comps_size = th->cover_size; + uint32_t ncomps = th->cover_size; kcov_comparison_t* start = (kcov_comparison_t*)th->cover_data; - kcov_comparison_t* end = start + comps_size; + kcov_comparison_t* end = start + ncomps; + if ((uint64_t*)end >= th->cover_data + kCoverSize) + fail("too many comparisons %u", ncomps); std::sort(start, end); - comps_size = std::unique(start, end) - start; - for (uint32_t i = 0; i < comps_size; ++i) - start[i].write(write_output); + ncomps = std::unique(start, end) - start; + for (uint32_t i = 0; i < ncomps; ++i) { + if (start[i].ignore()) + continue; + comps_size++; + start[i].write(); + } } else { // Write out feedback signals. // Currently it is code edges computed as xor of @@ -761,10 +767,10 @@ uint64_t read_input(uint64_t** input_posp, bool peek) return *input_pos; } -void kcov_comparison_t::write(uint32_t* (*write_one)(uint32_t)) +void kcov_comparison_t::write() { // Write order: type arg1 arg2 pc. - write_one((uint32_t)type); + write_output((uint32_t)type); // KCOV converts all arguments of size x first to uintx_t and then to // uint64_t. We want to properly extend signed values, e.g we want @@ -788,15 +794,15 @@ void kcov_comparison_t::write(uint32_t* (*write_one)(uint32_t)) } bool is_size_8 = (type & KCOV_CMP_SIZE_MASK) == KCOV_CMP_SIZE8; if (!is_size_8) { - write_one((uint32_t)arg1); - write_one((uint32_t)arg2); + write_output((uint32_t)arg1); + write_output((uint32_t)arg2); return; } // If we have 64 bits arguments then write them in Little-endian. - write_one((uint32_t)(arg1 & 0xFFFFFFFF)); - write_one((uint32_t)(arg1 >> 32)); - write_one((uint32_t)(arg2 & 0xFFFFFFFF)); - write_one((uint32_t)(arg2 >> 32)); + write_output((uint32_t)(arg1 & 0xFFFFFFFF)); + write_output((uint32_t)(arg1 >> 32)); + write_output((uint32_t)(arg2 & 0xFFFFFFFF)); + write_output((uint32_t)(arg2 >> 32)); } bool kcov_comparison_t::operator==(const struct kcov_comparison_t& other) const |
