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.
GetBuffer()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.
FreeBuffer(void * buffer)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.
SharedMemIPCClient(void * shared_mem)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.
DoCall(CrossCallParams * params,CrossCallReturn * answer)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.
LockFreeChannel(bool * severe_failure)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.
ChannelIndexFromBuffer(const void * buffer)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