security/sandbox/win/src/sharedmem_ipc_server.cc

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial