mirror of
https://github.com/chromium/crashpad.git
synced 2025-03-09 14:06:33 +00:00
Add a ReceiveLarge parameter to MachMessageServer::Run().
Previously, MachMessageServer::Run() only provided two strategies for dealing with large messages, indicated by mach_msg() returning MACH_RCV_TOO_LARGE: the receive buffer could be reallocated and the message received, or the entire function could return MACH_RCV_TOO_LARGE to the caller. There are situations where an intermediate behavior might be desirable. This intermediate behavior would allow the function to continue waiting for another message without returning an error to the caller or attempting to receive the large message. This is desirable when dealing with fixed-sized messages and a receiver that might be sent messages by unknown, possibly-malicious callers. This can happen when the corresponding send right is published with the bootstrap server, for example. Existing users continue to request their existing behavior, typically receiving an error when encountering a large message. catch_exception_tool will use the new “ignore” behavior when running in persistent mode. TEST=util_test MachMessageServer.* R=rsesek@chromium.org Review URL: https://codereview.chromium.org/756803002
This commit is contained in:
parent
04aaa36026
commit
79b4434c81
@ -247,6 +247,7 @@ class TestSimulateCrashMac final : public MachMultiprocess,
|
||||
MACH_MSG_OPTION_NONE,
|
||||
MachMessageServer::kOneShot,
|
||||
MachMessageServer::kBlocking,
|
||||
MachMessageServer::kReceiveLargeError,
|
||||
MACH_MSG_TIMEOUT_NONE);
|
||||
EXPECT_EQ(MACH_MSG_SUCCESS, mr)
|
||||
<< MachErrorMessage(mr, "MachMessageServer::Run");
|
||||
@ -258,6 +259,7 @@ class TestSimulateCrashMac final : public MachMultiprocess,
|
||||
MACH_MSG_OPTION_NONE,
|
||||
MachMessageServer::kOneShot,
|
||||
MachMessageServer::kBlocking,
|
||||
MachMessageServer::kReceiveLargeError,
|
||||
MACH_MSG_TIMEOUT_NONE);
|
||||
EXPECT_EQ(MACH_MSG_SUCCESS, mr)
|
||||
<< MachErrorMessage(mr, "MachMessageServer::Run");
|
||||
|
@ -224,6 +224,7 @@ class TestMachOImageAnnotationsReader final : public MachMultiprocess,
|
||||
MACH_MSG_OPTION_NONE,
|
||||
MachMessageServer::kOneShot,
|
||||
MachMessageServer::kBlocking,
|
||||
MachMessageServer::kReceiveLargeError,
|
||||
MACH_MSG_TIMEOUT_NONE);
|
||||
EXPECT_EQ(MACH_MSG_SUCCESS, mr)
|
||||
<< MachErrorMessage(mr, "MachMessageServer::Run");
|
||||
|
@ -275,6 +275,13 @@ int CatchExceptionToolMain(int argc, char* argv[]) {
|
||||
int exceptions_handled = 0;
|
||||
ExceptionServer exception_server(options, me, &exceptions_handled);
|
||||
|
||||
// Assume that if persistent mode has been requested, it’s desirable to ignore
|
||||
// large messages and keep running.
|
||||
MachMessageServer::ReceiveLarge receive_large =
|
||||
(options.persistent == MachMessageServer::kPersistent)
|
||||
? MachMessageServer::kReceiveLargeIgnore
|
||||
: MachMessageServer::kReceiveLargeError;
|
||||
|
||||
mach_msg_timeout_t timeout_ms = options.timeout_secs
|
||||
? options.timeout_secs * 1000
|
||||
: MACH_MSG_TIMEOUT_NONE;
|
||||
@ -284,6 +291,7 @@ int CatchExceptionToolMain(int argc, char* argv[]) {
|
||||
MACH_MSG_OPTION_NONE,
|
||||
options.persistent,
|
||||
options.nonblocking,
|
||||
receive_large,
|
||||
timeout_ms);
|
||||
if (mr == MACH_RCV_TIMED_OUT && options.timeout_secs && options.persistent &&
|
||||
exceptions_handled) {
|
||||
|
@ -134,12 +134,14 @@ class TestExcClientVariants : public UniversalMachExcServer,
|
||||
// MachMultiprocess:
|
||||
|
||||
void MachMultiprocessParent() override {
|
||||
kern_return_t kr = MachMessageServer::Run(this,
|
||||
LocalPort(),
|
||||
MACH_MSG_OPTION_NONE,
|
||||
MachMessageServer::kOneShot,
|
||||
MachMessageServer::kBlocking,
|
||||
0);
|
||||
kern_return_t kr =
|
||||
MachMessageServer::Run(this,
|
||||
LocalPort(),
|
||||
MACH_MSG_OPTION_NONE,
|
||||
MachMessageServer::kOneShot,
|
||||
MachMessageServer::kBlocking,
|
||||
MachMessageServer::kReceiveLargeError,
|
||||
0);
|
||||
EXPECT_EQ(KERN_SUCCESS, kr)
|
||||
<< MachErrorMessage(kr, "MachMessageServer::Run");
|
||||
|
||||
|
@ -940,12 +940,14 @@ class TestExcServerVariants : public UniversalMachExcServer,
|
||||
// MachMultiprocess:
|
||||
|
||||
void MachMultiprocessParent() override {
|
||||
kern_return_t kr = MachMessageServer::Run(this,
|
||||
LocalPort(),
|
||||
MACH_MSG_OPTION_NONE,
|
||||
MachMessageServer::kOneShot,
|
||||
MachMessageServer::kBlocking,
|
||||
0);
|
||||
kern_return_t kr =
|
||||
MachMessageServer::Run(this,
|
||||
LocalPort(),
|
||||
MACH_MSG_OPTION_NONE,
|
||||
MachMessageServer::kOneShot,
|
||||
MachMessageServer::kBlocking,
|
||||
MachMessageServer::kReceiveLargeError,
|
||||
0);
|
||||
EXPECT_EQ(KERN_SUCCESS, kr)
|
||||
<< MachErrorMessage(kr, "MachMessageServer::Run");
|
||||
|
||||
|
@ -442,12 +442,14 @@ class TestExceptionPorts : public UniversalMachExcServer,
|
||||
CheckedWriteFD(WritePipeFD(), &c, 1);
|
||||
|
||||
if (who_crashes_ != kNobodyCrashes) {
|
||||
kern_return_t kr = MachMessageServer::Run(this,
|
||||
local_port,
|
||||
MACH_MSG_OPTION_NONE,
|
||||
MachMessageServer::kOneShot,
|
||||
MachMessageServer::kBlocking,
|
||||
0);
|
||||
kern_return_t kr =
|
||||
MachMessageServer::Run(this,
|
||||
local_port,
|
||||
MACH_MSG_OPTION_NONE,
|
||||
MachMessageServer::kOneShot,
|
||||
MachMessageServer::kBlocking,
|
||||
MachMessageServer::kReceiveLargeError,
|
||||
0);
|
||||
EXPECT_EQ(KERN_SUCCESS, kr)
|
||||
<< MachErrorMessage(kr, "MachMessageServer::Run");
|
||||
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include "base/mac/mach_logging.h"
|
||||
#include "base/mac/scoped_mach_vm.h"
|
||||
#include "util/misc/clock.h"
|
||||
|
||||
@ -82,6 +83,7 @@ mach_msg_return_t MachMessageServer::Run(Interface* interface,
|
||||
mach_msg_options_t options,
|
||||
Persistent persistent,
|
||||
Nonblocking nonblocking,
|
||||
ReceiveLarge receive_large,
|
||||
mach_msg_timeout_t timeout_ms) {
|
||||
options &= ~(MACH_RCV_MSG | MACH_SEND_MSG);
|
||||
|
||||
@ -101,10 +103,16 @@ mach_msg_return_t MachMessageServer::Run(Interface* interface,
|
||||
deadline = 0;
|
||||
}
|
||||
|
||||
if (receive_large == kReceiveLargeResize) {
|
||||
options |= MACH_RCV_LARGE;
|
||||
} else {
|
||||
options &= ~MACH_RCV_LARGE;
|
||||
}
|
||||
|
||||
mach_msg_size_t trailer_alloc = REQUESTED_TRAILER_SIZE(options);
|
||||
mach_msg_size_t max_request_size = interface->MachMessageServerRequestSize();
|
||||
mach_msg_size_t request_alloc = round_page(max_request_size + trailer_alloc);
|
||||
mach_msg_size_t request_size = (options & MACH_RCV_LARGE)
|
||||
mach_msg_size_t request_size = (receive_large == kReceiveLargeResize)
|
||||
? request_alloc
|
||||
: max_request_size + trailer_alloc;
|
||||
|
||||
@ -135,6 +143,7 @@ mach_msg_return_t MachMessageServer::Run(Interface* interface,
|
||||
this_request_alloc);
|
||||
request_header = reinterpret_cast<mach_msg_header_t*>(request_addr);
|
||||
|
||||
bool run_mach_msg_receive = false;
|
||||
do {
|
||||
// If |options| contains MACH_RCV_INTERRUPT, retry mach_msg() in a loop
|
||||
// when it returns MACH_RCV_INTERRUPTED to recompute |remaining_ms|
|
||||
@ -153,11 +162,19 @@ mach_msg_return_t MachMessageServer::Run(Interface* interface,
|
||||
receive_port,
|
||||
remaining_ms,
|
||||
MACH_PORT_NULL);
|
||||
} while (kr == MACH_RCV_INTERRUPTED);
|
||||
|
||||
if (kr == MACH_RCV_TOO_LARGE && receive_large == kReceiveLargeIgnore) {
|
||||
MACH_LOG(WARNING, kr) << "mach_msg: ignoring large message";
|
||||
run_mach_msg_receive = true;
|
||||
} else if (kr == MACH_RCV_INTERRUPTED) {
|
||||
run_mach_msg_receive = true;
|
||||
}
|
||||
} while (run_mach_msg_receive);
|
||||
|
||||
if (kr == MACH_MSG_SUCCESS) {
|
||||
request_scoper.swap(trial_request_scoper);
|
||||
} else if (kr == MACH_RCV_TOO_LARGE && options & MACH_RCV_LARGE) {
|
||||
} else if (kr == MACH_RCV_TOO_LARGE &&
|
||||
receive_large == kReceiveLargeResize) {
|
||||
this_request_size =
|
||||
round_page(request_header->msgh_size + trailer_alloc);
|
||||
this_request_alloc = this_request_size;
|
||||
|
@ -103,6 +103,32 @@ class MachMessageServer {
|
||||
kNonblocking,
|
||||
};
|
||||
|
||||
//! \brief Determines how to handle the reception of messages larger than the
|
||||
//! size of the buffer allocated to store them.
|
||||
enum ReceiveLarge {
|
||||
//! \brief Return `MACH_RCV_TOO_LARGE` upon receipt of a large message.
|
||||
//!
|
||||
//! This mimics the default behavior of `mach_msg_server()` when `options`
|
||||
//! does not contain `MACH_RCV_LARGE`.
|
||||
kReceiveLargeError = 0,
|
||||
|
||||
//! \brief Ignore large messages, and attempt to receive the next queued
|
||||
//! message upon encountering one.
|
||||
//!
|
||||
//! When a large message is encountered, a warning will be logged.
|
||||
//!
|
||||
//! `mach_msg()` will be called to receive the next message after a large
|
||||
//! one even when accompanied by a #Persistent value of #kOneShot.
|
||||
kReceiveLargeIgnore,
|
||||
|
||||
//! \brief Allocate an appropriately-sized buffer upon encountering a large
|
||||
//! message. The buffer will be used to receive the message. This
|
||||
//!
|
||||
//! This mimics the behavior of `mach_msg_server()` when `options` contains
|
||||
//! `MACH_RCV_LARGE`.
|
||||
kReceiveLargeResize,
|
||||
};
|
||||
|
||||
//! \brief Runs a Mach message server to handle a Mach RPC request for MIG
|
||||
//! servers.
|
||||
//!
|
||||
@ -124,9 +150,12 @@ class MachMessageServer {
|
||||
//! handle the request and populate the reply.
|
||||
//! \param[in] receive_port The port on which to receive the request message.
|
||||
//! \param[in] options Options suitable for mach_msg. For the defaults, use
|
||||
//! `MACH_MSG_OPTION_NONE`.
|
||||
//! `MACH_MSG_OPTION_NONE`. `MACH_RCV_LARGE` when specified here is
|
||||
//! ignored. Set \a receive_large to #kReceiveLargeResize instead.
|
||||
//! \param[in] persistent Chooses between one-shot and persistent operation.
|
||||
//! \param[in] nonblocking Chooses between blocking and nonblocking operation.
|
||||
//! \param[in] receive_large Determines the behavior upon encountering a
|
||||
//! message larger than the receive buffer’s size.
|
||||
//! \param[in] timeout_ms When \a nonblocking is `false`, the the maximum
|
||||
//! duration that this entire function will run, in milliseconds, or
|
||||
//! `MACH_MSG_TIMEOUT_NONE` to specify no timeout (infinite waiting). When
|
||||
@ -146,6 +175,7 @@ class MachMessageServer {
|
||||
mach_msg_options_t options,
|
||||
Persistent persistent,
|
||||
Nonblocking nonblocking,
|
||||
ReceiveLarge receive_large,
|
||||
mach_msg_timeout_t timeout_ms);
|
||||
|
||||
private:
|
||||
|
@ -58,6 +58,7 @@ class TestMachMessageServer : public MachMessageServer::Interface,
|
||||
server_options(MACH_MSG_OPTION_NONE),
|
||||
server_persistent(MachMessageServer::kOneShot),
|
||||
server_nonblocking(MachMessageServer::kBlocking),
|
||||
server_receive_large(MachMessageServer::kReceiveLargeError),
|
||||
server_timeout_ms(MACH_MSG_TIMEOUT_NONE),
|
||||
server_mig_retcode(KERN_SUCCESS),
|
||||
server_destroy_complex(true),
|
||||
@ -93,6 +94,9 @@ class TestMachMessageServer : public MachMessageServer::Interface,
|
||||
// Whether the server should run in blocking or nonblocking mode.
|
||||
MachMessageServer::Nonblocking server_nonblocking;
|
||||
|
||||
// The strategy for handling large messages.
|
||||
MachMessageServer::ReceiveLarge server_receive_large;
|
||||
|
||||
// The server’s timeout.
|
||||
mach_msg_timeout_t server_timeout_ms;
|
||||
|
||||
@ -134,9 +138,8 @@ class TestMachMessageServer : public MachMessageServer::Interface,
|
||||
bool client_send_complex;
|
||||
|
||||
// true if the client should send a larger message than the server has
|
||||
// allocated space to receive. If server_options contains MACH_RCV_LARGE,
|
||||
// the server will resize its buffer to receive the message. Otherwise, the
|
||||
// message will be destroyed and the server will return MACH_RCV_TOO_LARGE.
|
||||
// allocated space to receive. The server’s response is directed by
|
||||
// server_receive_large.
|
||||
bool client_send_large;
|
||||
|
||||
// The type of reply port that the client should provide in its request’s
|
||||
@ -342,6 +345,7 @@ class TestMachMessageServer : public MachMessageServer::Interface,
|
||||
options_.server_options,
|
||||
options_.server_persistent,
|
||||
options_.server_nonblocking,
|
||||
options_.server_receive_large,
|
||||
options_.server_timeout_ms)))
|
||||
<< MachErrorMessage(kr, "MachMessageServer");
|
||||
|
||||
@ -802,11 +806,11 @@ TEST(MachMessageServer, ComplexNotDestroyedNoReply) {
|
||||
test_mach_message_server.Test();
|
||||
}
|
||||
|
||||
TEST(MachMessageServer, LargeUnexpected) {
|
||||
TEST(MachMessageServer, ReceiveLargeError) {
|
||||
// The client sends a request to the server that is larger than the server is
|
||||
// expecting. The server did not specify MACH_RCV_LARGE in its options, so the
|
||||
// request is destroyed and the server returns a MACH_RCV_TOO_LARGE error. The
|
||||
// client does not receive a reply.
|
||||
// expecting. server_receive_large is kReceiveLargeError, so the request is
|
||||
// destroyed and the server returns a MACH_RCV_TOO_LARGE error. The client
|
||||
// does not receive a reply.
|
||||
TestMachMessageServer::Options options;
|
||||
options.expect_server_result = MACH_RCV_TOO_LARGE;
|
||||
options.expect_server_transaction_count = 0;
|
||||
@ -816,18 +820,35 @@ TEST(MachMessageServer, LargeUnexpected) {
|
||||
test_mach_message_server.Test();
|
||||
}
|
||||
|
||||
TEST(MachMessageServer, LargeExpected) {
|
||||
TEST(MachMessageServer, ReceiveLargeRetry) {
|
||||
// The client sends a request to the server that is larger than the server is
|
||||
// initially expecting. The server did specify MACH_RCV_LARGE in its options,
|
||||
// so a new buffer is allocated to receive the message. The server receives
|
||||
// the large request message, processes it, and returns a reply to the client.
|
||||
// initially expecting. server_receive_large is kReceiveLargeResize, so a new
|
||||
// buffer is allocated to receive the message. The server receives the large
|
||||
// request message, processes it, and returns a reply to the client.
|
||||
TestMachMessageServer::Options options;
|
||||
options.server_options = MACH_RCV_LARGE;
|
||||
options.server_receive_large = MachMessageServer::kReceiveLargeResize;
|
||||
options.client_send_large = true;
|
||||
TestMachMessageServer test_mach_message_server(options);
|
||||
test_mach_message_server.Test();
|
||||
}
|
||||
|
||||
TEST(MachMessageServer, ReceiveLargeIgnore) {
|
||||
// The client sends a request to the server that is larger than the server is
|
||||
// expecting. server_receive_large is kReceiveLargeIgnore, so the request is
|
||||
// destroyed but the server does not consider this an error. The server is
|
||||
// running in blocking mode with a timeout, and continues to wait for a
|
||||
// message until it times out. The client does not receive a reply.
|
||||
TestMachMessageServer::Options options;
|
||||
options.server_receive_large = MachMessageServer::kReceiveLargeIgnore;
|
||||
options.server_timeout_ms = 10;
|
||||
options.expect_server_result = MACH_RCV_TIMED_OUT;
|
||||
options.expect_server_transaction_count = 0;
|
||||
options.client_send_large = true;
|
||||
options.client_expect_reply = false;
|
||||
TestMachMessageServer test_mach_message_server(options);
|
||||
test_mach_message_server.Test();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace test
|
||||
} // namespace crashpad
|
||||
|
Loading…
x
Reference in New Issue
Block a user