blob: 4c4fcf9c9412c28ff682209b93ce5bec04c0b521 [file] [log] [blame]
// Copyright 2014 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 "sandbox/mac/xpc_message_server.h"
#include <Block.h>
#include <mach/mach.h>
#include <servers/bootstrap.h>
#include "base/command_line.h"
#include "base/logging.h"
#include "base/mac/mac_util.h"
#include "base/mac/scoped_mach_port.h"
#include "base/process/kill.h"
#include "base/test/multiprocess_test.h"
#include "sandbox/mac/xpc.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/multiprocess_func_list.h"
#if defined(MAC_OS_X_VERSION_10_7) && \
MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_7
// Redeclare methods that only exist on 10.7+ to suppress
// -Wpartial-availability warnings.
extern "C" {
XPC_EXPORT XPC_WARN_RESULT XPC_NONNULL_ALL int64_t
xpc_dictionary_get_int64(xpc_object_t xdict, const char* key);
XPC_EXPORT XPC_WARN_RESULT XPC_NONNULL_ALL uint64_t
xpc_dictionary_get_uint64(xpc_object_t xdict, const char* key);
XPC_EXPORT XPC_NONNULL1 XPC_NONNULL2 void
xpc_dictionary_set_uint64(xpc_object_t xdict, const char* key, uint64_t value);
XPC_EXPORT XPC_MALLOC XPC_RETURNS_RETAINED XPC_WARN_RESULT xpc_object_t
xpc_dictionary_create(const char* const* keys,
const xpc_object_t* values,
size_t count);
} // extern "C"
#endif
namespace sandbox {
class XPCMessageServerTest : public testing::Test {
public:
void SetUp() override {
if (!RunXPCTest())
return;
ASSERT_TRUE(InitializeXPC());
}
bool RunXPCTest() {
return base::mac::IsOSMountainLionOrLater();
}
};
// A MessageDemuxer that manages a test server and executes a block for every
// message.
class BlockDemuxer : public MessageDemuxer {
public:
BlockDemuxer()
: demux_block_(NULL),
server_(this, MACH_PORT_NULL),
pipe_(NULL) {
}
~BlockDemuxer() override {
if (pipe_)
xpc_release(pipe_);
if (demux_block_)
Block_release(demux_block_);
}
// Starts running the server, given a block to handle incoming IPC messages.
bool Initialize(void (^demux_block)(IPCMessage request)) {
if (!server_.Initialize())
return false;
// Create a send right on the port so that the XPC pipe can be created.
if (mach_port_insert_right(mach_task_self(), server_.GetServerPort(),
server_.GetServerPort(), MACH_MSG_TYPE_MAKE_SEND) != KERN_SUCCESS) {
return false;
}
scoped_send_right_.reset(server_.GetServerPort());
demux_block_ = Block_copy(demux_block);
pipe_ = xpc_pipe_create_from_port(server_.GetServerPort(), 0);
return true;
}
void DemuxMessage(IPCMessage request) override {
demux_block_(request);
}
xpc_pipe_t pipe() { return pipe_; }
XPCMessageServer* server() { return &server_; }
private:
void (^demux_block_)(IPCMessage request);
XPCMessageServer server_;
base::mac::ScopedMachSendRight scoped_send_right_;
xpc_pipe_t pipe_;
};
#define XPC_TEST_F(name) TEST_F(XPCMessageServerTest, name) { \
if (!RunXPCTest()) \
return; \
XPC_TEST_F(ReceiveMessage) // {
BlockDemuxer fixture;
XPCMessageServer* server = fixture.server();
uint64_t __block value = 0;
ASSERT_TRUE(fixture.Initialize(^(IPCMessage request) {
value = xpc_dictionary_get_uint64(request.xpc, "test_value");
server->SendReply(server->CreateReply(request));
}));
xpc_object_t request = xpc_dictionary_create(NULL, NULL, 0);
xpc_dictionary_set_uint64(request, "test_value", 42);
xpc_object_t reply;
EXPECT_EQ(0, xpc_pipe_routine(fixture.pipe(), request, &reply));
EXPECT_EQ(42u, value);
xpc_release(request);
xpc_release(reply);
}
XPC_TEST_F(RejectMessage) // {
BlockDemuxer fixture;
XPCMessageServer* server = fixture.server();
ASSERT_TRUE(fixture.Initialize(^(IPCMessage request) {
server->RejectMessage(request, EPERM);
}));
xpc_object_t request = xpc_dictionary_create(NULL, NULL, 0);
xpc_object_t reply;
EXPECT_EQ(0, xpc_pipe_routine(fixture.pipe(), request, &reply));
EXPECT_EQ(EPERM, xpc_dictionary_get_int64(reply, "error"));
xpc_release(request);
xpc_release(reply);
}
char kGetSenderPID[] = "org.chromium.sandbox.test.GetSenderPID";
XPC_TEST_F(GetSenderPID) // {
BlockDemuxer fixture;
XPCMessageServer* server = fixture.server();
pid_t __block sender_pid = 0;
int64_t __block child_pid = 0;
ASSERT_TRUE(fixture.Initialize(^(IPCMessage request) {
sender_pid = server->GetMessageSenderPID(request);
child_pid = xpc_dictionary_get_int64(request.xpc, "child_pid");
}));
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
kern_return_t kr = bootstrap_register(bootstrap_port, kGetSenderPID,
server->GetServerPort());
#pragma GCC diagnostic pop
ASSERT_EQ(KERN_SUCCESS, kr);
base::Process child = base::SpawnMultiProcessTestChild(
"GetSenderPID",
base::GetMultiProcessTestChildBaseCommandLine(),
base::LaunchOptions());
ASSERT_TRUE(child.IsValid());
int exit_code = -1;
ASSERT_TRUE(child.WaitForExit(&exit_code));
EXPECT_EQ(0, exit_code);
EXPECT_EQ(child.Pid(), sender_pid);
EXPECT_EQ(child.Pid(), child_pid);
EXPECT_EQ(sender_pid, child_pid);
}
MULTIPROCESS_TEST_MAIN(GetSenderPID) {
CHECK(sandbox::InitializeXPC());
mach_port_t port = MACH_PORT_NULL;
CHECK_EQ(KERN_SUCCESS, bootstrap_look_up(bootstrap_port, kGetSenderPID,
&port));
base::mac::ScopedMachSendRight scoped_port(port);
xpc_pipe_t pipe = xpc_pipe_create_from_port(port, 0);
xpc_object_t message = xpc_dictionary_create(NULL, NULL, 0);
xpc_dictionary_set_int64(message, "child_pid", getpid());
CHECK_EQ(0, xpc_pipe_simpleroutine(pipe, message));
xpc_release(message);
xpc_release(pipe);
return 0;
}
XPC_TEST_F(ForwardMessage) // {
BlockDemuxer first;
XPCMessageServer* first_server = first.server();
BlockDemuxer second;
XPCMessageServer* second_server = second.server();
ASSERT_TRUE(first.Initialize(^(IPCMessage request) {
xpc_dictionary_set_int64(request.xpc, "seen_by_first", 1);
first_server->ForwardMessage(request, second_server->GetServerPort());
}));
ASSERT_TRUE(second.Initialize(^(IPCMessage request) {
IPCMessage reply = second_server->CreateReply(request);
xpc_dictionary_set_int64(reply.xpc, "seen_by_first",
xpc_dictionary_get_int64(request.xpc, "seen_by_first"));
xpc_dictionary_set_int64(reply.xpc, "seen_by_second", 2);
second_server->SendReply(reply);
}));
xpc_object_t request = xpc_dictionary_create(NULL, NULL, 0);
xpc_object_t reply;
ASSERT_EQ(0, xpc_pipe_routine(first.pipe(), request, &reply));
EXPECT_EQ(1, xpc_dictionary_get_int64(reply, "seen_by_first"));
EXPECT_EQ(2, xpc_dictionary_get_int64(reply, "seen_by_second"));
xpc_release(request);
xpc_release(reply);
}
} // namespace sandbox