// Copyright 2008, Google Inc. // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include "sandbox/src/sharedmem_ipc_client.h" #include "sandbox/src/sandbox.h" #include "sandbox/src/crosscall_client.h" #include "sandbox/src/crosscall_params.h" #include "base/logging.h" namespace sandbox { // Get the base of the data buffer of the channel; this is where the input // parameters get serialized. Since they get serialized directly into the // channel we avoid one copy. void* SharedMemIPCClient::GetBuffer() { bool failure = false; size_t ix = LockFreeChannel(&failure); if (failure) { return NULL; } return reinterpret_cast(control_) + control_->channels[ix].channel_base; } // If we need to cancel an IPC before issuing DoCall // our client should call FreeBuffer with the same pointer // returned by GetBuffer. void SharedMemIPCClient::FreeBuffer(void* buffer) { size_t num = ChannelIndexFromBuffer(buffer); ChannelControl* channel = control_->channels; LONG result = ::InterlockedExchange(&channel[num].state, kFreeChannel); DCHECK(kFreeChannel != result); result; } // The constructor simply casts the shared memory to the internal // structures. This is a cheap step that is why this IPC object can // and should be constructed per call. SharedMemIPCClient::SharedMemIPCClient(void* shared_mem) : control_(reinterpret_cast(shared_mem)) { first_base_ = reinterpret_cast(shared_mem) + control_->channels[0].channel_base; // There must be at least one channel. DCHECK(0 != control_->channels_count); } // Do the IPC. At this point the channel should have already been // filled with the serialized input parameters. // We follow the pattern explained in the header file. ResultCode SharedMemIPCClient::DoCall(CrossCallParams* params, CrossCallReturn* answer) { if (!control_->server_alive) return SBOX_ERROR_CHANNEL_ERROR; size_t num = ChannelIndexFromBuffer(params->GetBuffer()); ChannelControl* channel = control_->channels; // Note that the IPC tag goes outside the buffer as well inside // the buffer. This should enable the server to prioritize based on // IPC tags without having to de-serialize the entire message. channel[num].ipc_tag = params->GetTag(); // Wait for the server to service this IPC call. After kIPCWaitTimeOut1 // we check if the server_alive mutex was abandoned which will indicate // that the server has died. // While the atomic signaling and waiting is not a requirement, it // is nice because we save a trip to kernel. DWORD wait = ::SignalObjectAndWait(channel[num].ping_event, channel[num].pong_event, kIPCWaitTimeOut1, FALSE); if (WAIT_TIMEOUT == wait) { // The server is taking too long. Enter a loop were we check if the // server_alive mutex has been abandoned which would signal a server crash // or else we keep waiting for a response. while (true) { wait = ::WaitForSingleObject(control_->server_alive, 0); if (WAIT_TIMEOUT == wait) { // Server seems still alive. We already signaled so here we just wait. wait = ::WaitForSingleObject(channel[num].pong_event, kIPCWaitTimeOut1); if (WAIT_OBJECT_0 == wait) { // The server took a long time but responded. break; } else if (WAIT_TIMEOUT == wait) { continue; } else { return SBOX_ERROR_CHANNEL_ERROR; } } else { // The server has crashed and windows has signaled the mutex as // abandoned. ::InterlockedExchange(&channel[num].state, kAbandonnedChannel); control_->server_alive = 0; return SBOX_ERROR_CHANNEL_ERROR; } } } else if (WAIT_OBJECT_0 != wait) { // Probably the server crashed before the kIPCWaitTimeOut1 occurred. return SBOX_ERROR_CHANNEL_ERROR; } // The server has returned an answer, copy it and free the channel. memcpy(answer, params->GetCallReturn(), sizeof(CrossCallReturn)); // Return the IPC state It can indicate that while the IPC has // completed some error in the Broker has caused to not return valid // results. return answer->call_outcome; } // Locking a channel is a simple as looping over all the channels // looking for one that is has state = kFreeChannel and atomically // swapping it to kBusyChannel. // If there is no free channel, then we must back off so some other // thread makes progress and frees a channel. To back off we sleep. size_t SharedMemIPCClient::LockFreeChannel(bool* severe_failure) { if (0 == control_->channels_count) { *severe_failure = true; return 0; } ChannelControl* channel = control_->channels; do { for (size_t ix = 0; ix != control_->channels_count; ++ix) { if (kFreeChannel == ::InterlockedCompareExchange(&channel[ix].state, kBusyChannel, kFreeChannel)) { *severe_failure = false; return ix; } } // We did not find any available channel, maybe the server is dead. DWORD wait = ::WaitForSingleObject(control_->server_alive, kIPCWaitTimeOut2); if (WAIT_TIMEOUT != wait) { // The server is dead and we outlive it enough to get in trouble. *severe_failure = true; return 0; } } while (true); } // Find out which channel we are from the pointer returned by GetBuffer. size_t SharedMemIPCClient::ChannelIndexFromBuffer(const void* buffer) { ptrdiff_t d = reinterpret_cast(buffer) - first_base_; size_t num = d/kIPCChannelSize; DCHECK(num < control_->channels_count); return (num); } } // namespace sandbox