You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
219 lines
5.8 KiB
219 lines
5.8 KiB
// Copyright (c) 2012 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"
|
|
|
|
#if defined(OS_POSIX)
|
|
#if defined(OS_MACOSX)
|
|
extern "C" {
|
|
#include <sandbox.h>
|
|
};
|
|
#endif
|
|
#include <fcntl.h>
|
|
#include <stddef.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
|
|
#include <memory>
|
|
#include <queue>
|
|
|
|
#include "base/callback.h"
|
|
#include "base/file_descriptor_posix.h"
|
|
#include "base/location.h"
|
|
#include "base/pickle.h"
|
|
#include "base/posix/eintr_wrapper.h"
|
|
#include "base/run_loop.h"
|
|
#include "base/single_thread_task_runner.h"
|
|
#include "base/synchronization/waitable_event.h"
|
|
#include "base/threading/thread.h"
|
|
#include "base/threading/thread_task_runner_handle.h"
|
|
#include "ipc/ipc_message_attachment_set.h"
|
|
#include "ipc/ipc_message_utils.h"
|
|
#include "ipc/ipc_test_base.h"
|
|
|
|
#if defined(OS_POSIX)
|
|
#include "base/macros.h"
|
|
#endif
|
|
|
|
#if defined(OS_MACOSX)
|
|
#include "sandbox/mac/seatbelt.h"
|
|
#endif
|
|
|
|
namespace {
|
|
|
|
const unsigned kNumFDsToSend = 7; // per message
|
|
const unsigned kNumMessages = 20;
|
|
const char* kDevZeroPath = "/dev/zero";
|
|
|
|
#if defined(OS_POSIX) || defined(OS_FUCHSIA)
|
|
static_assert(kNumFDsToSend ==
|
|
IPC::MessageAttachmentSet::kMaxDescriptorsPerMessage,
|
|
"The number of FDs to send must be kMaxDescriptorsPerMessage.");
|
|
#endif
|
|
|
|
class MyChannelDescriptorListenerBase : public IPC::Listener {
|
|
public:
|
|
bool OnMessageReceived(const IPC::Message& message) override {
|
|
base::PickleIterator iter(message);
|
|
base::FileDescriptor descriptor;
|
|
while (IPC::ParamTraits<base::FileDescriptor>::Read(
|
|
&message, &iter, &descriptor)) {
|
|
HandleFD(descriptor.fd);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
protected:
|
|
virtual void HandleFD(int fd) = 0;
|
|
};
|
|
|
|
class MyChannelDescriptorListener : public MyChannelDescriptorListenerBase {
|
|
public:
|
|
explicit MyChannelDescriptorListener(ino_t expected_inode_num)
|
|
: MyChannelDescriptorListenerBase(),
|
|
expected_inode_num_(expected_inode_num),
|
|
num_fds_received_(0) {
|
|
}
|
|
|
|
unsigned num_fds_received() const {
|
|
return num_fds_received_;
|
|
}
|
|
|
|
void OnChannelError() override {
|
|
base::RunLoop::QuitCurrentWhenIdleDeprecated();
|
|
}
|
|
|
|
protected:
|
|
void HandleFD(int fd) override {
|
|
ASSERT_GE(fd, 0);
|
|
// 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 0 bytes.
|
|
|
|
struct stat st;
|
|
ASSERT_EQ(fstat(fd, &st), 0);
|
|
|
|
ASSERT_EQ(close(fd), 0);
|
|
|
|
// Compare inode numbers to check that the file sent over the wire is
|
|
// actually the one expected.
|
|
ASSERT_EQ(expected_inode_num_, st.st_ino);
|
|
|
|
++num_fds_received_;
|
|
if (num_fds_received_ == kNumFDsToSend * kNumMessages)
|
|
base::RunLoop::QuitCurrentWhenIdleDeprecated();
|
|
}
|
|
|
|
private:
|
|
ino_t expected_inode_num_;
|
|
unsigned num_fds_received_;
|
|
};
|
|
|
|
class IPCSendFdsTest : public IPCChannelMojoTestBase {
|
|
protected:
|
|
void RunServer() {
|
|
// Set up IPC channel and start client.
|
|
MyChannelDescriptorListener listener(-1);
|
|
CreateChannel(&listener);
|
|
ASSERT_TRUE(ConnectChannel());
|
|
|
|
for (unsigned i = 0; i < kNumMessages; ++i) {
|
|
IPC::Message* message =
|
|
new IPC::Message(0, 3, IPC::Message::PRIORITY_NORMAL);
|
|
for (unsigned j = 0; j < kNumFDsToSend; ++j) {
|
|
const int fd = open(kDevZeroPath, O_RDONLY);
|
|
ASSERT_GE(fd, 0);
|
|
base::FileDescriptor descriptor(fd, true);
|
|
IPC::ParamTraits<base::FileDescriptor>::Write(message, descriptor);
|
|
}
|
|
ASSERT_TRUE(sender()->Send(message));
|
|
}
|
|
|
|
// Run message loop.
|
|
base::RunLoop().Run();
|
|
|
|
// Close the channel so the client's OnChannelError() gets fired.
|
|
channel()->Close();
|
|
|
|
EXPECT_TRUE(WaitForClientShutdown());
|
|
DestroyChannel();
|
|
}
|
|
};
|
|
|
|
TEST_F(IPCSendFdsTest, DescriptorTest) {
|
|
Init("SendFdsClient");
|
|
RunServer();
|
|
}
|
|
|
|
class SendFdsTestClientFixture : public IpcChannelMojoTestClient {
|
|
protected:
|
|
void SendFdsClientCommon(const std::string& test_client_name,
|
|
ino_t expected_inode_num) {
|
|
MyChannelDescriptorListener listener(expected_inode_num);
|
|
|
|
// Set up IPC channel.
|
|
Connect(&listener);
|
|
|
|
// Run message loop.
|
|
base::RunLoop().Run();
|
|
|
|
// Verify that the message loop was exited due to getting the correct number
|
|
// of descriptors, and not because of the channel closing unexpectedly.
|
|
EXPECT_EQ(kNumFDsToSend * kNumMessages, listener.num_fds_received());
|
|
|
|
Close();
|
|
}
|
|
};
|
|
|
|
DEFINE_IPC_CHANNEL_MOJO_TEST_CLIENT_WITH_CUSTOM_FIXTURE(
|
|
SendFdsClient,
|
|
SendFdsTestClientFixture) {
|
|
struct stat st;
|
|
int fd = open(kDevZeroPath, O_RDONLY);
|
|
fstat(fd, &st);
|
|
EXPECT_GE(IGNORE_EINTR(close(fd)), 0);
|
|
SendFdsClientCommon("SendFdsClient", st.st_ino);
|
|
}
|
|
|
|
#if defined(OS_MACOSX)
|
|
// Test that FDs are correctly sent to a sandboxed process.
|
|
// TODO(port): Make this test cross-platform.
|
|
TEST_F(IPCSendFdsTest, DescriptorTestSandboxed) {
|
|
Init("SendFdsSandboxedClient");
|
|
RunServer();
|
|
}
|
|
|
|
DEFINE_IPC_CHANNEL_MOJO_TEST_CLIENT_WITH_CUSTOM_FIXTURE(
|
|
SendFdsSandboxedClient,
|
|
SendFdsTestClientFixture) {
|
|
struct stat st;
|
|
const int fd = open(kDevZeroPath, O_RDONLY);
|
|
fstat(fd, &st);
|
|
ASSERT_LE(0, IGNORE_EINTR(close(fd)));
|
|
|
|
// Enable the sandbox.
|
|
char* error_buff = NULL;
|
|
int error = sandbox::Seatbelt::Init(
|
|
sandbox::Seatbelt::kProfilePureComputation, SANDBOX_NAMED, &error_buff);
|
|
ASSERT_EQ(0, error);
|
|
ASSERT_FALSE(error_buff);
|
|
|
|
sandbox::Seatbelt::FreeError(error_buff);
|
|
|
|
// Make sure sandbox is really enabled.
|
|
ASSERT_EQ(-1, open(kDevZeroPath, O_RDONLY))
|
|
<< "Sandbox wasn't properly enabled";
|
|
|
|
// See if we can receive a file descriptor.
|
|
SendFdsClientCommon("SendFdsSandboxedClient", st.st_ino);
|
|
}
|
|
#endif // defined(OS_MACOSX)
|
|
|
|
} // namespace
|
|
|
|
#endif // defined(OS_POSIX)
|