|
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(kFreeChannel != 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(num < control_->channels_count); |
|
149 return (num); |
|
150 } |
|
151 |
|
152 } // namespace sandbox |