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