|
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 "base/callback.h" |
|
6 #include "base/logging.h" |
|
7 #include "base/memory/scoped_ptr.h" |
|
8 #include "sandbox/win/src/sharedmem_ipc_server.h" |
|
9 #include "sandbox/win/src/sharedmem_ipc_client.h" |
|
10 #include "sandbox/win/src/sandbox.h" |
|
11 #include "sandbox/win/src/sandbox_types.h" |
|
12 #include "sandbox/win/src/crosscall_params.h" |
|
13 #include "sandbox/win/src/crosscall_server.h" |
|
14 |
|
15 namespace sandbox { |
|
16 |
|
17 SharedMemIPCServer::SharedMemIPCServer(HANDLE target_process, |
|
18 DWORD target_process_id, |
|
19 HANDLE target_job, |
|
20 ThreadProvider* thread_provider, |
|
21 Dispatcher* dispatcher) |
|
22 : client_control_(NULL), |
|
23 thread_provider_(thread_provider), |
|
24 target_process_(target_process), |
|
25 target_process_id_(target_process_id), |
|
26 target_job_object_(target_job), |
|
27 call_dispatcher_(dispatcher) { |
|
28 } |
|
29 |
|
30 SharedMemIPCServer::~SharedMemIPCServer() { |
|
31 // Free the wait handles associated with the thread pool. |
|
32 if (!thread_provider_->UnRegisterWaits(this)) { |
|
33 // Better to leak than to crash. |
|
34 return; |
|
35 } |
|
36 // Free the IPC signal events. |
|
37 ServerContexts::iterator it; |
|
38 for (it = server_contexts_.begin(); it != server_contexts_.end(); ++it) { |
|
39 ServerControl* context = (*it); |
|
40 ::CloseHandle(context->ping_event); |
|
41 ::CloseHandle(context->pong_event); |
|
42 delete context; |
|
43 } |
|
44 } |
|
45 |
|
46 bool SharedMemIPCServer::Init(void* shared_mem, uint32 shared_size, |
|
47 uint32 channel_size) { |
|
48 // The shared memory needs to be at least as big as a channel. |
|
49 if (shared_size < channel_size) { |
|
50 return false; |
|
51 } |
|
52 // The channel size should be aligned. |
|
53 if (0 != (channel_size % 32)) { |
|
54 return false; |
|
55 } |
|
56 |
|
57 // Calculate how many channels we can fit in the shared memory. |
|
58 shared_size -= offsetof(IPCControl, channels); |
|
59 size_t channel_count = shared_size / (sizeof(ChannelControl) + channel_size); |
|
60 |
|
61 // If we cannot fit even one channel we bail out. |
|
62 if (0 == channel_count) { |
|
63 return false; |
|
64 } |
|
65 // Calculate the start of the first channel. |
|
66 size_t base_start = (sizeof(ChannelControl)* channel_count) + |
|
67 offsetof(IPCControl, channels); |
|
68 |
|
69 client_control_ = reinterpret_cast<IPCControl*>(shared_mem); |
|
70 client_control_->channels_count = 0; |
|
71 |
|
72 // This is the initialization that we do per-channel. Basically: |
|
73 // 1) make two events (ping & pong) |
|
74 // 2) create handles to the events for the client and the server. |
|
75 // 3) initialize the channel (client_context) with the state. |
|
76 // 4) initialize the server side of the channel (service_context). |
|
77 // 5) call the thread provider RegisterWait to register the ping events. |
|
78 for (size_t ix = 0; ix != channel_count; ++ix) { |
|
79 ChannelControl* client_context = &client_control_->channels[ix]; |
|
80 ServerControl* service_context = new ServerControl; |
|
81 server_contexts_.push_back(service_context); |
|
82 |
|
83 if (!MakeEvents(&service_context->ping_event, |
|
84 &service_context->pong_event, |
|
85 &client_context->ping_event, |
|
86 &client_context->pong_event)) { |
|
87 return false; |
|
88 } |
|
89 |
|
90 client_context->channel_base = base_start; |
|
91 client_context->state = kFreeChannel; |
|
92 |
|
93 // Note that some of these values are available as members of this |
|
94 // object but we put them again into the service_context because we |
|
95 // will be called on a static method (ThreadPingEventReady) |
|
96 service_context->shared_base = reinterpret_cast<char*>(shared_mem); |
|
97 service_context->channel_size = channel_size; |
|
98 service_context->channel = client_context; |
|
99 service_context->channel_buffer = service_context->shared_base + |
|
100 client_context->channel_base; |
|
101 service_context->dispatcher = call_dispatcher_; |
|
102 service_context->target_info.process = target_process_; |
|
103 service_context->target_info.process_id = target_process_id_; |
|
104 service_context->target_info.job_object = target_job_object_; |
|
105 // Advance to the next channel. |
|
106 base_start += channel_size; |
|
107 // Register the ping event with the threadpool. |
|
108 thread_provider_->RegisterWait(this, service_context->ping_event, |
|
109 ThreadPingEventReady, service_context); |
|
110 } |
|
111 |
|
112 // We create a mutex that the server locks. If the server dies unexpectedly, |
|
113 // the thread that owns it will fail to release the lock and windows will |
|
114 // report to the target (when it tries to acquire it) that the wait was |
|
115 // abandoned. Note: We purposely leak the local handle because we want it to |
|
116 // be closed by Windows itself so it is properly marked as abandoned if the |
|
117 // server dies. |
|
118 if (!::DuplicateHandle(::GetCurrentProcess(), |
|
119 ::CreateMutexW(NULL, TRUE, NULL), |
|
120 target_process_, &client_control_->server_alive, |
|
121 SYNCHRONIZE | EVENT_MODIFY_STATE, FALSE, 0)) { |
|
122 return false; |
|
123 } |
|
124 // This last setting indicates to the client all is setup. |
|
125 client_control_->channels_count = channel_count; |
|
126 return true; |
|
127 } |
|
128 |
|
129 // Releases memory allocated for IPC arguments, if needed. |
|
130 void ReleaseArgs(const IPCParams* ipc_params, void* args[kMaxIpcParams]) { |
|
131 for (size_t i = 0; i < kMaxIpcParams; i++) { |
|
132 switch (ipc_params->args[i]) { |
|
133 case WCHAR_TYPE: { |
|
134 delete reinterpret_cast<std::wstring*>(args[i]); |
|
135 args[i] = NULL; |
|
136 break; |
|
137 } |
|
138 case INOUTPTR_TYPE: { |
|
139 delete reinterpret_cast<CountedBuffer*>(args[i]); |
|
140 args[i] = NULL; |
|
141 break; |
|
142 } |
|
143 default: break; |
|
144 } |
|
145 } |
|
146 } |
|
147 |
|
148 // Fills up the list of arguments (args and ipc_params) for an IPC call. |
|
149 bool GetArgs(CrossCallParamsEx* params, IPCParams* ipc_params, |
|
150 void* args[kMaxIpcParams]) { |
|
151 if (kMaxIpcParams < params->GetParamsCount()) |
|
152 return false; |
|
153 |
|
154 for (uint32 i = 0; i < params->GetParamsCount(); i++) { |
|
155 uint32 size; |
|
156 ArgType type; |
|
157 args[i] = params->GetRawParameter(i, &size, &type); |
|
158 if (args[i]) { |
|
159 ipc_params->args[i] = type; |
|
160 switch (type) { |
|
161 case WCHAR_TYPE: { |
|
162 scoped_ptr<std::wstring> data(new std::wstring); |
|
163 if (!params->GetParameterStr(i, data.get())) { |
|
164 args[i] = 0; |
|
165 ReleaseArgs(ipc_params, args); |
|
166 return false; |
|
167 } |
|
168 args[i] = data.release(); |
|
169 break; |
|
170 } |
|
171 case ULONG_TYPE: { |
|
172 uint32 data; |
|
173 if (!params->GetParameter32(i, &data)) { |
|
174 ReleaseArgs(ipc_params, args); |
|
175 return false; |
|
176 } |
|
177 IPCInt ipc_int(data); |
|
178 args[i] = ipc_int.AsVoidPtr(); |
|
179 break; |
|
180 } |
|
181 case VOIDPTR_TYPE : { |
|
182 void* data; |
|
183 if (!params->GetParameterVoidPtr(i, &data)) { |
|
184 ReleaseArgs(ipc_params, args); |
|
185 return false; |
|
186 } |
|
187 args[i] = data; |
|
188 break; |
|
189 } |
|
190 case INOUTPTR_TYPE: { |
|
191 if (!args[i]) { |
|
192 ReleaseArgs(ipc_params, args); |
|
193 return false; |
|
194 } |
|
195 CountedBuffer* buffer = new CountedBuffer(args[i] , size); |
|
196 args[i] = buffer; |
|
197 break; |
|
198 } |
|
199 default: break; |
|
200 } |
|
201 } |
|
202 } |
|
203 return true; |
|
204 } |
|
205 |
|
206 bool SharedMemIPCServer::InvokeCallback(const ServerControl* service_context, |
|
207 void* ipc_buffer, |
|
208 CrossCallReturn* call_result) { |
|
209 // Set the default error code; |
|
210 SetCallError(SBOX_ERROR_INVALID_IPC, call_result); |
|
211 uint32 output_size = 0; |
|
212 // Parse, verify and copy the message. The handler operates on a copy |
|
213 // of the message so the client cannot play dirty tricks by changing the |
|
214 // data in the channel while the IPC is being processed. |
|
215 scoped_ptr<CrossCallParamsEx> params( |
|
216 CrossCallParamsEx::CreateFromBuffer(ipc_buffer, |
|
217 service_context->channel_size, |
|
218 &output_size)); |
|
219 if (!params.get()) |
|
220 return false; |
|
221 |
|
222 uint32 tag = params->GetTag(); |
|
223 COMPILE_ASSERT(0 == INVALID_TYPE, Incorrect_type_enum); |
|
224 IPCParams ipc_params = {0}; |
|
225 ipc_params.ipc_tag = tag; |
|
226 |
|
227 void* args[kMaxIpcParams]; |
|
228 if (!GetArgs(params.get(), &ipc_params, args)) |
|
229 return false; |
|
230 |
|
231 IPCInfo ipc_info = {0}; |
|
232 ipc_info.ipc_tag = tag; |
|
233 ipc_info.client_info = &service_context->target_info; |
|
234 Dispatcher* dispatcher = service_context->dispatcher; |
|
235 DCHECK(dispatcher); |
|
236 bool error = true; |
|
237 Dispatcher* handler = NULL; |
|
238 |
|
239 Dispatcher::CallbackGeneric callback_generic; |
|
240 handler = dispatcher->OnMessageReady(&ipc_params, &callback_generic); |
|
241 if (handler) { |
|
242 switch (params->GetParamsCount()) { |
|
243 case 0: { |
|
244 // Ask the IPC dispatcher if she can service this IPC. |
|
245 Dispatcher::Callback0 callback = |
|
246 reinterpret_cast<Dispatcher::Callback0>(callback_generic); |
|
247 if (!(handler->*callback)(&ipc_info)) |
|
248 break; |
|
249 error = false; |
|
250 break; |
|
251 } |
|
252 case 1: { |
|
253 Dispatcher::Callback1 callback = |
|
254 reinterpret_cast<Dispatcher::Callback1>(callback_generic); |
|
255 if (!(handler->*callback)(&ipc_info, args[0])) |
|
256 break; |
|
257 error = false; |
|
258 break; |
|
259 } |
|
260 case 2: { |
|
261 Dispatcher::Callback2 callback = |
|
262 reinterpret_cast<Dispatcher::Callback2>(callback_generic); |
|
263 if (!(handler->*callback)(&ipc_info, args[0], args[1])) |
|
264 break; |
|
265 error = false; |
|
266 break; |
|
267 } |
|
268 case 3: { |
|
269 Dispatcher::Callback3 callback = |
|
270 reinterpret_cast<Dispatcher::Callback3>(callback_generic); |
|
271 if (!(handler->*callback)(&ipc_info, args[0], args[1], args[2])) |
|
272 break; |
|
273 error = false; |
|
274 break; |
|
275 } |
|
276 case 4: { |
|
277 Dispatcher::Callback4 callback = |
|
278 reinterpret_cast<Dispatcher::Callback4>(callback_generic); |
|
279 if (!(handler->*callback)(&ipc_info, args[0], args[1], args[2], |
|
280 args[3])) |
|
281 break; |
|
282 error = false; |
|
283 break; |
|
284 } |
|
285 case 5: { |
|
286 Dispatcher::Callback5 callback = |
|
287 reinterpret_cast<Dispatcher::Callback5>(callback_generic); |
|
288 if (!(handler->*callback)(&ipc_info, args[0], args[1], args[2], args[3], |
|
289 args[4])) |
|
290 break; |
|
291 error = false; |
|
292 break; |
|
293 } |
|
294 case 6: { |
|
295 Dispatcher::Callback6 callback = |
|
296 reinterpret_cast<Dispatcher::Callback6>(callback_generic); |
|
297 if (!(handler->*callback)(&ipc_info, args[0], args[1], args[2], args[3], |
|
298 args[4], args[5])) |
|
299 break; |
|
300 error = false; |
|
301 break; |
|
302 } |
|
303 case 7: { |
|
304 Dispatcher::Callback7 callback = |
|
305 reinterpret_cast<Dispatcher::Callback7>(callback_generic); |
|
306 if (!(handler->*callback)(&ipc_info, args[0], args[1], args[2], args[3], |
|
307 args[4], args[5], args[6])) |
|
308 break; |
|
309 error = false; |
|
310 break; |
|
311 } |
|
312 case 8: { |
|
313 Dispatcher::Callback8 callback = |
|
314 reinterpret_cast<Dispatcher::Callback8>(callback_generic); |
|
315 if (!(handler->*callback)(&ipc_info, args[0], args[1], args[2], args[3], |
|
316 args[4], args[5], args[6], args[7])) |
|
317 break; |
|
318 error = false; |
|
319 break; |
|
320 } |
|
321 case 9: { |
|
322 Dispatcher::Callback9 callback = |
|
323 reinterpret_cast<Dispatcher::Callback9>(callback_generic); |
|
324 if (!(handler->*callback)(&ipc_info, args[0], args[1], args[2], args[3], |
|
325 args[4], args[5], args[6], args[7], args[8])) |
|
326 break; |
|
327 error = false; |
|
328 break; |
|
329 } |
|
330 default: { |
|
331 NOTREACHED(); |
|
332 break; |
|
333 } |
|
334 } |
|
335 } |
|
336 |
|
337 if (error) { |
|
338 if (handler) |
|
339 SetCallError(SBOX_ERROR_FAILED_IPC, call_result); |
|
340 } else { |
|
341 memcpy(call_result, &ipc_info.return_info, sizeof(*call_result)); |
|
342 SetCallSuccess(call_result); |
|
343 if (params->IsInOut()) { |
|
344 // Maybe the params got changed by the broker. We need to upadte the |
|
345 // memory section. |
|
346 memcpy(ipc_buffer, params.get(), output_size); |
|
347 } |
|
348 } |
|
349 |
|
350 ReleaseArgs(&ipc_params, args); |
|
351 |
|
352 return !error; |
|
353 } |
|
354 |
|
355 // This function gets called by a thread from the thread pool when a |
|
356 // ping event fires. The context is the same as passed in the RegisterWait() |
|
357 // call above. |
|
358 void __stdcall SharedMemIPCServer::ThreadPingEventReady(void* context, |
|
359 unsigned char) { |
|
360 if (NULL == context) { |
|
361 DCHECK(false); |
|
362 return; |
|
363 } |
|
364 ServerControl* service_context = reinterpret_cast<ServerControl*>(context); |
|
365 // Since the event fired, the channel *must* be busy. Change to kAckChannel |
|
366 // while we service it. |
|
367 LONG last_state = |
|
368 ::InterlockedCompareExchange(&service_context->channel->state, |
|
369 kAckChannel, kBusyChannel); |
|
370 if (kBusyChannel != last_state) { |
|
371 DCHECK(false); |
|
372 return; |
|
373 } |
|
374 |
|
375 // Prepare the result structure. At this point we will return some result |
|
376 // even if the IPC is invalid, malformed or has no handler. |
|
377 CrossCallReturn call_result = {0}; |
|
378 void* buffer = service_context->channel_buffer; |
|
379 |
|
380 InvokeCallback(service_context, buffer, &call_result); |
|
381 |
|
382 // Copy the answer back into the channel and signal the pong event. This |
|
383 // should wake up the client so he can finish the the ipc cycle. |
|
384 CrossCallParams* call_params = reinterpret_cast<CrossCallParams*>(buffer); |
|
385 memcpy(call_params->GetCallReturn(), &call_result, sizeof(call_result)); |
|
386 ::InterlockedExchange(&service_context->channel->state, kAckChannel); |
|
387 ::SetEvent(service_context->pong_event); |
|
388 } |
|
389 |
|
390 bool SharedMemIPCServer::MakeEvents(HANDLE* server_ping, HANDLE* server_pong, |
|
391 HANDLE* client_ping, HANDLE* client_pong) { |
|
392 // Note that the IPC client has no right to delete the events. That would |
|
393 // cause problems. The server *owns* the events. |
|
394 const DWORD kDesiredAccess = SYNCHRONIZE | EVENT_MODIFY_STATE; |
|
395 |
|
396 // The events are auto reset, and start not signaled. |
|
397 *server_ping = ::CreateEventW(NULL, FALSE, FALSE, NULL); |
|
398 if (!::DuplicateHandle(::GetCurrentProcess(), *server_ping, target_process_, |
|
399 client_ping, kDesiredAccess, FALSE, 0)) { |
|
400 return false; |
|
401 } |
|
402 *server_pong = ::CreateEventW(NULL, FALSE, FALSE, NULL); |
|
403 if (!::DuplicateHandle(::GetCurrentProcess(), *server_pong, target_process_, |
|
404 client_pong, kDesiredAccess, FALSE, 0)) { |
|
405 return false; |
|
406 } |
|
407 return true; |
|
408 } |
|
409 |
|
410 } // namespace sandbox |