| OLD | NEW |
| (Empty) |
| 1 // Copyright 2007-2010 Google Inc. | |
| 2 // | |
| 3 // Licensed under the Apache License, Version 2.0 (the "License"); | |
| 4 // you may not use this file except in compliance with the License. | |
| 5 // You may obtain a copy of the License at | |
| 6 // | |
| 7 // http://www.apache.org/licenses/LICENSE-2.0 | |
| 8 // | |
| 9 // Unless required by applicable law or agreed to in writing, software | |
| 10 // distributed under the License is distributed on an "AS IS" BASIS, | |
| 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 12 // See the License for the specific language governing permissions and | |
| 13 // limitations under the License. | |
| 14 // ======================================================================== | |
| 15 | |
| 16 #include <string> | |
| 17 #include "omaha/base/app_util.h" | |
| 18 #include "omaha/base/atl_regexp.h" | |
| 19 #include "omaha/base/constants.h" | |
| 20 #include "omaha/base/const_addresses.h" | |
| 21 #include "omaha/base/const_object_names.h" | |
| 22 #include "omaha/base/error.h" | |
| 23 #include "omaha/base/file.h" | |
| 24 #include "omaha/base/scoped_any.h" | |
| 25 #include "omaha/base/time.h" | |
| 26 #include "omaha/base/user_info.h" | |
| 27 #include "omaha/base/utils.h" | |
| 28 #include "omaha/base/vistautil.h" | |
| 29 #include "omaha/common/config_manager.h" | |
| 30 #include "omaha/common/event_logger.h" | |
| 31 #include "omaha/common/goopdate_utils.h" | |
| 32 #include "omaha/goopdate/crash.h" | |
| 33 #include "omaha/testing/unit_test.h" | |
| 34 | |
| 35 // TODO(omaha): Modify the tests to avoid writing files to the staging | |
| 36 // directory, which should not be modified after building. | |
| 37 | |
| 38 using google_breakpad::ClientInfo; | |
| 39 using google_breakpad::CustomClientInfo; | |
| 40 using google_breakpad::CustomInfoEntry; | |
| 41 using google_breakpad::ExceptionHandler; | |
| 42 | |
| 43 namespace omaha { | |
| 44 | |
| 45 namespace { | |
| 46 | |
| 47 const TCHAR kMiniDumpFilename[] = _T("minidump.dmp"); | |
| 48 const TCHAR kCustomInfoFilename[] = _T("minidump.txt"); | |
| 49 const TCHAR kTestFilenamePattern[] = _T("minidump.*"); | |
| 50 | |
| 51 } // namespace | |
| 52 | |
| 53 class CrashTest : public testing::Test { | |
| 54 protected: | |
| 55 // Initialize the crash reporting for the machine case. The user case is | |
| 56 // simpler and specific tests can reinitialize for the user case if needed. | |
| 57 virtual void SetUp() { | |
| 58 module_dir_ = app_util::GetModuleDirectory(NULL); | |
| 59 EXPECT_HRESULT_SUCCEEDED(Crash::Initialize(true)); | |
| 60 } | |
| 61 | |
| 62 virtual void TearDown() { | |
| 63 EXPECT_HRESULT_SUCCEEDED(DeleteDirectory(Crash::crash_dir_)); | |
| 64 } | |
| 65 | |
| 66 static void CallbackHelper(const wchar_t* dump_path, | |
| 67 const wchar_t* minidump_id) { | |
| 68 CString postfix_string(kCrashVersionPostfixString); | |
| 69 postfix_string.Append(_T(".ut")); | |
| 70 CString crash_filename; | |
| 71 crash_filename.Format(_T("%s\\%s.dmp"), dump_path, minidump_id); | |
| 72 Crash::set_max_reports_per_day(kMaxReportsPerDayFromUnittests); | |
| 73 Crash::set_version_postfix(postfix_string); | |
| 74 Crash::set_crash_report_url(kUrlCrashReport); | |
| 75 EXPECT_SUCCEEDED(Crash::Report(true, crash_filename, CString(), false)); | |
| 76 } | |
| 77 | |
| 78 static bool MinidumpCallback(const wchar_t* dump_path, | |
| 79 const wchar_t* minidump_id, | |
| 80 void* context, | |
| 81 EXCEPTION_POINTERS*, | |
| 82 MDRawAssertionInfo*, | |
| 83 bool succeeded) { | |
| 84 EXPECT_TRUE(dump_path); | |
| 85 EXPECT_TRUE(minidump_id); | |
| 86 EXPECT_TRUE(!context); | |
| 87 EXPECT_SUCCEEDED(succeeded); | |
| 88 | |
| 89 CallbackHelper(dump_path, minidump_id); | |
| 90 return true; | |
| 91 } | |
| 92 | |
| 93 static void BuildPipeSecurityAttributesTest(bool is_machine) { | |
| 94 CSecurityAttributes pipe_sec_attrs; | |
| 95 EXPECT_TRUE( | |
| 96 Crash::BuildPipeSecurityAttributes(is_machine, &pipe_sec_attrs)); | |
| 97 LPVOID sec_desc(pipe_sec_attrs.lpSecurityDescriptor); | |
| 98 CSecurityDesc sd1(*reinterpret_cast<SECURITY_DESCRIPTOR*>(sec_desc)); | |
| 99 CString sddl1; | |
| 100 | |
| 101 sd1.ToString(&sddl1, OWNER_SECURITY_INFORMATION | | |
| 102 GROUP_SECURITY_INFORMATION | | |
| 103 DACL_SECURITY_INFORMATION | | |
| 104 SACL_SECURITY_INFORMATION | | |
| 105 LABEL_SECURITY_INFORMATION); | |
| 106 | |
| 107 CSecurityDesc sd2; | |
| 108 EXPECT_TRUE(Crash::AddPipeSecurityDaclToDesc(is_machine, &sd2)); | |
| 109 EXPECT_HRESULT_SUCCEEDED( | |
| 110 vista_util::AddLowIntegritySaclToExistingDesc(&sd2)); | |
| 111 | |
| 112 CString sddl2; | |
| 113 sd2.ToString(&sddl2, OWNER_SECURITY_INFORMATION | | |
| 114 GROUP_SECURITY_INFORMATION | | |
| 115 DACL_SECURITY_INFORMATION | | |
| 116 SACL_SECURITY_INFORMATION | | |
| 117 LABEL_SECURITY_INFORMATION); | |
| 118 | |
| 119 EXPECT_STREQ(sddl2, sddl1); | |
| 120 | |
| 121 if (vista_util::IsVistaOrLater()) { | |
| 122 // The low integrity SACL is at the end of the SDDL string. | |
| 123 EXPECT_STREQ(LOW_INTEGRITY_SDDL_SACL, | |
| 124 sddl1.Right(arraysize(LOW_INTEGRITY_SDDL_SACL) - 1)); | |
| 125 } | |
| 126 } | |
| 127 | |
| 128 static HRESULT StartSenderWithCommandLine(CString* cmd_line) { | |
| 129 return Crash::StartSenderWithCommandLine(cmd_line); | |
| 130 } | |
| 131 | |
| 132 // Returns the strings of the last Update2 event in the event log. | |
| 133 static CString GetLastCrashEventStrings() { | |
| 134 const size_t kBufferSize = 1024; | |
| 135 uint8 buffer[kBufferSize] = {0}; | |
| 136 EVENTLOGRECORD* rec = reinterpret_cast<EVENTLOGRECORD*>(buffer); | |
| 137 | |
| 138 rec->Length = kBufferSize; | |
| 139 EXPECT_SUCCEEDED(EventLogger::ReadLastEvent(_T("Update2"), rec)); | |
| 140 EXPECT_EQ(kCrashUploadEventId, rec->EventID); | |
| 141 | |
| 142 const TCHAR* strings = reinterpret_cast<const TCHAR*>( | |
| 143 (reinterpret_cast<uint8*>(buffer + rec->StringOffset))); | |
| 144 | |
| 145 return CString(strings); | |
| 146 } | |
| 147 | |
| 148 CString module_dir_; | |
| 149 | |
| 150 static const int kMaxReportsPerDayFromUnittests = INT_MAX; | |
| 151 }; | |
| 152 | |
| 153 TEST_F(CrashTest, CreateCustomInfoFile) { | |
| 154 CString expected_custom_info_file_path; | |
| 155 expected_custom_info_file_path.Format(_T("%s\\%s"), | |
| 156 module_dir_, kCustomInfoFilename); | |
| 157 | |
| 158 CString crash_filename; | |
| 159 crash_filename.Format(_T("%s\\%s"), module_dir_, kMiniDumpFilename); | |
| 160 CustomInfoEntry info_entry(_T("foo"), _T("bar")); | |
| 161 CustomClientInfo custom_client_info = {&info_entry, 1}; | |
| 162 | |
| 163 CString actual_custom_info_filepath; | |
| 164 EXPECT_SUCCEEDED(Crash::CreateCustomInfoFile(crash_filename, | |
| 165 custom_client_info, | |
| 166 &actual_custom_info_filepath)); | |
| 167 EXPECT_STREQ(expected_custom_info_file_path, actual_custom_info_filepath); | |
| 168 EXPECT_TRUE(File::Exists(actual_custom_info_filepath)); | |
| 169 EXPECT_TRUE(::DeleteFile(actual_custom_info_filepath)); | |
| 170 | |
| 171 // Tests an invalid file name. | |
| 172 EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_INVALID_NAME), | |
| 173 Crash::CreateCustomInfoFile(_T("C:\\\"minidump.dmp"), | |
| 174 custom_client_info, | |
| 175 &actual_custom_info_filepath)); | |
| 176 } | |
| 177 | |
| 178 // Tests sending an Omaha crash. | |
| 179 TEST_F(CrashTest, Report_OmahaCrash) { | |
| 180 CString crash_filename; | |
| 181 crash_filename.Format(_T("%s\\%s"), module_dir_, kMiniDumpFilename); | |
| 182 | |
| 183 ::DeleteFile(crash_filename); | |
| 184 | |
| 185 EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), | |
| 186 Crash::Report(true, crash_filename, _T(""), _T(""))); | |
| 187 | |
| 188 // Copy the minidump and the corresponding info file. | |
| 189 CString test_dir; | |
| 190 test_dir.Format(_T("%s\\unittest_support"), module_dir_); | |
| 191 ASSERT_SUCCEEDED(File::CopyWildcards(test_dir, // From. | |
| 192 module_dir_, // To. | |
| 193 kTestFilenamePattern, | |
| 194 true)); | |
| 195 | |
| 196 ASSERT_TRUE(File::Exists(crash_filename)); | |
| 197 | |
| 198 ASSERT_SUCCEEDED(Crash::Report(true, crash_filename, _T(""), _T(""))); | |
| 199 | |
| 200 // The crash artifacts should be deleted after the crash is reported. | |
| 201 EXPECT_FALSE(File::Exists(crash_filename)); | |
| 202 } | |
| 203 | |
| 204 // Tests sending an out-of-process crash. | |
| 205 // This test will write an entry with the source "Update2" in the Event Log. | |
| 206 TEST_F(CrashTest, Report_ProductCrash) { | |
| 207 CString crash_filename; | |
| 208 CString custom_info_filename; | |
| 209 crash_filename.Format(_T("%s\\%s"), module_dir_, kMiniDumpFilename); | |
| 210 custom_info_filename.Format(_T("%s\\%s"), module_dir_, kCustomInfoFilename); | |
| 211 | |
| 212 ::DeleteFile(crash_filename); | |
| 213 ::DeleteFile(custom_info_filename); | |
| 214 | |
| 215 EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), | |
| 216 Crash::Report(true, crash_filename, custom_info_filename, _T(""))); | |
| 217 | |
| 218 // Copy the minidump and the corresponding info file. | |
| 219 CString test_dir; | |
| 220 test_dir.Format(_T("%s\\unittest_support"), module_dir_); | |
| 221 ASSERT_SUCCEEDED(File::CopyWildcards(test_dir, // From. | |
| 222 module_dir_, // To. | |
| 223 kTestFilenamePattern, | |
| 224 true)); | |
| 225 | |
| 226 ASSERT_TRUE(File::Exists(crash_filename)); | |
| 227 ASSERT_TRUE(File::Exists(custom_info_filename)); | |
| 228 | |
| 229 ASSERT_SUCCEEDED(Crash::Report(true, crash_filename, | |
| 230 custom_info_filename, _T(""))); | |
| 231 | |
| 232 // Check the 'crash uploaded' event log. | |
| 233 const CString strings = GetLastCrashEventStrings(); | |
| 234 | |
| 235 // Verify that the strings include the Id token. | |
| 236 AtlRE crash_id_regex(_T("Id={\\h+}.")); | |
| 237 CString crash_id; | |
| 238 EXPECT_TRUE(AtlRE::PartialMatch(strings, crash_id_regex, &crash_id)); | |
| 239 | |
| 240 // The crash artifacts should be deleted after the crash is reported. | |
| 241 EXPECT_FALSE(File::Exists(crash_filename)); | |
| 242 EXPECT_FALSE(File::Exists(custom_info_filename)); | |
| 243 } | |
| 244 | |
| 245 // Tests generation of a minidump and uploading it to the staging server. | |
| 246 TEST_F(CrashTest, WriteMinidump) { | |
| 247 ASSERT_TRUE(!Crash::crash_dir_.IsEmpty()); | |
| 248 | |
| 249 MINIDUMP_TYPE dump_type = MiniDumpNormal; | |
| 250 ExceptionHandler handler(Crash::crash_dir_.GetString(), NULL, | |
| 251 &MinidumpCallback, NULL, | |
| 252 ExceptionHandler::HANDLER_NONE, | |
| 253 dump_type, NULL, NULL); | |
| 254 ASSERT_TRUE(handler.WriteMinidump()); | |
| 255 } | |
| 256 | |
| 257 // Tests the retrieval of the exception information from an existing mini dump. | |
| 258 TEST_F(CrashTest, GetExceptionInfo) { | |
| 259 const uint32 kExceptionAddress = 0x12345670; | |
| 260 const uint32 kExceptionCode = 0xc0000005; | |
| 261 | |
| 262 CString filename; | |
| 263 filename.AppendFormat(_T("%s\\unittest_support\\%s"), | |
| 264 module_dir_, kMiniDumpFilename); | |
| 265 MINIDUMP_EXCEPTION ex_info = {0}; | |
| 266 ASSERT_SUCCEEDED(Crash::GetExceptionInfo(filename, &ex_info)); | |
| 267 EXPECT_EQ(kExceptionAddress, ex_info.ExceptionAddress); | |
| 268 EXPECT_EQ(kExceptionCode, ex_info.ExceptionCode); | |
| 269 } | |
| 270 | |
| 271 TEST_F(CrashTest, IsCrashReportProcess) { | |
| 272 // Clear the environment variable. | |
| 273 ::SetEnvironmentVariable(kNoCrashHandlerEnvVariableName, NULL); | |
| 274 | |
| 275 bool is_crash_report_process = false; | |
| 276 EXPECT_SUCCEEDED(Crash::IsCrashReportProcess(&is_crash_report_process)); | |
| 277 EXPECT_FALSE(is_crash_report_process); | |
| 278 | |
| 279 EXPECT_TRUE(::SetEnvironmentVariable(kNoCrashHandlerEnvVariableName, | |
| 280 _T("1"))); | |
| 281 is_crash_report_process = false; | |
| 282 EXPECT_SUCCEEDED(Crash::IsCrashReportProcess(&is_crash_report_process)); | |
| 283 EXPECT_TRUE(is_crash_report_process); | |
| 284 | |
| 285 // Clear the environment variable. | |
| 286 EXPECT_TRUE(::SetEnvironmentVariable(kNoCrashHandlerEnvVariableName, NULL)); | |
| 287 } | |
| 288 | |
| 289 TEST_F(CrashTest, GetProductName) { | |
| 290 Crash::ParameterMap parameters; | |
| 291 EXPECT_STREQ(SHORT_COMPANY_NAME _T(" Error Reporting"), | |
| 292 Crash::GetProductName(parameters)); | |
| 293 | |
| 294 parameters[_T("prod")] = _T("Update2"); | |
| 295 EXPECT_STREQ(_T("Update2"), Crash::GetProductName(parameters)); | |
| 296 } | |
| 297 | |
| 298 TEST_F(CrashTest, SaveLastCrash) { | |
| 299 // Copy a test file into the module directory to use as a crash file. | |
| 300 CString test_file; // The unit test support file. | |
| 301 CString crash_file; // The crash file to be backed up. | |
| 302 test_file.AppendFormat(_T("%s\\unittest_support\\%s"), | |
| 303 module_dir_, kMiniDumpFilename); | |
| 304 crash_file.AppendFormat(_T("%s\\%s"), Crash::crash_dir_, kMiniDumpFilename); | |
| 305 EXPECT_TRUE(File::Exists(test_file)); | |
| 306 EXPECT_TRUE(::CopyFile(test_file, crash_file, false)); | |
| 307 EXPECT_TRUE(File::Exists(crash_file)); | |
| 308 | |
| 309 EXPECT_HRESULT_SUCCEEDED(Crash::SaveLastCrash(crash_file, _T("test"))); | |
| 310 | |
| 311 CString saved_crash_file; // The name of backup crash file. | |
| 312 saved_crash_file.AppendFormat(_T("%s\\test-last.dmp"), Crash::crash_dir_); | |
| 313 EXPECT_TRUE(File::Exists(saved_crash_file)); | |
| 314 | |
| 315 EXPECT_TRUE(::DeleteFile(saved_crash_file)); | |
| 316 } | |
| 317 | |
| 318 TEST_F(CrashTest, StartServer) { | |
| 319 // Terminate all processes to avoid conflicts on the crash services pipe. | |
| 320 TerminateAllGoogleUpdateProcesses(); | |
| 321 | |
| 322 EXPECT_HRESULT_SUCCEEDED(Crash::StartServer()); | |
| 323 | |
| 324 // Try opening the crash services pipe. | |
| 325 CString user_sid; | |
| 326 EXPECT_HRESULT_SUCCEEDED(user_info::GetProcessUser(NULL, NULL, &user_sid)); | |
| 327 CString pipe_name; | |
| 328 pipe_name.AppendFormat(_T("\\\\.\\pipe\\%sCrashServices\\%s"), | |
| 329 SHORT_COMPANY_NAME, user_sid); | |
| 330 scoped_pipe pipe_handle(::CreateFile(pipe_name, | |
| 331 GENERIC_READ | GENERIC_WRITE, | |
| 332 FILE_SHARE_READ | FILE_SHARE_WRITE, | |
| 333 NULL, | |
| 334 OPEN_EXISTING, | |
| 335 0, | |
| 336 NULL)); | |
| 337 EXPECT_TRUE(pipe_handle); | |
| 338 | |
| 339 Crash::StopServer(); | |
| 340 } | |
| 341 | |
| 342 TEST_F(CrashTest, CleanStaleCrashes) { | |
| 343 // Copy a test file into the module directory to use as a crash file. | |
| 344 CString test_file; // The unit test support file. | |
| 345 CString crash_file; // The crash file to be backed up. | |
| 346 test_file.AppendFormat(_T("%s\\unittest_support\\%s"), | |
| 347 module_dir_, kMiniDumpFilename); | |
| 348 crash_file.AppendFormat(_T("%s\\%s.dmp"), | |
| 349 Crash::crash_dir_, | |
| 350 _T("5695F1E0-95BD-4bc2-99C0-E9DCC0AC5274")); | |
| 351 EXPECT_TRUE(File::Exists(test_file)); | |
| 352 EXPECT_TRUE(::CopyFile(test_file, crash_file, false)); | |
| 353 EXPECT_TRUE(File::Exists(crash_file)); | |
| 354 | |
| 355 FILETIME time_created = {0}; | |
| 356 time64 now = GetCurrent100NSTime(); | |
| 357 | |
| 358 // Create a time value 23 hours in the past. Expect the crash file remains. | |
| 359 Time64ToFileTime(now - 23 * kHoursTo100ns, &time_created); | |
| 360 EXPECT_HRESULT_SUCCEEDED(File::SetFileTime(crash_file, &time_created, | |
| 361 NULL, NULL)); | |
| 362 Crash::CleanStaleCrashes(); | |
| 363 EXPECT_TRUE(File::Exists(crash_file)); | |
| 364 | |
| 365 // Create a time value 25 hours in the past. Expect the crash file is deleted. | |
| 366 Time64ToFileTime(now - 25 * kHoursTo100ns, &time_created); | |
| 367 EXPECT_HRESULT_SUCCEEDED(File::SetFileTime(crash_file, &time_created, | |
| 368 NULL, NULL)); | |
| 369 Crash::CleanStaleCrashes(); | |
| 370 EXPECT_FALSE(File::Exists(crash_file)); | |
| 371 } | |
| 372 | |
| 373 // Installs and uninstalls the crash handler in the user case. | |
| 374 TEST_F(CrashTest, InstallCrashHandler) { | |
| 375 EXPECT_HRESULT_SUCCEEDED(Crash::InstallCrashHandler(false)); | |
| 376 Crash::UninstallCrashHandler(); | |
| 377 } | |
| 378 | |
| 379 // Makes sure that the security descriptor that BuildPipeSecurityAttributes | |
| 380 // creates matches the security descriptor built by adding the DACL first, and | |
| 381 // then using AddLowIntegritySaclToExistingDesc(). The latter method uses an | |
| 382 // approach similar to what is documented in MSDN: | |
| 383 // http://msdn.microsoft.com/en-us/library/bb625960.aspx | |
| 384 // | |
| 385 // Also, makes sure that the security descriptor that | |
| 386 // BuildPipeSecurityAttributes creates has the low integrity SACL within it. | |
| 387 TEST_F(CrashTest, BuildPipeSecurityAttributes) { | |
| 388 BuildPipeSecurityAttributesTest(true); | |
| 389 BuildPipeSecurityAttributesTest(false); | |
| 390 } | |
| 391 | |
| 392 TEST_F(CrashTest, StartSenderWithCommandLine) { | |
| 393 // Negative test. | |
| 394 CString filename(_T("DoesNotExist.exe")); | |
| 395 EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), | |
| 396 StartSenderWithCommandLine(&filename)); | |
| 397 } | |
| 398 | |
| 399 } // namespace omaha | |
| 400 | |
| OLD | NEW |