blob: 3b845f8cf3c24b6acfae103e02ddd686b9de03e4 [file] [log] [blame]
// Copyright (c) 2009 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 "build/build_config.h"
#include "ipc/ipc_tests.h"
#if defined(OS_MACOSX)
extern "C" {
#include <sandbox.h>
}
#endif
#include <fcntl.h>
#include <sys/stat.h>
#include "base/eintr_wrapper.h"
#include "base/message_loop.h"
#include "ipc/ipc_channel.h"
#include "ipc/ipc_message_utils.h"
#include "testing/multiprocess_func_list.h"
#if defined(OS_POSIX)
#include "base/file_descriptor_posix.h"
namespace {
const unsigned kNumFDsToSend = 20;
const char* kDevZeroPath = "/dev/zero";
static void VerifyAndCloseDescriptor(int fd, ino_t inode_num) {
// Check that we can read from the FD.
char buf;
ssize_t amt_read = read(fd, &buf, 1);
ASSERT_EQ(amt_read, 1);
ASSERT_EQ(buf, 0); // /dev/zero always reads NUL bytes.
struct stat st;
ASSERT_EQ(fstat(fd, &st), 0);
ASSERT_EQ(close(fd), 0);
// We compare iNode numbers to check that the file sent over the wire
// was actually the same physical file as the one we were expecting.
ASSERT_EQ(inode_num, st.st_ino);
}
class MyChannelDescriptorListener : public IPC::Channel::Listener {
public:
MyChannelDescriptorListener(ino_t expected_inode_num)
: expected_inode_num_(expected_inode_num),
num_fds_received_(0) {}
virtual bool OnMessageReceived(const IPC::Message& message) {
void* iter = NULL;
++num_fds_received_;
base::FileDescriptor descriptor;
IPC::ParamTraits<base::FileDescriptor>::Read(
&message, &iter, &descriptor);
VerifyAndCloseDescriptor(descriptor.fd, expected_inode_num_);
if (num_fds_received_ == kNumFDsToSend) {
MessageLoop::current()->Quit();
}
return true;
}
virtual void OnChannelError() {
MessageLoop::current()->Quit();
}
bool GotExpectedNumberOfDescriptors() {
return kNumFDsToSend == num_fds_received_;
}
private:
ino_t expected_inode_num_;
unsigned num_fds_received_;
};
void TestDescriptorServer(IPC::Channel &chan,
base::ProcessHandle process_handle) {
ASSERT_TRUE(process_handle);
for (unsigned i = 0; i < kNumFDsToSend; ++i) {
base::FileDescriptor descriptor;
const int fd = open(kDevZeroPath, O_RDONLY);
ASSERT_GE(fd, 0);
descriptor.auto_close = true;
descriptor.fd = fd;
IPC::Message* message = new IPC::Message(0, // routing_id
3, // message type
IPC::Message::PRIORITY_NORMAL);
IPC::ParamTraits<base::FileDescriptor>::Write(message, descriptor);
ASSERT_TRUE(chan.Send(message));
}
// Run message loop.
MessageLoop::current()->Run();
// Close Channel so client gets its OnChannelError() callback fired.
chan.Close();
// Cleanup child process.
EXPECT_TRUE(base::WaitForSingleProcess(process_handle, 5000));
}
int TestDescriptorClient(ino_t expected_inode_num) {
MessageLoopForIO main_message_loop;
MyChannelDescriptorListener listener(expected_inode_num);
// Setup IPC channel.
IPC::Channel chan(kTestClientChannel, IPC::Channel::MODE_CLIENT,
&listener);
CHECK(chan.Connect());
// Run message loop so IPC Channel can handle message IO.
MessageLoop::current()->Run();
// Verify that the message loop was exited due to getting the correct
// number of descriptors, and not because the channel closing unexpectedly.
CHECK(listener.GotExpectedNumberOfDescriptors());
return 0;
}
} // namespace
// ---------------------------------------------------------------------------
#if defined(OS_MACOSX)
// TODO(port): Make this test cross-platform.
MULTIPROCESS_TEST_MAIN(RunTestDescriptorClientSandboxed) {
struct stat st;
const int fd = open(kDevZeroPath, O_RDONLY);
fstat(fd, &st);
if (HANDLE_EINTR(close(fd)) < 0) {
return -1;
}
// Enable the Sandbox.
char* error_buff = NULL;
int error = sandbox_init(kSBXProfilePureComputation, SANDBOX_NAMED,
&error_buff);
bool success = (error == 0 && error_buff == NULL);
if (!success) {
return -1;
}
sandbox_free_error(error_buff);
// Make sure Sandbox is really enabled.
if (open(kDevZeroPath, O_RDONLY) != -1) {
LOG(ERROR) << "Sandbox wasn't properly enabled";
return -1;
}
// See if we can receive a file descriptor.
return TestDescriptorClient(st.st_ino);
}
// Test that FDs are correctly sent to a sandboxed process.
TEST_F(IPCChannelTest, DescriptorTestSandboxed) {
// Setup IPC channel.
MyChannelDescriptorListener listener(-1);
IPC::Channel chan(kTestClientChannel, IPC::Channel::MODE_SERVER,
&listener);
ASSERT_TRUE(chan.Connect());
base::ProcessHandle process_handle = SpawnChild(
TEST_DESCRIPTOR_CLIENT_SANDBOXED,
&chan);
TestDescriptorServer(chan, process_handle);
}
#endif // defined(OS_MACOSX)
MULTIPROCESS_TEST_MAIN(RunTestDescriptorClient) {
struct stat st;
const int fd = open(kDevZeroPath, O_RDONLY);
fstat(fd, &st);
EXPECT_GE(HANDLE_EINTR(close(fd)), 0);
return TestDescriptorClient(st.st_ino);
}
TEST_F(IPCChannelTest, DescriptorTest) {
// Setup IPC channel.
MyChannelDescriptorListener listener(-1);
IPC::Channel chan(kTestClientChannel, IPC::Channel::MODE_SERVER,
&listener);
ASSERT_TRUE(chan.Connect());
base::ProcessHandle process_handle = SpawnChild(TEST_DESCRIPTOR_CLIENT,
&chan);
TestDescriptorServer(chan, process_handle);
}
#endif // defined(OS_POSIX)