Bug 1860108 - Add IPC_SingleMessage fuzzer. r=nika,truber

Differential Revision: https://phabricator.services.mozilla.com/D191468
This commit is contained in:
Christian Holler (:decoder) 2023-11-06 22:22:59 +00:00
parent 0f45ed2bb6
commit c0d3b745c4
3 changed files with 232 additions and 3 deletions

View File

@ -1703,6 +1703,13 @@ void MessageChannel::DispatchMessage(ActorLifecycleProxy* aProxy,
UniquePtr<Message> reply;
#ifdef FUZZING_SNAPSHOT
if (IsCrossProcess()) {
aMsg = mozilla::fuzzing::IPCFuzzController::instance().replaceIPCMessage(
std::move(aMsg));
}
#endif
IPC_LOG("DispatchMessage: seqno=%d, xid=%d", aMsg->seqno(),
aMsg->transaction_id());
AddProfilerMarker(*aMsg, MessageDirection::eReceiving);
@ -1736,6 +1743,12 @@ void MessageChannel::DispatchMessage(ActorLifecycleProxy* aProxy,
}
}
#ifdef FUZZING_SNAPSHOT
if (aMsg->IsFuzzMsg()) {
mozilla::fuzzing::IPCFuzzController::instance().syncAfterReplace();
}
#endif
if (reply && ChannelConnected == mChannelState) {
IPC_LOG("Sending reply seqno=%d, xid=%d", aMsg->seqno(),
aMsg->transaction_id());

View File

@ -22,6 +22,10 @@
#include "mozilla/ipc/PBackground.h"
#include "mozilla/dom/PContent.h"
#include <fstream>
#include <sstream>
#include <algorithm>
using namespace mojo::core::ports;
using namespace mozilla::ipc;
@ -67,6 +71,21 @@ IPCFuzzController::IPCFuzzController()
portNameToIndex["PProfiler"] = 6;
portNameToIndex["PVRManager"] = 7;
portNameToIndex["PCanvasManager"] = 8;
// Used to select the n-th trigger message as a starting point for fuzzing
// in single message mode. A value of 1 will skip the first matching message
// and start fuzzing on the second message, and so on.
if (!!getenv("MOZ_FUZZ_IPC_TRIGGER_SINGLEMSG_WAIT")) {
mIPCTriggerSingleMsgWait =
atoi(getenv("MOZ_FUZZ_IPC_TRIGGER_SINGLEMSG_WAIT"));
}
// When set, dump all IPC message at or above the specified size to files.
// Useful to collect samples of different types in one run.
if (!!getenv("MOZ_FUZZ_IPC_DUMP_ALL_MSGS_SIZE")) {
mIPCDumpAllMsgsSize.emplace(
atoi(getenv("MOZ_FUZZ_IPC_DUMP_ALL_MSGS_SIZE")));
}
}
// static
@ -79,7 +98,8 @@ void IPCFuzzController::InitializeIPCTypes() {
const char* cons = "Constructor";
size_t cons_len = strlen(cons);
const char* targetName = getenv("MOZ_FUZZ_IPC_TRIGGER");
const char* targetNameTrigger = getenv("MOZ_FUZZ_IPC_TRIGGER");
const char* targetNameDump = getenv("MOZ_FUZZ_IPC_DUMPMSG");
for (uint32_t start = 0; start < LastMsgIndex; ++start) {
uint32_t i;
@ -88,13 +108,20 @@ void IPCFuzzController::InitializeIPCTypes() {
if (name[0] == '<') break;
if (targetName && !strcmp(name, targetName)) {
if (targetNameTrigger && !strcmp(name, targetNameTrigger)) {
MOZ_FUZZING_NYX_PRINTF(
"INFO: [InitializeIPCTypes] Located trigger message (%s, %d)\n",
targetName, i);
targetNameTrigger, i);
mIPCTriggerMsg = i;
}
if (targetNameDump && !strcmp(name, targetNameDump)) {
MOZ_FUZZING_NYX_PRINTF(
"INFO: [InitializeIPCTypes] Located dump message (%s, %d)\n",
targetNameDump, i);
mIPCDumpMsg.emplace(i);
}
size_t len = strlen(name);
if (len > cons_len && !memcmp(cons, name + len - cons_len, cons_len)) {
constructorTypes.insert(i);
@ -891,5 +918,182 @@ void IPCFuzzController::SynchronizeOnMessageExecution(
}
}
static void dumpIPCMessageToFile(const UniquePtr<IPC::Message>& aMsg,
uint32_t aDumpCount, bool aUseNyx = false) {
std::stringstream dumpFilename;
std::string msgName(IPC::StringFromIPCMessageType(aMsg->type()));
std::replace(msgName.begin(), msgName.end(), ':', '_');
if (aUseNyx) {
dumpFilename << "seeds/";
}
dumpFilename << msgName << aDumpCount << ".bin";
Pickle::BufferList::IterImpl iter(aMsg->Buffers());
Vector<char, 256, InfallibleAllocPolicy> dumpBuffer;
if (!dumpBuffer.initLengthUninitialized(sizeof(IPC::Message::Header) +
aMsg->Buffers().Size())) {
MOZ_FUZZING_NYX_ABORT("dumpBuffer.initLengthUninitialized failed\n");
}
if (!aMsg->Buffers().ReadBytes(
iter,
reinterpret_cast<char*>(dumpBuffer.begin() +
sizeof(IPC::Message::Header)),
dumpBuffer.length() - sizeof(IPC::Message::Header))) {
MOZ_FUZZING_NYX_ABORT("ReadBytes failed\n");
}
memcpy(dumpBuffer.begin(), aMsg->header(), sizeof(IPC::Message::Header));
if (aUseNyx) {
MOZ_FUZZING_NYX_PRINTF("INFO: Calling dump_file: %s Size: %zu\n",
dumpFilename.str().c_str(), dumpBuffer.length());
Nyx::instance().dump_file(reinterpret_cast<char*>(dumpBuffer.begin()),
dumpBuffer.length(), dumpFilename.str().c_str());
} else {
std::fstream file;
file.open(dumpFilename.str(), std::ios::out | std::ios::binary);
file.write(reinterpret_cast<char*>(dumpBuffer.begin()),
dumpBuffer.length());
file.close();
}
}
UniquePtr<IPC::Message> IPCFuzzController::replaceIPCMessage(
UniquePtr<IPC::Message> aMsg) {
if (!mozilla::fuzzing::Nyx::instance().is_enabled("IPC_SingleMessage")) {
// Fuzzer is not enabled.
return aMsg;
}
if (!XRE_IsParentProcess()) {
// For now we only care about things in the parent process.
return aMsg;
}
if (aMsg->type() != mIPCTriggerMsg) {
if ((mIPCDumpMsg && aMsg->type() == mIPCDumpMsg.value()) ||
(mIPCDumpAllMsgsSize.isSome() &&
aMsg->Buffers().Size() >= mIPCDumpAllMsgsSize.value())) {
dumpIPCMessageToFile(aMsg, mIPCDumpCount);
mIPCDumpCount++;
}
// Not the trigger message. Output additional information here for
// automation purposes. This shouldn't be an issue as we will only
// output these messages until we take a snapshot.
MOZ_FUZZING_NYX_PRINTF("INFO: [OnIPCMessage] Message: %s Size: %u\n",
IPC::StringFromIPCMessageType(aMsg->type()),
aMsg->header()->payload_size);
return aMsg;
} else {
// Dump the trigger message through Nyx in case we want to use it
// as a seed to AFL++ outside of the VM.
dumpIPCMessageToFile(aMsg, mIPCDumpCount, true /* aUseNyx */);
mIPCDumpCount++;
if (mIPCTriggerSingleMsgWait > 0) {
mIPCTriggerSingleMsgWait--;
return aMsg;
}
}
const size_t maxMsgSize = 4096;
Vector<char, 256, InfallibleAllocPolicy> buffer;
if (!buffer.initLengthUninitialized(maxMsgSize)) {
MOZ_FUZZING_NYX_ABORT("ERROR: Failed to initialize buffer!\n");
}
char* ipcMsgData = buffer.begin();
// Copy the header of the original message
memcpy(ipcMsgData, aMsg->header(), sizeof(IPC::Message::Header));
IPC::Message::Header* ipchdr = (IPC::Message::Header*)ipcMsgData;
// //
// *** Snapshot Point *** //
// //
MOZ_FUZZING_NYX_PRINT("INFO: Performing snapshot...\n");
Nyx::instance().start();
IPCFuzzController::instance().useLastActor = 0;
IPCFuzzController::instance().useLastPortName = false;
MOZ_FUZZING_NYX_DEBUG("DEBUG: Requesting data...\n");
// Grab enough data to send at most `maxMsgSize` bytes
uint32_t bufsize =
Nyx::instance().get_raw_data((uint8_t*)buffer.begin(), buffer.length());
if (bufsize == 0xFFFFFFFF) {
MOZ_FUZZING_NYX_DEBUG("Nyx: Out of data.\n");
Nyx::instance().release(0);
}
#ifdef FUZZ_DEBUG
MOZ_FUZZING_NYX_PRINTF("DEBUG: Got buffer of size %u...\n", bufsize);
#endif
// Payload must be int aligned
bufsize -= bufsize % 4;
// Need at least a header and the control bytes.
if (bufsize < sizeof(IPC::Message::Header)) {
MOZ_FUZZING_NYX_DEBUG("INFO: Not enough data to craft IPC message.\n");
Nyx::instance().release(0);
}
buffer.shrinkTo(bufsize);
size_t ipcMsgLen = buffer.length();
ipchdr->payload_size = ipcMsgLen - sizeof(IPC::Message::Header);
if (Nyx::instance().is_replay()) {
MOZ_FUZZING_NYX_PRINT("INFO: Replaying IPC packet with payload:\n");
for (uint32_t i = 0; i < ipcMsgLen - sizeof(IPC::Message::Header); ++i) {
if (i % 16 == 0) {
MOZ_FUZZING_NYX_PRINT("\n ");
}
MOZ_FUZZING_NYX_PRINTF(
"0x%02X ",
(unsigned char)(ipcMsgData[sizeof(IPC::Message::Header) + i]));
}
MOZ_FUZZING_NYX_PRINT("\n");
}
UniquePtr<IPC::Message> msg(new IPC::Message(ipcMsgData, ipcMsgLen));
// This marks the message as a fuzzing message. Without this, it will
// be ignored by MessageTask and also not even scheduled by NodeChannel
// in asynchronous mode. We use this to ignore any IPC activity that
// happens just while we are fuzzing.
msg->SetFuzzMsg();
return msg;
}
void IPCFuzzController::syncAfterReplace() {
if (!mozilla::fuzzing::Nyx::instance().is_enabled("IPC_SingleMessage")) {
// Fuzzer is not enabled.
return;
}
if (!XRE_IsParentProcess()) {
// For now we only care about things in the parent process.
return;
}
if (!Nyx::instance().started()) {
// Not started yet
return;
}
MOZ_FUZZING_NYX_DEBUG(
"DEBUG: ======== END OF ITERATION (RELEASE) ========\n");
Nyx::instance().release(1);
}
} // namespace fuzzing
} // namespace mozilla

View File

@ -111,6 +111,10 @@ class IPCFuzzController {
void AddToplevelActor(mojo::core::ports::PortName name,
mozilla::ipc::ProtocolId protocolId);
// Used for the IPC_SingleMessage fuzzer
UniquePtr<IPC::Message> replaceIPCMessage(UniquePtr<IPC::Message> aMsg);
void syncAfterReplace();
private:
// This is a mapping from port name to a pair of last seen sequence numbers.
std::unordered_map<mojo::core::ports::PortName, SeqNoPair> portSeqNos;
@ -190,6 +194,14 @@ class IPCFuzzController {
// a specific actor.
uint32_t mIPCTriggerMsg;
// Used to dump IPC messages in single message mode
Maybe<uint32_t> mIPCDumpMsg;
Maybe<uint32_t> mIPCDumpAllMsgsSize;
uint32_t mIPCDumpCount = 0;
// Used to select a particular packet instance in single message mode
uint32_t mIPCTriggerSingleMsgWait = 0;
IPCFuzzController();
NYX_DISALLOW_COPY_AND_ASSIGN(IPCFuzzController);
};