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 
CountCrash()40 void CountCrash() {
41   ++s_crashes;
42 }
43 
IsMetrics()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 {
SetUp()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:
ExpectFileEquals(const char * golden,const FilePath & file_path)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 
SplitLines(const std::string & lines) const84   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 
TEST_F(UserCollectorTest,ParseCrashAttributes)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 
TEST_F(UserCollectorTest,ShouldDumpDeveloperImageOverridesConsent)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 
TEST_F(UserCollectorTest,ShouldDumpUseConsentProductionImage)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 
TEST_F(UserCollectorTest,HandleCrashWithoutConsent)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 
TEST_F(UserCollectorTest,HandleNonChromeCrashWithConsent)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 
TEST_F(UserCollectorTest,GetProcessPath)167 TEST_F(UserCollectorTest, GetProcessPath) {
168   FilePath path = collector_.GetProcessPath(100);
169   ASSERT_EQ("/proc/100", path.value());
170 }
171 
TEST_F(UserCollectorTest,GetSymlinkTarget)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 
TEST_F(UserCollectorTest,GetExecutableBaseNameFromPid)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 
TEST_F(UserCollectorTest,GetFirstLineWithPrefix)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 
TEST_F(UserCollectorTest,GetIdFromStatus)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 
TEST_F(UserCollectorTest,GetStateFromStatus)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 
TEST_F(UserCollectorTest,GetUserInfoFromName)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 
TEST_F(UserCollectorTest,CopyOffProcFilesBadPath)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 
TEST_F(UserCollectorTest,CopyOffProcFilesBadPid)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 
TEST_F(UserCollectorTest,CopyOffProcFilesOK)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 
TEST_F(UserCollectorTest,ValidateProcFiles)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 
TEST_F(UserCollectorTest,ValidateCoreFile)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