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.

     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"
    15 namespace sandbox {
    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 }
    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 }
    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   }
    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);
    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);
    69   client_control_ = reinterpret_cast<IPCControl*>(shared_mem);
    70   client_control_->channels_count = 0;
    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);
    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     }
    90     client_context->channel_base = base_start;
    91     client_context->state = kFreeChannel;
    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   }
   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 }
   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 }
   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;
   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 }
   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;
   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;
   227   void* args[kMaxIpcParams];
   228   if (!GetArgs(params.get(), &ipc_params, args))
   229     return false;
   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;
   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   }
   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   }
   350   ReleaseArgs(&ipc_params, args);
   352   return !error;
   353 }
   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   }
   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;
   380   InvokeCallback(service_context, buffer, &call_result);
   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 }
   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;
   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 }
   410 }  // namespace sandbox

mercurial