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
12namespace 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.
17void* 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.
30void 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.
41SharedMemIPCClient::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.
52ResultCode 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.
117size_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.
145size_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