sharedmem_ipc_client.cc 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  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. #include "sandbox/win/src/sharedmem_ipc_client.h"
  5. #include <stddef.h>
  6. #include <string.h>
  7. #include "base/check_op.h"
  8. #include "sandbox/win/src/crosscall_client.h"
  9. #include "sandbox/win/src/crosscall_params.h"
  10. #include "sandbox/win/src/sandbox.h"
  11. #include "sandbox/win/src/sandbox_nt_types.h"
  12. #include "sandbox/win/src/sandbox_nt_util.h"
  13. namespace sandbox {
  14. namespace {
  15. DWORD SignalObjectAndWaitWrapper(HANDLE object_to_signal,
  16. HANDLE object_to_wait_on,
  17. DWORD millis) {
  18. LARGE_INTEGER timeout;
  19. timeout.QuadPart = millis * -10000LL;
  20. NTSTATUS status = GetNtExports()->SignalAndWaitForSingleObject(
  21. object_to_signal, object_to_wait_on, FALSE,
  22. millis == INFINITE ? nullptr : &timeout);
  23. if (!NT_SUCCESS(status))
  24. return WAIT_FAILED;
  25. return status;
  26. }
  27. DWORD WaitForSingleObjectWrapper(HANDLE handle, DWORD millis) {
  28. LARGE_INTEGER timeout;
  29. timeout.QuadPart = millis * -10000LL;
  30. NTSTATUS status = GetNtExports()->WaitForSingleObject(
  31. handle, FALSE, millis == INFINITE ? nullptr : &timeout);
  32. if (!NT_SUCCESS(status))
  33. return WAIT_FAILED;
  34. return status;
  35. }
  36. } // namespace
  37. // Get the base of the data buffer of the channel; this is where the input
  38. // parameters get serialized. Since they get serialized directly into the
  39. // channel we avoid one copy.
  40. void* SharedMemIPCClient::GetBuffer() {
  41. bool failure = false;
  42. size_t ix = LockFreeChannel(&failure);
  43. if (failure)
  44. return nullptr;
  45. return reinterpret_cast<char*>(control_.get()) +
  46. control_->channels[ix].channel_base;
  47. }
  48. // If we need to cancel an IPC before issuing DoCall
  49. // our client should call FreeBuffer with the same pointer
  50. // returned by GetBuffer.
  51. void SharedMemIPCClient::FreeBuffer(void* buffer) {
  52. size_t num = ChannelIndexFromBuffer(buffer);
  53. ChannelControl* channel = control_->channels;
  54. LONG result = ::InterlockedExchange(&channel[num].state, kFreeChannel);
  55. DCHECK_NE(kFreeChannel, static_cast<ChannelState>(result));
  56. }
  57. // The constructor simply casts the shared memory to the internal
  58. // structures. This is a cheap step that is why this IPC object can
  59. // and should be constructed per call.
  60. SharedMemIPCClient::SharedMemIPCClient(void* shared_mem)
  61. : control_(reinterpret_cast<IPCControl*>(shared_mem)) {
  62. first_base_ =
  63. reinterpret_cast<char*>(shared_mem) + control_->channels[0].channel_base;
  64. // There must be at least one channel.
  65. DCHECK(0 != control_->channels_count);
  66. }
  67. // Do the IPC. At this point the channel should have already been
  68. // filled with the serialized input parameters.
  69. // We follow the pattern explained in the header file.
  70. ResultCode SharedMemIPCClient::DoCall(CrossCallParams* params,
  71. CrossCallReturn* answer) {
  72. if (!control_->server_alive)
  73. return SBOX_ERROR_CHANNEL_ERROR;
  74. size_t num = ChannelIndexFromBuffer(params->GetBuffer());
  75. ChannelControl* channel = control_->channels;
  76. // Note that the IPC tag goes outside the buffer as well inside
  77. // the buffer. This should enable the server to prioritize based on
  78. // IPC tags without having to de-serialize the entire message.
  79. channel[num].ipc_tag = params->GetTag();
  80. // Wait for the server to service this IPC call. After kIPCWaitTimeOut1
  81. // we check if the server_alive mutex was abandoned which will indicate
  82. // that the server has died.
  83. // While the atomic signaling and waiting is not a requirement, it
  84. // is nice because we save a trip to kernel.
  85. DWORD wait = SignalObjectAndWaitWrapper(
  86. channel[num].ping_event, channel[num].pong_event, kIPCWaitTimeOut1);
  87. if (WAIT_TIMEOUT == wait) {
  88. // The server is taking too long. Enter a loop were we check if the
  89. // server_alive mutex has been abandoned which would signal a server crash
  90. // or else we keep waiting for a response.
  91. while (true) {
  92. wait = WaitForSingleObjectWrapper(control_->server_alive, 0);
  93. if (WAIT_TIMEOUT == wait) {
  94. // Server seems still alive. We already signaled so here we just wait.
  95. wait = WaitForSingleObjectWrapper(channel[num].pong_event,
  96. kIPCWaitTimeOut1);
  97. if (WAIT_OBJECT_0 == wait) {
  98. // The server took a long time but responded.
  99. break;
  100. } else if (WAIT_TIMEOUT == wait) {
  101. continue;
  102. } else {
  103. return SBOX_ERROR_CHANNEL_ERROR;
  104. }
  105. } else {
  106. // The server has crashed and windows has signaled the mutex as
  107. // abandoned.
  108. ::InterlockedExchange(&channel[num].state, kAbandonedChannel);
  109. control_->server_alive = 0;
  110. return SBOX_ERROR_CHANNEL_ERROR;
  111. }
  112. }
  113. } else if (WAIT_OBJECT_0 != wait) {
  114. // Probably the server crashed before the kIPCWaitTimeOut1 occurred.
  115. return SBOX_ERROR_CHANNEL_ERROR;
  116. }
  117. // The server has returned an answer, copy it and free the channel.
  118. memcpy_wrapper(answer, params->GetCallReturn(), sizeof(CrossCallReturn));
  119. // Return the IPC state It can indicate that while the IPC has
  120. // completed some error in the Broker has caused to not return valid
  121. // results.
  122. return answer->call_outcome;
  123. }
  124. // Locking a channel is a simple as looping over all the channels
  125. // looking for one that is has state = kFreeChannel and atomically
  126. // swapping it to kBusyChannel.
  127. // If there is no free channel, then we must back off so some other
  128. // thread makes progress and frees a channel. To back off we sleep.
  129. size_t SharedMemIPCClient::LockFreeChannel(bool* severe_failure) {
  130. if (0 == control_->channels_count) {
  131. *severe_failure = true;
  132. return 0;
  133. }
  134. ChannelControl* channel = control_->channels;
  135. do {
  136. for (size_t ix = 0; ix != control_->channels_count; ++ix) {
  137. if (kFreeChannel == ::InterlockedCompareExchange(
  138. &channel[ix].state, kBusyChannel, kFreeChannel)) {
  139. *severe_failure = false;
  140. return ix;
  141. }
  142. }
  143. // We did not find any available channel, maybe the server is dead.
  144. DWORD wait =
  145. WaitForSingleObjectWrapper(control_->server_alive, kIPCWaitTimeOut2);
  146. if (WAIT_TIMEOUT != wait) {
  147. // The server is dead and we outlive it enough to get in trouble.
  148. *severe_failure = true;
  149. return 0;
  150. }
  151. } while (true);
  152. }
  153. // Find out which channel we are from the pointer returned by GetBuffer.
  154. size_t SharedMemIPCClient::ChannelIndexFromBuffer(const void* buffer) {
  155. ptrdiff_t d = reinterpret_cast<const char*>(buffer) - first_base_;
  156. size_t num = d / kIPCChannelSize;
  157. DCHECK_LT(num, control_->channels_count);
  158. return (num);
  159. }
  160. } // namespace sandbox