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 <string.h> | |
6 #include "sandbox/src/sharedmem_ipc_client.h" | |
7 #include "sandbox/src/sandbox.h" | |
8 #include "sandbox/src/crosscall_client.h" | |
9 #include "sandbox/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 | |
OLD | NEW |