security/sandbox/win/src/sharedmem_ipc_client.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) 2006-2008 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 <string.h>
     6 #include "sandbox/win/src/sharedmem_ipc_client.h"
     7 #include "sandbox/win/src/sandbox.h"
     8 #include "sandbox/win/src/crosscall_client.h"
     9 #include "sandbox/win/src/crosscall_params.h"
    10 #include "base/logging.h"
    12 namespace sandbox {
    14 // Get the base of the data buffer of the channel; this is where the input
    15 // parameters get serialized. Since they get serialized directly into the
    16 // channel we avoid one copy.
    17 void* SharedMemIPCClient::GetBuffer() {
    18   bool failure = false;
    19   size_t ix = LockFreeChannel(&failure);
    20   if (failure) {
    21     return NULL;
    22   }
    23   return reinterpret_cast<char*>(control_) +
    24          control_->channels[ix].channel_base;
    25 }
    27 // If we need to cancel an IPC before issuing DoCall
    28 // our client should call FreeBuffer with the same pointer
    29 // returned by GetBuffer.
    30 void SharedMemIPCClient::FreeBuffer(void* buffer) {
    31   size_t num = ChannelIndexFromBuffer(buffer);
    32   ChannelControl* channel = control_->channels;
    33   LONG result = ::InterlockedExchange(&channel[num].state, kFreeChannel);
    34   DCHECK(kFreeChannel != result);
    35   result;
    36 }
    38 // The constructor simply casts the shared memory to the internal
    39 // structures. This is a cheap step that is why this IPC object can
    40 // and should be constructed per call.
    41 SharedMemIPCClient::SharedMemIPCClient(void* shared_mem)
    42     : control_(reinterpret_cast<IPCControl*>(shared_mem)) {
    43   first_base_ = reinterpret_cast<char*>(shared_mem) +
    44                control_->channels[0].channel_base;
    45   // There must be at least one channel.
    46   DCHECK(0 != control_->channels_count);
    47 }
    49 // Do the IPC. At this point the channel should have already been
    50 // filled with the serialized input parameters.
    51 // We follow the pattern explained in the header file.
    52 ResultCode SharedMemIPCClient::DoCall(CrossCallParams* params,
    53                                       CrossCallReturn* answer) {
    54   if (!control_->server_alive)
    55     return SBOX_ERROR_CHANNEL_ERROR;
    57   size_t num = ChannelIndexFromBuffer(params->GetBuffer());
    58   ChannelControl* channel = control_->channels;
    59   // Note that the IPC tag goes outside the buffer as well inside
    60   // the buffer. This should enable the server to prioritize based on
    61   // IPC tags without having to de-serialize the entire message.
    62   channel[num].ipc_tag = params->GetTag();
    64   // Wait for the server to service this IPC call. After kIPCWaitTimeOut1
    65   // we check if the server_alive mutex was abandoned which will indicate
    66   // that the server has died.
    68   // While the atomic signaling and waiting is not a requirement, it
    69   // is nice because we save a trip to kernel.
    70   DWORD wait = ::SignalObjectAndWait(channel[num].ping_event,
    71                                      channel[num].pong_event,
    72                                      kIPCWaitTimeOut1, FALSE);
    73   if (WAIT_TIMEOUT == wait) {
    74     // The server is taking too long. Enter a loop were we check if the
    75     // server_alive mutex has been abandoned which would signal a server crash
    76     // or else we keep waiting for a response.
    77     while (true) {
    78       wait = ::WaitForSingleObject(control_->server_alive, 0);
    79       if (WAIT_TIMEOUT == wait) {
    80         // Server seems still alive. We already signaled so here we just wait.
    81         wait = ::WaitForSingleObject(channel[num].pong_event, kIPCWaitTimeOut1);
    82         if (WAIT_OBJECT_0 == wait) {
    83           // The server took a long time but responded.
    84           break;
    85         } else if (WAIT_TIMEOUT == wait) {
    86           continue;
    87         } else {
    88           return SBOX_ERROR_CHANNEL_ERROR;
    89         }
    90       } else {
    91         // The server has crashed and windows has signaled the mutex as
    92         // abandoned.
    93         ::InterlockedExchange(&channel[num].state, kAbandonnedChannel);
    94         control_->server_alive = 0;
    95         return SBOX_ERROR_CHANNEL_ERROR;
    96       }
    97     }
    98   } else if (WAIT_OBJECT_0 != wait) {
    99     // Probably the server crashed before the kIPCWaitTimeOut1 occurred.
   100     return SBOX_ERROR_CHANNEL_ERROR;
   101   }
   103   // The server has returned an answer, copy it and free the channel.
   104   memcpy(answer, params->GetCallReturn(), sizeof(CrossCallReturn));
   106   // Return the IPC state It can indicate that while the IPC has
   107   // completed some error in the Broker has caused to not return valid
   108   // results.
   109   return answer->call_outcome;
   110 }
   112 // Locking a channel is a simple as looping over all the channels
   113 // looking for one that is has state = kFreeChannel and atomically
   114 // swapping it to kBusyChannel.
   115 // If there is no free channel, then we must back off so some other
   116 // thread makes progress and frees a channel. To back off we sleep.
   117 size_t SharedMemIPCClient::LockFreeChannel(bool* severe_failure) {
   118   if (0 == control_->channels_count) {
   119     *severe_failure = true;
   120     return 0;
   121   }
   122   ChannelControl* channel = control_->channels;
   123   do {
   124     for (size_t ix = 0; ix != control_->channels_count; ++ix) {
   125       if (kFreeChannel == ::InterlockedCompareExchange(&channel[ix].state,
   126                                                        kBusyChannel,
   127                                                        kFreeChannel)) {
   128           *severe_failure = false;
   129           return ix;
   130       }
   131     }
   132     // We did not find any available channel, maybe the server is dead.
   133     DWORD wait = ::WaitForSingleObject(control_->server_alive,
   134                                        kIPCWaitTimeOut2);
   135     if (WAIT_TIMEOUT != wait) {
   136       // The server is dead and we outlive it enough to get in trouble.
   137       *severe_failure = true;
   138       return 0;
   139     }
   140   }
   141   while (true);
   142 }
   144 // Find out which channel we are from the pointer returned by GetBuffer.
   145 size_t SharedMemIPCClient::ChannelIndexFromBuffer(const void* buffer) {
   146   ptrdiff_t d = reinterpret_cast<const char*>(buffer) - first_base_;
   147   size_t num = d/kIPCChannelSize;
   148   DCHECK(num < control_->channels_count);
   149   return (num);
   150 }
   152 }  // namespace sandbox

mercurial