Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(131)

Side by Side Diff: ipc/ipc_send_fds_test.cc

Issue 25325002: workaround for mac kernel bug (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: test now actually triggers kernel bug, auto-flush CloseFD messages Created 7 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« ipc/ipc_channel_posix.cc ('K') | « ipc/ipc_channel_win.cc ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "build/build_config.h" 5 #include "build/build_config.h"
6 6
7 #if defined(OS_POSIX) 7 #if defined(OS_POSIX)
8 #if defined(OS_MACOSX) 8 #if defined(OS_MACOSX)
9 extern "C" { 9 extern "C" {
10 #include <sandbox.h> 10 #include <sandbox.h>
11 } 11 }
12 #endif 12 #endif
13 #include <fcntl.h> 13 #include <fcntl.h>
14 #include <sys/resource.h>
15 #include <sys/socket.h>
14 #include <sys/stat.h> 16 #include <sys/stat.h>
15 #include <unistd.h> 17 #include <unistd.h>
16 18
19 #include <queue>
20
21 #include "base/callback.h"
17 #include "base/file_descriptor_posix.h" 22 #include "base/file_descriptor_posix.h"
18 #include "base/message_loop/message_loop.h" 23 #include "base/message_loop/message_loop.h"
19 #include "base/pickle.h" 24 #include "base/pickle.h"
20 #include "base/posix/eintr_wrapper.h" 25 #include "base/posix/eintr_wrapper.h"
26 #include "base/synchronization/waitable_event.h"
21 #include "ipc/ipc_message_utils.h" 27 #include "ipc/ipc_message_utils.h"
22 #include "ipc/ipc_test_base.h" 28 #include "ipc/ipc_test_base.h"
23 29
24 namespace { 30 namespace {
25 31
26 const unsigned kNumFDsToSend = 20; 32 const unsigned kNumFDsToSend = 20;
27 const char* kDevZeroPath = "/dev/zero"; 33 const char* kDevZeroPath = "/dev/zero";
28 34
29 static void VerifyAndCloseDescriptor(int fd, ino_t inode_num) { 35 class MyChannelDescriptorListenerBase : public IPC::Listener {
30 // Check that we can read from the FD.
31 char buf;
32 ssize_t amt_read = read(fd, &buf, 1);
33 ASSERT_EQ(amt_read, 1);
34 ASSERT_EQ(buf, 0); // /dev/zero always reads 0 bytes.
35
36 struct stat st;
37 ASSERT_EQ(fstat(fd, &st), 0);
38
39 ASSERT_EQ(close(fd), 0);
40
41 // Compare inode numbers to check that the file sent over the wire is actually
42 // the one expected.
43 ASSERT_EQ(inode_num, st.st_ino);
44 }
45
46 class MyChannelDescriptorListener : public IPC::Listener {
47 public: 36 public:
48 explicit MyChannelDescriptorListener(ino_t expected_inode_num)
49 : expected_inode_num_(expected_inode_num),
50 num_fds_received_(0) {}
51
52 virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE { 37 virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE {
53 PickleIterator iter(message); 38 PickleIterator iter(message);
54 39
55 ++num_fds_received_;
56 base::FileDescriptor descriptor; 40 base::FileDescriptor descriptor;
57 41
58 IPC::ParamTraits<base::FileDescriptor>::Read(&message, &iter, &descriptor); 42 IPC::ParamTraits<base::FileDescriptor>::Read(&message, &iter, &descriptor);
59 43
60 VerifyAndCloseDescriptor(descriptor.fd, expected_inode_num_); 44 HandleFD(descriptor.fd);
61 if (num_fds_received_ == kNumFDsToSend) 45 return true;
62 base::MessageLoop::current()->Quit(); 46 }
63 47
64 return true; 48 protected:
49 virtual void HandleFD(int fd) = 0;
50 };
51
52 class MyChannelDescriptorListener : public MyChannelDescriptorListenerBase {
53 public:
54 explicit MyChannelDescriptorListener(ino_t expected_inode_num)
55 : MyChannelDescriptorListenerBase(),
56 expected_inode_num_(expected_inode_num),
57 num_fds_received_(0) {
58 }
59
60 bool GotExpectedNumberOfDescriptors() const {
61 return num_fds_received_ == kNumFDsToSend;
65 } 62 }
66 63
67 virtual void OnChannelError() OVERRIDE { 64 virtual void OnChannelError() OVERRIDE {
68 base::MessageLoop::current()->Quit(); 65 base::MessageLoop::current()->Quit();
69 } 66 }
70 67
71 bool GotExpectedNumberOfDescriptors() const { 68 protected:
72 return num_fds_received_ == kNumFDsToSend; 69 virtual void HandleFD(int fd) OVERRIDE {
70 // Check that we can read from the FD.
71 char buf;
72 ssize_t amt_read = read(fd, &buf, 1);
73 ASSERT_EQ(amt_read, 1);
74 ASSERT_EQ(buf, 0); // /dev/zero always reads 0 bytes.
75
76 struct stat st;
77 ASSERT_EQ(fstat(fd, &st), 0);
78
79 ASSERT_EQ(close(fd), 0);
80
81 // Compare inode numbers to check that the file sent over the wire is
82 // actually the one expected.
83 ASSERT_EQ(expected_inode_num_, st.st_ino);
84
85 ++num_fds_received_;
86 if (num_fds_received_ == kNumFDsToSend)
87 base::MessageLoop::current()->Quit();
73 } 88 }
74 89
75 private: 90 private:
76 ino_t expected_inode_num_; 91 ino_t expected_inode_num_;
77 unsigned num_fds_received_; 92 unsigned num_fds_received_;
78 }; 93 };
79 94
95
80 class IPCSendFdsTest : public IPCTestBase { 96 class IPCSendFdsTest : public IPCTestBase {
81 protected: 97 protected:
82 void RunServer() { 98 void RunServer() {
83 // Set up IPC channel and start client. 99 // Set up IPC channel and start client.
84 MyChannelDescriptorListener listener(-1); 100 MyChannelDescriptorListener listener(-1);
85 CreateChannel(&listener); 101 CreateChannel(&listener);
86 ASSERT_TRUE(ConnectChannel()); 102 ASSERT_TRUE(ConnectChannel());
87 ASSERT_TRUE(StartClient()); 103 ASSERT_TRUE(StartClient());
88 104
89 for (unsigned i = 0; i < kNumFDsToSend; ++i) { 105 for (unsigned i = 0; i < kNumFDsToSend; ++i) {
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after
171 if (open(kDevZeroPath, O_RDONLY) != -1) { 187 if (open(kDevZeroPath, O_RDONLY) != -1) {
172 LOG(ERROR) << "Sandbox wasn't properly enabled"; 188 LOG(ERROR) << "Sandbox wasn't properly enabled";
173 return -1; 189 return -1;
174 } 190 }
175 191
176 // See if we can receive a file descriptor. 192 // See if we can receive a file descriptor.
177 return SendFdsClientCommon("SendFdsSandboxedClient", st.st_ino); 193 return SendFdsClientCommon("SendFdsSandboxedClient", st.st_ino);
178 } 194 }
179 #endif // defined(OS_MACOSX) 195 #endif // defined(OS_MACOSX)
180 196
197
198 class MyCBListener : public MyChannelDescriptorListenerBase {
199 public:
200 MyCBListener(base::Callback<void(int)> cb, int fds_to_send)
201 : MyChannelDescriptorListenerBase(),
202 cb_(cb) {
203 }
204
205 protected:
206 virtual void HandleFD(int fd) OVERRIDE {
207 cb_.Run(fd);
208 }
209 private:
210 base::Callback<void(int)> cb_;
211 };
212
213 std::pair<int, int> make_socket_pair() {
214 int pipe_fds[2];
215 CHECK_EQ(0, HANDLE_EINTR(socketpair(AF_UNIX, SOCK_STREAM, 0, pipe_fds)));
216 return std::pair<int, int>(pipe_fds[0], pipe_fds[1]);
217 }
218
219 static void null_cb(int unused_fd) {
220 NOTREACHED();
221 }
222
223 class PipeChannelHelper {
224 public:
225 PipeChannelHelper(base::Thread* in_thread,
226 base::Thread* out_thread,
227 base::Callback<void(int)> cb,
228 int fds_to_send) :
229 in_thread_(in_thread),
230 out_thread_(out_thread),
231 cb_listener_(cb, fds_to_send),
232 null_listener_(base::Bind(&null_cb), 0) {
233 IPC::ChannelHandle in_handle("IN");
234 in.reset(new IPC::Channel(in_handle,
235 IPC::Channel::MODE_SERVER,
236 &null_listener_));
237 base::FileDescriptor out_fd(in->TakeClientFileDescriptor(), false);
238 IPC::ChannelHandle out_handle("OUT", out_fd);
239 out.reset(new IPC::Channel(out_handle,
240 IPC::Channel::MODE_CLIENT,
241 &cb_listener_));
242 // PostTask the connect calls to make sure the callbacks happens
243 // on the right threads.
244 in_thread->message_loop()->PostTask(
245 FROM_HERE,
246 base::Bind(&PipeChannelHelper::Connect, in.get()));
247 out_thread->message_loop()->PostTask(
248 FROM_HERE,
249 base::Bind(&PipeChannelHelper::Connect, out.get()));
250 }
251
252 static void DestroyChannel(scoped_ptr<IPC::Channel> *c,
253 base::WaitableEvent *event) {
254 c->reset(0);
255 event->Signal();
256 }
257
258 ~PipeChannelHelper() {
259 base::WaitableEvent a(true, false);
260 base::WaitableEvent b(true, false);
261 in_thread_->message_loop()->PostTask(
262 FROM_HERE,
263 base::Bind(&PipeChannelHelper::DestroyChannel, &in, &a));
264 out_thread_->message_loop()->PostTask(
265 FROM_HERE,
266 base::Bind(&PipeChannelHelper::DestroyChannel, &out, &b));
267 a.Wait();
268 b.Wait();
269 }
270
271 static void Connect(IPC::Channel *channel) {
272 EXPECT_TRUE(channel->Connect());
273 }
274
275 void Send(int fd) {
276 CHECK_EQ(base::MessageLoop::current(), in_thread_->message_loop());
277
278 ASSERT_GE(fd, 0);
279 base::FileDescriptor descriptor(fd, true);
280
281 IPC::Message* message =
282 new IPC::Message(0, 3, IPC::Message::PRIORITY_NORMAL);
283 IPC::ParamTraits<base::FileDescriptor>::Write(message, descriptor);
284 ASSERT_TRUE(in->Send(message));
285 }
286
287 private:
288 scoped_ptr<IPC::Channel> in, out;
289 base::Thread* in_thread_;
290 base::Thread* out_thread_;
291 MyCBListener cb_listener_;
292 MyCBListener null_listener_;
293 };
294
295 // This test is meant to provoke a kernel bug on OSX, and to prove
296 // that the workaround for it is working. It sets up two pipes and three
297 // threads, the producer thread creates socketpairs and sends one of the fds
298 // over pipe1 to the middleman thread. The middleman thread simply takes the fd
299 // sends it over pipe2 to the consumer thread. The consumer thread writes a byte
300 // to each fd it receives and then closes the pipe. The producer thread reads
301 // the bytes back from each pair of pipes and make sure that everything worked.
302 // This feedback mechanism makes sure that not too many file descriptors are
303 // in flight at the same time. For more info on the bug, see:
304 // http://crbug.com/298276
305 class IPCMultiSendingFdsTest : public testing::Test {
306 public:
307 IPCMultiSendingFdsTest() : received_(true, false) {}
308
309 void Producer(PipeChannelHelper* dest,
310 base::Thread* t,
311 int pipes_to_send) {
312 for (int i = 0; i < pipes_to_send; i++) {
313 received_.Reset();
314 std::pair<int, int> pipe_fds = make_socket_pair();
315 t->message_loop()->PostTask(
316 FROM_HERE,
317 base::Bind(&PipeChannelHelper::Send,
318 base::Unretained(dest),
319 pipe_fds.second));
320 char tmp = 'x';
321 CHECK_EQ(1, HANDLE_EINTR(write(pipe_fds.first, &tmp, 1)));
322 CHECK_EQ(0, HANDLE_EINTR(close(pipe_fds.first)));
323 received_.Wait();
324 }
325 }
326
327 void ConsumerHandleFD(int fd) {
328 char tmp = 'y';
329 CHECK_EQ(1, HANDLE_EINTR(read(fd, &tmp, 1)));
330 CHECK_EQ(tmp, 'x');
331 CHECK_EQ(0, HANDLE_EINTR(close(fd)));
332 received_.Signal();
333 }
334
335 base::Thread* CreateThread(const char* name) {
336 base::Thread* ret = new base::Thread(name);
337 base::Thread::Options options;
338 options.message_loop_type = base::MessageLoop::TYPE_IO;
339 ret->StartWithOptions(options);
340 return ret;
341 }
342
343 void Run() {
344 struct rlimit limit;
345 getrlimit(RLIMIT_NOFILE, &limit);
346 limit.rlim_cur = 8192;
347 setrlimit(RLIMIT_NOFILE, &limit);
348 // Even with 10000 sends, this test only fails one time in five or so.
349 const int pipes_to_send = 50000;
350 scoped_ptr<base::Thread> producer(CreateThread("producer"));
351 scoped_ptr<base::Thread> middleman(CreateThread("middleman"));
352 scoped_ptr<base::Thread> consumer(CreateThread("consumer"));
353 PipeChannelHelper pipe1(
354 middleman.get(),
355 consumer.get(),
356 base::Bind(&IPCMultiSendingFdsTest::ConsumerHandleFD,
357 base::Unretained(this)),
358 pipes_to_send);
359 PipeChannelHelper pipe2(
360 producer.get(),
361 middleman.get(),
362 base::Bind(&PipeChannelHelper::Send, base::Unretained(&pipe1)),
363 pipes_to_send);
364 Producer(&pipe2, producer.get(), pipes_to_send);
365 }
366
367 private:
368 base::WaitableEvent received_;
369 };
370
371 TEST_F(IPCMultiSendingFdsTest, StressTest) {
372 Run();
373 }
374
181 } // namespace 375 } // namespace
182 376
183 #endif // defined(OS_POSIX) 377 #endif // defined(OS_POSIX)
OLDNEW
« ipc/ipc_channel_posix.cc ('K') | « ipc/ipc_channel_win.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698