Home | History | Annotate | Download | only in ipc
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "build/build_config.h"
      6 
      7 #if defined(OS_POSIX)
      8 #if defined(OS_MACOSX)
      9 extern "C" {
     10 #include <sandbox.h>
     11 }
     12 #endif
     13 #include <fcntl.h>
     14 #include <sys/socket.h>
     15 #include <sys/stat.h>
     16 #include <unistd.h>
     17 
     18 #include <queue>
     19 
     20 #include "base/callback.h"
     21 #include "base/file_descriptor_posix.h"
     22 #include "base/message_loop/message_loop.h"
     23 #include "base/pickle.h"
     24 #include "base/posix/eintr_wrapper.h"
     25 #include "base/synchronization/waitable_event.h"
     26 #include "ipc/ipc_message_utils.h"
     27 #include "ipc/ipc_test_base.h"
     28 
     29 namespace {
     30 
     31 const unsigned kNumFDsToSend = 20;
     32 const char* kDevZeroPath = "/dev/zero";
     33 
     34 class MyChannelDescriptorListenerBase : public IPC::Listener {
     35  public:
     36   virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE {
     37     PickleIterator iter(message);
     38 
     39     base::FileDescriptor descriptor;
     40 
     41     IPC::ParamTraits<base::FileDescriptor>::Read(&message, &iter, &descriptor);
     42 
     43     HandleFD(descriptor.fd);
     44     return true;
     45   }
     46 
     47  protected:
     48   virtual void HandleFD(int fd) = 0;
     49 };
     50 
     51 class MyChannelDescriptorListener : public MyChannelDescriptorListenerBase {
     52  public:
     53   explicit MyChannelDescriptorListener(ino_t expected_inode_num)
     54       : MyChannelDescriptorListenerBase(),
     55         expected_inode_num_(expected_inode_num),
     56         num_fds_received_(0) {
     57   }
     58 
     59   bool GotExpectedNumberOfDescriptors() const {
     60     return num_fds_received_ == kNumFDsToSend;
     61   }
     62 
     63   virtual void OnChannelError() OVERRIDE {
     64     base::MessageLoop::current()->Quit();
     65   }
     66 
     67  protected:
     68   virtual void HandleFD(int fd) OVERRIDE {
     69     // Check that we can read from the FD.
     70     char buf;
     71     ssize_t amt_read = read(fd, &buf, 1);
     72     ASSERT_EQ(amt_read, 1);
     73     ASSERT_EQ(buf, 0);  // /dev/zero always reads 0 bytes.
     74 
     75     struct stat st;
     76     ASSERT_EQ(fstat(fd, &st), 0);
     77 
     78     ASSERT_EQ(close(fd), 0);
     79 
     80     // Compare inode numbers to check that the file sent over the wire is
     81     // actually the one expected.
     82     ASSERT_EQ(expected_inode_num_, st.st_ino);
     83 
     84     ++num_fds_received_;
     85     if (num_fds_received_ == kNumFDsToSend)
     86       base::MessageLoop::current()->Quit();
     87   }
     88 
     89  private:
     90   ino_t expected_inode_num_;
     91   unsigned num_fds_received_;
     92 };
     93 
     94 
     95 class IPCSendFdsTest : public IPCTestBase {
     96  protected:
     97   void RunServer() {
     98     // Set up IPC channel and start client.
     99     MyChannelDescriptorListener listener(-1);
    100     CreateChannel(&listener);
    101     ASSERT_TRUE(ConnectChannel());
    102     ASSERT_TRUE(StartClient());
    103 
    104     for (unsigned i = 0; i < kNumFDsToSend; ++i) {
    105       const int fd = open(kDevZeroPath, O_RDONLY);
    106       ASSERT_GE(fd, 0);
    107       base::FileDescriptor descriptor(fd, true);
    108 
    109       IPC::Message* message =
    110           new IPC::Message(0, 3, IPC::Message::PRIORITY_NORMAL);
    111       IPC::ParamTraits<base::FileDescriptor>::Write(message, descriptor);
    112       ASSERT_TRUE(sender()->Send(message));
    113     }
    114 
    115     // Run message loop.
    116     base::MessageLoop::current()->Run();
    117 
    118     // Close the channel so the client's OnChannelError() gets fired.
    119     channel()->Close();
    120 
    121     EXPECT_TRUE(WaitForClientShutdown());
    122     DestroyChannel();
    123   }
    124 };
    125 
    126 TEST_F(IPCSendFdsTest, DescriptorTest) {
    127   Init("SendFdsClient");
    128   RunServer();
    129 }
    130 
    131 int SendFdsClientCommon(const std::string& test_client_name,
    132                         ino_t expected_inode_num) {
    133   base::MessageLoopForIO main_message_loop;
    134   MyChannelDescriptorListener listener(expected_inode_num);
    135 
    136   // Set up IPC channel.
    137   IPC::Channel channel(IPCTestBase::GetChannelName(test_client_name),
    138                        IPC::Channel::MODE_CLIENT,
    139                        &listener);
    140   CHECK(channel.Connect());
    141 
    142   // Run message loop.
    143   base::MessageLoop::current()->Run();
    144 
    145   // Verify that the message loop was exited due to getting the correct number
    146   // of descriptors, and not because of the channel closing unexpectedly.
    147   CHECK(listener.GotExpectedNumberOfDescriptors());
    148 
    149   return 0;
    150 }
    151 
    152 MULTIPROCESS_IPC_TEST_CLIENT_MAIN(SendFdsClient) {
    153   struct stat st;
    154   int fd = open(kDevZeroPath, O_RDONLY);
    155   fstat(fd, &st);
    156   EXPECT_GE(IGNORE_EINTR(close(fd)), 0);
    157   return SendFdsClientCommon("SendFdsClient", st.st_ino);
    158 }
    159 
    160 #if defined(OS_MACOSX)
    161 // Test that FDs are correctly sent to a sandboxed process.
    162 // TODO(port): Make this test cross-platform.
    163 TEST_F(IPCSendFdsTest, DescriptorTestSandboxed) {
    164   Init("SendFdsSandboxedClient");
    165   RunServer();
    166 }
    167 
    168 MULTIPROCESS_IPC_TEST_CLIENT_MAIN(SendFdsSandboxedClient) {
    169   struct stat st;
    170   const int fd = open(kDevZeroPath, O_RDONLY);
    171   fstat(fd, &st);
    172   if (IGNORE_EINTR(close(fd)) < 0)
    173     return -1;
    174 
    175   // Enable the sandbox.
    176   char* error_buff = NULL;
    177   int error = sandbox_init(kSBXProfilePureComputation, SANDBOX_NAMED,
    178                            &error_buff);
    179   bool success = (error == 0 && error_buff == NULL);
    180   if (!success)
    181     return -1;
    182 
    183   sandbox_free_error(error_buff);
    184 
    185   // Make sure sandbox is really enabled.
    186   if (open(kDevZeroPath, O_RDONLY) != -1) {
    187     LOG(ERROR) << "Sandbox wasn't properly enabled";
    188     return -1;
    189   }
    190 
    191   // See if we can receive a file descriptor.
    192   return SendFdsClientCommon("SendFdsSandboxedClient", st.st_ino);
    193 }
    194 #endif  // defined(OS_MACOSX)
    195 
    196 
    197 class MyCBListener : public MyChannelDescriptorListenerBase {
    198  public:
    199   MyCBListener(base::Callback<void(int)> cb, int fds_to_send)
    200       : MyChannelDescriptorListenerBase(),
    201         cb_(cb) {
    202     }
    203 
    204  protected:
    205   virtual void HandleFD(int fd) OVERRIDE {
    206     cb_.Run(fd);
    207   }
    208  private:
    209   base::Callback<void(int)> cb_;
    210 };
    211 
    212 std::pair<int, int> make_socket_pair() {
    213   int pipe_fds[2];
    214   CHECK_EQ(0, HANDLE_EINTR(socketpair(AF_UNIX, SOCK_STREAM, 0, pipe_fds)));
    215   return std::pair<int, int>(pipe_fds[0], pipe_fds[1]);
    216 }
    217 
    218 static void null_cb(int unused_fd) {
    219   NOTREACHED();
    220 }
    221 
    222 class PipeChannelHelper {
    223  public:
    224   PipeChannelHelper(base::Thread* in_thread,
    225                     base::Thread* out_thread,
    226                     base::Callback<void(int)> cb,
    227                     int fds_to_send) :
    228       in_thread_(in_thread),
    229       out_thread_(out_thread),
    230       cb_listener_(cb, fds_to_send),
    231       null_listener_(base::Bind(&null_cb), 0) {
    232   }
    233 
    234   void Init() {
    235     IPC::ChannelHandle in_handle("IN");
    236     in.reset(new IPC::Channel(in_handle,
    237                               IPC::Channel::MODE_SERVER,
    238                               &null_listener_));
    239     base::FileDescriptor out_fd(in->TakeClientFileDescriptor(), false);
    240     IPC::ChannelHandle out_handle("OUT", out_fd);
    241     out.reset(new IPC::Channel(out_handle,
    242                                IPC::Channel::MODE_CLIENT,
    243                                &cb_listener_));
    244     // PostTask the connect calls to make sure the callbacks happens
    245     // on the right threads.
    246     in_thread_->message_loop()->PostTask(
    247         FROM_HERE,
    248         base::Bind(&PipeChannelHelper::Connect, in.get()));
    249     out_thread_->message_loop()->PostTask(
    250         FROM_HERE,
    251         base::Bind(&PipeChannelHelper::Connect, out.get()));
    252   }
    253 
    254   static void DestroyChannel(scoped_ptr<IPC::Channel> *c,
    255                              base::WaitableEvent *event) {
    256     c->reset(0);
    257     event->Signal();
    258   }
    259 
    260   ~PipeChannelHelper() {
    261     base::WaitableEvent a(true, false);
    262     base::WaitableEvent b(true, false);
    263     in_thread_->message_loop()->PostTask(
    264         FROM_HERE,
    265         base::Bind(&PipeChannelHelper::DestroyChannel, &in, &a));
    266     out_thread_->message_loop()->PostTask(
    267         FROM_HERE,
    268         base::Bind(&PipeChannelHelper::DestroyChannel, &out, &b));
    269     a.Wait();
    270     b.Wait();
    271   }
    272 
    273   static void Connect(IPC::Channel *channel) {
    274     EXPECT_TRUE(channel->Connect());
    275   }
    276 
    277   void Send(int fd) {
    278     CHECK_EQ(base::MessageLoop::current(), in_thread_->message_loop());
    279 
    280     ASSERT_GE(fd, 0);
    281     base::FileDescriptor descriptor(fd, true);
    282 
    283     IPC::Message* message =
    284         new IPC::Message(0, 3, IPC::Message::PRIORITY_NORMAL);
    285     IPC::ParamTraits<base::FileDescriptor>::Write(message, descriptor);
    286     ASSERT_TRUE(in->Send(message));
    287   }
    288 
    289  private:
    290   scoped_ptr<IPC::Channel> in, out;
    291   base::Thread* in_thread_;
    292   base::Thread* out_thread_;
    293   MyCBListener cb_listener_;
    294   MyCBListener null_listener_;
    295 };
    296 
    297 // This test is meant to provoke a kernel bug on OSX, and to prove
    298 // that the workaround for it is working. It sets up two pipes and three
    299 // threads, the producer thread creates socketpairs and sends one of the fds
    300 // over pipe1 to the middleman thread. The middleman thread simply takes the fd
    301 // sends it over pipe2 to the consumer thread. The consumer thread writes a byte
    302 // to each fd it receives and then closes the pipe. The producer thread reads
    303 // the bytes back from each pair of pipes and make sure that everything worked.
    304 // This feedback mechanism makes sure that not too many file descriptors are
    305 // in flight at the same time. For more info on the bug, see:
    306 // http://crbug.com/298276
    307 class IPCMultiSendingFdsTest : public testing::Test {
    308  public:
    309   IPCMultiSendingFdsTest() : received_(true, false) {}
    310 
    311   void Producer(PipeChannelHelper* dest,
    312                 base::Thread* t,
    313                 int pipes_to_send) {
    314     for (int i = 0; i < pipes_to_send; i++) {
    315       received_.Reset();
    316       std::pair<int, int> pipe_fds = make_socket_pair();
    317       t->message_loop()->PostTask(
    318           FROM_HERE,
    319           base::Bind(&PipeChannelHelper::Send,
    320                      base::Unretained(dest),
    321                      pipe_fds.second));
    322       char tmp = 'x';
    323       CHECK_EQ(1, HANDLE_EINTR(write(pipe_fds.first, &tmp, 1)));
    324       CHECK_EQ(0, IGNORE_EINTR(close(pipe_fds.first)));
    325       received_.Wait();
    326     }
    327   }
    328 
    329   void ConsumerHandleFD(int fd) {
    330     char tmp = 'y';
    331     CHECK_EQ(1, HANDLE_EINTR(read(fd, &tmp, 1)));
    332     CHECK_EQ(tmp, 'x');
    333     CHECK_EQ(0, IGNORE_EINTR(close(fd)));
    334     received_.Signal();
    335   }
    336 
    337   base::Thread* CreateThread(const char* name) {
    338     base::Thread* ret = new base::Thread(name);
    339     base::Thread::Options options;
    340     options.message_loop_type = base::MessageLoop::TYPE_IO;
    341     ret->StartWithOptions(options);
    342     return ret;
    343   }
    344 
    345   void Run() {
    346     // On my mac, this test fails roughly 35 times per
    347     // million sends with low load, but much more with high load.
    348     // Unless the workaround is in place. With 10000 sends, we
    349     // should see at least a 3% failure rate.
    350     const int pipes_to_send = 20000;
    351     scoped_ptr<base::Thread> producer(CreateThread("producer"));
    352     scoped_ptr<base::Thread> middleman(CreateThread("middleman"));
    353     scoped_ptr<base::Thread> consumer(CreateThread("consumer"));
    354     PipeChannelHelper pipe1(
    355         middleman.get(),
    356         consumer.get(),
    357         base::Bind(&IPCMultiSendingFdsTest::ConsumerHandleFD,
    358                    base::Unretained(this)),
    359         pipes_to_send);
    360     PipeChannelHelper pipe2(
    361         producer.get(),
    362         middleman.get(),
    363         base::Bind(&PipeChannelHelper::Send, base::Unretained(&pipe1)),
    364         pipes_to_send);
    365     pipe1.Init();
    366     pipe2.Init();
    367     Producer(&pipe2, producer.get(), pipes_to_send);
    368   }
    369 
    370  private:
    371   base::WaitableEvent received_;
    372 };
    373 
    374 TEST_F(IPCMultiSendingFdsTest, StressTest) {
    375   Run();
    376 }
    377 
    378 }  // namespace
    379 
    380 #endif  // defined(OS_POSIX)
    381