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.

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

mercurial