Chromium Code Reviews
|
| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2009 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 "chrome_frame/crash_reporting/nt_loader.h" | |
| 5 | |
| 6 #include <tlhelp32.h> | |
| 7 #include <winnt.h> | |
| 8 #include <base/at_exit.h> | |
| 9 #include <base/scoped_handle.h> | |
| 10 #include <base/sys_info.h> | |
| 11 #include <base/thread.h> | |
| 12 #include "chrome_frame/crash_reporting/crash_dll.h" | |
| 13 #include "gtest/gtest.h" | |
| 14 | |
| 15 namespace { | |
| 16 void AssertIsCriticalSection(CRITICAL_SECTION* critsec) { | |
| 17 // Assert on some of the internals of the debug info if it has one. | |
| 18 RTL_CRITICAL_SECTION_DEBUG* debug = critsec->DebugInfo; | |
| 19 if (debug) { | |
| 20 ASSERT_EQ(RTL_CRITSECT_TYPE, debug->Type); | |
| 21 ASSERT_EQ(critsec, debug->CriticalSection); | |
| 22 } | |
| 23 | |
| 24 // TODO(siggi): assert on the semaphore handle & object type? | |
| 25 } | |
| 26 | |
| 27 class ScopedEnterCriticalSection { | |
| 28 public: | |
| 29 ScopedEnterCriticalSection(CRITICAL_SECTION* critsec) : critsec_(critsec) { | |
| 30 ::EnterCriticalSection(critsec_); | |
| 31 } | |
| 32 | |
| 33 ~ScopedEnterCriticalSection() { | |
| 34 ::LeaveCriticalSection(critsec_); | |
| 35 } | |
| 36 | |
| 37 private: | |
| 38 CRITICAL_SECTION* critsec_; | |
| 39 }; | |
| 40 | |
| 41 std::wstring FromUnicodeString(const UNICODE_STRING& str) { | |
| 42 return std::wstring(str.Buffer, str.Length / sizeof(str.Buffer[0])); | |
| 43 } | |
| 44 | |
| 45 } // namespace | |
| 46 | |
| 47 using namespace nt_loader; | |
| 48 | |
| 49 TEST(NtLoader, OwnsCriticalSection) { | |
| 50 // Use of Thread requires an atexit manager. | |
| 51 base::AtExitManager at_exit; | |
| 52 | |
| 53 CRITICAL_SECTION cs = {}; | |
| 54 ::InitializeCriticalSection(&cs); | |
| 55 EXPECT_FALSE(OwnsCriticalSection(&cs)); | |
| 56 | |
| 57 // Enter the critsec and assert we own it. | |
| 58 { | |
| 59 ScopedEnterCriticalSection lock1(&cs); | |
| 60 | |
| 61 EXPECT_TRUE(OwnsCriticalSection(&cs)); | |
| 62 | |
| 63 // Re-enter the critsec and assert we own it. | |
| 64 ScopedEnterCriticalSection lock2(&cs); | |
| 65 | |
| 66 EXPECT_TRUE(OwnsCriticalSection(&cs)); | |
| 67 } | |
| 68 | |
| 69 // Should no longer own it. | |
| 70 EXPECT_FALSE(OwnsCriticalSection(&cs)); | |
|
robertshield
2010/03/11 18:30:38
I believe this should be an ASSERT_FALSE. Reason b
Sigurður Ásgeirsson
2010/03/11 18:40:21
Well, the ownership of the CS is not in question h
| |
| 71 | |
| 72 // Make another thread grab it. | |
| 73 base::Thread other("Other threads"); | |
| 74 ASSERT_TRUE(other.Start()); | |
| 75 other.message_loop()->PostTask( | |
| 76 FROM_HERE, NewRunnableFunction(::EnterCriticalSection, &cs)); | |
| 77 | |
| 78 ScopedHandle event(::CreateEvent(NULL, FALSE, FALSE, NULL)); | |
| 79 other.message_loop()->PostTask( | |
| 80 FROM_HERE, NewRunnableFunction(::SetEvent, event.Get())); | |
| 81 | |
| 82 ASSERT_EQ(WAIT_OBJECT_0, ::WaitForSingleObject(event.Get(), INFINITE)); | |
| 83 | |
| 84 // We still shouldn't own it - the other thread does. | |
| 85 EXPECT_FALSE(OwnsCriticalSection(&cs)); | |
| 86 // And we shouldn't be able to enter it. | |
| 87 EXPECT_EQ(0, ::TryEnterCriticalSection(&cs)); | |
|
robertshield
2010/03/11 18:30:38
ASSERT here too? should we Leave it if this succee
Sigurður Ásgeirsson
2010/03/11 18:40:21
Yeah, same deal, this is more to verify that I did
| |
| 88 | |
| 89 // Make the other thread release it. | |
| 90 other.message_loop()->PostTask( | |
| 91 FROM_HERE, NewRunnableFunction(::LeaveCriticalSection, &cs)); | |
| 92 | |
| 93 other.Stop(); | |
| 94 | |
| 95 ::DeleteCriticalSection(&cs); | |
| 96 } | |
| 97 | |
| 98 TEST(NtLoader, GetLoaderLock) { | |
| 99 CRITICAL_SECTION* loader_lock = GetLoaderLock(); | |
| 100 | |
| 101 AssertIsCriticalSection(loader_lock); | |
| 102 | |
| 103 // We should be able to enter and leave the loader's lock without trouble. | |
| 104 EnterCriticalSection(loader_lock); | |
| 105 LeaveCriticalSection(loader_lock); | |
| 106 } | |
| 107 | |
| 108 TEST(NtLoader, OwnsLoaderLock) { | |
| 109 CRITICAL_SECTION* loader_lock = GetLoaderLock(); | |
| 110 | |
| 111 EXPECT_FALSE(OwnsLoaderLock()); | |
| 112 EnterCriticalSection(loader_lock); | |
| 113 EXPECT_TRUE(OwnsLoaderLock()); | |
| 114 LeaveCriticalSection(loader_lock); | |
| 115 EXPECT_FALSE(OwnsLoaderLock()); | |
| 116 } | |
| 117 | |
| 118 TEST(NtLoader, GetLoaderEntry) { | |
| 119 ScopedEnterCriticalSection lock(GetLoaderLock()); | |
| 120 | |
| 121 // Get all modules in the current process. | |
| 122 ScopedHandle snap(::CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, | |
| 123 ::GetCurrentProcessId())); | |
| 124 EXPECT_TRUE(snap.Get() != NULL); | |
| 125 | |
| 126 // Walk them, while checking we get an entry for each, and that it | |
| 127 // contains sane information. | |
| 128 MODULEENTRY32 module = { sizeof(module) }; | |
| 129 ASSERT_TRUE(::Module32First(snap.Get(), &module)); | |
| 130 do { | |
| 131 nt_loader::LDR_DATA_TABLE_ENTRY* entry = | |
| 132 nt_loader::GetLoaderEntry(module.hModule); | |
| 133 ASSERT_TRUE(entry != NULL); | |
| 134 ASSERT_EQ(module.hModule, reinterpret_cast<HMODULE>(entry->DllBase)); | |
| 135 ASSERT_STREQ(module.szModule, | |
| 136 FromUnicodeString(entry->BaseDllName).c_str()); | |
| 137 ASSERT_STREQ(module.szExePath, | |
| 138 FromUnicodeString(entry->FullDllName).c_str()); | |
| 139 } while(::Module32Next(snap.Get(), &module)); | |
| 140 } | |
| 141 | |
| 142 namespace { | |
| 143 | |
| 144 typedef void (*ExceptionFunction)(EXCEPTION_POINTERS* ex_ptrs); | |
| 145 | |
| 146 class NtLoaderTest: public testing::Test { | |
| 147 public: | |
| 148 NtLoaderTest() : veh_id_(NULL), exception_function_(NULL) { | |
| 149 EXPECT_EQ(NULL, current_); | |
| 150 current_ = this; | |
| 151 } | |
| 152 | |
| 153 ~NtLoaderTest() { | |
| 154 EXPECT_TRUE(this == current_); | |
| 155 current_ = NULL; | |
| 156 } | |
| 157 | |
| 158 void SetUp() { | |
| 159 veh_id_ = ::AddVectoredExceptionHandler(FALSE, &ExceptionHandler); | |
| 160 EXPECT_TRUE(veh_id_ != NULL); | |
| 161 | |
| 162 // Clear the crash DLL environment. | |
| 163 ::SetEnvironmentVariable(kCrashOnLoadMode, NULL); | |
| 164 ::SetEnvironmentVariable(kCrashOnUnloadMode, NULL); | |
| 165 } | |
| 166 | |
| 167 void TearDown() { | |
| 168 if (veh_id_ != NULL) | |
| 169 EXPECT_NE(0, ::RemoveVectoredExceptionHandler(veh_id_)); | |
| 170 | |
| 171 // Clear the crash DLL environment. | |
| 172 ::SetEnvironmentVariable(kCrashOnLoadMode, NULL); | |
| 173 ::SetEnvironmentVariable(kCrashOnUnloadMode, NULL); | |
| 174 } | |
| 175 | |
| 176 void set_exception_function(ExceptionFunction func) { | |
| 177 exception_function_ = func; | |
| 178 } | |
| 179 | |
| 180 private: | |
| 181 static LONG NTAPI ExceptionHandler(EXCEPTION_POINTERS* ex_ptrs){ | |
| 182 // Dispatch the exception to any exception function, | |
| 183 // but only on the main thread. | |
| 184 if (main_thread_ == ::GetCurrentThreadId() && | |
| 185 current_ != NULL && | |
| 186 current_->exception_function_ != NULL) | |
| 187 current_->exception_function_(ex_ptrs); | |
| 188 | |
| 189 return ExceptionContinueSearch; | |
| 190 } | |
| 191 | |
| 192 void* veh_id_; | |
| 193 ExceptionFunction exception_function_; | |
| 194 | |
| 195 static NtLoaderTest* current_; | |
| 196 static DWORD main_thread_; | |
| 197 }; | |
| 198 | |
| 199 NtLoaderTest* NtLoaderTest::current_ = NULL; | |
| 200 DWORD NtLoaderTest::main_thread_ = ::GetCurrentThreadId(); | |
| 201 | |
| 202 const wchar_t kCrashDllName[] = L"crash_dll.dll"; | |
| 203 | |
| 204 } // namespace | |
| 205 | |
| 206 static int exceptions_handled = 0; | |
| 207 static void OnCrashDuringLoadLibrary(EXCEPTION_POINTERS* ex_ptrs) { | |
| 208 ASSERT_EQ(STATUS_ACCESS_VIOLATION, ex_ptrs->ExceptionRecord->ExceptionCode); | |
| 209 ASSERT_EQ(2, ex_ptrs->ExceptionRecord->NumberParameters); | |
| 210 ASSERT_EQ(EXCEPTION_WRITE_FAULT, | |
| 211 ex_ptrs->ExceptionRecord->ExceptionInformation[0]); | |
| 212 ASSERT_EQ(kCrashAddress, | |
| 213 ex_ptrs->ExceptionRecord->ExceptionInformation[1]); | |
| 214 | |
| 215 // Bump the exceptions count. | |
| 216 exceptions_handled++; | |
| 217 | |
| 218 EXPECT_TRUE(OwnsLoaderLock()); | |
| 219 | |
| 220 HMODULE crash_dll = ::GetModuleHandle(kCrashDllName); | |
| 221 ASSERT_TRUE(crash_dll != NULL); | |
| 222 | |
| 223 nt_loader::LDR_DATA_TABLE_ENTRY* entry = GetLoaderEntry(crash_dll); | |
| 224 ASSERT_TRUE(entry != NULL); | |
| 225 ASSERT_EQ(0, entry->Flags & LDRP_PROCESS_ATTACH_CALLED); | |
| 226 } | |
| 227 | |
| 228 TEST_F(NtLoaderTest, CrashOnLoadLibrary) { | |
| 229 exceptions_handled = 0; | |
| 230 set_exception_function(OnCrashDuringLoadLibrary); | |
| 231 | |
| 232 // Setup to crash on load. | |
| 233 ::SetEnvironmentVariable(kCrashOnLoadMode, L"1"); | |
| 234 | |
| 235 // And load it. | |
| 236 HMODULE module = ::LoadLibrary(kCrashDllName); | |
| 237 DWORD err = ::GetLastError(); | |
| 238 EXPECT_EQ(NULL, module); | |
| 239 EXPECT_EQ(ERROR_NOACCESS, err); | |
| 240 EXPECT_EQ(1, exceptions_handled); | |
| 241 | |
| 242 if (module != NULL) | |
| 243 ::FreeLibrary(module); | |
| 244 } | |
| 245 | |
| 246 static void OnCrashDuringUnloadLibrary(EXCEPTION_POINTERS* ex_ptrs) { | |
| 247 ASSERT_EQ(STATUS_ACCESS_VIOLATION, ex_ptrs->ExceptionRecord->ExceptionCode); | |
| 248 ASSERT_EQ(2, ex_ptrs->ExceptionRecord->NumberParameters); | |
| 249 ASSERT_EQ(EXCEPTION_WRITE_FAULT, | |
| 250 ex_ptrs->ExceptionRecord->ExceptionInformation[0]); | |
| 251 ASSERT_EQ(kCrashAddress, | |
| 252 ex_ptrs->ExceptionRecord->ExceptionInformation[1]); | |
| 253 | |
| 254 // Bump the exceptions count. | |
| 255 exceptions_handled++; | |
| 256 | |
| 257 EXPECT_TRUE(OwnsLoaderLock()); | |
| 258 | |
| 259 HMODULE crash_dll = ::GetModuleHandle(kCrashDllName); | |
| 260 ASSERT_TRUE(crash_dll == NULL); | |
| 261 | |
| 262 nt_loader::LDR_DATA_TABLE_ENTRY* entry = GetLoaderEntry(crash_dll); | |
| 263 ASSERT_TRUE(entry == NULL); | |
| 264 } | |
| 265 | |
| 266 TEST_F(NtLoaderTest, CrashOnUnloadLibrary) { | |
| 267 // Setup to crash on unload. | |
| 268 ::SetEnvironmentVariable(kCrashOnUnloadMode, L"1"); | |
| 269 | |
| 270 // And load it. | |
| 271 HMODULE module = ::LoadLibrary(kCrashDllName); | |
| 272 EXPECT_TRUE(module != NULL); | |
| 273 | |
| 274 exceptions_handled = 0; | |
| 275 set_exception_function(OnCrashDuringUnloadLibrary); | |
| 276 | |
| 277 // We should crash during unload. | |
| 278 if (module != NULL) | |
| 279 ::FreeLibrary(module); | |
| 280 | |
| 281 EXPECT_EQ(1, exceptions_handled); | |
|
robertshield
2010/03/11 18:30:38
cool tests :)
Sigurður Ásgeirsson
2010/03/11 18:40:21
Thanks, but the CRT bug is totally uncool, and it
| |
| 282 } | |
| OLD | NEW |