189 lines
5.9 KiB
C++
189 lines
5.9 KiB
C++
|
|
// Copyright 2011 The Chromium Authors
|
||
|
|
// Use of this source code is governed by a BSD-style license that can be
|
||
|
|
// found in the LICENSE file.
|
||
|
|
|
||
|
|
#include <windows.h>
|
||
|
|
|
||
|
|
#include <winternl.h>
|
||
|
|
|
||
|
|
#include <string>
|
||
|
|
#include <utility>
|
||
|
|
|
||
|
|
#include "base/base_switches.h"
|
||
|
|
#include "base/command_line.h"
|
||
|
|
#include "base/files/file_path.h"
|
||
|
|
#include "base/scoped_native_library.h"
|
||
|
|
#include "base/test/multiprocess_test.h"
|
||
|
|
#include "base/test/test_timeouts.h"
|
||
|
|
#include "base/win/scoped_handle.h"
|
||
|
|
#include "build/build_config.h"
|
||
|
|
#include "testing/gtest/include/gtest/gtest.h"
|
||
|
|
#include "testing/multiprocess_func_list.h"
|
||
|
|
|
||
|
|
namespace base {
|
||
|
|
namespace win {
|
||
|
|
|
||
|
|
namespace {
|
||
|
|
|
||
|
|
std::string FailureMessage(const std::string& msg) {
|
||
|
|
#if defined(NDEBUG) && defined(OFFICIAL_BUILD)
|
||
|
|
// Official release builds strip all fatal messages for saving binary size,
|
||
|
|
// see base/check.h.
|
||
|
|
return "";
|
||
|
|
#else
|
||
|
|
return msg;
|
||
|
|
#endif // defined(NDEBUG) && defined(OFFICIAL_BUILD)
|
||
|
|
}
|
||
|
|
|
||
|
|
} // namespace
|
||
|
|
|
||
|
|
namespace testing {
|
||
|
|
extern "C" bool __declspec(dllexport) RunTest();
|
||
|
|
} // namespace testing
|
||
|
|
|
||
|
|
using ScopedHandleTest = ::testing::Test;
|
||
|
|
using ScopedHandleDeathTest = ::testing::Test;
|
||
|
|
|
||
|
|
TEST_F(ScopedHandleTest, ScopedHandle) {
|
||
|
|
// Any illegal error code will do. We just need to test that it is preserved
|
||
|
|
// by ScopedHandle to avoid https://crbug.com/528394.
|
||
|
|
const DWORD magic_error = 0x12345678;
|
||
|
|
|
||
|
|
HANDLE handle = ::CreateMutex(nullptr, false, nullptr);
|
||
|
|
// Call SetLastError after creating the handle.
|
||
|
|
::SetLastError(magic_error);
|
||
|
|
base::win::ScopedHandle handle_holder(handle);
|
||
|
|
EXPECT_EQ(magic_error, ::GetLastError());
|
||
|
|
|
||
|
|
// Create a new handle and then set LastError again.
|
||
|
|
handle = ::CreateMutex(nullptr, false, nullptr);
|
||
|
|
::SetLastError(magic_error);
|
||
|
|
handle_holder.Set(handle);
|
||
|
|
EXPECT_EQ(magic_error, ::GetLastError());
|
||
|
|
|
||
|
|
// Create a new handle and then set LastError again.
|
||
|
|
handle = ::CreateMutex(nullptr, false, nullptr);
|
||
|
|
base::win::ScopedHandle handle_source(handle);
|
||
|
|
::SetLastError(magic_error);
|
||
|
|
handle_holder = std::move(handle_source);
|
||
|
|
EXPECT_EQ(magic_error, ::GetLastError());
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST_F(ScopedHandleDeathTest, HandleVerifierTrackedHasBeenClosed) {
|
||
|
|
HANDLE handle = ::CreateMutex(nullptr, false, nullptr);
|
||
|
|
ASSERT_NE(HANDLE(nullptr), handle);
|
||
|
|
|
||
|
|
ASSERT_DEATH(
|
||
|
|
{
|
||
|
|
base::win::ScopedHandle handle_holder(handle);
|
||
|
|
::NtClose(handle);
|
||
|
|
// Destructing a ScopedHandle with an illegally closed handle should
|
||
|
|
// fail.
|
||
|
|
},
|
||
|
|
FailureMessage("CloseHandle failed"));
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST_F(ScopedHandleDeathTest, HandleVerifierCloseTrackedHandle) {
|
||
|
|
ASSERT_DEATH(
|
||
|
|
{
|
||
|
|
HANDLE handle = ::CreateMutex(nullptr, false, nullptr);
|
||
|
|
ASSERT_NE(HANDLE(nullptr), handle);
|
||
|
|
|
||
|
|
// Start tracking the handle so that closes outside of the checker are
|
||
|
|
// caught.
|
||
|
|
base::win::CheckedScopedHandle handle_holder(handle);
|
||
|
|
|
||
|
|
// Closing a tracked handle using ::CloseHandle should crash due to hook
|
||
|
|
// noticing the illegal close.
|
||
|
|
::CloseHandle(handle);
|
||
|
|
},
|
||
|
|
// This test must match the CloseHandleHook causing this failure, because
|
||
|
|
// if the hook doesn't crash and instead the handle is double closed by
|
||
|
|
// the `handle_holder` going out of scope, then there is still a crash,
|
||
|
|
// but a different crash and one we are not explicitly testing here. This
|
||
|
|
// other crash is tested in HandleVerifierTrackedHasBeenClosed above.
|
||
|
|
FailureMessage("CloseHandleHook validation failure"));
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST_F(ScopedHandleDeathTest, HandleVerifierDoubleTracking) {
|
||
|
|
HANDLE handle = ::CreateMutex(nullptr, false, nullptr);
|
||
|
|
ASSERT_NE(HANDLE(nullptr), handle);
|
||
|
|
|
||
|
|
base::win::CheckedScopedHandle handle_holder(handle);
|
||
|
|
|
||
|
|
ASSERT_DEATH({ base::win::CheckedScopedHandle handle_holder2(handle); },
|
||
|
|
FailureMessage("Handle Already Tracked"));
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST_F(ScopedHandleDeathTest, HandleVerifierWrongOwner) {
|
||
|
|
HANDLE handle = ::CreateMutex(nullptr, false, nullptr);
|
||
|
|
ASSERT_NE(HANDLE(nullptr), handle);
|
||
|
|
|
||
|
|
base::win::CheckedScopedHandle handle_holder(handle);
|
||
|
|
ASSERT_DEATH(
|
||
|
|
{
|
||
|
|
base::win::CheckedScopedHandle handle_holder2;
|
||
|
|
handle_holder2.handle_ = handle;
|
||
|
|
},
|
||
|
|
FailureMessage("Closing a handle owned by something else"));
|
||
|
|
ASSERT_TRUE(handle_holder.is_valid());
|
||
|
|
handle_holder.Close();
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST_F(ScopedHandleDeathTest, HandleVerifierUntrackedHandle) {
|
||
|
|
HANDLE handle = ::CreateMutex(nullptr, false, nullptr);
|
||
|
|
ASSERT_NE(HANDLE(nullptr), handle);
|
||
|
|
|
||
|
|
ASSERT_DEATH(
|
||
|
|
{
|
||
|
|
base::win::CheckedScopedHandle handle_holder;
|
||
|
|
handle_holder.handle_ = handle;
|
||
|
|
},
|
||
|
|
FailureMessage("Closing an untracked handle"));
|
||
|
|
|
||
|
|
ASSERT_TRUE(::CloseHandle(handle));
|
||
|
|
}
|
||
|
|
|
||
|
|
// Under ASan, the multi-process test crashes during process shutdown for
|
||
|
|
// unknown reasons. Disable it for now. http://crbug.com/685262
|
||
|
|
#if defined(ADDRESS_SANITIZER)
|
||
|
|
#define MAYBE_MultiProcess DISABLED_MultiProcess
|
||
|
|
#else
|
||
|
|
#define MAYBE_MultiProcess MultiProcess
|
||
|
|
#endif
|
||
|
|
|
||
|
|
TEST_F(ScopedHandleTest, MAYBE_MultiProcess) {
|
||
|
|
// Initializing ICU in the child process causes a scoped handle to be created
|
||
|
|
// before the test gets a chance to test the race condition, so disable ICU
|
||
|
|
// for the child process here.
|
||
|
|
CommandLine command_line(base::GetMultiProcessTestChildBaseCommandLine());
|
||
|
|
command_line.AppendSwitch(switches::kTestDoNotInitializeIcu);
|
||
|
|
|
||
|
|
base::Process test_child_process = base::SpawnMultiProcessTestChild(
|
||
|
|
"HandleVerifierChildProcess", command_line, LaunchOptions());
|
||
|
|
|
||
|
|
int rv = -1;
|
||
|
|
ASSERT_TRUE(test_child_process.WaitForExitWithTimeout(
|
||
|
|
TestTimeouts::action_timeout(), &rv));
|
||
|
|
EXPECT_EQ(0, rv);
|
||
|
|
}
|
||
|
|
|
||
|
|
MULTIPROCESS_TEST_MAIN(HandleVerifierChildProcess) {
|
||
|
|
ScopedNativeLibrary module(
|
||
|
|
FilePath(FILE_PATH_LITERAL("scoped_handle_test_dll.dll")));
|
||
|
|
|
||
|
|
if (!module.is_valid())
|
||
|
|
return 1;
|
||
|
|
auto run_test_function = reinterpret_cast<decltype(&testing::RunTest)>(
|
||
|
|
module.GetFunctionPointer("RunTest"));
|
||
|
|
if (!run_test_function)
|
||
|
|
return 1;
|
||
|
|
if (!run_test_function())
|
||
|
|
return 1;
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
} // namespace win
|
||
|
|
} // namespace base
|