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