mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 13:21:05 +00:00
Bug 1860108 - Add IPC_SingleMessage fuzzer. r=nika,truber
Differential Revision: https://phabricator.services.mozilla.com/D191468
This commit is contained in:
parent
0f45ed2bb6
commit
c0d3b745c4
@ -1703,6 +1703,13 @@ void MessageChannel::DispatchMessage(ActorLifecycleProxy* aProxy,
|
|||||||
|
|
||||||
UniquePtr<Message> reply;
|
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(),
|
IPC_LOG("DispatchMessage: seqno=%d, xid=%d", aMsg->seqno(),
|
||||||
aMsg->transaction_id());
|
aMsg->transaction_id());
|
||||||
AddProfilerMarker(*aMsg, MessageDirection::eReceiving);
|
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) {
|
if (reply && ChannelConnected == mChannelState) {
|
||||||
IPC_LOG("Sending reply seqno=%d, xid=%d", aMsg->seqno(),
|
IPC_LOG("Sending reply seqno=%d, xid=%d", aMsg->seqno(),
|
||||||
aMsg->transaction_id());
|
aMsg->transaction_id());
|
||||||
|
@ -22,6 +22,10 @@
|
|||||||
#include "mozilla/ipc/PBackground.h"
|
#include "mozilla/ipc/PBackground.h"
|
||||||
#include "mozilla/dom/PContent.h"
|
#include "mozilla/dom/PContent.h"
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
using namespace mojo::core::ports;
|
using namespace mojo::core::ports;
|
||||||
using namespace mozilla::ipc;
|
using namespace mozilla::ipc;
|
||||||
|
|
||||||
@ -67,6 +71,21 @@ IPCFuzzController::IPCFuzzController()
|
|||||||
portNameToIndex["PProfiler"] = 6;
|
portNameToIndex["PProfiler"] = 6;
|
||||||
portNameToIndex["PVRManager"] = 7;
|
portNameToIndex["PVRManager"] = 7;
|
||||||
portNameToIndex["PCanvasManager"] = 8;
|
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
|
// static
|
||||||
@ -79,7 +98,8 @@ void IPCFuzzController::InitializeIPCTypes() {
|
|||||||
const char* cons = "Constructor";
|
const char* cons = "Constructor";
|
||||||
size_t cons_len = strlen(cons);
|
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) {
|
for (uint32_t start = 0; start < LastMsgIndex; ++start) {
|
||||||
uint32_t i;
|
uint32_t i;
|
||||||
@ -88,13 +108,20 @@ void IPCFuzzController::InitializeIPCTypes() {
|
|||||||
|
|
||||||
if (name[0] == '<') break;
|
if (name[0] == '<') break;
|
||||||
|
|
||||||
if (targetName && !strcmp(name, targetName)) {
|
if (targetNameTrigger && !strcmp(name, targetNameTrigger)) {
|
||||||
MOZ_FUZZING_NYX_PRINTF(
|
MOZ_FUZZING_NYX_PRINTF(
|
||||||
"INFO: [InitializeIPCTypes] Located trigger message (%s, %d)\n",
|
"INFO: [InitializeIPCTypes] Located trigger message (%s, %d)\n",
|
||||||
targetName, i);
|
targetNameTrigger, i);
|
||||||
mIPCTriggerMsg = 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);
|
size_t len = strlen(name);
|
||||||
if (len > cons_len && !memcmp(cons, name + len - cons_len, cons_len)) {
|
if (len > cons_len && !memcmp(cons, name + len - cons_len, cons_len)) {
|
||||||
constructorTypes.insert(i);
|
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 fuzzing
|
||||||
} // namespace mozilla
|
} // namespace mozilla
|
||||||
|
@ -111,6 +111,10 @@ class IPCFuzzController {
|
|||||||
void AddToplevelActor(mojo::core::ports::PortName name,
|
void AddToplevelActor(mojo::core::ports::PortName name,
|
||||||
mozilla::ipc::ProtocolId protocolId);
|
mozilla::ipc::ProtocolId protocolId);
|
||||||
|
|
||||||
|
// Used for the IPC_SingleMessage fuzzer
|
||||||
|
UniquePtr<IPC::Message> replaceIPCMessage(UniquePtr<IPC::Message> aMsg);
|
||||||
|
void syncAfterReplace();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// This is a mapping from port name to a pair of last seen sequence numbers.
|
// This is a mapping from port name to a pair of last seen sequence numbers.
|
||||||
std::unordered_map<mojo::core::ports::PortName, SeqNoPair> portSeqNos;
|
std::unordered_map<mojo::core::ports::PortName, SeqNoPair> portSeqNos;
|
||||||
@ -190,6 +194,14 @@ class IPCFuzzController {
|
|||||||
// a specific actor.
|
// a specific actor.
|
||||||
uint32_t mIPCTriggerMsg;
|
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();
|
IPCFuzzController();
|
||||||
NYX_DISALLOW_COPY_AND_ASSIGN(IPCFuzzController);
|
NYX_DISALLOW_COPY_AND_ASSIGN(IPCFuzzController);
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user