blob: 3e13540c573d2b4fad5369f80ce7360d82d83ce0 [file] [log] [blame]
// Copyright 2021 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/sys/fuzzing/framework/engine/module-proxy.h"
#include <lib/syslog/cpp/macros.h>
namespace fuzzing {
namespace {
// Convert counter values to "features" in the same manner as AFL, described here:
// http://lcamtuf.coredump.cx/afl/technical_details.txt.
//
// This implementation is based on the one used by libFuzzer. Do not try to optimize this further;
// as written the compiler is able to effectively optimize it at -O2 and higher.
uint8_t to_feature(uint8_t counter) {
uint8_t feature = 0;
if (counter >= 128) {
feature = 1 << 7;
} else if (counter >= 32) {
feature = 1 << 6;
} else if (counter >= 16) {
feature = 1 << 5;
} else if (counter >= 8) {
feature = 1 << 4;
} else if (counter >= 4) {
feature = 1 << 3;
} else if (counter >= 3) {
feature = 1 << 2;
} else if (counter >= 2) {
feature = 1 << 1;
} else if (counter >= 1) {
feature = 1 << 0;
}
return feature;
}
// High bit in each byte of a uint64_t. See |MeasureImpl|.
constexpr uint64_t kHiBitsMask = 0x80'80'80'80'80'80'80'80ULL;
} // namespace
ModuleProxy::ModuleProxy(Identifier id, size_t size) : id_(id), num_u64s_(size / sizeof(uint64_t)) {
// This method expects 64-bit alignment to simplify iteration.
FX_CHECK(size % sizeof(uint64_t) == 0);
features_.reset(new uint64_t[num_u64s_]);
accumulated_.reset(new uint64_t[num_u64s_]);
Clear();
}
void ModuleProxy::Add(void* counters, size_t counters_len) {
FX_CHECK(counters_len == size());
// This method expects 64-bit alignment to simplify iteration.
FX_CHECK(reinterpret_cast<uintptr_t>(counters) % sizeof(uint64_t) == 0);
counters_.push_back(reinterpret_cast<uint64_t*>(counters));
}
void ModuleProxy::Remove(void* counters) {
counters_.erase(
std::remove(counters_.begin(), counters_.end(), reinterpret_cast<uint64_t*>(counters)),
counters_.end());
}
size_t ModuleProxy::Measure() { return MeasureImpl(/* accumulate */ false); }
size_t ModuleProxy::Accumulate() { return MeasureImpl(/* accumulate */ true); }
size_t ModuleProxy::MeasureImpl(bool accumulate) {
size_t num_new_features = 0;
memset(features_.get(), 0, size());
// First, sum all counters into the features array.
for (auto* counters : counters_) {
for (size_t i = 0; i < num_u64s_; ++i) {
if (counters[i]) {
// Sums over 128 map to the same feature and don't need to be distinguish. This means we can
// get the right features by simply adding 7-bits of each byte in parallel and ORing the
// high bits. This avoids overflowing from one byte to the next.
uint64_t hi_bits = (counters[i] | features_[i]) & kHiBitsMask;
features_[i] = ((features_[i] & ~kHiBitsMask) + (counters[i] & ~kHiBitsMask)) | hi_bits;
}
}
}
// Next, convert the summed counters to features.
for (size_t i = 0; i < num_u64s_; ++i) {
if (features_[i]) {
uint8_t* feature = reinterpret_cast<uint8_t*>(&features_[i]);
for (size_t j = 0; j < sizeof(uint64_t); ++j) {
feature[j] = static_cast<uint8_t>(to_feature(feature[j]));
}
num_new_features += __builtin_popcountll(~accumulated_[i] & features_[i]);
if (accumulate) {
accumulated_[i] |= features_[i];
}
}
}
return num_new_features;
}
size_t ModuleProxy::GetCoverage(size_t* out_num_features) {
size_t num_pcs = 0;
size_t num_features = 0;
for (size_t i = 0; i < num_u64s_; ++i) {
auto accumulated = accumulated_[i];
if (accumulated) {
num_features += __builtin_popcountll(accumulated_[i]);
for (; accumulated; accumulated >>= 8) {
if (accumulated & 0xff) {
num_pcs++;
}
}
}
}
if (out_num_features) {
*out_num_features = num_features;
}
return num_pcs;
}
void ModuleProxy::Clear() { memset(accumulated_.get(), 0, size()); }
} // namespace fuzzing