Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(339)

Side by Side Diff: chrome_frame/crash_reporting/nt_loader_unittest.cc

Issue 882001: Utility functions to interact with the NT loader's data structures and associ... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: Created 10 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
Property Changes:
Added: svn:eol-style
+ LF
OLDNEW
(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 }
OLDNEW
« chrome_frame/crash_reporting/nt_loader.cc ('K') | « chrome_frame/crash_reporting/nt_loader.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698