security/sandbox/win/src/ipc_unittest.cc

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
michael@0 2 // Use of this source code is governed by a BSD-style license that can be
michael@0 3 // found in the LICENSE file.
michael@0 4
michael@0 5 #include "base/basictypes.h"
michael@0 6 #include "sandbox/win/src/crosscall_client.h"
michael@0 7 #include "sandbox/win/src/crosscall_server.h"
michael@0 8 #include "sandbox/win/src/sharedmem_ipc_client.h"
michael@0 9 #include "sandbox/win/src/sharedmem_ipc_server.h"
michael@0 10 #include "testing/gtest/include/gtest/gtest.h"
michael@0 11
michael@0 12 namespace sandbox {
michael@0 13
michael@0 14 // Helper function to make the fake shared memory with some
michael@0 15 // basic elements initialized.
michael@0 16 IPCControl* MakeChannels(size_t channel_size, size_t total_shared_size,
michael@0 17 size_t* base_start) {
michael@0 18 // Allocate memory
michael@0 19 char* mem = new char[total_shared_size];
michael@0 20 memset(mem, 0, total_shared_size);
michael@0 21 // Calculate how many channels we can fit in the shared memory.
michael@0 22 total_shared_size -= offsetof(IPCControl, channels);
michael@0 23 size_t channel_count =
michael@0 24 total_shared_size / (sizeof(ChannelControl) + channel_size);
michael@0 25 // Calculate the start of the first channel.
michael@0 26 *base_start = (sizeof(ChannelControl)* channel_count) +
michael@0 27 offsetof(IPCControl, channels);
michael@0 28 // Setup client structure.
michael@0 29 IPCControl* client_control = reinterpret_cast<IPCControl*>(mem);
michael@0 30 client_control->channels_count = channel_count;
michael@0 31 return client_control;
michael@0 32 }
michael@0 33
michael@0 34 enum TestFixMode {
michael@0 35 FIX_NO_EVENTS,
michael@0 36 FIX_PONG_READY,
michael@0 37 FIX_PONG_NOT_READY
michael@0 38 };
michael@0 39
michael@0 40 void FixChannels(IPCControl* client_control, size_t base_start,
michael@0 41 size_t channel_size, TestFixMode mode) {
michael@0 42 for (size_t ix = 0; ix != client_control->channels_count; ++ix) {
michael@0 43 ChannelControl& channel = client_control->channels[ix];
michael@0 44 channel.channel_base = base_start;
michael@0 45 channel.state = kFreeChannel;
michael@0 46 if (mode != FIX_NO_EVENTS) {
michael@0 47 BOOL signaled = (FIX_PONG_READY == mode)? TRUE : FALSE;
michael@0 48 channel.ping_event = ::CreateEventW(NULL, FALSE, FALSE, NULL);
michael@0 49 channel.pong_event = ::CreateEventW(NULL, FALSE, signaled, NULL);
michael@0 50 }
michael@0 51 base_start += channel_size;
michael@0 52 }
michael@0 53 }
michael@0 54
michael@0 55 void CloseChannelEvents(IPCControl* client_control) {
michael@0 56 for (size_t ix = 0; ix != client_control->channels_count; ++ix) {
michael@0 57 ChannelControl& channel = client_control->channels[ix];
michael@0 58 ::CloseHandle(channel.ping_event);
michael@0 59 ::CloseHandle(channel.pong_event);
michael@0 60 }
michael@0 61 }
michael@0 62
michael@0 63 TEST(IPCTest, ChannelMaker) {
michael@0 64 // Test that our testing rig is computing offsets properly. We should have
michael@0 65 // 5 channnels and the offset to the first channel is 108 bytes in 32 bits
michael@0 66 // and 216 in 64 bits.
michael@0 67 size_t channel_start = 0;
michael@0 68 IPCControl* client_control = MakeChannels(12 * 64, 4096, &channel_start);
michael@0 69 ASSERT_TRUE(NULL != client_control);
michael@0 70 EXPECT_EQ(5, client_control->channels_count);
michael@0 71 #if defined(_WIN64)
michael@0 72 EXPECT_EQ(216, channel_start);
michael@0 73 #else
michael@0 74 EXPECT_EQ(108, channel_start);
michael@0 75 #endif
michael@0 76 delete[] reinterpret_cast<char*>(client_control);
michael@0 77 }
michael@0 78
michael@0 79 TEST(IPCTest, ClientLockUnlock) {
michael@0 80 // Make 7 channels of kIPCChannelSize (1kb) each. Test that we lock and
michael@0 81 // unlock channels properly.
michael@0 82 size_t base_start = 0;
michael@0 83 IPCControl* client_control =
michael@0 84 MakeChannels(kIPCChannelSize, 4096 * 2, &base_start);
michael@0 85 FixChannels(client_control, base_start, kIPCChannelSize, FIX_NO_EVENTS);
michael@0 86
michael@0 87 char* mem = reinterpret_cast<char*>(client_control);
michael@0 88 SharedMemIPCClient client(mem);
michael@0 89
michael@0 90 // Test that we lock the first 3 channels in sequence.
michael@0 91 void* buff0 = client.GetBuffer();
michael@0 92 EXPECT_TRUE(mem + client_control->channels[0].channel_base == buff0);
michael@0 93 EXPECT_EQ(kBusyChannel, client_control->channels[0].state);
michael@0 94 EXPECT_EQ(kFreeChannel, client_control->channels[1].state);
michael@0 95 EXPECT_EQ(kFreeChannel, client_control->channels[2].state);
michael@0 96 EXPECT_EQ(kFreeChannel, client_control->channels[3].state);
michael@0 97 EXPECT_EQ(kFreeChannel, client_control->channels[4].state);
michael@0 98 EXPECT_EQ(kFreeChannel, client_control->channels[5].state);
michael@0 99
michael@0 100 void* buff1 = client.GetBuffer();
michael@0 101 EXPECT_TRUE(mem + client_control->channels[1].channel_base == buff1);
michael@0 102 EXPECT_EQ(kBusyChannel, client_control->channels[0].state);
michael@0 103 EXPECT_EQ(kBusyChannel, client_control->channels[1].state);
michael@0 104 EXPECT_EQ(kFreeChannel, client_control->channels[2].state);
michael@0 105 EXPECT_EQ(kFreeChannel, client_control->channels[3].state);
michael@0 106 EXPECT_EQ(kFreeChannel, client_control->channels[4].state);
michael@0 107 EXPECT_EQ(kFreeChannel, client_control->channels[5].state);
michael@0 108
michael@0 109 void* buff2 = client.GetBuffer();
michael@0 110 EXPECT_TRUE(mem + client_control->channels[2].channel_base == buff2);
michael@0 111 EXPECT_EQ(kBusyChannel, client_control->channels[0].state);
michael@0 112 EXPECT_EQ(kBusyChannel, client_control->channels[1].state);
michael@0 113 EXPECT_EQ(kBusyChannel, client_control->channels[2].state);
michael@0 114 EXPECT_EQ(kFreeChannel, client_control->channels[3].state);
michael@0 115 EXPECT_EQ(kFreeChannel, client_control->channels[4].state);
michael@0 116 EXPECT_EQ(kFreeChannel, client_control->channels[5].state);
michael@0 117
michael@0 118 // Test that we unlock and re-lock the right channel.
michael@0 119 client.FreeBuffer(buff1);
michael@0 120 EXPECT_EQ(kBusyChannel, client_control->channels[0].state);
michael@0 121 EXPECT_EQ(kFreeChannel, client_control->channels[1].state);
michael@0 122 EXPECT_EQ(kBusyChannel, client_control->channels[2].state);
michael@0 123 EXPECT_EQ(kFreeChannel, client_control->channels[3].state);
michael@0 124 EXPECT_EQ(kFreeChannel, client_control->channels[4].state);
michael@0 125 EXPECT_EQ(kFreeChannel, client_control->channels[5].state);
michael@0 126
michael@0 127 void* buff2b = client.GetBuffer();
michael@0 128 EXPECT_TRUE(mem + client_control->channels[1].channel_base == buff2b);
michael@0 129 EXPECT_EQ(kBusyChannel, client_control->channels[0].state);
michael@0 130 EXPECT_EQ(kBusyChannel, client_control->channels[1].state);
michael@0 131 EXPECT_EQ(kBusyChannel, client_control->channels[2].state);
michael@0 132 EXPECT_EQ(kFreeChannel, client_control->channels[3].state);
michael@0 133 EXPECT_EQ(kFreeChannel, client_control->channels[4].state);
michael@0 134 EXPECT_EQ(kFreeChannel, client_control->channels[5].state);
michael@0 135
michael@0 136 client.FreeBuffer(buff0);
michael@0 137 EXPECT_EQ(kFreeChannel, client_control->channels[0].state);
michael@0 138 EXPECT_EQ(kBusyChannel, client_control->channels[1].state);
michael@0 139 EXPECT_EQ(kBusyChannel, client_control->channels[2].state);
michael@0 140 EXPECT_EQ(kFreeChannel, client_control->channels[3].state);
michael@0 141 EXPECT_EQ(kFreeChannel, client_control->channels[4].state);
michael@0 142 EXPECT_EQ(kFreeChannel, client_control->channels[5].state);
michael@0 143
michael@0 144 delete[] reinterpret_cast<char*>(client_control);
michael@0 145 }
michael@0 146
michael@0 147 TEST(IPCTest, CrossCallStrPacking) {
michael@0 148 // This test tries the CrossCall object with null and non-null string
michael@0 149 // combination of parameters, integer types and verifies that the unpacker
michael@0 150 // can read them properly.
michael@0 151 size_t base_start = 0;
michael@0 152 IPCControl* client_control =
michael@0 153 MakeChannels(kIPCChannelSize, 4096 * 4, &base_start);
michael@0 154 client_control->server_alive = HANDLE(1);
michael@0 155 FixChannels(client_control, base_start, kIPCChannelSize, FIX_PONG_READY);
michael@0 156
michael@0 157 char* mem = reinterpret_cast<char*>(client_control);
michael@0 158 SharedMemIPCClient client(mem);
michael@0 159
michael@0 160 CrossCallReturn answer;
michael@0 161 uint32 tag1 = 666;
michael@0 162 const wchar_t text[] = L"98765 - 43210";
michael@0 163 std::wstring copied_text;
michael@0 164 CrossCallParamsEx* actual_params;
michael@0 165
michael@0 166 CrossCall(client, tag1, text, &answer);
michael@0 167 actual_params = reinterpret_cast<CrossCallParamsEx*>(client.GetBuffer());
michael@0 168 EXPECT_EQ(1, actual_params->GetParamsCount());
michael@0 169 EXPECT_EQ(tag1, actual_params->GetTag());
michael@0 170 EXPECT_TRUE(actual_params->GetParameterStr(0, &copied_text));
michael@0 171 EXPECT_STREQ(text, copied_text.c_str());
michael@0 172
michael@0 173 // Check with an empty string.
michael@0 174 uint32 tag2 = 777;
michael@0 175 const wchar_t* null_text = NULL;
michael@0 176 CrossCall(client, tag2, null_text, &answer);
michael@0 177 actual_params = reinterpret_cast<CrossCallParamsEx*>(client.GetBuffer());
michael@0 178 EXPECT_EQ(1, actual_params->GetParamsCount());
michael@0 179 EXPECT_EQ(tag2, actual_params->GetTag());
michael@0 180 uint32 param_size = 1;
michael@0 181 ArgType type = INVALID_TYPE;
michael@0 182 void* param_addr = actual_params->GetRawParameter(0, &param_size, &type);
michael@0 183 EXPECT_TRUE(NULL != param_addr);
michael@0 184 EXPECT_EQ(0, param_size);
michael@0 185 EXPECT_EQ(WCHAR_TYPE, type);
michael@0 186 EXPECT_TRUE(actual_params->GetParameterStr(0, &copied_text));
michael@0 187
michael@0 188 uint32 tag3 = 888;
michael@0 189 param_size = 1;
michael@0 190 copied_text.clear();
michael@0 191
michael@0 192 // Check with an empty string and a non-empty string.
michael@0 193 CrossCall(client, tag3, null_text, text, &answer);
michael@0 194 actual_params = reinterpret_cast<CrossCallParamsEx*>(client.GetBuffer());
michael@0 195 EXPECT_EQ(2, actual_params->GetParamsCount());
michael@0 196 EXPECT_EQ(tag3, actual_params->GetTag());
michael@0 197 type = INVALID_TYPE;
michael@0 198 param_addr = actual_params->GetRawParameter(0, &param_size, &type);
michael@0 199 EXPECT_TRUE(NULL != param_addr);
michael@0 200 EXPECT_EQ(0, param_size);
michael@0 201 EXPECT_EQ(WCHAR_TYPE, type);
michael@0 202 EXPECT_TRUE(actual_params->GetParameterStr(0, &copied_text));
michael@0 203 EXPECT_TRUE(actual_params->GetParameterStr(1, &copied_text));
michael@0 204 EXPECT_STREQ(text, copied_text.c_str());
michael@0 205
michael@0 206 param_size = 1;
michael@0 207 std::wstring copied_text_p0, copied_text_p2;
michael@0 208
michael@0 209 const wchar_t text2[] = L"AeFG";
michael@0 210 CrossCall(client, tag1, text2, null_text, text, &answer);
michael@0 211 actual_params = reinterpret_cast<CrossCallParamsEx*>(client.GetBuffer());
michael@0 212 EXPECT_EQ(3, actual_params->GetParamsCount());
michael@0 213 EXPECT_EQ(tag1, actual_params->GetTag());
michael@0 214 EXPECT_TRUE(actual_params->GetParameterStr(0, &copied_text_p0));
michael@0 215 EXPECT_STREQ(text2, copied_text_p0.c_str());
michael@0 216 EXPECT_TRUE(actual_params->GetParameterStr(2, &copied_text_p2));
michael@0 217 EXPECT_STREQ(text, copied_text_p2.c_str());
michael@0 218 type = INVALID_TYPE;
michael@0 219 param_addr = actual_params->GetRawParameter(1, &param_size, &type);
michael@0 220 EXPECT_TRUE(NULL != param_addr);
michael@0 221 EXPECT_EQ(0, param_size);
michael@0 222 EXPECT_EQ(WCHAR_TYPE, type);
michael@0 223
michael@0 224 CloseChannelEvents(client_control);
michael@0 225 delete[] reinterpret_cast<char*>(client_control);
michael@0 226 }
michael@0 227
michael@0 228 TEST(IPCTest, CrossCallIntPacking) {
michael@0 229 // Check handling for regular 32 bit integers used in Windows.
michael@0 230 size_t base_start = 0;
michael@0 231 IPCControl* client_control =
michael@0 232 MakeChannels(kIPCChannelSize, 4096 * 4, &base_start);
michael@0 233 client_control->server_alive = HANDLE(1);
michael@0 234 FixChannels(client_control, base_start, kIPCChannelSize, FIX_PONG_READY);
michael@0 235
michael@0 236 uint32 tag1 = 999;
michael@0 237 uint32 tag2 = 111;
michael@0 238 const wchar_t text[] = L"godzilla";
michael@0 239 CrossCallParamsEx* actual_params;
michael@0 240
michael@0 241 char* mem = reinterpret_cast<char*>(client_control);
michael@0 242 SharedMemIPCClient client(mem);
michael@0 243
michael@0 244 CrossCallReturn answer;
michael@0 245 DWORD dw = 0xE6578;
michael@0 246 CrossCall(client, tag2, dw, &answer);
michael@0 247 actual_params = reinterpret_cast<CrossCallParamsEx*>(client.GetBuffer());
michael@0 248 EXPECT_EQ(1, actual_params->GetParamsCount());
michael@0 249 EXPECT_EQ(tag2, actual_params->GetTag());
michael@0 250 ArgType type = INVALID_TYPE;
michael@0 251 uint32 param_size = 1;
michael@0 252 void* param_addr = actual_params->GetRawParameter(0, &param_size, &type);
michael@0 253 ASSERT_EQ(sizeof(dw), param_size);
michael@0 254 EXPECT_EQ(ULONG_TYPE, type);
michael@0 255 ASSERT_TRUE(NULL != param_addr);
michael@0 256 EXPECT_EQ(0, memcmp(&dw, param_addr, param_size));
michael@0 257
michael@0 258 // Check handling for windows HANDLES.
michael@0 259 HANDLE h = HANDLE(0x70000500);
michael@0 260 CrossCall(client, tag1, text, h, &answer);
michael@0 261 actual_params = reinterpret_cast<CrossCallParamsEx*>(client.GetBuffer());
michael@0 262 EXPECT_EQ(2, actual_params->GetParamsCount());
michael@0 263 EXPECT_EQ(tag1, actual_params->GetTag());
michael@0 264 type = INVALID_TYPE;
michael@0 265 param_addr = actual_params->GetRawParameter(1, &param_size, &type);
michael@0 266 ASSERT_EQ(sizeof(h), param_size);
michael@0 267 EXPECT_EQ(VOIDPTR_TYPE, type);
michael@0 268 ASSERT_TRUE(NULL != param_addr);
michael@0 269 EXPECT_EQ(0, memcmp(&h, param_addr, param_size));
michael@0 270
michael@0 271 // Check combination of 32 and 64 bits.
michael@0 272 CrossCall(client, tag2, h, dw, h, &answer);
michael@0 273 actual_params = reinterpret_cast<CrossCallParamsEx*>(client.GetBuffer());
michael@0 274 EXPECT_EQ(3, actual_params->GetParamsCount());
michael@0 275 EXPECT_EQ(tag2, actual_params->GetTag());
michael@0 276 type = INVALID_TYPE;
michael@0 277 param_addr = actual_params->GetRawParameter(0, &param_size, &type);
michael@0 278 ASSERT_EQ(sizeof(h), param_size);
michael@0 279 EXPECT_EQ(VOIDPTR_TYPE, type);
michael@0 280 ASSERT_TRUE(NULL != param_addr);
michael@0 281 EXPECT_EQ(0, memcmp(&h, param_addr, param_size));
michael@0 282 type = INVALID_TYPE;
michael@0 283 param_addr = actual_params->GetRawParameter(1, &param_size, &type);
michael@0 284 ASSERT_EQ(sizeof(dw), param_size);
michael@0 285 EXPECT_EQ(ULONG_TYPE, type);
michael@0 286 ASSERT_TRUE(NULL != param_addr);
michael@0 287 EXPECT_EQ(0, memcmp(&dw, param_addr, param_size));
michael@0 288 type = INVALID_TYPE;
michael@0 289 param_addr = actual_params->GetRawParameter(2, &param_size, &type);
michael@0 290 ASSERT_EQ(sizeof(h), param_size);
michael@0 291 EXPECT_EQ(VOIDPTR_TYPE, type);
michael@0 292 ASSERT_TRUE(NULL != param_addr);
michael@0 293 EXPECT_EQ(0, memcmp(&h, param_addr, param_size));
michael@0 294
michael@0 295 CloseChannelEvents(client_control);
michael@0 296 delete[] reinterpret_cast<char*>(client_control);
michael@0 297 }
michael@0 298
michael@0 299 TEST(IPCTest, CrossCallValidation) {
michael@0 300 // First a sanity test with a well formed parameter object.
michael@0 301 unsigned long value = 124816;
michael@0 302 const uint32 kTag = 33;
michael@0 303 const uint32 kBufferSize = 256;
michael@0 304 ActualCallParams<1, kBufferSize> params_1(kTag);
michael@0 305 params_1.CopyParamIn(0, &value, sizeof(value), false, ULONG_TYPE);
michael@0 306 void* buffer = const_cast<void*>(params_1.GetBuffer());
michael@0 307
michael@0 308 uint32 out_size = 0;
michael@0 309 CrossCallParamsEx* ccp = 0;
michael@0 310 ccp = CrossCallParamsEx::CreateFromBuffer(buffer, params_1.GetSize(),
michael@0 311 &out_size);
michael@0 312 ASSERT_TRUE(NULL != ccp);
michael@0 313 EXPECT_TRUE(ccp->GetBuffer() != buffer);
michael@0 314 EXPECT_EQ(kTag, ccp->GetTag());
michael@0 315 EXPECT_EQ(1, ccp->GetParamsCount());
michael@0 316 delete[] (reinterpret_cast<char*>(ccp));
michael@0 317
michael@0 318 #if defined(NDEBUG)
michael@0 319 // Test hat we handle integer overflow on the number of params
michael@0 320 // correctly. We use a test-only ctor for ActualCallParams that
michael@0 321 // allows to create malformed cross-call buffers.
michael@0 322 const int32 kPtrDiffSz = sizeof(ptrdiff_t);
michael@0 323 for (int32 ix = -1; ix != 3; ++ix) {
michael@0 324 uint32 fake_num_params = (kuint32max / kPtrDiffSz) + ix;
michael@0 325 ActualCallParams<1, kBufferSize> params_2(kTag, fake_num_params);
michael@0 326 params_2.CopyParamIn(0, &value, sizeof(value), false, ULONG_TYPE);
michael@0 327 buffer = const_cast<void*>(params_2.GetBuffer());
michael@0 328
michael@0 329 EXPECT_TRUE(NULL != buffer);
michael@0 330 ccp = CrossCallParamsEx::CreateFromBuffer(buffer, params_1.GetSize(),
michael@0 331 &out_size);
michael@0 332 // If the buffer is malformed the return is NULL.
michael@0 333 EXPECT_TRUE(NULL == ccp);
michael@0 334 }
michael@0 335 #endif // defined(NDEBUG)
michael@0 336
michael@0 337 ActualCallParams<1, kBufferSize> params_3(kTag, 1);
michael@0 338 params_3.CopyParamIn(0, &value, sizeof(value), false, ULONG_TYPE);
michael@0 339 buffer = const_cast<void*>(params_3.GetBuffer());
michael@0 340 EXPECT_TRUE(NULL != buffer);
michael@0 341
michael@0 342 uint32 correct_size = params_3.OverrideSize(1);
michael@0 343 ccp = CrossCallParamsEx::CreateFromBuffer(buffer, kBufferSize, &out_size);
michael@0 344 EXPECT_TRUE(NULL == ccp);
michael@0 345
michael@0 346 // The correct_size is 8 bytes aligned.
michael@0 347 params_3.OverrideSize(correct_size - 7);
michael@0 348 ccp = CrossCallParamsEx::CreateFromBuffer(buffer, kBufferSize, &out_size);
michael@0 349 EXPECT_TRUE(NULL == ccp);
michael@0 350
michael@0 351 params_3.OverrideSize(correct_size);
michael@0 352 ccp = CrossCallParamsEx::CreateFromBuffer(buffer, kBufferSize, &out_size);
michael@0 353 EXPECT_TRUE(NULL != ccp);
michael@0 354
michael@0 355 // Make sure that two parameters work as expected.
michael@0 356 ActualCallParams<2, kBufferSize> params_4(kTag, 2);
michael@0 357 params_4.CopyParamIn(0, &value, sizeof(value), false, ULONG_TYPE);
michael@0 358 params_4.CopyParamIn(1, buffer, sizeof(buffer), false, VOIDPTR_TYPE);
michael@0 359 buffer = const_cast<void*>(params_4.GetBuffer());
michael@0 360 EXPECT_TRUE(NULL != buffer);
michael@0 361
michael@0 362 ccp = CrossCallParamsEx::CreateFromBuffer(buffer, kBufferSize, &out_size);
michael@0 363 EXPECT_TRUE(NULL != ccp);
michael@0 364
michael@0 365 #if defined(_WIN64)
michael@0 366 correct_size = params_4.OverrideSize(1);
michael@0 367 params_4.OverrideSize(correct_size - 1);
michael@0 368 ccp = CrossCallParamsEx::CreateFromBuffer(buffer, kBufferSize, &out_size);
michael@0 369 EXPECT_TRUE(NULL == ccp);
michael@0 370 #endif
michael@0 371 }
michael@0 372
michael@0 373 // This structure is passed to the mock server threads to simulate
michael@0 374 // the server side IPC so it has the required kernel objects.
michael@0 375 struct ServerEvents {
michael@0 376 HANDLE ping;
michael@0 377 HANDLE pong;
michael@0 378 volatile LONG* state;
michael@0 379 HANDLE mutex;
michael@0 380 };
michael@0 381
michael@0 382 // This is the server thread that quicky answers an IPC and exits.
michael@0 383 DWORD WINAPI QuickResponseServer(PVOID param) {
michael@0 384 ServerEvents* events = reinterpret_cast<ServerEvents*>(param);
michael@0 385 DWORD wait_result = 0;
michael@0 386 wait_result = ::WaitForSingleObject(events->ping, INFINITE);
michael@0 387 ::InterlockedExchange(events->state, kAckChannel);
michael@0 388 ::SetEvent(events->pong);
michael@0 389 return wait_result;
michael@0 390 }
michael@0 391
michael@0 392 class CrossCallParamsMock : public CrossCallParams {
michael@0 393 public:
michael@0 394 CrossCallParamsMock(uint32 tag, uint32 params_count)
michael@0 395 : CrossCallParams(tag, params_count) {
michael@0 396 }
michael@0 397 private:
michael@0 398 void* params[4];
michael@0 399 };
michael@0 400
michael@0 401 void FakeOkAnswerInChannel(void* channel) {
michael@0 402 CrossCallReturn* answer = reinterpret_cast<CrossCallReturn*>(channel);
michael@0 403 answer->call_outcome = SBOX_ALL_OK;
michael@0 404 }
michael@0 405
michael@0 406 // Create two threads that will quickly answer IPCs; the first one
michael@0 407 // using channel 1 (channel 0 is busy) and one using channel 0. No time-out
michael@0 408 // should occur.
michael@0 409 TEST(IPCTest, ClientFastServer) {
michael@0 410 const size_t channel_size = kIPCChannelSize;
michael@0 411 size_t base_start = 0;
michael@0 412 IPCControl* client_control =
michael@0 413 MakeChannels(channel_size, 4096 * 2, &base_start);
michael@0 414 FixChannels(client_control, base_start, kIPCChannelSize, FIX_PONG_NOT_READY);
michael@0 415 client_control->server_alive = ::CreateMutex(NULL, FALSE, NULL);
michael@0 416
michael@0 417 char* mem = reinterpret_cast<char*>(client_control);
michael@0 418 SharedMemIPCClient client(mem);
michael@0 419
michael@0 420 ServerEvents events = {0};
michael@0 421 events.ping = client_control->channels[1].ping_event;
michael@0 422 events.pong = client_control->channels[1].pong_event;
michael@0 423 events.state = &client_control->channels[1].state;
michael@0 424
michael@0 425 HANDLE t1 = ::CreateThread(NULL, 0, QuickResponseServer, &events, 0, NULL);
michael@0 426 ASSERT_TRUE(NULL != t1);
michael@0 427 ::CloseHandle(t1);
michael@0 428
michael@0 429 void* buff0 = client.GetBuffer();
michael@0 430 EXPECT_TRUE(mem + client_control->channels[0].channel_base == buff0);
michael@0 431 EXPECT_EQ(kBusyChannel, client_control->channels[0].state);
michael@0 432 EXPECT_EQ(kFreeChannel, client_control->channels[1].state);
michael@0 433 EXPECT_EQ(kFreeChannel, client_control->channels[2].state);
michael@0 434
michael@0 435 void* buff1 = client.GetBuffer();
michael@0 436 EXPECT_TRUE(mem + client_control->channels[1].channel_base == buff1);
michael@0 437 EXPECT_EQ(kBusyChannel, client_control->channels[0].state);
michael@0 438 EXPECT_EQ(kBusyChannel, client_control->channels[1].state);
michael@0 439 EXPECT_EQ(kFreeChannel, client_control->channels[2].state);
michael@0 440
michael@0 441 EXPECT_EQ(0, client_control->channels[1].ipc_tag);
michael@0 442
michael@0 443 uint32 tag = 7654;
michael@0 444 CrossCallReturn answer;
michael@0 445 CrossCallParamsMock* params1 = new(buff1) CrossCallParamsMock(tag, 1);
michael@0 446 FakeOkAnswerInChannel(buff1);
michael@0 447
michael@0 448 ResultCode result = client.DoCall(params1, &answer);
michael@0 449 if (SBOX_ERROR_CHANNEL_ERROR != result)
michael@0 450 client.FreeBuffer(buff1);
michael@0 451
michael@0 452 EXPECT_TRUE(SBOX_ALL_OK == result);
michael@0 453 EXPECT_EQ(tag, client_control->channels[1].ipc_tag);
michael@0 454 EXPECT_EQ(kBusyChannel, client_control->channels[0].state);
michael@0 455 EXPECT_EQ(kFreeChannel, client_control->channels[1].state);
michael@0 456 EXPECT_EQ(kFreeChannel, client_control->channels[2].state);
michael@0 457
michael@0 458 HANDLE t2 = ::CreateThread(NULL, 0, QuickResponseServer, &events, 0, NULL);
michael@0 459 ASSERT_TRUE(NULL != t2);
michael@0 460 ::CloseHandle(t2);
michael@0 461
michael@0 462 client.FreeBuffer(buff0);
michael@0 463 events.ping = client_control->channels[0].ping_event;
michael@0 464 events.pong = client_control->channels[0].pong_event;
michael@0 465 events.state = &client_control->channels[0].state;
michael@0 466
michael@0 467 tag = 4567;
michael@0 468 CrossCallParamsMock* params2 = new(buff0) CrossCallParamsMock(tag, 1);
michael@0 469 FakeOkAnswerInChannel(buff0);
michael@0 470
michael@0 471 result = client.DoCall(params2, &answer);
michael@0 472 if (SBOX_ERROR_CHANNEL_ERROR != result)
michael@0 473 client.FreeBuffer(buff0);
michael@0 474
michael@0 475 EXPECT_TRUE(SBOX_ALL_OK == result);
michael@0 476 EXPECT_EQ(tag, client_control->channels[0].ipc_tag);
michael@0 477 EXPECT_EQ(kFreeChannel, client_control->channels[0].state);
michael@0 478 EXPECT_EQ(kFreeChannel, client_control->channels[1].state);
michael@0 479 EXPECT_EQ(kFreeChannel, client_control->channels[2].state);
michael@0 480
michael@0 481 CloseChannelEvents(client_control);
michael@0 482 ::CloseHandle(client_control->server_alive);
michael@0 483
michael@0 484 delete[] reinterpret_cast<char*>(client_control);
michael@0 485 }
michael@0 486
michael@0 487 // This is the server thread that very slowly answers an IPC and exits. Note
michael@0 488 // that the pong event needs to be signaled twice.
michael@0 489 DWORD WINAPI SlowResponseServer(PVOID param) {
michael@0 490 ServerEvents* events = reinterpret_cast<ServerEvents*>(param);
michael@0 491 DWORD wait_result = 0;
michael@0 492 wait_result = ::WaitForSingleObject(events->ping, INFINITE);
michael@0 493 ::Sleep(kIPCWaitTimeOut1 + kIPCWaitTimeOut2 + 200);
michael@0 494 ::InterlockedExchange(events->state, kAckChannel);
michael@0 495 ::SetEvent(events->pong);
michael@0 496 return wait_result;
michael@0 497 }
michael@0 498
michael@0 499 // This thread's job is to keep the mutex locked.
michael@0 500 DWORD WINAPI MainServerThread(PVOID param) {
michael@0 501 ServerEvents* events = reinterpret_cast<ServerEvents*>(param);
michael@0 502 DWORD wait_result = 0;
michael@0 503 wait_result = ::WaitForSingleObject(events->mutex, INFINITE);
michael@0 504 Sleep(kIPCWaitTimeOut1 * 20);
michael@0 505 return wait_result;
michael@0 506 }
michael@0 507
michael@0 508 // Creates a server thread that answers the IPC so slow that is guaranteed to
michael@0 509 // trigger the time-out code path in the client. A second thread is created
michael@0 510 // to hold locked the server_alive mutex: this signals the client that the
michael@0 511 // server is not dead and it retries the wait.
michael@0 512 TEST(IPCTest, ClientSlowServer) {
michael@0 513 size_t base_start = 0;
michael@0 514 IPCControl* client_control =
michael@0 515 MakeChannels(kIPCChannelSize, 4096*2, &base_start);
michael@0 516 FixChannels(client_control, base_start, kIPCChannelSize, FIX_PONG_NOT_READY);
michael@0 517 client_control->server_alive = ::CreateMutex(NULL, FALSE, NULL);
michael@0 518
michael@0 519 char* mem = reinterpret_cast<char*>(client_control);
michael@0 520 SharedMemIPCClient client(mem);
michael@0 521
michael@0 522 ServerEvents events = {0};
michael@0 523 events.ping = client_control->channels[0].ping_event;
michael@0 524 events.pong = client_control->channels[0].pong_event;
michael@0 525 events.state = &client_control->channels[0].state;
michael@0 526
michael@0 527 HANDLE t1 = ::CreateThread(NULL, 0, SlowResponseServer, &events, 0, NULL);
michael@0 528 ASSERT_TRUE(NULL != t1);
michael@0 529 ::CloseHandle(t1);
michael@0 530
michael@0 531 ServerEvents events2 = {0};
michael@0 532 events2.pong = events.pong;
michael@0 533 events2.mutex = client_control->server_alive;
michael@0 534
michael@0 535 HANDLE t2 = ::CreateThread(NULL, 0, MainServerThread, &events2, 0, NULL);
michael@0 536 ASSERT_TRUE(NULL != t2);
michael@0 537 ::CloseHandle(t2);
michael@0 538
michael@0 539 ::Sleep(1);
michael@0 540
michael@0 541 void* buff0 = client.GetBuffer();
michael@0 542 uint32 tag = 4321;
michael@0 543 CrossCallReturn answer;
michael@0 544 CrossCallParamsMock* params1 = new(buff0) CrossCallParamsMock(tag, 1);
michael@0 545 FakeOkAnswerInChannel(buff0);
michael@0 546
michael@0 547 ResultCode result = client.DoCall(params1, &answer);
michael@0 548 if (SBOX_ERROR_CHANNEL_ERROR != result)
michael@0 549 client.FreeBuffer(buff0);
michael@0 550
michael@0 551 EXPECT_TRUE(SBOX_ALL_OK == result);
michael@0 552 EXPECT_EQ(tag, client_control->channels[0].ipc_tag);
michael@0 553 EXPECT_EQ(kFreeChannel, client_control->channels[0].state);
michael@0 554
michael@0 555 CloseChannelEvents(client_control);
michael@0 556 ::CloseHandle(client_control->server_alive);
michael@0 557 delete[] reinterpret_cast<char*>(client_control);
michael@0 558 }
michael@0 559
michael@0 560 // This test-only IPC dispatcher has two handlers with the same signature
michael@0 561 // but only CallOneHandler should be used.
michael@0 562 class UnitTestIPCDispatcher : public Dispatcher {
michael@0 563 public:
michael@0 564 enum {
michael@0 565 CALL_ONE_TAG = 78,
michael@0 566 CALL_TWO_TAG = 87
michael@0 567 };
michael@0 568
michael@0 569 UnitTestIPCDispatcher();
michael@0 570 ~UnitTestIPCDispatcher() {};
michael@0 571
michael@0 572 virtual bool SetupService(InterceptionManager* manager, int service) {
michael@0 573 return true;
michael@0 574 }
michael@0 575
michael@0 576 private:
michael@0 577 bool CallOneHandler(IPCInfo* ipc, HANDLE p1, DWORD p2) {
michael@0 578 ipc->return_info.extended[0].handle = p1;
michael@0 579 ipc->return_info.extended[1].unsigned_int = p2;
michael@0 580 return true;
michael@0 581 }
michael@0 582
michael@0 583 bool CallTwoHandler(IPCInfo* ipc, HANDLE p1, DWORD p2) {
michael@0 584 return true;
michael@0 585 }
michael@0 586 };
michael@0 587
michael@0 588 UnitTestIPCDispatcher::UnitTestIPCDispatcher() {
michael@0 589 static const IPCCall call_one = {
michael@0 590 {CALL_ONE_TAG, VOIDPTR_TYPE, ULONG_TYPE},
michael@0 591 reinterpret_cast<CallbackGeneric>(
michael@0 592 &UnitTestIPCDispatcher::CallOneHandler)
michael@0 593 };
michael@0 594 static const IPCCall call_two = {
michael@0 595 {CALL_TWO_TAG, VOIDPTR_TYPE, ULONG_TYPE},
michael@0 596 reinterpret_cast<CallbackGeneric>(
michael@0 597 &UnitTestIPCDispatcher::CallTwoHandler)
michael@0 598 };
michael@0 599 ipc_calls_.push_back(call_one);
michael@0 600 ipc_calls_.push_back(call_two);
michael@0 601 }
michael@0 602
michael@0 603 // This test does most of the shared memory IPC client-server roundtrip
michael@0 604 // and tests the packing, unpacking and call dispatching.
michael@0 605 TEST(IPCTest, SharedMemServerTests) {
michael@0 606 size_t base_start = 0;
michael@0 607 IPCControl* client_control =
michael@0 608 MakeChannels(kIPCChannelSize, 4096, &base_start);
michael@0 609 client_control->server_alive = HANDLE(1);
michael@0 610 FixChannels(client_control, base_start, kIPCChannelSize, FIX_PONG_READY);
michael@0 611
michael@0 612 char* mem = reinterpret_cast<char*>(client_control);
michael@0 613 SharedMemIPCClient client(mem);
michael@0 614
michael@0 615 CrossCallReturn answer;
michael@0 616 HANDLE bar = HANDLE(191919);
michael@0 617 DWORD foo = 6767676;
michael@0 618 CrossCall(client, UnitTestIPCDispatcher::CALL_ONE_TAG, bar, foo, &answer);
michael@0 619 void* buff = client.GetBuffer();
michael@0 620 ASSERT_TRUE(NULL != buff);
michael@0 621
michael@0 622 UnitTestIPCDispatcher dispatcher;
michael@0 623 // Since we are directly calling InvokeCallback, most of this structure
michael@0 624 // can be set to NULL.
michael@0 625 sandbox::SharedMemIPCServer::ServerControl srv_control = {
michael@0 626 NULL, NULL, kIPCChannelSize, NULL,
michael@0 627 reinterpret_cast<char*>(client_control),
michael@0 628 NULL, &dispatcher, {0} };
michael@0 629
michael@0 630 sandbox::CrossCallReturn call_return = {0};
michael@0 631 EXPECT_TRUE(SharedMemIPCServer::InvokeCallback(&srv_control, buff,
michael@0 632 &call_return));
michael@0 633 EXPECT_EQ(SBOX_ALL_OK, call_return.call_outcome);
michael@0 634 EXPECT_TRUE(bar == call_return.extended[0].handle);
michael@0 635 EXPECT_EQ(foo, call_return.extended[1].unsigned_int);
michael@0 636
michael@0 637 CloseChannelEvents(client_control);
michael@0 638 delete[] reinterpret_cast<char*>(client_control);
michael@0 639 }
michael@0 640
michael@0 641 } // namespace sandbox

mercurial