From 4bcf1c120956613b5f899fb1d6f677961ea8806d Mon Sep 17 00:00:00 2001 From: "jln@chromium.org" Date: Fri, 13 Jul 2012 20:05:09 +0000 Subject: Move Windows sandbox - Move Windows sandbox to sandbox/win - Update sandbox_win.gypi git-svn-id: svn://svn.chromium.org/chrome/trunk/src@146625 0039d316-1c4b-4281-b951-d872f2087c98 --- sandbox/win/src/ipc_unittest.cc | 641 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 641 insertions(+) create mode 100644 sandbox/win/src/ipc_unittest.cc (limited to 'sandbox/win/src/ipc_unittest.cc') diff --git a/sandbox/win/src/ipc_unittest.cc b/sandbox/win/src/ipc_unittest.cc new file mode 100644 index 0000000..e1fb7c1 --- /dev/null +++ b/sandbox/win/src/ipc_unittest.cc @@ -0,0 +1,641 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/basictypes.h" +#include "sandbox/src/crosscall_client.h" +#include "sandbox/src/crosscall_server.h" +#include "sandbox/src/sharedmem_ipc_client.h" +#include "sandbox/src/sharedmem_ipc_server.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace sandbox { + +// Helper function to make the fake shared memory with some +// basic elements initialized. +IPCControl* MakeChannels(size_t channel_size, size_t total_shared_size, + size_t* base_start) { + // Allocate memory + char* mem = new char[total_shared_size]; + memset(mem, 0, total_shared_size); + // Calculate how many channels we can fit in the shared memory. + total_shared_size -= offsetof(IPCControl, channels); + size_t channel_count = + total_shared_size / (sizeof(ChannelControl) + channel_size); + // Calculate the start of the first channel. + *base_start = (sizeof(ChannelControl)* channel_count) + + offsetof(IPCControl, channels); + // Setup client structure. + IPCControl* client_control = reinterpret_cast(mem); + client_control->channels_count = channel_count; + return client_control; +} + +enum TestFixMode { + FIX_NO_EVENTS, + FIX_PONG_READY, + FIX_PONG_NOT_READY +}; + +void FixChannels(IPCControl* client_control, size_t base_start, + size_t channel_size, TestFixMode mode) { + for (size_t ix = 0; ix != client_control->channels_count; ++ix) { + ChannelControl& channel = client_control->channels[ix]; + channel.channel_base = base_start; + channel.state = kFreeChannel; + if (mode != FIX_NO_EVENTS) { + BOOL signaled = (FIX_PONG_READY == mode)? TRUE : FALSE; + channel.ping_event = ::CreateEventW(NULL, FALSE, FALSE, NULL); + channel.pong_event = ::CreateEventW(NULL, FALSE, signaled, NULL); + } + base_start += channel_size; + } +} + +void CloseChannelEvents(IPCControl* client_control) { + for (size_t ix = 0; ix != client_control->channels_count; ++ix) { + ChannelControl& channel = client_control->channels[ix]; + ::CloseHandle(channel.ping_event); + ::CloseHandle(channel.pong_event); + } +} + +TEST(IPCTest, ChannelMaker) { + // Test that our testing rig is computing offsets properly. We should have + // 5 channnels and the offset to the first channel is 108 bytes in 32 bits + // and 216 in 64 bits. + size_t channel_start = 0; + IPCControl* client_control = MakeChannels(12 * 64, 4096, &channel_start); + ASSERT_TRUE(NULL != client_control); + EXPECT_EQ(5, client_control->channels_count); +#if defined(_WIN64) + EXPECT_EQ(216, channel_start); +#else + EXPECT_EQ(108, channel_start); +#endif + delete[] reinterpret_cast(client_control); +} + +TEST(IPCTest, ClientLockUnlock) { + // Make 7 channels of kIPCChannelSize (1kb) each. Test that we lock and + // unlock channels properly. + size_t base_start = 0; + IPCControl* client_control = + MakeChannels(kIPCChannelSize, 4096 * 2, &base_start); + FixChannels(client_control, base_start, kIPCChannelSize, FIX_NO_EVENTS); + + char* mem = reinterpret_cast(client_control); + SharedMemIPCClient client(mem); + + // Test that we lock the first 3 channels in sequence. + void* buff0 = client.GetBuffer(); + EXPECT_TRUE(mem + client_control->channels[0].channel_base == buff0); + EXPECT_EQ(kBusyChannel, client_control->channels[0].state); + EXPECT_EQ(kFreeChannel, client_control->channels[1].state); + EXPECT_EQ(kFreeChannel, client_control->channels[2].state); + EXPECT_EQ(kFreeChannel, client_control->channels[3].state); + EXPECT_EQ(kFreeChannel, client_control->channels[4].state); + EXPECT_EQ(kFreeChannel, client_control->channels[5].state); + + void* buff1 = client.GetBuffer(); + EXPECT_TRUE(mem + client_control->channels[1].channel_base == buff1); + EXPECT_EQ(kBusyChannel, client_control->channels[0].state); + EXPECT_EQ(kBusyChannel, client_control->channels[1].state); + EXPECT_EQ(kFreeChannel, client_control->channels[2].state); + EXPECT_EQ(kFreeChannel, client_control->channels[3].state); + EXPECT_EQ(kFreeChannel, client_control->channels[4].state); + EXPECT_EQ(kFreeChannel, client_control->channels[5].state); + + void* buff2 = client.GetBuffer(); + EXPECT_TRUE(mem + client_control->channels[2].channel_base == buff2); + EXPECT_EQ(kBusyChannel, client_control->channels[0].state); + EXPECT_EQ(kBusyChannel, client_control->channels[1].state); + EXPECT_EQ(kBusyChannel, client_control->channels[2].state); + EXPECT_EQ(kFreeChannel, client_control->channels[3].state); + EXPECT_EQ(kFreeChannel, client_control->channels[4].state); + EXPECT_EQ(kFreeChannel, client_control->channels[5].state); + + // Test that we unlock and re-lock the right channel. + client.FreeBuffer(buff1); + EXPECT_EQ(kBusyChannel, client_control->channels[0].state); + EXPECT_EQ(kFreeChannel, client_control->channels[1].state); + EXPECT_EQ(kBusyChannel, client_control->channels[2].state); + EXPECT_EQ(kFreeChannel, client_control->channels[3].state); + EXPECT_EQ(kFreeChannel, client_control->channels[4].state); + EXPECT_EQ(kFreeChannel, client_control->channels[5].state); + + void* buff2b = client.GetBuffer(); + EXPECT_TRUE(mem + client_control->channels[1].channel_base == buff2b); + EXPECT_EQ(kBusyChannel, client_control->channels[0].state); + EXPECT_EQ(kBusyChannel, client_control->channels[1].state); + EXPECT_EQ(kBusyChannel, client_control->channels[2].state); + EXPECT_EQ(kFreeChannel, client_control->channels[3].state); + EXPECT_EQ(kFreeChannel, client_control->channels[4].state); + EXPECT_EQ(kFreeChannel, client_control->channels[5].state); + + client.FreeBuffer(buff0); + EXPECT_EQ(kFreeChannel, client_control->channels[0].state); + EXPECT_EQ(kBusyChannel, client_control->channels[1].state); + EXPECT_EQ(kBusyChannel, client_control->channels[2].state); + EXPECT_EQ(kFreeChannel, client_control->channels[3].state); + EXPECT_EQ(kFreeChannel, client_control->channels[4].state); + EXPECT_EQ(kFreeChannel, client_control->channels[5].state); + + delete[] reinterpret_cast(client_control); +} + +TEST(IPCTest, CrossCallStrPacking) { + // This test tries the CrossCall object with null and non-null string + // combination of parameters, integer types and verifies that the unpacker + // can read them properly. + size_t base_start = 0; + IPCControl* client_control = + MakeChannels(kIPCChannelSize, 4096 * 4, &base_start); + client_control->server_alive = HANDLE(1); + FixChannels(client_control, base_start, kIPCChannelSize, FIX_PONG_READY); + + char* mem = reinterpret_cast(client_control); + SharedMemIPCClient client(mem); + + CrossCallReturn answer; + uint32 tag1 = 666; + const wchar_t text[] = L"98765 - 43210"; + std::wstring copied_text; + CrossCallParamsEx* actual_params; + + CrossCall(client, tag1, text, &answer); + actual_params = reinterpret_cast(client.GetBuffer()); + EXPECT_EQ(1, actual_params->GetParamsCount()); + EXPECT_EQ(tag1, actual_params->GetTag()); + EXPECT_TRUE(actual_params->GetParameterStr(0, &copied_text)); + EXPECT_STREQ(text, copied_text.c_str()); + + // Check with an empty string. + uint32 tag2 = 777; + const wchar_t* null_text = NULL; + CrossCall(client, tag2, null_text, &answer); + actual_params = reinterpret_cast(client.GetBuffer()); + EXPECT_EQ(1, actual_params->GetParamsCount()); + EXPECT_EQ(tag2, actual_params->GetTag()); + uint32 param_size = 1; + ArgType type = INVALID_TYPE; + void* param_addr = actual_params->GetRawParameter(0, ¶m_size, &type); + EXPECT_TRUE(NULL != param_addr); + EXPECT_EQ(0, param_size); + EXPECT_EQ(WCHAR_TYPE, type); + EXPECT_TRUE(actual_params->GetParameterStr(0, &copied_text)); + + uint32 tag3 = 888; + param_size = 1; + copied_text.clear(); + + // Check with an empty string and a non-empty string. + CrossCall(client, tag3, null_text, text, &answer); + actual_params = reinterpret_cast(client.GetBuffer()); + EXPECT_EQ(2, actual_params->GetParamsCount()); + EXPECT_EQ(tag3, actual_params->GetTag()); + type = INVALID_TYPE; + param_addr = actual_params->GetRawParameter(0, ¶m_size, &type); + EXPECT_TRUE(NULL != param_addr); + EXPECT_EQ(0, param_size); + EXPECT_EQ(WCHAR_TYPE, type); + EXPECT_TRUE(actual_params->GetParameterStr(0, &copied_text)); + EXPECT_TRUE(actual_params->GetParameterStr(1, &copied_text)); + EXPECT_STREQ(text, copied_text.c_str()); + + param_size = 1; + std::wstring copied_text_p0, copied_text_p2; + + const wchar_t text2[] = L"AeFG"; + CrossCall(client, tag1, text2, null_text, text, &answer); + actual_params = reinterpret_cast(client.GetBuffer()); + EXPECT_EQ(3, actual_params->GetParamsCount()); + EXPECT_EQ(tag1, actual_params->GetTag()); + EXPECT_TRUE(actual_params->GetParameterStr(0, &copied_text_p0)); + EXPECT_STREQ(text2, copied_text_p0.c_str()); + EXPECT_TRUE(actual_params->GetParameterStr(2, &copied_text_p2)); + EXPECT_STREQ(text, copied_text_p2.c_str()); + type = INVALID_TYPE; + param_addr = actual_params->GetRawParameter(1, ¶m_size, &type); + EXPECT_TRUE(NULL != param_addr); + EXPECT_EQ(0, param_size); + EXPECT_EQ(WCHAR_TYPE, type); + + CloseChannelEvents(client_control); + delete[] reinterpret_cast(client_control); +} + +TEST(IPCTest, CrossCallIntPacking) { + // Check handling for regular 32 bit integers used in Windows. + size_t base_start = 0; + IPCControl* client_control = + MakeChannels(kIPCChannelSize, 4096 * 4, &base_start); + client_control->server_alive = HANDLE(1); + FixChannels(client_control, base_start, kIPCChannelSize, FIX_PONG_READY); + + uint32 tag1 = 999; + uint32 tag2 = 111; + const wchar_t text[] = L"godzilla"; + CrossCallParamsEx* actual_params; + + char* mem = reinterpret_cast(client_control); + SharedMemIPCClient client(mem); + + CrossCallReturn answer; + DWORD dw = 0xE6578; + CrossCall(client, tag2, dw, &answer); + actual_params = reinterpret_cast(client.GetBuffer()); + EXPECT_EQ(1, actual_params->GetParamsCount()); + EXPECT_EQ(tag2, actual_params->GetTag()); + ArgType type = INVALID_TYPE; + uint32 param_size = 1; + void* param_addr = actual_params->GetRawParameter(0, ¶m_size, &type); + ASSERT_EQ(sizeof(dw), param_size); + EXPECT_EQ(ULONG_TYPE, type); + ASSERT_TRUE(NULL != param_addr); + EXPECT_EQ(0, memcmp(&dw, param_addr, param_size)); + + // Check handling for windows HANDLES. + HANDLE h = HANDLE(0x70000500); + CrossCall(client, tag1, text, h, &answer); + actual_params = reinterpret_cast(client.GetBuffer()); + EXPECT_EQ(2, actual_params->GetParamsCount()); + EXPECT_EQ(tag1, actual_params->GetTag()); + type = INVALID_TYPE; + param_addr = actual_params->GetRawParameter(1, ¶m_size, &type); + ASSERT_EQ(sizeof(h), param_size); + EXPECT_EQ(VOIDPTR_TYPE, type); + ASSERT_TRUE(NULL != param_addr); + EXPECT_EQ(0, memcmp(&h, param_addr, param_size)); + + // Check combination of 32 and 64 bits. + CrossCall(client, tag2, h, dw, h, &answer); + actual_params = reinterpret_cast(client.GetBuffer()); + EXPECT_EQ(3, actual_params->GetParamsCount()); + EXPECT_EQ(tag2, actual_params->GetTag()); + type = INVALID_TYPE; + param_addr = actual_params->GetRawParameter(0, ¶m_size, &type); + ASSERT_EQ(sizeof(h), param_size); + EXPECT_EQ(VOIDPTR_TYPE, type); + ASSERT_TRUE(NULL != param_addr); + EXPECT_EQ(0, memcmp(&h, param_addr, param_size)); + type = INVALID_TYPE; + param_addr = actual_params->GetRawParameter(1, ¶m_size, &type); + ASSERT_EQ(sizeof(dw), param_size); + EXPECT_EQ(ULONG_TYPE, type); + ASSERT_TRUE(NULL != param_addr); + EXPECT_EQ(0, memcmp(&dw, param_addr, param_size)); + type = INVALID_TYPE; + param_addr = actual_params->GetRawParameter(2, ¶m_size, &type); + ASSERT_EQ(sizeof(h), param_size); + EXPECT_EQ(VOIDPTR_TYPE, type); + ASSERT_TRUE(NULL != param_addr); + EXPECT_EQ(0, memcmp(&h, param_addr, param_size)); + + CloseChannelEvents(client_control); + delete[] reinterpret_cast(client_control); +} + +TEST(IPCTest, CrossCallValidation) { + // First a sanity test with a well formed parameter object. + unsigned long value = 124816; + const uint32 kTag = 33; + const uint32 kBufferSize = 256; + ActualCallParams<1, kBufferSize> params_1(kTag); + params_1.CopyParamIn(0, &value, sizeof(value), false, ULONG_TYPE); + void* buffer = const_cast(params_1.GetBuffer()); + + uint32 out_size = 0; + CrossCallParamsEx* ccp = 0; + ccp = CrossCallParamsEx::CreateFromBuffer(buffer, params_1.GetSize(), + &out_size); + ASSERT_TRUE(NULL != ccp); + EXPECT_TRUE(ccp->GetBuffer() != buffer); + EXPECT_EQ(kTag, ccp->GetTag()); + EXPECT_EQ(1, ccp->GetParamsCount()); + delete[] (reinterpret_cast(ccp)); + +#if defined(NDEBUG) + // Test hat we handle integer overflow on the number of params + // correctly. We use a test-only ctor for ActualCallParams that + // allows to create malformed cross-call buffers. + const int32 kPtrDiffSz = sizeof(ptrdiff_t); + for (int32 ix = -1; ix != 3; ++ix) { + uint32 fake_num_params = (kuint32max / kPtrDiffSz) + ix; + ActualCallParams<1, kBufferSize> params_2(kTag, fake_num_params); + params_2.CopyParamIn(0, &value, sizeof(value), false, ULONG_TYPE); + buffer = const_cast(params_2.GetBuffer()); + + EXPECT_TRUE(NULL != buffer); + ccp = CrossCallParamsEx::CreateFromBuffer(buffer, params_1.GetSize(), + &out_size); + // If the buffer is malformed the return is NULL. + EXPECT_TRUE(NULL == ccp); + } +#endif // defined(NDEBUG) + + ActualCallParams<1, kBufferSize> params_3(kTag, 1); + params_3.CopyParamIn(0, &value, sizeof(value), false, ULONG_TYPE); + buffer = const_cast(params_3.GetBuffer()); + EXPECT_TRUE(NULL != buffer); + + uint32 correct_size = params_3.OverrideSize(1); + ccp = CrossCallParamsEx::CreateFromBuffer(buffer, kBufferSize, &out_size); + EXPECT_TRUE(NULL == ccp); + + // The correct_size is 8 bytes aligned. + params_3.OverrideSize(correct_size - 7); + ccp = CrossCallParamsEx::CreateFromBuffer(buffer, kBufferSize, &out_size); + EXPECT_TRUE(NULL == ccp); + + params_3.OverrideSize(correct_size); + ccp = CrossCallParamsEx::CreateFromBuffer(buffer, kBufferSize, &out_size); + EXPECT_TRUE(NULL != ccp); + + // Make sure that two parameters work as expected. + ActualCallParams<2, kBufferSize> params_4(kTag, 2); + params_4.CopyParamIn(0, &value, sizeof(value), false, ULONG_TYPE); + params_4.CopyParamIn(1, buffer, sizeof(buffer), false, VOIDPTR_TYPE); + buffer = const_cast(params_4.GetBuffer()); + EXPECT_TRUE(NULL != buffer); + + ccp = CrossCallParamsEx::CreateFromBuffer(buffer, kBufferSize, &out_size); + EXPECT_TRUE(NULL != ccp); + +#if defined(_WIN64) + correct_size = params_4.OverrideSize(1); + params_4.OverrideSize(correct_size - 1); + ccp = CrossCallParamsEx::CreateFromBuffer(buffer, kBufferSize, &out_size); + EXPECT_TRUE(NULL == ccp); +#endif +} + +// This structure is passed to the mock server threads to simulate +// the server side IPC so it has the required kernel objects. +struct ServerEvents { + HANDLE ping; + HANDLE pong; + volatile LONG* state; + HANDLE mutex; +}; + +// This is the server thread that quicky answers an IPC and exits. +DWORD WINAPI QuickResponseServer(PVOID param) { + ServerEvents* events = reinterpret_cast(param); + DWORD wait_result = 0; + wait_result = ::WaitForSingleObject(events->ping, INFINITE); + ::InterlockedExchange(events->state, kAckChannel); + ::SetEvent(events->pong); + return wait_result; +} + +class CrossCallParamsMock : public CrossCallParams { + public: + CrossCallParamsMock(uint32 tag, uint32 params_count) + : CrossCallParams(tag, params_count) { + } + private: + void* params[4]; +}; + +void FakeOkAnswerInChannel(void* channel) { + CrossCallReturn* answer = reinterpret_cast(channel); + answer->call_outcome = SBOX_ALL_OK; +} + +// Create two threads that will quickly answer IPCs; the first one +// using channel 1 (channel 0 is busy) and one using channel 0. No time-out +// should occur. +TEST(IPCTest, ClientFastServer) { + const size_t channel_size = kIPCChannelSize; + size_t base_start = 0; + IPCControl* client_control = + MakeChannels(channel_size, 4096 * 2, &base_start); + FixChannels(client_control, base_start, kIPCChannelSize, FIX_PONG_NOT_READY); + client_control->server_alive = ::CreateMutex(NULL, FALSE, NULL); + + char* mem = reinterpret_cast(client_control); + SharedMemIPCClient client(mem); + + ServerEvents events = {0}; + events.ping = client_control->channels[1].ping_event; + events.pong = client_control->channels[1].pong_event; + events.state = &client_control->channels[1].state; + + HANDLE t1 = ::CreateThread(NULL, 0, QuickResponseServer, &events, 0, NULL); + ASSERT_TRUE(NULL != t1); + ::CloseHandle(t1); + + void* buff0 = client.GetBuffer(); + EXPECT_TRUE(mem + client_control->channels[0].channel_base == buff0); + EXPECT_EQ(kBusyChannel, client_control->channels[0].state); + EXPECT_EQ(kFreeChannel, client_control->channels[1].state); + EXPECT_EQ(kFreeChannel, client_control->channels[2].state); + + void* buff1 = client.GetBuffer(); + EXPECT_TRUE(mem + client_control->channels[1].channel_base == buff1); + EXPECT_EQ(kBusyChannel, client_control->channels[0].state); + EXPECT_EQ(kBusyChannel, client_control->channels[1].state); + EXPECT_EQ(kFreeChannel, client_control->channels[2].state); + + EXPECT_EQ(0, client_control->channels[1].ipc_tag); + + uint32 tag = 7654; + CrossCallReturn answer; + CrossCallParamsMock* params1 = new(buff1) CrossCallParamsMock(tag, 1); + FakeOkAnswerInChannel(buff1); + + ResultCode result = client.DoCall(params1, &answer); + if (SBOX_ERROR_CHANNEL_ERROR != result) + client.FreeBuffer(buff1); + + EXPECT_TRUE(SBOX_ALL_OK == result); + EXPECT_EQ(tag, client_control->channels[1].ipc_tag); + EXPECT_EQ(kBusyChannel, client_control->channels[0].state); + EXPECT_EQ(kFreeChannel, client_control->channels[1].state); + EXPECT_EQ(kFreeChannel, client_control->channels[2].state); + + HANDLE t2 = ::CreateThread(NULL, 0, QuickResponseServer, &events, 0, NULL); + ASSERT_TRUE(NULL != t2); + ::CloseHandle(t2); + + client.FreeBuffer(buff0); + events.ping = client_control->channels[0].ping_event; + events.pong = client_control->channels[0].pong_event; + events.state = &client_control->channels[0].state; + + tag = 4567; + CrossCallParamsMock* params2 = new(buff0) CrossCallParamsMock(tag, 1); + FakeOkAnswerInChannel(buff0); + + result = client.DoCall(params2, &answer); + if (SBOX_ERROR_CHANNEL_ERROR != result) + client.FreeBuffer(buff0); + + EXPECT_TRUE(SBOX_ALL_OK == result); + EXPECT_EQ(tag, client_control->channels[0].ipc_tag); + EXPECT_EQ(kFreeChannel, client_control->channels[0].state); + EXPECT_EQ(kFreeChannel, client_control->channels[1].state); + EXPECT_EQ(kFreeChannel, client_control->channels[2].state); + + CloseChannelEvents(client_control); + ::CloseHandle(client_control->server_alive); + + delete[] reinterpret_cast(client_control); +} + +// This is the server thread that very slowly answers an IPC and exits. Note +// that the pong event needs to be signaled twice. +DWORD WINAPI SlowResponseServer(PVOID param) { + ServerEvents* events = reinterpret_cast(param); + DWORD wait_result = 0; + wait_result = ::WaitForSingleObject(events->ping, INFINITE); + ::Sleep(kIPCWaitTimeOut1 + kIPCWaitTimeOut2 + 200); + ::InterlockedExchange(events->state, kAckChannel); + ::SetEvent(events->pong); + return wait_result; +} + +// This thread's job is to keep the mutex locked. +DWORD WINAPI MainServerThread(PVOID param) { + ServerEvents* events = reinterpret_cast(param); + DWORD wait_result = 0; + wait_result = ::WaitForSingleObject(events->mutex, INFINITE); + Sleep(kIPCWaitTimeOut1 * 20); + return wait_result; +} + +// Creates a server thread that answers the IPC so slow that is guaranteed to +// trigger the time-out code path in the client. A second thread is created +// to hold locked the server_alive mutex: this signals the client that the +// server is not dead and it retries the wait. +TEST(IPCTest, ClientSlowServer) { + size_t base_start = 0; + IPCControl* client_control = + MakeChannels(kIPCChannelSize, 4096*2, &base_start); + FixChannels(client_control, base_start, kIPCChannelSize, FIX_PONG_NOT_READY); + client_control->server_alive = ::CreateMutex(NULL, FALSE, NULL); + + char* mem = reinterpret_cast(client_control); + SharedMemIPCClient client(mem); + + ServerEvents events = {0}; + events.ping = client_control->channels[0].ping_event; + events.pong = client_control->channels[0].pong_event; + events.state = &client_control->channels[0].state; + + HANDLE t1 = ::CreateThread(NULL, 0, SlowResponseServer, &events, 0, NULL); + ASSERT_TRUE(NULL != t1); + ::CloseHandle(t1); + + ServerEvents events2 = {0}; + events2.pong = events.pong; + events2.mutex = client_control->server_alive; + + HANDLE t2 = ::CreateThread(NULL, 0, MainServerThread, &events2, 0, NULL); + ASSERT_TRUE(NULL != t2); + ::CloseHandle(t2); + + ::Sleep(1); + + void* buff0 = client.GetBuffer(); + uint32 tag = 4321; + CrossCallReturn answer; + CrossCallParamsMock* params1 = new(buff0) CrossCallParamsMock(tag, 1); + FakeOkAnswerInChannel(buff0); + + ResultCode result = client.DoCall(params1, &answer); + if (SBOX_ERROR_CHANNEL_ERROR != result) + client.FreeBuffer(buff0); + + EXPECT_TRUE(SBOX_ALL_OK == result); + EXPECT_EQ(tag, client_control->channels[0].ipc_tag); + EXPECT_EQ(kFreeChannel, client_control->channels[0].state); + + CloseChannelEvents(client_control); + ::CloseHandle(client_control->server_alive); + delete[] reinterpret_cast(client_control); +} + +// This test-only IPC dispatcher has two handlers with the same signature +// but only CallOneHandler should be used. +class UnitTestIPCDispatcher : public Dispatcher { + public: + enum { + CALL_ONE_TAG = 78, + CALL_TWO_TAG = 87 + }; + + UnitTestIPCDispatcher(); + ~UnitTestIPCDispatcher() {}; + + virtual bool SetupService(InterceptionManager* manager, int service) { + return true; + } + + private: + bool CallOneHandler(IPCInfo* ipc, HANDLE p1, DWORD p2) { + ipc->return_info.extended[0].handle = p1; + ipc->return_info.extended[1].unsigned_int = p2; + return true; + } + + bool CallTwoHandler(IPCInfo* ipc, HANDLE p1, DWORD p2) { + return true; + } +}; + +UnitTestIPCDispatcher::UnitTestIPCDispatcher() { + static const IPCCall call_one = { + {CALL_ONE_TAG, VOIDPTR_TYPE, ULONG_TYPE}, + reinterpret_cast( + &UnitTestIPCDispatcher::CallOneHandler) + }; + static const IPCCall call_two = { + {CALL_TWO_TAG, VOIDPTR_TYPE, ULONG_TYPE}, + reinterpret_cast( + &UnitTestIPCDispatcher::CallTwoHandler) + }; + ipc_calls_.push_back(call_one); + ipc_calls_.push_back(call_two); +} + +// This test does most of the shared memory IPC client-server roundtrip +// and tests the packing, unpacking and call dispatching. +TEST(IPCTest, SharedMemServerTests) { + size_t base_start = 0; + IPCControl* client_control = + MakeChannels(kIPCChannelSize, 4096, &base_start); + client_control->server_alive = HANDLE(1); + FixChannels(client_control, base_start, kIPCChannelSize, FIX_PONG_READY); + + char* mem = reinterpret_cast(client_control); + SharedMemIPCClient client(mem); + + CrossCallReturn answer; + HANDLE bar = HANDLE(191919); + DWORD foo = 6767676; + CrossCall(client, UnitTestIPCDispatcher::CALL_ONE_TAG, bar, foo, &answer); + void* buff = client.GetBuffer(); + ASSERT_TRUE(NULL != buff); + + UnitTestIPCDispatcher dispatcher; + // Since we are directly calling InvokeCallback, most of this structure + // can be set to NULL. + sandbox::SharedMemIPCServer::ServerControl srv_control = { + NULL, NULL, kIPCChannelSize, NULL, + reinterpret_cast(client_control), + NULL, &dispatcher, {0} }; + + sandbox::CrossCallReturn call_return = {0}; + EXPECT_TRUE(SharedMemIPCServer::InvokeCallback(&srv_control, buff, + &call_return)); + EXPECT_EQ(SBOX_ALL_OK, call_return.call_outcome); + EXPECT_TRUE(bar == call_return.extended[0].handle); + EXPECT_EQ(foo, call_return.extended[1].unsigned_int); + + CloseChannelEvents(client_control); + delete[] reinterpret_cast(client_control); +} + +} // namespace sandbox -- cgit v1.1