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.
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"
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() {
19 size_t ix
= LockFreeChannel(&failure
);
23 return reinterpret_cast<char*>(control_
) +
24 control_
->channels
[ix
].channel_base
;
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
));
37 // The constructor simply casts the shared memory to the internal
38 // structures. This is a cheap step that is why this IPC object can
39 // and should be constructed per call.
40 SharedMemIPCClient::SharedMemIPCClient(void* shared_mem
)
41 : control_(reinterpret_cast<IPCControl
*>(shared_mem
)) {
42 first_base_
= reinterpret_cast<char*>(shared_mem
) +
43 control_
->channels
[0].channel_base
;
44 // There must be at least one channel.
45 DCHECK(0 != control_
->channels_count
);
48 // Do the IPC. At this point the channel should have already been
49 // filled with the serialized input parameters.
50 // We follow the pattern explained in the header file.
51 ResultCode
SharedMemIPCClient::DoCall(CrossCallParams
* params
,
52 CrossCallReturn
* answer
) {
53 if (!control_
->server_alive
)
54 return SBOX_ERROR_CHANNEL_ERROR
;
56 size_t num
= ChannelIndexFromBuffer(params
->GetBuffer());
57 ChannelControl
* channel
= control_
->channels
;
58 // Note that the IPC tag goes outside the buffer as well inside
59 // the buffer. This should enable the server to prioritize based on
60 // IPC tags without having to de-serialize the entire message.
61 channel
[num
].ipc_tag
= params
->GetTag();
63 // Wait for the server to service this IPC call. After kIPCWaitTimeOut1
64 // we check if the server_alive mutex was abandoned which will indicate
65 // that the server has died.
67 // While the atomic signaling and waiting is not a requirement, it
68 // is nice because we save a trip to kernel.
69 DWORD wait
= ::SignalObjectAndWait(channel
[num
].ping_event
,
70 channel
[num
].pong_event
,
71 kIPCWaitTimeOut1
, FALSE
);
72 if (WAIT_TIMEOUT
== wait
) {
73 // The server is taking too long. Enter a loop were we check if the
74 // server_alive mutex has been abandoned which would signal a server crash
75 // or else we keep waiting for a response.
77 wait
= ::WaitForSingleObject(control_
->server_alive
, 0);
78 if (WAIT_TIMEOUT
== wait
) {
79 // Server seems still alive. We already signaled so here we just wait.
80 wait
= ::WaitForSingleObject(channel
[num
].pong_event
, kIPCWaitTimeOut1
);
81 if (WAIT_OBJECT_0
== wait
) {
82 // The server took a long time but responded.
84 } else if (WAIT_TIMEOUT
== wait
) {
87 return SBOX_ERROR_CHANNEL_ERROR
;
90 // The server has crashed and windows has signaled the mutex as
92 ::InterlockedExchange(&channel
[num
].state
, kAbandonedChannel
);
93 control_
->server_alive
= 0;
94 return SBOX_ERROR_CHANNEL_ERROR
;
97 } else if (WAIT_OBJECT_0
!= wait
) {
98 // Probably the server crashed before the kIPCWaitTimeOut1 occurred.
99 return SBOX_ERROR_CHANNEL_ERROR
;
102 // The server has returned an answer, copy it and free the channel.
103 memcpy(answer
, params
->GetCallReturn(), sizeof(CrossCallReturn
));
105 // Return the IPC state It can indicate that while the IPC has
106 // completed some error in the Broker has caused to not return valid
108 return answer
->call_outcome
;
111 // Locking a channel is a simple as looping over all the channels
112 // looking for one that is has state = kFreeChannel and atomically
113 // swapping it to kBusyChannel.
114 // If there is no free channel, then we must back off so some other
115 // thread makes progress and frees a channel. To back off we sleep.
116 size_t SharedMemIPCClient::LockFreeChannel(bool* severe_failure
) {
117 if (0 == control_
->channels_count
) {
118 *severe_failure
= true;
121 ChannelControl
* channel
= control_
->channels
;
123 for (size_t ix
= 0; ix
!= control_
->channels_count
; ++ix
) {
124 if (kFreeChannel
== ::InterlockedCompareExchange(&channel
[ix
].state
,
127 *severe_failure
= false;
131 // We did not find any available channel, maybe the server is dead.
132 DWORD wait
= ::WaitForSingleObject(control_
->server_alive
,
134 if (WAIT_TIMEOUT
!= wait
) {
135 // The server is dead and we outlive it enough to get in trouble.
136 *severe_failure
= true;
143 // Find out which channel we are from the pointer returned by GetBuffer.
144 size_t SharedMemIPCClient::ChannelIndexFromBuffer(const void* buffer
) {
145 ptrdiff_t d
= reinterpret_cast<const char*>(buffer
) - first_base_
;
146 size_t num
= d
/kIPCChannelSize
;
147 DCHECK_LT(num
, control_
->channels_count
);
151 } // namespace sandbox