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