Home | History | Annotate | Download | only in src
      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.
      4 
      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"
     11 
     12 namespace sandbox {
     13 
     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 }
     26 
     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 }
     37 
     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 }
     48 
     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;
     56 
     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();
     63 
     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.
     67 
     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   }
    102 
    103   // The server has returned an answer, copy it and free the channel.
    104   memcpy(answer, params->GetCallReturn(), sizeof(CrossCallReturn));
    105 
    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 }
    111 
    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 }
    143 
    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 }
    151 
    152 }  // namespace sandbox
    153