1 /* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include "user_collector.h" 18 19 #include <elf.h> 20 #include <sys/cdefs.h> // For __WORDSIZE 21 #include <unistd.h> 22 23 #include <base/files/file_util.h> 24 #include <base/files/scoped_temp_dir.h> 25 #include <base/strings/string_split.h> 26 #include <brillo/syslog_logging.h> 27 #include <gmock/gmock.h> 28 #include <gtest/gtest.h> 29 30 using base::FilePath; 31 using brillo::FindLog; 32 33 namespace { 34 35 int s_crashes = 0; 36 bool s_metrics = false; 37 38 const char kFilePath[] = "/my/path"; 39 40 void CountCrash() { 41 ++s_crashes; 42 } 43 44 bool IsMetrics() { 45 return s_metrics; 46 } 47 48 } // namespace 49 50 class UserCollectorMock : public UserCollector { 51 public: 52 MOCK_METHOD0(SetUpDBus, void()); 53 }; 54 55 class UserCollectorTest : public ::testing::Test { 56 void SetUp() { 57 s_crashes = 0; 58 59 EXPECT_CALL(collector_, SetUpDBus()).WillRepeatedly(testing::Return()); 60 61 collector_.Initialize(CountCrash, 62 kFilePath, 63 IsMetrics, 64 false, 65 false, 66 false, 67 ""); 68 69 EXPECT_TRUE(test_dir_.CreateUniqueTempDir()); 70 71 mkdir(test_dir_.path().Append("test").value().c_str(), 0777); 72 pid_ = getpid(); 73 brillo::ClearLog(); 74 } 75 76 protected: 77 void ExpectFileEquals(const char *golden, 78 const FilePath &file_path) { 79 std::string contents; 80 EXPECT_TRUE(base::ReadFileToString(file_path, &contents)); 81 EXPECT_EQ(golden, contents); 82 } 83 84 std::vector<std::string> SplitLines(const std::string &lines) const { 85 return base::SplitString(lines, "\n", base::TRIM_WHITESPACE, 86 base::SPLIT_WANT_ALL); 87 } 88 89 UserCollectorMock collector_; 90 pid_t pid_; 91 base::ScopedTempDir test_dir_; 92 }; 93 94 TEST_F(UserCollectorTest, ParseCrashAttributes) { 95 pid_t pid; 96 int signal; 97 uid_t uid; 98 gid_t gid; 99 std::string exec_name; 100 EXPECT_TRUE(collector_.ParseCrashAttributes("123456:11:1000:2000:foobar", 101 &pid, &signal, &uid, &gid, &exec_name)); 102 EXPECT_EQ(123456, pid); 103 EXPECT_EQ(11, signal); 104 EXPECT_EQ(1000, uid); 105 EXPECT_EQ(2000, gid); 106 EXPECT_EQ("foobar", exec_name); 107 EXPECT_TRUE(collector_.ParseCrashAttributes("4321:6:barfoo", 108 &pid, &signal, &uid, &gid, &exec_name)); 109 EXPECT_EQ(4321, pid); 110 EXPECT_EQ(6, signal); 111 EXPECT_EQ(-1, uid); 112 EXPECT_EQ(-1, gid); 113 EXPECT_EQ("barfoo", exec_name); 114 115 EXPECT_FALSE(collector_.ParseCrashAttributes("123456:11", 116 &pid, &signal, &uid, &gid, &exec_name)); 117 118 EXPECT_TRUE(collector_.ParseCrashAttributes("123456:11:exec:extra", 119 &pid, &signal, &uid, &gid, &exec_name)); 120 EXPECT_EQ("exec:extra", exec_name); 121 122 EXPECT_FALSE(collector_.ParseCrashAttributes("12345p:11:foobar", 123 &pid, &signal, &uid, &gid, &exec_name)); 124 125 EXPECT_FALSE(collector_.ParseCrashAttributes("123456:1 :foobar", 126 &pid, &signal, &uid, &gid, &exec_name)); 127 128 EXPECT_FALSE(collector_.ParseCrashAttributes("123456::foobar", 129 &pid, &signal, &uid, &gid, &exec_name)); 130 } 131 132 TEST_F(UserCollectorTest, ShouldDumpDeveloperImageOverridesConsent) { 133 std::string reason; 134 EXPECT_TRUE(collector_.ShouldDump(false, true, &reason)); 135 EXPECT_EQ("developer build - not testing - always dumping", reason); 136 137 // When running a crash test, behave as normal. 138 EXPECT_FALSE(collector_.ShouldDump(false, false, &reason)); 139 EXPECT_EQ("ignoring - no consent", reason); 140 } 141 142 TEST_F(UserCollectorTest, ShouldDumpUseConsentProductionImage) { 143 std::string result; 144 EXPECT_FALSE(collector_.ShouldDump(false, false, &result)); 145 EXPECT_EQ("ignoring - no consent", result); 146 147 EXPECT_TRUE(collector_.ShouldDump(true, false, &result)); 148 EXPECT_EQ("handling", result); 149 } 150 151 TEST_F(UserCollectorTest, HandleCrashWithoutConsent) { 152 s_metrics = false; 153 collector_.HandleCrash("20:10:ignored", "foobar"); 154 EXPECT_TRUE(FindLog( 155 "Received crash notification for foobar[20] sig 10")); 156 ASSERT_EQ(s_crashes, 0); 157 } 158 159 TEST_F(UserCollectorTest, HandleNonChromeCrashWithConsent) { 160 s_metrics = true; 161 collector_.HandleCrash("5:2:ignored", "chromeos-wm"); 162 EXPECT_TRUE(FindLog( 163 "Received crash notification for chromeos-wm[5] sig 2")); 164 ASSERT_EQ(s_crashes, 1); 165 } 166 167 TEST_F(UserCollectorTest, GetProcessPath) { 168 FilePath path = collector_.GetProcessPath(100); 169 ASSERT_EQ("/proc/100", path.value()); 170 } 171 172 TEST_F(UserCollectorTest, GetSymlinkTarget) { 173 FilePath result; 174 ASSERT_FALSE(collector_.GetSymlinkTarget(FilePath("/does_not_exist"), 175 &result)); 176 ASSERT_TRUE(FindLog( 177 "Readlink failed on /does_not_exist with 2")); 178 std::string long_link = test_dir_.path().value(); 179 for (int i = 0; i < 50; ++i) 180 long_link += "0123456789"; 181 long_link += "/gold"; 182 183 for (size_t len = 1; len <= long_link.size(); ++len) { 184 std::string this_link; 185 static const char* kLink = 186 test_dir_.path().Append("test/this_link").value().c_str(); 187 this_link.assign(long_link.c_str(), len); 188 ASSERT_EQ(len, this_link.size()); 189 unlink(kLink); 190 ASSERT_EQ(0, symlink(this_link.c_str(), kLink)); 191 ASSERT_TRUE(collector_.GetSymlinkTarget(FilePath(kLink), &result)); 192 ASSERT_EQ(this_link, result.value()); 193 } 194 } 195 196 TEST_F(UserCollectorTest, GetExecutableBaseNameFromPid) { 197 std::string base_name; 198 EXPECT_FALSE(collector_.GetExecutableBaseNameFromPid(0, &base_name)); 199 EXPECT_TRUE(FindLog( 200 "Readlink failed on /proc/0/exe with 2")); 201 EXPECT_TRUE(FindLog( 202 "GetSymlinkTarget failed - Path /proc/0 DirectoryExists: 0")); 203 EXPECT_TRUE(FindLog("stat /proc/0/exe failed: -1 2")); 204 205 brillo::ClearLog(); 206 pid_t my_pid = getpid(); 207 EXPECT_TRUE(collector_.GetExecutableBaseNameFromPid(my_pid, &base_name)); 208 EXPECT_FALSE(FindLog("Readlink failed")); 209 EXPECT_EQ("crash_reporter_tests", base_name); 210 } 211 212 TEST_F(UserCollectorTest, GetFirstLineWithPrefix) { 213 std::vector<std::string> lines; 214 std::string line; 215 216 EXPECT_FALSE(collector_.GetFirstLineWithPrefix(lines, "Name:", &line)); 217 EXPECT_EQ("", line); 218 219 lines.push_back("Name:\tls"); 220 lines.push_back("State:\tR (running)"); 221 lines.push_back(" Foo:\t1000"); 222 223 line.clear(); 224 EXPECT_TRUE(collector_.GetFirstLineWithPrefix(lines, "Name:", &line)); 225 EXPECT_EQ(lines[0], line); 226 227 line.clear(); 228 EXPECT_TRUE(collector_.GetFirstLineWithPrefix(lines, "State:", &line)); 229 EXPECT_EQ(lines[1], line); 230 231 line.clear(); 232 EXPECT_FALSE(collector_.GetFirstLineWithPrefix(lines, "Foo:", &line)); 233 EXPECT_EQ("", line); 234 235 line.clear(); 236 EXPECT_TRUE(collector_.GetFirstLineWithPrefix(lines, " Foo:", &line)); 237 EXPECT_EQ(lines[2], line); 238 239 line.clear(); 240 EXPECT_FALSE(collector_.GetFirstLineWithPrefix(lines, "Bar:", &line)); 241 EXPECT_EQ("", line); 242 } 243 244 TEST_F(UserCollectorTest, GetIdFromStatus) { 245 int id = 1; 246 EXPECT_FALSE(collector_.GetIdFromStatus(UserCollector::kUserId, 247 UserCollector::kIdEffective, 248 SplitLines("nothing here"), 249 &id)); 250 EXPECT_EQ(id, 1); 251 252 // Not enough parameters. 253 EXPECT_FALSE(collector_.GetIdFromStatus(UserCollector::kUserId, 254 UserCollector::kIdReal, 255 SplitLines("line 1\nUid:\t1\n"), 256 &id)); 257 258 const std::vector<std::string> valid_contents = 259 SplitLines("\nUid:\t1\t2\t3\t4\nGid:\t5\t6\t7\t8\n"); 260 EXPECT_TRUE(collector_.GetIdFromStatus(UserCollector::kUserId, 261 UserCollector::kIdReal, 262 valid_contents, 263 &id)); 264 EXPECT_EQ(1, id); 265 266 EXPECT_TRUE(collector_.GetIdFromStatus(UserCollector::kUserId, 267 UserCollector::kIdEffective, 268 valid_contents, 269 &id)); 270 EXPECT_EQ(2, id); 271 272 EXPECT_TRUE(collector_.GetIdFromStatus(UserCollector::kUserId, 273 UserCollector::kIdFileSystem, 274 valid_contents, 275 &id)); 276 EXPECT_EQ(4, id); 277 278 EXPECT_TRUE(collector_.GetIdFromStatus(UserCollector::kGroupId, 279 UserCollector::kIdEffective, 280 valid_contents, 281 &id)); 282 EXPECT_EQ(6, id); 283 284 EXPECT_TRUE(collector_.GetIdFromStatus(UserCollector::kGroupId, 285 UserCollector::kIdSet, 286 valid_contents, 287 &id)); 288 EXPECT_EQ(7, id); 289 290 EXPECT_FALSE(collector_.GetIdFromStatus(UserCollector::kGroupId, 291 UserCollector::IdKind(5), 292 valid_contents, 293 &id)); 294 EXPECT_FALSE(collector_.GetIdFromStatus(UserCollector::kGroupId, 295 UserCollector::IdKind(-1), 296 valid_contents, 297 &id)); 298 299 // Fail if junk after number 300 EXPECT_FALSE(collector_.GetIdFromStatus(UserCollector::kUserId, 301 UserCollector::kIdReal, 302 SplitLines("Uid:\t1f\t2\t3\t4\n"), 303 &id)); 304 EXPECT_TRUE(collector_.GetIdFromStatus(UserCollector::kUserId, 305 UserCollector::kIdReal, 306 SplitLines("Uid:\t1\t2\t3\t4\n"), 307 &id)); 308 EXPECT_EQ(1, id); 309 310 // Fail if more than 4 numbers. 311 EXPECT_FALSE(collector_.GetIdFromStatus(UserCollector::kUserId, 312 UserCollector::kIdReal, 313 SplitLines("Uid:\t1\t2\t3\t4\t5\n"), 314 &id)); 315 } 316 317 TEST_F(UserCollectorTest, GetStateFromStatus) { 318 std::string state; 319 EXPECT_FALSE(collector_.GetStateFromStatus(SplitLines("nothing here"), 320 &state)); 321 EXPECT_EQ("", state); 322 323 EXPECT_TRUE(collector_.GetStateFromStatus(SplitLines("State:\tR (running)"), 324 &state)); 325 EXPECT_EQ("R (running)", state); 326 327 EXPECT_TRUE(collector_.GetStateFromStatus( 328 SplitLines("Name:\tls\nState:\tZ (zombie)\n"), &state)); 329 EXPECT_EQ("Z (zombie)", state); 330 } 331 332 TEST_F(UserCollectorTest, GetUserInfoFromName) { 333 gid_t gid = 100; 334 uid_t uid = 100; 335 EXPECT_TRUE(collector_.GetUserInfoFromName("root", &uid, &gid)); 336 EXPECT_EQ(0, uid); 337 EXPECT_EQ(0, gid); 338 } 339 340 TEST_F(UserCollectorTest, CopyOffProcFilesBadPath) { 341 // Try a path that is not writable. 342 ASSERT_FALSE(collector_.CopyOffProcFiles(pid_, FilePath("/bad/path"))); 343 EXPECT_TRUE(FindLog("Could not create /bad/path")); 344 } 345 346 TEST_F(UserCollectorTest, CopyOffProcFilesBadPid) { 347 FilePath container_path(test_dir_.path().Append("test/container")); 348 ASSERT_FALSE(collector_.CopyOffProcFiles(0, container_path)); 349 EXPECT_TRUE(FindLog("Path /proc/0 does not exist")); 350 } 351 352 TEST_F(UserCollectorTest, CopyOffProcFilesOK) { 353 FilePath container_path(test_dir_.path().Append("test/container")); 354 ASSERT_TRUE(collector_.CopyOffProcFiles(pid_, container_path)); 355 EXPECT_FALSE(FindLog("Could not copy")); 356 static struct { 357 const char *name; 358 bool exists; 359 } expectations[] = { 360 { "auxv", true }, 361 { "cmdline", true }, 362 { "environ", true }, 363 { "maps", true }, 364 { "mem", false }, 365 { "mounts", false }, 366 { "sched", false }, 367 { "status", true } 368 }; 369 for (unsigned i = 0; i < sizeof(expectations)/sizeof(expectations[0]); ++i) { 370 EXPECT_EQ(expectations[i].exists, 371 base::PathExists( 372 container_path.Append(expectations[i].name))); 373 } 374 } 375 376 TEST_F(UserCollectorTest, ValidateProcFiles) { 377 FilePath container_dir = test_dir_.path(); 378 379 // maps file not exists (i.e. GetFileSize fails) 380 EXPECT_FALSE(collector_.ValidateProcFiles(container_dir)); 381 382 // maps file is empty 383 FilePath maps_file = container_dir.Append("maps"); 384 ASSERT_EQ(0, base::WriteFile(maps_file, nullptr, 0)); 385 ASSERT_TRUE(base::PathExists(maps_file)); 386 EXPECT_FALSE(collector_.ValidateProcFiles(container_dir)); 387 388 // maps file is not empty 389 const char data[] = "test data"; 390 ASSERT_EQ(sizeof(data), base::WriteFile(maps_file, data, sizeof(data))); 391 ASSERT_TRUE(base::PathExists(maps_file)); 392 EXPECT_TRUE(collector_.ValidateProcFiles(container_dir)); 393 } 394 395 TEST_F(UserCollectorTest, ValidateCoreFile) { 396 FilePath container_dir = test_dir_.path(); 397 FilePath core_file = container_dir.Append("core"); 398 399 // Core file does not exist 400 EXPECT_EQ(UserCollector::kErrorInvalidCoreFile, 401 collector_.ValidateCoreFile(core_file)); 402 char e_ident[EI_NIDENT]; 403 e_ident[EI_MAG0] = ELFMAG0; 404 e_ident[EI_MAG1] = ELFMAG1; 405 e_ident[EI_MAG2] = ELFMAG2; 406 e_ident[EI_MAG3] = ELFMAG3; 407 #if __WORDSIZE == 32 408 e_ident[EI_CLASS] = ELFCLASS32; 409 #elif __WORDSIZE == 64 410 e_ident[EI_CLASS] = ELFCLASS64; 411 #else 412 #error Unknown/unsupported value of __WORDSIZE. 413 #endif 414 415 // Core file has the expected header 416 ASSERT_TRUE(base::WriteFile(core_file, e_ident, sizeof(e_ident))); 417 EXPECT_EQ(UserCollector::kErrorNone, 418 collector_.ValidateCoreFile(core_file)); 419 420 #if __WORDSIZE == 64 421 // 32-bit core file on 64-bit platform 422 e_ident[EI_CLASS] = ELFCLASS32; 423 ASSERT_TRUE(base::WriteFile(core_file, e_ident, sizeof(e_ident))); 424 EXPECT_EQ(UserCollector::kErrorUnsupported32BitCoreFile, 425 collector_.ValidateCoreFile(core_file)); 426 e_ident[EI_CLASS] = ELFCLASS64; 427 #endif 428 429 // Invalid core files 430 ASSERT_TRUE(base::WriteFile(core_file, e_ident, sizeof(e_ident) - 1)); 431 EXPECT_EQ(UserCollector::kErrorInvalidCoreFile, 432 collector_.ValidateCoreFile(core_file)); 433 434 e_ident[EI_MAG0] = 0; 435 ASSERT_TRUE(base::WriteFile(core_file, e_ident, sizeof(e_ident))); 436 EXPECT_EQ(UserCollector::kErrorInvalidCoreFile, 437 collector_.ValidateCoreFile(core_file)); 438 } 439