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_NE(kFreeChannel, static_cast<ChannelState>(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_LT(num, control_->channels_count); 149 return (num); 150 } 151 152 } // namespace sandbox 153