forked from chromium/chromium
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add a new LPM fuzzer for Mach message servers.
This fuzzer tool allows modeling the complex Mach IPC structures, including the generation and transfer of port rights. It also adds a build rule to convert textproto to binarypb, so that the seed corpus for these fuzzers can be human-editable/readable. Adds a simple fuzzer for the MachPortRendezvousServer. Bug: 932175 Change-Id: I909e4a8bac802ea1d4d73d26fcb0834803324360 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1526561 Reviewed-by: Mark Mentovai <mark@chromium.org> Reviewed-by: Jonathan Metzman <metzman@chromium.org> Reviewed-by: Nico Weber <thakis@chromium.org> Cr-Commit-Position: refs/heads/master@{#641854}
- Loading branch information
Showing
11 changed files
with
512 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
// Copyright 2019 The Chromium 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 "base/mac/mach_port_rendezvous.h" | ||
|
||
#include "base/logging.h" | ||
#include "base/mac/mach_logging.h" | ||
#include "base/synchronization/lock.h" | ||
#include "testing/libfuzzer/fuzzers/mach/mach_message_converter.h" | ||
#include "testing/libfuzzer/proto/lpm_interface.h" | ||
|
||
namespace base { | ||
|
||
struct MachPortRendezvousFuzzer { | ||
MachPortRendezvousFuzzer() { | ||
logging::SetMinLogLevel(logging::LOG_FATAL); | ||
|
||
mach_port_t port = | ||
base::MachPortRendezvousServer::GetInstance()->server_port_.get(); | ||
kern_return_t kr = mach_port_insert_right(mach_task_self(), port, port, | ||
MACH_MSG_TYPE_MAKE_SEND); | ||
MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_port_insert_right"; | ||
|
||
server_send_right.reset(port); | ||
} | ||
|
||
void ClearClientData() { | ||
base::MachPortRendezvousServer::GetInstance()->client_data_.clear(); | ||
} | ||
|
||
base::mac::ScopedMachSendRight server_send_right; | ||
}; | ||
|
||
} // namespace base | ||
|
||
DEFINE_BINARY_PROTO_FUZZER(const mach_fuzzer::MachMessage& message) { | ||
static base::MachPortRendezvousFuzzer environment; | ||
|
||
{ | ||
auto* server = base::MachPortRendezvousServer::GetInstance(); | ||
base::AutoLock lock(server->GetLock()); | ||
environment.ClearClientData(); | ||
server->RegisterPortsForPid( | ||
getpid(), {std::make_pair(0xbadbeef, base::MachRendezvousPort{ | ||
mach_task_self(), | ||
MACH_MSG_TYPE_COPY_SEND})}); | ||
} | ||
|
||
SendMessage(environment.server_send_right.get(), message); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
local_port: DEAD_NAME | ||
id: 0x6d727a76 | ||
include_body_if_not_complex: false |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
local_port: SEND | ||
id: 0x6d727a76 | ||
include_body_if_not_complex: false |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
# Copyright 2019 The Chromium Authors. All rights reserved. | ||
# Use of this source code is governed by a BSD-style license that can be | ||
# found in the LICENSE file. | ||
|
||
import("//third_party/protobuf/proto_library.gni") | ||
|
||
proto_library("proto") { | ||
sources = [ | ||
"mach_message.proto", | ||
] | ||
} | ||
|
||
source_set("converter") { | ||
sources = [ | ||
"mach_message_converter.cc", | ||
"mach_message_converter.h", | ||
] | ||
public_deps = [ | ||
":proto", | ||
] | ||
deps = [ | ||
"//base", | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
// Copyright 2019 The Chromium Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style license that can be | ||
// found in the LICENSE file. | ||
|
||
syntax = "proto2"; | ||
|
||
package mach_fuzzer; | ||
|
||
// Specifies a type of Mach port right to create. | ||
enum MachPortType { | ||
// Create a receive right and move it to the peer, while holding | ||
// a send right. | ||
RECEIVE = 0; | ||
// Create a receive right and make a send right during mach_msg. | ||
SEND = 1; | ||
// Create a receive right and vend a send-once right during mach_msg. | ||
SEND_ONCE = 2; | ||
// Create a dead name right and give a send right to it to the peer. | ||
DEAD_NAME = 3; | ||
// Create a receive right with no senders and move the receive right | ||
// to the peer. | ||
RECEIVE_NO_SENDERS = 4; | ||
} | ||
|
||
// Data to send in an out-of-line memory region in Mach message descriptor. | ||
message OutOfLineMemory { | ||
required bytes data = 1; | ||
} | ||
|
||
// Models a mach_msg_descriptor_t. | ||
message Descriptor { | ||
oneof descriptor_oneof { | ||
MachPortType port = 1; | ||
OutOfLineMemory ool = 2; | ||
} | ||
} | ||
|
||
// Models a Mach message structure including the header, optional body, | ||
// and inline data. | ||
message MachMessage { | ||
// Creates an optional port to put in msgh_local_port. If this is a receive | ||
// right, it will be dropped. | ||
optional MachPortType local_port = 1; | ||
|
||
// The msgh_id field. | ||
optional uint32 id = 2; | ||
|
||
// Optional Descriptors to carry in the message. | ||
repeated Descriptor descriptors = 3; | ||
|
||
// If no Descriptors are present, whether or not to include a mach_msg_body_t | ||
// in the message. | ||
required bool include_body_if_not_complex = 4; | ||
|
||
// Raw data bytes to send inline with the message. | ||
optional bytes data = 5; | ||
|
||
// Extensions can be used by clients to express structured message data, | ||
// which can be converted into bytes and placed in |data|. | ||
extensions 100 to 199; | ||
} |
171 changes: 171 additions & 0 deletions
171
testing/libfuzzer/fuzzers/mach/mach_message_converter.cc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,171 @@ | ||
// Copyright 2019 The Chromium 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 "testing/libfuzzer/fuzzers/mach/mach_message_converter.h" | ||
|
||
#include <string.h> | ||
#include <sys/types.h> | ||
|
||
#include <utility> | ||
|
||
#include "base/containers/buffer_iterator.h" | ||
#include "base/mac/mach_logging.h" | ||
#include "base/mac/scoped_mach_msg_destroy.h" | ||
|
||
namespace mach_fuzzer { | ||
|
||
namespace { | ||
|
||
SendablePort ConvertPort(const MachPortType& port_proto) { | ||
constexpr struct { | ||
bool insert_send_right; | ||
bool deallocate_receive_right; | ||
mach_msg_type_name_t disposition; | ||
} kPortRecipes[] = { | ||
[RECEIVE] = {true, false, MACH_MSG_TYPE_MOVE_RECEIVE}, | ||
[SEND] = {false, false, MACH_MSG_TYPE_MAKE_SEND}, | ||
[SEND_ONCE] = {false, false, MACH_MSG_TYPE_MAKE_SEND_ONCE}, | ||
[DEAD_NAME] = {true, true, MACH_MSG_TYPE_COPY_SEND}, | ||
[RECEIVE_NO_SENDERS] = {false, false, MACH_MSG_TYPE_MOVE_RECEIVE}, | ||
}; | ||
const auto* recipe = &kPortRecipes[port_proto]; | ||
|
||
SendablePort port; | ||
kern_return_t kr = mach_port_allocate( | ||
mach_task_self(), MACH_PORT_RIGHT_RECEIVE, | ||
base::mac::ScopedMachReceiveRight::Receiver(port.receive_right).get()); | ||
MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_port_allocate"; | ||
|
||
port.name = port.receive_right.get(); | ||
port.disposition = recipe->disposition; | ||
port.proto_type = port_proto; | ||
|
||
if (recipe->insert_send_right) { | ||
kr = mach_port_insert_right(mach_task_self(), port.name, port.name, | ||
MACH_MSG_TYPE_MAKE_SEND); | ||
MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_port_insert_right"; | ||
port.send_right.reset(port.name); | ||
} | ||
|
||
if (recipe->deallocate_receive_right) { | ||
port.receive_right.reset(); | ||
} | ||
|
||
return port; | ||
} | ||
|
||
bool ConvertDescriptor(base::BufferIterator<uint8_t>* iterator, | ||
const Descriptor& descriptor_proto, | ||
SendablePort* opt_port) { | ||
switch (descriptor_proto.descriptor_oneof_case()) { | ||
case Descriptor::kPort: { | ||
auto* descriptor = iterator->MutableObject<mach_msg_port_descriptor_t>(); | ||
SendablePort port = ConvertPort(descriptor_proto.port()); | ||
descriptor->name = port.name; | ||
descriptor->pad1 = 0; | ||
descriptor->pad2 = 0; | ||
descriptor->disposition = port.disposition; | ||
descriptor->type = MACH_MSG_PORT_DESCRIPTOR; | ||
*opt_port = std::move(port); | ||
return true; | ||
} | ||
case Descriptor::kOol: { | ||
auto* descriptor = iterator->MutableObject<mach_msg_ool_descriptor_t>(); | ||
descriptor->address = | ||
const_cast<char*>(descriptor_proto.ool().data().data()); | ||
descriptor->size = descriptor_proto.ool().data().size(); | ||
descriptor->copy = MACH_MSG_VIRTUAL_COPY; | ||
descriptor->pad1 = 0; | ||
descriptor->type = MACH_MSG_OOL_DESCRIPTOR; | ||
return true; | ||
} | ||
default: | ||
return false; | ||
} | ||
} | ||
|
||
} // namespace | ||
|
||
SendableMessage ConvertProtoToMachMessage(const MachMessage& proto) { | ||
SendableMessage message; | ||
|
||
const size_t descriptor_count = proto.descriptors().size(); | ||
const size_t data_size = proto.data().size(); | ||
const bool include_body = | ||
proto.include_body_if_not_complex() || descriptor_count > 0; | ||
|
||
// This is the maximum size of the message. Depending on the descriptor type, | ||
// the actual msgh_size may be less. | ||
const size_t message_size = | ||
sizeof(mach_msg_header_t) + (include_body ? sizeof(mach_msg_body_t) : 0) + | ||
(sizeof(mach_msg_descriptor_t) * descriptor_count) + data_size; | ||
message.buffer = std::make_unique<uint8_t[]>(round_msg(message_size)); | ||
|
||
base::BufferIterator<uint8_t> iterator(message.buffer.get(), message_size); | ||
|
||
auto* header = iterator.MutableObject<mach_msg_header_t>(); | ||
message.header = header; | ||
header->msgh_id = proto.id(); | ||
|
||
if (proto.has_local_port()) { | ||
SendablePort port = ConvertPort(proto.local_port()); | ||
auto disposition = port.disposition; | ||
// It's not legal to have a receive reply report. | ||
if (disposition != MACH_MSG_TYPE_MOVE_RECEIVE) { | ||
header->msgh_bits |= MACH_MSGH_BITS(0, disposition); | ||
header->msgh_local_port = port.name; | ||
message.ports.push_back(std::move(port)); | ||
} | ||
} | ||
|
||
if (include_body) { | ||
auto* body = iterator.MutableObject<mach_msg_body_t>(); | ||
body->msgh_descriptor_count = descriptor_count; | ||
} | ||
|
||
if (descriptor_count > 0) { | ||
header->msgh_bits |= MACH_MSGH_BITS_COMPLEX; | ||
for (const auto& descriptor : proto.descriptors()) { | ||
SendablePort opt_port; | ||
if (!ConvertDescriptor(&iterator, descriptor, &opt_port)) { | ||
return SendableMessage(); | ||
} | ||
if (opt_port.name != MACH_PORT_NULL) { | ||
message.ports.push_back(std::move(opt_port)); | ||
} | ||
} | ||
} | ||
|
||
auto data = iterator.MutableSpan<uint8_t>(data_size); | ||
memcpy(data.data(), proto.data().data(), proto.data().size()); | ||
|
||
header->msgh_size = round_msg(iterator.position()); | ||
|
||
return message; | ||
} | ||
|
||
SendResult SendMessage(mach_port_t remote_port, const MachMessage& proto) { | ||
SendResult result; | ||
result.message = ConvertProtoToMachMessage(proto); | ||
if (!result.message.header) { | ||
result.kr = KERN_FAILURE; | ||
return result; | ||
} | ||
|
||
result.message.header->msgh_remote_port = remote_port; | ||
result.message.header->msgh_bits |= | ||
MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0); | ||
|
||
base::ScopedMachMsgDestroy scoped_message(result.message.header); | ||
|
||
result.kr = mach_msg_send(result.message.header); | ||
|
||
if (result.kr == KERN_SUCCESS) { | ||
scoped_message.Disarm(); | ||
} | ||
|
||
return result; | ||
} | ||
|
||
} // namespace mach_fuzzer |
Oops, something went wrong.