OLD | NEW |
| (Empty) |
1 // Copyright (c) 2009 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 #include "chrome/common/ipc_tests.h" | |
8 | |
9 #if defined(OS_MACOSX) | |
10 extern "C" { | |
11 #include <sandbox.h> | |
12 } | |
13 #endif | |
14 #include <fcntl.h> | |
15 #include <sys/stat.h> | |
16 | |
17 #include "base/eintr_wrapper.h" | |
18 #include "base/message_loop.h" | |
19 #include "chrome/common/ipc_channel.h" | |
20 #include "chrome/common/ipc_message_utils.h" | |
21 | |
22 #if defined(OS_POSIX) | |
23 | |
24 namespace { | |
25 | |
26 const unsigned kNumFDsToSend = 20; | |
27 const char* kDevZeroPath = "/dev/zero"; | |
28 | |
29 static void VerifyAndCloseDescriptor(int fd, ino_t inode_num) { | |
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 NUL bytes. | |
35 | |
36 struct stat st; | |
37 ASSERT_EQ(fstat(fd, &st), 0); | |
38 | |
39 ASSERT_EQ(close(fd), 0); | |
40 | |
41 // We compare iNode numbers to check that the file sent over the wire | |
42 // was actually the same physical file as the one we were expecting. | |
43 ASSERT_EQ(inode_num, st.st_ino); | |
44 } | |
45 | |
46 class MyChannelDescriptorListener : public IPC::Channel::Listener { | |
47 public: | |
48 MyChannelDescriptorListener(ino_t expected_inode_num) | |
49 : expected_inode_num_(expected_inode_num), | |
50 num_fds_received_(0) {} | |
51 | |
52 virtual void OnMessageReceived(const IPC::Message& message) { | |
53 void* iter = NULL; | |
54 | |
55 ++num_fds_received_; | |
56 base::FileDescriptor descriptor; | |
57 | |
58 ASSERT_TRUE( | |
59 IPC::ParamTraits<base::FileDescriptor>::Read( | |
60 &message, &iter, &descriptor)); | |
61 | |
62 VerifyAndCloseDescriptor(descriptor.fd, expected_inode_num_); | |
63 if (num_fds_received_ == kNumFDsToSend) { | |
64 MessageLoop::current()->Quit(); | |
65 } | |
66 } | |
67 | |
68 virtual void OnChannelError() { | |
69 MessageLoop::current()->Quit(); | |
70 } | |
71 private: | |
72 ino_t expected_inode_num_; | |
73 unsigned num_fds_received_; | |
74 }; | |
75 | |
76 void TestDescriptorServer(IPC::Channel &chan, | |
77 base::ProcessHandle process_handle) { | |
78 ASSERT_TRUE(process_handle); | |
79 | |
80 for (unsigned i = 0; i < kNumFDsToSend; ++i) { | |
81 base::FileDescriptor descriptor; | |
82 const int fd = open(kDevZeroPath, O_RDONLY); | |
83 ASSERT_GE(fd, 0); | |
84 descriptor.auto_close = true; | |
85 descriptor.fd = fd; | |
86 | |
87 IPC::Message* message = new IPC::Message(0, // routing_id | |
88 3, // message type | |
89 IPC::Message::PRIORITY_NORMAL); | |
90 IPC::ParamTraits<base::FileDescriptor>::Write(message, descriptor); | |
91 chan.Send(message); | |
92 } | |
93 | |
94 // Run message loop. | |
95 MessageLoop::current()->Run(); | |
96 | |
97 // Close Channel so client gets its OnChannelError() callback fired. | |
98 chan.Close(); | |
99 | |
100 // Cleanup child process. | |
101 EXPECT_TRUE(base::WaitForSingleProcess(process_handle, 5000)); | |
102 } | |
103 | |
104 int TestDescriptorClient(ino_t expected_inode_num) { | |
105 MessageLoopForIO main_message_loop; | |
106 MyChannelDescriptorListener listener(expected_inode_num); | |
107 | |
108 // Setup IPC channel. | |
109 IPC::Channel chan(kTestClientChannel, IPC::Channel::MODE_CLIENT, | |
110 &listener); | |
111 chan.Connect(); | |
112 MessageLoop::current()->Run(); | |
113 | |
114 return 0; | |
115 } | |
116 | |
117 } // namespace | |
118 | |
119 // --------------------------------------------------------------------------- | |
120 #if defined(OS_MACOSX) | |
121 // TODO(port): Make this test cross-platform. | |
122 MULTIPROCESS_TEST_MAIN(RunTestDescriptorClientSandboxed) { | |
123 struct stat st; | |
124 const int fd = open(kDevZeroPath, O_RDONLY); | |
125 fstat(fd, &st); | |
126 HANDLE_EINTR(close(fd)); | |
127 | |
128 // Enable the Sandbox. | |
129 char* error_buff = NULL; | |
130 int error = sandbox_init(kSBXProfilePureComputation, SANDBOX_NAMED, | |
131 &error_buff); | |
132 bool success = (error == 0 && error_buff == NULL); | |
133 if (!success) { | |
134 return -1; | |
135 } | |
136 | |
137 sandbox_free_error(error_buff); | |
138 | |
139 // Make sure Sandbox is really enabled. | |
140 if (open(kDevZeroPath, O_RDONLY) != -1) { | |
141 LOG(ERROR) << "Sandbox wasn't properly enabled"; | |
142 return -1; | |
143 } | |
144 | |
145 // See if we can receive a file descriptor. | |
146 return TestDescriptorClient(st.st_ino); | |
147 } | |
148 | |
149 // Test that FDs are correctly sent to a sandboxed process. | |
150 TEST_F(IPCChannelTest, DescriptorTestSandboxed) { | |
151 // Setup IPC channel. | |
152 MyChannelDescriptorListener listener(-1); | |
153 | |
154 IPC::Channel chan(kTestClientChannel, IPC::Channel::MODE_SERVER, | |
155 &listener); | |
156 chan.Connect(); | |
157 | |
158 base::ProcessHandle process_handle = SpawnChild( | |
159 TEST_DESCRIPTOR_CLIENT_SANDBOXED, | |
160 &chan); | |
161 TestDescriptorServer(chan, process_handle); | |
162 } | |
163 #endif // defined(OS_MACOSX) | |
164 | |
165 MULTIPROCESS_TEST_MAIN(RunTestDescriptorClient) { | |
166 struct stat st; | |
167 const int fd = open(kDevZeroPath, O_RDONLY); | |
168 fstat(fd, &st); | |
169 HANDLE_EINTR(close(fd)); | |
170 | |
171 return TestDescriptorClient(st.st_ino); | |
172 } | |
173 | |
174 TEST_F(IPCChannelTest, DescriptorTest) { | |
175 // Setup IPC channel. | |
176 MyChannelDescriptorListener listener(-1); | |
177 | |
178 IPC::Channel chan(kTestClientChannel, IPC::Channel::MODE_SERVER, | |
179 &listener); | |
180 chan.Connect(); | |
181 | |
182 base::ProcessHandle process_handle = SpawnChild(TEST_DESCRIPTOR_CLIENT, | |
183 &chan); | |
184 TestDescriptorServer(chan, process_handle); | |
185 } | |
186 | |
187 #endif // defined(OS_POSIX) | |
OLD | NEW |