blob: 528c0b3a8a4cd1b094d51376d4a72588b67f4719 [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/realmfuzzer/target/module.h"
#include <lib/syslog/cpp/macros.h>
#include <zircon/status.h>
#include <third_party/modp_b64/modp_b64.h>
#include "src/sys/fuzzing/common/module.h"
namespace fuzzing {
zx_status_t Module::Import(uint8_t* counters, const uintptr_t* pcs, size_t num_pcs) {
FX_CHECK(counters && pcs && num_pcs);
if (auto status = counters_.Mirror(counters, num_pcs); status != ZX_OK) {
FX_LOGS(WARNING) << "Failed to mirror module counters: " << zx_status_get_string(status);
return status;
}
// Make a position independent table from the PCs.
auto pc_table = std::make_unique<ModulePC[]>(num_pcs);
for (size_t i = 0; i < num_pcs; ++i) {
pc_table[i].pc = pcs[i * 2] - pcs[0];
pc_table[i].flags = pcs[i * 2 + 1];
}
// Double hash using both FNV1a and DJB2a to reduce the likelihood of collisions. We could use a
// cryptographic hash here, but that introduces unwanted dependencies, and this is good enough.
// The algorithms are taken from http://www.isthe.com/chongo/tech/comp/fnv/index.html and
// http://www.cse.yorku.ca/~oz/hash.html.
uint64_t fnv1a = 14695981039346656037ULL;
uint64_t djb2a = 5381;
auto* u8 = reinterpret_cast<uint8_t*>(pc_table.get());
size_t size = num_pcs * sizeof(ModulePC);
while (size-- > 0) {
fnv1a = (fnv1a ^ *u8) * 1099511628211ULL;
djb2a = ((djb2a << 5) + djb2a) ^ *u8;
u8++;
}
legacy_id_ = Identifier{fnv1a, djb2a};
// To squeeze as much data as possible into the null-terminated 32-character name limit, this
// omits the most significant byte of each hash and all padding characters. Thus, the 7 bytes
// hashes produce 10 characters each. Combined with 11 characters for the |target_id| (see |Share|
// below), this gives a total string length of 31.
char id[modp_b64_encode_len(sizeof(uint64_t) * 2)];
auto len1 = modp_b64_encode(&id[0], reinterpret_cast<char*>(&fnv1a), sizeof(uint64_t) - 1);
len1 -= 2;
FX_DCHECK(id[len1] == '=');
auto len2 = modp_b64_encode(&id[len1], reinterpret_cast<char*>(&djb2a), sizeof(uint64_t) - 1);
len2 += len1 - 2;
FX_DCHECK(id[len2] == '=');
id_ = std::string(id, len2);
return ZX_OK;
}
zx_status_t Module::Share(uint64_t target_id, zx::vmo* out) const {
if (auto status = counters_.Share(out); status != ZX_OK) {
FX_LOGS(WARNING) << "Failed to share module: " << zx_status_get_string(status);
return status;
}
// As noted above, this trims the final padding character to fit the |target_id| into 11
// characters. Keep this in sync with the decoding routines in engine/coverage-data.cc!
char name[ZX_MAX_NAME_LEN];
auto len = modp_b64_encode(name, reinterpret_cast<char*>(&target_id), sizeof(uint64_t));
FX_CHECK(len != size_t(-1));
FX_CHECK(name[len - 1] == '=');
strncpy(&name[len - 1], id_.c_str(), sizeof(name) - (len - 1));
if (auto status = out->set_property(ZX_PROP_NAME, name, sizeof(name)); status != ZX_OK) {
FX_LOGS(WARNING) << "Failed to set module ID: " << zx_status_get_string(status);
return status;
}
return ZX_OK;
}
} // namespace fuzzing