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.
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"
16 // This handle must not be closed.
17 volatile HANDLE g_alive_mutex
= NULL
;
22 SharedMemIPCServer::SharedMemIPCServer(HANDLE target_process
,
23 DWORD target_process_id
,
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
40 HANDLE mutex
= ::CreateMutexW(NULL
, TRUE
, NULL
);
41 if (::InterlockedCompareExchangePointer(&g_alive_mutex
, mutex
, NULL
)) {
42 // We lost the race to create the mutex.
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.
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
);
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
) {
70 // The channel size should be aligned.
71 if (0 != (channel_size
% 32)) {
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
);
79 // If we cannot fit even one channel we bail out.
80 if (0 == channel_count
) {
83 // Calculate the start of the first channel.
84 size_t base_start
= (sizeof(ChannelControl
)* channel_count
) +
85 offsetof(IPCControl
, channels
);
87 client_control_
= reinterpret_cast<IPCControl
*>(shared_mem
);
88 client_control_
->channels_count
= 0;
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
);
101 if (!MakeEvents(&service_context
->ping_event
,
102 &service_context
->pong_event
,
103 &client_context
->ping_event
,
104 &client_context
->pong_event
)) {
108 client_context
->channel_base
= base_start
;
109 client_context
->state
= kFreeChannel
;
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
);
129 if (!::DuplicateHandle(::GetCurrentProcess(), g_alive_mutex
,
130 target_process_
, &client_control_
->server_alive
,
131 SYNCHRONIZE
| EVENT_MODIFY_STATE
, FALSE
, 0)) {
134 // This last setting indicates to the client all is setup.
135 client_control_
->channels_count
= channel_count
;
139 // Releases memory allocated for IPC arguments, if needed.
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
]) {
144 delete reinterpret_cast<base::string16
*>(args
[i
]);
148 case INOUTPTR_TYPE
: {
149 delete reinterpret_cast<CountedBuffer
*>(args
[i
]);
158 // Fills up the list of arguments (args and ipc_params) for an IPC call.
159 bool GetArgs(CrossCallParamsEx
* params
, IPCParams
* ipc_params
,
160 void* args
[kMaxIpcParams
]) {
161 if (kMaxIpcParams
< params
->GetParamsCount())
164 for (uint32 i
= 0; i
< params
->GetParamsCount(); i
++) {
167 args
[i
] = params
->GetRawParameter(i
, &size
, &type
);
169 ipc_params
->args
[i
] = type
;
172 scoped_ptr
<base::string16
> data(new base::string16
);
173 if (!params
->GetParameterStr(i
, data
.get())) {
175 ReleaseArgs(ipc_params
, args
);
178 args
[i
] = data
.release();
183 if (!params
->GetParameter32(i
, &data
)) {
184 ReleaseArgs(ipc_params
, args
);
187 IPCInt
ipc_int(data
);
188 args
[i
] = ipc_int
.AsVoidPtr();
191 case VOIDPTR_TYPE
: {
193 if (!params
->GetParameterVoidPtr(i
, &data
)) {
194 ReleaseArgs(ipc_params
, args
);
200 case INOUTPTR_TYPE
: {
202 ReleaseArgs(ipc_params
, args
);
205 CountedBuffer
* buffer
= new CountedBuffer(args
[i
] , size
);
216 bool SharedMemIPCServer::InvokeCallback(const ServerControl
* service_context
,
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
,
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
;
237 void* args
[kMaxIpcParams
];
238 if (!GetArgs(params
.get(), &ipc_params
, args
))
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
;
247 Dispatcher
* handler
= NULL
;
249 Dispatcher::CallbackGeneric callback_generic
;
250 handler
= dispatcher
->OnMessageReady(&ipc_params
, &callback_generic
);
252 switch (params
->GetParamsCount()) {
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
))
263 Dispatcher::Callback1 callback
=
264 reinterpret_cast<Dispatcher::Callback1
>(callback_generic
);
265 if (!(handler
->*callback
)(&ipc_info
, args
[0]))
271 Dispatcher::Callback2 callback
=
272 reinterpret_cast<Dispatcher::Callback2
>(callback_generic
);
273 if (!(handler
->*callback
)(&ipc_info
, args
[0], args
[1]))
279 Dispatcher::Callback3 callback
=
280 reinterpret_cast<Dispatcher::Callback3
>(callback_generic
);
281 if (!(handler
->*callback
)(&ipc_info
, args
[0], args
[1], args
[2]))
287 Dispatcher::Callback4 callback
=
288 reinterpret_cast<Dispatcher::Callback4
>(callback_generic
);
289 if (!(handler
->*callback
)(&ipc_info
, args
[0], args
[1], args
[2],
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],
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],
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]))
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]))
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]))
349 SetCallError(SBOX_ERROR_FAILED_IPC
, call_result
);
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
356 memcpy(ipc_buffer
, params
.get(), output_size
);
360 ReleaseArgs(&ipc_params
, args
);
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()
368 void __stdcall
SharedMemIPCServer::ThreadPingEventReady(void* context
,
370 if (NULL
== context
) {
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.
378 ::InterlockedCompareExchange(&service_context
->channel
->state
,
379 kAckChannel
, kBusyChannel
);
380 if (kBusyChannel
!= last_state
) {
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
;
390 InvokeCallback(service_context
, buffer
, &call_result
);
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
);
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
;
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)) {
412 *server_pong
= ::CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
413 if (!::DuplicateHandle(::GetCurrentProcess(), *server_pong
, target_process_
,
414 client_pong
, kDesiredAccess
, FALSE
, 0)) {
420 } // namespace sandbox