| OLD | NEW |
| (Empty) |
| 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/src/sharedmem_ipc_server.h" | |
| 9 #include "sandbox/src/sharedmem_ipc_client.h" | |
| 10 #include "sandbox/src/sandbox.h" | |
| 11 #include "sandbox/src/sandbox_types.h" | |
| 12 #include "sandbox/src/crosscall_params.h" | |
| 13 #include "sandbox/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 | |
| OLD | NEW |