- add sources.
[platform/framework/web/crosswalk.git] / src / sandbox / win / src / sharedmem_ipc_server.cc
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