security/sandbox/win/src/sharedmem_ipc_client.cc

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/security/sandbox/win/src/sharedmem_ipc_client.cc	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,152 @@
     1.4 +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
     1.5 +// Use of this source code is governed by a BSD-style license that can be
     1.6 +// found in the LICENSE file.
     1.7 +
     1.8 +#include <string.h>
     1.9 +#include "sandbox/win/src/sharedmem_ipc_client.h"
    1.10 +#include "sandbox/win/src/sandbox.h"
    1.11 +#include "sandbox/win/src/crosscall_client.h"
    1.12 +#include "sandbox/win/src/crosscall_params.h"
    1.13 +#include "base/logging.h"
    1.14 +
    1.15 +namespace sandbox {
    1.16 +
    1.17 +// Get the base of the data buffer of the channel; this is where the input
    1.18 +// parameters get serialized. Since they get serialized directly into the
    1.19 +// channel we avoid one copy.
    1.20 +void* SharedMemIPCClient::GetBuffer() {
    1.21 +  bool failure = false;
    1.22 +  size_t ix = LockFreeChannel(&failure);
    1.23 +  if (failure) {
    1.24 +    return NULL;
    1.25 +  }
    1.26 +  return reinterpret_cast<char*>(control_) +
    1.27 +         control_->channels[ix].channel_base;
    1.28 +}
    1.29 +
    1.30 +// If we need to cancel an IPC before issuing DoCall
    1.31 +// our client should call FreeBuffer with the same pointer
    1.32 +// returned by GetBuffer.
    1.33 +void SharedMemIPCClient::FreeBuffer(void* buffer) {
    1.34 +  size_t num = ChannelIndexFromBuffer(buffer);
    1.35 +  ChannelControl* channel = control_->channels;
    1.36 +  LONG result = ::InterlockedExchange(&channel[num].state, kFreeChannel);
    1.37 +  DCHECK(kFreeChannel != result);
    1.38 +  result;
    1.39 +}
    1.40 +
    1.41 +// The constructor simply casts the shared memory to the internal
    1.42 +// structures. This is a cheap step that is why this IPC object can
    1.43 +// and should be constructed per call.
    1.44 +SharedMemIPCClient::SharedMemIPCClient(void* shared_mem)
    1.45 +    : control_(reinterpret_cast<IPCControl*>(shared_mem)) {
    1.46 +  first_base_ = reinterpret_cast<char*>(shared_mem) +
    1.47 +               control_->channels[0].channel_base;
    1.48 +  // There must be at least one channel.
    1.49 +  DCHECK(0 != control_->channels_count);
    1.50 +}
    1.51 +
    1.52 +// Do the IPC. At this point the channel should have already been
    1.53 +// filled with the serialized input parameters.
    1.54 +// We follow the pattern explained in the header file.
    1.55 +ResultCode SharedMemIPCClient::DoCall(CrossCallParams* params,
    1.56 +                                      CrossCallReturn* answer) {
    1.57 +  if (!control_->server_alive)
    1.58 +    return SBOX_ERROR_CHANNEL_ERROR;
    1.59 +
    1.60 +  size_t num = ChannelIndexFromBuffer(params->GetBuffer());
    1.61 +  ChannelControl* channel = control_->channels;
    1.62 +  // Note that the IPC tag goes outside the buffer as well inside
    1.63 +  // the buffer. This should enable the server to prioritize based on
    1.64 +  // IPC tags without having to de-serialize the entire message.
    1.65 +  channel[num].ipc_tag = params->GetTag();
    1.66 +
    1.67 +  // Wait for the server to service this IPC call. After kIPCWaitTimeOut1
    1.68 +  // we check if the server_alive mutex was abandoned which will indicate
    1.69 +  // that the server has died.
    1.70 +
    1.71 +  // While the atomic signaling and waiting is not a requirement, it
    1.72 +  // is nice because we save a trip to kernel.
    1.73 +  DWORD wait = ::SignalObjectAndWait(channel[num].ping_event,
    1.74 +                                     channel[num].pong_event,
    1.75 +                                     kIPCWaitTimeOut1, FALSE);
    1.76 +  if (WAIT_TIMEOUT == wait) {
    1.77 +    // The server is taking too long. Enter a loop were we check if the
    1.78 +    // server_alive mutex has been abandoned which would signal a server crash
    1.79 +    // or else we keep waiting for a response.
    1.80 +    while (true) {
    1.81 +      wait = ::WaitForSingleObject(control_->server_alive, 0);
    1.82 +      if (WAIT_TIMEOUT == wait) {
    1.83 +        // Server seems still alive. We already signaled so here we just wait.
    1.84 +        wait = ::WaitForSingleObject(channel[num].pong_event, kIPCWaitTimeOut1);
    1.85 +        if (WAIT_OBJECT_0 == wait) {
    1.86 +          // The server took a long time but responded.
    1.87 +          break;
    1.88 +        } else if (WAIT_TIMEOUT == wait) {
    1.89 +          continue;
    1.90 +        } else {
    1.91 +          return SBOX_ERROR_CHANNEL_ERROR;
    1.92 +        }
    1.93 +      } else {
    1.94 +        // The server has crashed and windows has signaled the mutex as
    1.95 +        // abandoned.
    1.96 +        ::InterlockedExchange(&channel[num].state, kAbandonnedChannel);
    1.97 +        control_->server_alive = 0;
    1.98 +        return SBOX_ERROR_CHANNEL_ERROR;
    1.99 +      }
   1.100 +    }
   1.101 +  } else if (WAIT_OBJECT_0 != wait) {
   1.102 +    // Probably the server crashed before the kIPCWaitTimeOut1 occurred.
   1.103 +    return SBOX_ERROR_CHANNEL_ERROR;
   1.104 +  }
   1.105 +
   1.106 +  // The server has returned an answer, copy it and free the channel.
   1.107 +  memcpy(answer, params->GetCallReturn(), sizeof(CrossCallReturn));
   1.108 +
   1.109 +  // Return the IPC state It can indicate that while the IPC has
   1.110 +  // completed some error in the Broker has caused to not return valid
   1.111 +  // results.
   1.112 +  return answer->call_outcome;
   1.113 +}
   1.114 +
   1.115 +// Locking a channel is a simple as looping over all the channels
   1.116 +// looking for one that is has state = kFreeChannel and atomically
   1.117 +// swapping it to kBusyChannel.
   1.118 +// If there is no free channel, then we must back off so some other
   1.119 +// thread makes progress and frees a channel. To back off we sleep.
   1.120 +size_t SharedMemIPCClient::LockFreeChannel(bool* severe_failure) {
   1.121 +  if (0 == control_->channels_count) {
   1.122 +    *severe_failure = true;
   1.123 +    return 0;
   1.124 +  }
   1.125 +  ChannelControl* channel = control_->channels;
   1.126 +  do {
   1.127 +    for (size_t ix = 0; ix != control_->channels_count; ++ix) {
   1.128 +      if (kFreeChannel == ::InterlockedCompareExchange(&channel[ix].state,
   1.129 +                                                       kBusyChannel,
   1.130 +                                                       kFreeChannel)) {
   1.131 +          *severe_failure = false;
   1.132 +          return ix;
   1.133 +      }
   1.134 +    }
   1.135 +    // We did not find any available channel, maybe the server is dead.
   1.136 +    DWORD wait = ::WaitForSingleObject(control_->server_alive,
   1.137 +                                       kIPCWaitTimeOut2);
   1.138 +    if (WAIT_TIMEOUT != wait) {
   1.139 +      // The server is dead and we outlive it enough to get in trouble.
   1.140 +      *severe_failure = true;
   1.141 +      return 0;
   1.142 +    }
   1.143 +  }
   1.144 +  while (true);
   1.145 +}
   1.146 +
   1.147 +// Find out which channel we are from the pointer returned by GetBuffer.
   1.148 +size_t SharedMemIPCClient::ChannelIndexFromBuffer(const void* buffer) {
   1.149 +  ptrdiff_t d = reinterpret_cast<const char*>(buffer) - first_base_;
   1.150 +  size_t num = d/kIPCChannelSize;
   1.151 +  DCHECK(num < control_->channels_count);
   1.152 +  return (num);
   1.153 +}
   1.154 +
   1.155 +}  // namespace sandbox

mercurial