Home | History | Annotate | Download | only in src
      1 // Copyright (c) 2011 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 "base/strings/stringprintf.h"
      6 #include "base/win/scoped_handle.h"
      7 #include "sandbox/win/src/handle_closer_agent.h"
      8 #include "sandbox/win/src/sandbox.h"
      9 #include "sandbox/win/src/sandbox_factory.h"
     10 #include "sandbox/win/src/target_services.h"
     11 #include "sandbox/win/tests/common/controller.h"
     12 #include "testing/gtest/include/gtest/gtest.h"
     13 
     14 namespace {
     15 
     16 const wchar_t *kFileExtensions[] = { L".1", L".2", L".3", L".4" };
     17 
     18 // Returns a handle to a unique marker file that can be retrieved between runs.
     19 HANDLE GetMarkerFile(const wchar_t *extension) {
     20   wchar_t path_buffer[MAX_PATH + 1];
     21   CHECK(::GetTempPath(MAX_PATH, path_buffer));
     22   string16 marker_path = path_buffer;
     23   marker_path += L"\\sbox_marker_";
     24 
     25   // Generate a unique value from the exe's size and timestamp.
     26   CHECK(::GetModuleFileName(NULL, path_buffer, MAX_PATH));
     27   base::win::ScopedHandle module(::CreateFile(path_buffer,
     28                                  FILE_READ_ATTRIBUTES, FILE_SHARE_READ, NULL,
     29                                  OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL));
     30   CHECK(module.IsValid());
     31   FILETIME timestamp;
     32   CHECK(::GetFileTime(module, &timestamp, NULL, NULL));
     33   marker_path += base::StringPrintf(L"%08x%08x%08x",
     34                                     ::GetFileSize(module, NULL),
     35                                     timestamp.dwLowDateTime,
     36                                     timestamp.dwHighDateTime);
     37   marker_path += extension;
     38 
     39   // Make the file delete-on-close so cleanup is automatic.
     40   return CreateFile(marker_path.c_str(), FILE_ALL_ACCESS,
     41       FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
     42       NULL, OPEN_ALWAYS, FILE_FLAG_DELETE_ON_CLOSE, NULL);
     43 }
     44 
     45 // Used by the thread pool tests.
     46 HANDLE finish_event;
     47 const int kWaitCount = 20;
     48 
     49 }  // namespace
     50 
     51 namespace sandbox {
     52 
     53 // Checks for the presence of a list of files (in object path form).
     54 // Format: CheckForFileHandle (Y|N) \path\to\file1 [\path\to\file2 ...]
     55 // - Y or N depending if the file should exist or not.
     56 SBOX_TESTS_COMMAND int CheckForFileHandles(int argc, wchar_t **argv) {
     57   if (argc < 2)
     58     return SBOX_TEST_FAILED_TO_RUN_TEST;
     59   bool should_find = argv[0][0] == L'Y';
     60   if (argv[0][1] != L'\0' || !should_find && argv[0][0] != L'N')
     61     return SBOX_TEST_FAILED_TO_RUN_TEST;
     62 
     63   static int state = BEFORE_INIT;
     64   switch (state++) {
     65     case BEFORE_INIT:
     66       // Create a unique marker file that is open while the test is running.
     67       // The handles leak, but it will be closed by the test or on exit.
     68       for (int i = 0; i < arraysize(kFileExtensions); ++i)
     69         EXPECT_NE(GetMarkerFile(kFileExtensions[i]), INVALID_HANDLE_VALUE);
     70       return SBOX_TEST_SUCCEEDED;
     71 
     72     case AFTER_REVERT: {
     73       // Brute force the handle table to find what we're looking for.
     74       DWORD handle_count = UINT_MAX;
     75       const int kInvalidHandleThreshold = 100;
     76       const size_t kHandleOffset = sizeof(HANDLE);
     77       HANDLE handle = NULL;
     78       int invalid_count = 0;
     79       string16 handle_name;
     80 
     81       if (!::GetProcessHandleCount(::GetCurrentProcess(), &handle_count))
     82         return SBOX_TEST_FAILED_TO_RUN_TEST;
     83 
     84       while (handle_count && invalid_count < kInvalidHandleThreshold) {
     85         reinterpret_cast<size_t&>(handle) += kHandleOffset;
     86         if (GetHandleName(handle, &handle_name)) {
     87           for (int i = 1; i < argc; ++i) {
     88             if (handle_name == argv[i])
     89               return should_find ? SBOX_TEST_SUCCEEDED : SBOX_TEST_FAILED;
     90           }
     91           --handle_count;
     92         } else {
     93           ++invalid_count;
     94         }
     95       }
     96 
     97       return should_find ? SBOX_TEST_FAILED : SBOX_TEST_SUCCEEDED;
     98     }
     99 
    100     default:  // Do nothing.
    101       break;
    102   }
    103 
    104   return SBOX_TEST_SUCCEEDED;
    105 }
    106 
    107 TEST(HandleCloserTest, CheckForMarkerFiles) {
    108   TestRunner runner;
    109   runner.SetTimeout(2000);
    110   runner.SetTestState(EVERY_STATE);
    111   sandbox::TargetPolicy* policy = runner.GetPolicy();
    112 
    113   string16 command = string16(L"CheckForFileHandles Y");
    114   for (int i = 0; i < arraysize(kFileExtensions); ++i) {
    115     string16 handle_name;
    116     base::win::ScopedHandle marker(GetMarkerFile(kFileExtensions[i]));
    117     CHECK(marker.IsValid());
    118     CHECK(sandbox::GetHandleName(marker, &handle_name));
    119     command += (L" ");
    120     command += handle_name;
    121   }
    122 
    123   EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(command.c_str())) <<
    124     "Failed: " << command;
    125 }
    126 
    127 TEST(HandleCloserTest, CloseMarkerFiles) {
    128   TestRunner runner;
    129   runner.SetTimeout(2000);
    130   runner.SetTestState(EVERY_STATE);
    131   sandbox::TargetPolicy* policy = runner.GetPolicy();
    132 
    133   string16 command = string16(L"CheckForFileHandles N");
    134   for (int i = 0; i < arraysize(kFileExtensions); ++i) {
    135     string16 handle_name;
    136     base::win::ScopedHandle marker(GetMarkerFile(kFileExtensions[i]));
    137     CHECK(marker.IsValid());
    138     CHECK(sandbox::GetHandleName(marker, &handle_name));
    139     CHECK_EQ(policy->AddKernelObjectToClose(L"File", handle_name.c_str()),
    140               SBOX_ALL_OK);
    141     command += (L" ");
    142     command += handle_name;
    143   }
    144 
    145   EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(command.c_str())) <<
    146     "Failed: " << command;
    147 }
    148 
    149 void WINAPI ThreadPoolTask(void* event, BOOLEAN timeout) {
    150   static volatile LONG waiters_remaining = kWaitCount;
    151   CHECK(!timeout);
    152   CHECK(::CloseHandle(event));
    153   if (::InterlockedDecrement(&waiters_remaining) == 0)
    154     CHECK(::SetEvent(finish_event));
    155 }
    156 
    157 // Run a thread pool inside a sandbox without a CSRSS connection.
    158 SBOX_TESTS_COMMAND int RunThreadPool(int argc, wchar_t **argv) {
    159   HANDLE wait_list[20];
    160   CHECK(finish_event = ::CreateEvent(NULL, TRUE, FALSE, NULL));
    161 
    162   // Set up a bunch of waiters.
    163   HANDLE pool = NULL;
    164   for (int i = 0; i < kWaitCount; ++i) {
    165     HANDLE event = ::CreateEvent(NULL, TRUE, FALSE, NULL);
    166     CHECK(event);
    167     CHECK(::RegisterWaitForSingleObject(&pool, event, ThreadPoolTask, event,
    168                                         INFINITE, WT_EXECUTEONLYONCE));
    169     wait_list[i] = event;
    170   }
    171 
    172   // Signal all the waiters.
    173   for (int i = 0; i < kWaitCount; ++i)
    174     CHECK(::SetEvent(wait_list[i]));
    175 
    176   CHECK_EQ(::WaitForSingleObject(finish_event, INFINITE), WAIT_OBJECT_0);
    177   CHECK(::CloseHandle(finish_event));
    178 
    179   return SBOX_TEST_SUCCEEDED;
    180 }
    181 
    182 TEST(HandleCloserTest, RunThreadPool) {
    183   TestRunner runner;
    184   runner.SetTimeout(2000);
    185   runner.SetTestState(AFTER_REVERT);
    186   sandbox::TargetPolicy* policy = runner.GetPolicy();
    187 
    188   // Sever the CSRSS connection by closing ALPC ports inside the sandbox.
    189   CHECK_EQ(policy->AddKernelObjectToClose(L"ALPC Port", NULL), SBOX_ALL_OK);
    190 
    191   EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"RunThreadPool"));
    192 }
    193 
    194 }  // namespace sandbox
    195