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 <fcntl.h>
21 #include <grp.h>  // For struct group.
22 #include <pcrecpp.h>
23 #include <pwd.h>  // For struct passwd.
24 #include <stdint.h>
25 #include <sys/cdefs.h>  // For __WORDSIZE
26 #include <sys/fsuid.h>
27 #include <sys/types.h>  // For getpwuid_r, getgrnam_r, WEXITSTATUS.
28 #include <unistd.h>  // For setgroups
29 
30 #include <iostream>  // For std::oct
31 #include <string>
32 #include <vector>
33 
34 #include <base/files/file_util.h>
35 #include <base/logging.h>
36 #include <base/posix/eintr_wrapper.h>
37 #include <base/strings/string_split.h>
38 #include <base/strings/string_util.h>
39 #include <base/strings/stringprintf.h>
40 #include <brillo/osrelease_reader.h>
41 #include <brillo/process.h>
42 #include <brillo/syslog_logging.h>
43 #include <cutils/properties.h>
44 #include <private/android_filesystem_config.h>
45 
46 static const char kCollectionErrorSignature[] =
47     "crash_reporter-user-collection";
48 static const char kCorePatternProperty[] = "crash_reporter.coredump.enabled";
49 static const char kCoreToMinidumpConverterPath[] = "/system/bin/core2md";
50 
51 static const char kStatePrefix[] = "State:\t";
52 
53 static const char kCoreTempFolder[] = "/data/misc/crash_reporter/tmp";
54 
55 // Define an otherwise invalid value that represents an unknown UID and GID.
56 static const uid_t kUnknownUid = -1;
57 static const gid_t kUnknownGid = -1;
58 
59 const char *UserCollector::kUserId = "Uid:\t";
60 const char *UserCollector::kGroupId = "Gid:\t";
61 
62 // Product information keys in the /etc/os-release.d folder.
63 static const char kBdkVersionKey[] = "bdk_version";
64 static const char kProductIDKey[] = "product_id";
65 static const char kProductVersionKey[] = "product_version";
66 
67 
68 using base::FilePath;
69 using base::StringPrintf;
70 
UserCollector()71 UserCollector::UserCollector()
72     : generate_diagnostics_(false),
73       initialized_(false) {
74 }
75 
Initialize(UserCollector::CountCrashFunction count_crash_function,const std::string & our_path,UserCollector::IsFeedbackAllowedFunction is_feedback_allowed_function,bool generate_diagnostics,bool core2md_failure,bool directory_failure,const std::string & filter_in)76 void UserCollector::Initialize(
77     UserCollector::CountCrashFunction count_crash_function,
78     const std::string &our_path,
79     UserCollector::IsFeedbackAllowedFunction is_feedback_allowed_function,
80     bool generate_diagnostics,
81     bool core2md_failure,
82     bool directory_failure,
83     const std::string &filter_in) {
84   CrashCollector::Initialize(count_crash_function,
85                              is_feedback_allowed_function);
86   our_path_ = our_path;
87   initialized_ = true;
88   generate_diagnostics_ = generate_diagnostics;
89   core2md_failure_ = core2md_failure;
90   directory_failure_ = directory_failure;
91   filter_in_ = filter_in;
92 
93   gid_t groups[] = { AID_ROOT, AID_SYSTEM, AID_DBUS, AID_READPROC };
94   if (setgroups(arraysize(groups), groups) != 0) {
95     PLOG(FATAL) << "Unable to set groups to root, system, dbus, and readproc";
96   }
97 }
98 
~UserCollector()99 UserCollector::~UserCollector() {
100 }
101 
GetErrorTypeSignature(ErrorType error_type) const102 std::string UserCollector::GetErrorTypeSignature(ErrorType error_type) const {
103   switch (error_type) {
104     case kErrorSystemIssue:
105       return "system-issue";
106     case kErrorReadCoreData:
107       return "read-core-data";
108     case kErrorUnusableProcFiles:
109       return "unusable-proc-files";
110     case kErrorInvalidCoreFile:
111       return "invalid-core-file";
112     case kErrorUnsupported32BitCoreFile:
113       return "unsupported-32bit-core-file";
114     case kErrorCore2MinidumpConversion:
115       return "core2md-conversion";
116     default:
117       return "";
118   }
119 }
120 
SetUpInternal(bool enabled)121 bool UserCollector::SetUpInternal(bool enabled) {
122   CHECK(initialized_);
123   LOG(INFO) << (enabled ? "Enabling" : "Disabling") << " user crash handling";
124 
125   property_set(kCorePatternProperty, enabled ? "1" : "0");
126 
127   return true;
128 }
129 
GetFirstLineWithPrefix(const std::vector<std::string> & lines,const char * prefix,std::string * line)130 bool UserCollector::GetFirstLineWithPrefix(
131     const std::vector<std::string> &lines,
132     const char *prefix, std::string *line) {
133   std::vector<std::string>::const_iterator line_iterator;
134   for (line_iterator = lines.begin(); line_iterator != lines.end();
135        ++line_iterator) {
136     if (line_iterator->find(prefix) == 0) {
137       *line = *line_iterator;
138       return true;
139     }
140   }
141   return false;
142 }
143 
GetIdFromStatus(const char * prefix,IdKind kind,const std::vector<std::string> & status_lines,int * id)144 bool UserCollector::GetIdFromStatus(
145     const char *prefix, IdKind kind,
146     const std::vector<std::string> &status_lines, int *id) {
147   // From fs/proc/array.c:task_state(), this file contains:
148   // \nUid:\t<uid>\t<euid>\t<suid>\t<fsuid>\n
149   std::string id_line;
150   if (!GetFirstLineWithPrefix(status_lines, prefix, &id_line)) {
151     return false;
152   }
153   std::string id_substring = id_line.substr(strlen(prefix), std::string::npos);
154   std::vector<std::string> ids = base::SplitString(
155       id_substring, "\t", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
156   if (ids.size() != kIdMax || kind < 0 || kind >= kIdMax) {
157     return false;
158   }
159   const char *number = ids[kind].c_str();
160   char *end_number = nullptr;
161   *id = strtol(number, &end_number, 10);
162   if (*end_number != '\0') {
163     return false;
164   }
165   return true;
166 }
167 
GetStateFromStatus(const std::vector<std::string> & status_lines,std::string * state)168 bool UserCollector::GetStateFromStatus(
169     const std::vector<std::string> &status_lines, std::string *state) {
170   std::string state_line;
171   if (!GetFirstLineWithPrefix(status_lines, kStatePrefix, &state_line)) {
172     return false;
173   }
174   *state = state_line.substr(strlen(kStatePrefix), std::string::npos);
175   return true;
176 }
177 
EnqueueCollectionErrorLog(pid_t pid,ErrorType error_type,const std::string & exec)178 void UserCollector::EnqueueCollectionErrorLog(pid_t pid,
179                                               ErrorType error_type,
180                                               const std::string &exec) {
181   FilePath crash_path;
182   LOG(INFO) << "Writing conversion problems as separate crash report.";
183   if (!GetCreatedCrashDirectoryByEuid(0, &crash_path, nullptr)) {
184     LOG(ERROR) << "Could not even get log directory; out of space?";
185     return;
186   }
187   AddCrashMetaData("sig", kCollectionErrorSignature);
188   AddCrashMetaData("error_type", GetErrorTypeSignature(error_type));
189   std::string dump_basename = FormatDumpBasename(exec, time(nullptr), pid);
190   std::string error_log = brillo::GetLog();
191   FilePath diag_log_path = GetCrashPath(crash_path, dump_basename, "diaglog");
192   if (GetLogContents(FilePath(log_config_path_), kCollectionErrorSignature,
193                      diag_log_path)) {
194     // We load the contents of diag_log into memory and append it to
195     // the error log.  We cannot just append to files because we need
196     // to always create new files to prevent attack.
197     std::string diag_log_contents;
198     base::ReadFileToString(diag_log_path, &diag_log_contents);
199     error_log.append(diag_log_contents);
200     base::DeleteFile(diag_log_path, false);
201   }
202   FilePath log_path = GetCrashPath(crash_path, dump_basename, "log");
203   FilePath meta_path = GetCrashPath(crash_path, dump_basename, "meta");
204   // We must use WriteNewFile instead of base::WriteFile as we do
205   // not want to write with root access to a symlink that an attacker
206   // might have created.
207   if (WriteNewFile(log_path, error_log.data(), error_log.length()) < 0) {
208     LOG(ERROR) << "Error writing new file " << log_path.value();
209     return;
210   }
211   WriteCrashMetaData(meta_path, exec, log_path.value());
212 }
213 
CopyOffProcFiles(pid_t pid,const FilePath & container_dir)214 bool UserCollector::CopyOffProcFiles(pid_t pid,
215                                      const FilePath &container_dir) {
216   if (!base::CreateDirectory(container_dir)) {
217     PLOG(ERROR) << "Could not create " << container_dir.value();
218     return false;
219   }
220   int dir_mask = base::FILE_PERMISSION_READ_BY_USER
221                  | base::FILE_PERMISSION_WRITE_BY_USER
222                  | base::FILE_PERMISSION_EXECUTE_BY_USER
223                  | base::FILE_PERMISSION_READ_BY_GROUP
224                  | base::FILE_PERMISSION_WRITE_BY_GROUP;
225   if (!base::SetPosixFilePermissions(container_dir,
226                                      base::FILE_PERMISSION_MASK & dir_mask)) {
227     PLOG(ERROR) << "Could not set permissions for " << container_dir.value()
228                 << " to " << std::oct
229                 << (base::FILE_PERMISSION_MASK & dir_mask);
230     return false;
231   }
232   FilePath process_path = GetProcessPath(pid);
233   if (!base::PathExists(process_path)) {
234     LOG(ERROR) << "Path " << process_path.value() << " does not exist";
235     return false;
236   }
237   static const char *proc_files[] = {
238     "auxv",
239     "cmdline",
240     "environ",
241     "maps",
242     "status"
243   };
244   for (unsigned i = 0; i < arraysize(proc_files); ++i) {
245     if (!base::CopyFile(process_path.Append(proc_files[i]),
246                         container_dir.Append(proc_files[i]))) {
247       LOG(ERROR) << "Could not copy " << proc_files[i] << " file";
248       return false;
249     }
250   }
251   return true;
252 }
253 
ValidateProcFiles(const FilePath & container_dir) const254 bool UserCollector::ValidateProcFiles(const FilePath &container_dir) const {
255   // Check if the maps file is empty, which could be due to the crashed
256   // process being reaped by the kernel before finishing a core dump.
257   int64_t file_size = 0;
258   if (!base::GetFileSize(container_dir.Append("maps"), &file_size)) {
259     LOG(ERROR) << "Could not get the size of maps file";
260     return false;
261   }
262   if (file_size == 0) {
263     LOG(ERROR) << "maps file is empty";
264     return false;
265   }
266   return true;
267 }
268 
ValidateCoreFile(const FilePath & core_path) const269 UserCollector::ErrorType UserCollector::ValidateCoreFile(
270     const FilePath &core_path) const {
271   int fd = HANDLE_EINTR(open(core_path.value().c_str(), O_RDONLY));
272   if (fd < 0) {
273     PLOG(ERROR) << "Could not open core file " << core_path.value();
274     return kErrorInvalidCoreFile;
275   }
276 
277   char e_ident[EI_NIDENT];
278   bool read_ok = base::ReadFromFD(fd, e_ident, sizeof(e_ident));
279   IGNORE_EINTR(close(fd));
280   if (!read_ok) {
281     LOG(ERROR) << "Could not read header of core file";
282     return kErrorInvalidCoreFile;
283   }
284 
285   if (e_ident[EI_MAG0] != ELFMAG0 || e_ident[EI_MAG1] != ELFMAG1 ||
286       e_ident[EI_MAG2] != ELFMAG2 || e_ident[EI_MAG3] != ELFMAG3) {
287     LOG(ERROR) << "Invalid core file";
288     return kErrorInvalidCoreFile;
289   }
290 
291 #if __WORDSIZE == 64
292   // TODO(benchan, mkrebs): Remove this check once core2md can
293   // handles both 32-bit and 64-bit ELF on a 64-bit platform.
294   if (e_ident[EI_CLASS] == ELFCLASS32) {
295     LOG(ERROR) << "Conversion of 32-bit core file on 64-bit platform is "
296                << "currently not supported";
297     return kErrorUnsupported32BitCoreFile;
298   }
299 #endif
300 
301   return kErrorNone;
302 }
303 
GetCreatedCrashDirectory(pid_t pid,uid_t supplied_ruid,FilePath * crash_file_path,bool * out_of_capacity)304 bool UserCollector::GetCreatedCrashDirectory(pid_t pid, uid_t supplied_ruid,
305                                              FilePath *crash_file_path,
306                                              bool *out_of_capacity) {
307   FilePath process_path = GetProcessPath(pid);
308   std::string status;
309   if (directory_failure_) {
310     LOG(ERROR) << "Purposefully failing to create spool directory";
311     return false;
312   }
313 
314   uid_t uid;
315   if (base::ReadFileToString(process_path.Append("status"), &status)) {
316     std::vector<std::string> status_lines = base::SplitString(
317         status, "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
318 
319     std::string process_state;
320     if (!GetStateFromStatus(status_lines, &process_state)) {
321       LOG(ERROR) << "Could not find process state in status file";
322       return false;
323     }
324     LOG(INFO) << "State of crashed process [" << pid << "]: " << process_state;
325 
326     // Get effective UID of crashing process.
327     int id;
328     if (!GetIdFromStatus(kUserId, kIdEffective, status_lines, &id)) {
329       LOG(ERROR) << "Could not find euid in status file";
330       return false;
331     }
332     uid = id;
333   } else if (supplied_ruid != kUnknownUid) {
334     LOG(INFO) << "Using supplied UID " << supplied_ruid
335               << " for crashed process [" << pid
336               << "] due to error reading status file";
337     uid = supplied_ruid;
338   } else {
339     LOG(ERROR) << "Could not read status file and kernel did not supply UID";
340     LOG(INFO) << "Path " << process_path.value() << " DirectoryExists: "
341               << base::DirectoryExists(process_path);
342     return false;
343   }
344 
345   if (!GetCreatedCrashDirectoryByEuid(uid, crash_file_path, out_of_capacity)) {
346     LOG(ERROR) << "Could not create crash directory";
347     return false;
348   }
349   return true;
350 }
351 
CopyStdinToCoreFile(const FilePath & core_path)352 bool UserCollector::CopyStdinToCoreFile(const FilePath &core_path) {
353   // Copy off all stdin to a core file.
354   FilePath stdin_path("/proc/self/fd/0");
355   if (base::CopyFile(stdin_path, core_path)) {
356     return true;
357   }
358 
359   PLOG(ERROR) << "Could not write core file";
360   // If the file system was full, make sure we remove any remnants.
361   base::DeleteFile(core_path, false);
362   return false;
363 }
364 
RunCoreToMinidump(const FilePath & core_path,const FilePath & procfs_directory,const FilePath & minidump_path,const FilePath & temp_directory)365 bool UserCollector::RunCoreToMinidump(const FilePath &core_path,
366                                       const FilePath &procfs_directory,
367                                       const FilePath &minidump_path,
368                                       const FilePath &temp_directory) {
369   FilePath output_path = temp_directory.Append("output");
370   brillo::ProcessImpl core2md;
371   core2md.RedirectOutput(output_path.value());
372   core2md.AddArg(kCoreToMinidumpConverterPath);
373   core2md.AddArg(core_path.value());
374   core2md.AddArg(procfs_directory.value());
375 
376   if (!core2md_failure_) {
377     core2md.AddArg(minidump_path.value());
378   } else {
379     // To test how core2md errors are propagaged, cause an error
380     // by forgetting a required argument.
381   }
382 
383   int errorlevel = core2md.Run();
384 
385   std::string output;
386   base::ReadFileToString(output_path, &output);
387   if (errorlevel != 0) {
388     LOG(ERROR) << "Problem during " << kCoreToMinidumpConverterPath
389                << " [result=" << errorlevel << "]: " << output;
390     return false;
391   }
392 
393   if (!base::PathExists(minidump_path)) {
394     LOG(ERROR) << "Minidump file " << minidump_path.value()
395                << " was not created";
396     return false;
397   }
398   return true;
399 }
400 
ConvertCoreToMinidump(pid_t pid,const FilePath & container_dir,const FilePath & core_path,const FilePath & minidump_path)401 UserCollector::ErrorType UserCollector::ConvertCoreToMinidump(
402     pid_t pid,
403     const FilePath &container_dir,
404     const FilePath &core_path,
405     const FilePath &minidump_path) {
406   // If proc files are unuable, we continue to read the core file from stdin,
407   // but only skip the core-to-minidump conversion, so that we may still use
408   // the core file for debugging.
409   bool proc_files_usable =
410       CopyOffProcFiles(pid, container_dir) && ValidateProcFiles(container_dir);
411 
412   // Switch back to the original UID/GID.
413   gid_t rgid, egid, sgid;
414   if (getresgid(&rgid, &egid, &sgid) != 0) {
415     PLOG(FATAL) << "Unable to read saved gid";
416   }
417   if (setresgid(sgid, sgid, -1) != 0) {
418     PLOG(FATAL) << "Unable to set real group ID back to saved gid";
419   } else {
420     if (getresgid(&rgid, &egid, &sgid) != 0) {
421       // If the groups cannot be read at this point, the rgid variable will
422       // contain the previously read group ID from before changing it.  This
423       // will cause the chown call below to set the incorrect group for
424       // non-root crashes.  But do not treat this as a fatal error, so that
425       // the rest of the collection will continue for potential manual
426       // collection by a developer.
427       PLOG(ERROR) << "Unable to read real group ID after setting it";
428     }
429   }
430 
431   uid_t ruid, euid, suid;
432   if (getresuid(&ruid, &euid, &suid) != 0) {
433     PLOG(FATAL) << "Unable to read saved uid";
434   }
435   if (setresuid(suid, suid, -1) != 0) {
436     PLOG(FATAL) << "Unable to set real user ID back to saved uid";
437   } else {
438     if (getresuid(&ruid, &euid, &suid) != 0) {
439       // If the user ID cannot be read at this point, the ruid variable will
440       // contain the previously read user ID from before changing it.  This
441       // will cause the chown call below to set the incorrect user for
442       // non-root crashes.  But do not treat this as a fatal error, so that
443       // the rest of the collection will continue for potential manual
444       // collection by a developer.
445       PLOG(ERROR) << "Unable to read real user ID after setting it";
446     }
447   }
448 
449   if (!CopyStdinToCoreFile(core_path)) {
450     return kErrorReadCoreData;
451   }
452 
453   if (!proc_files_usable) {
454     LOG(INFO) << "Skipped converting core file to minidump due to "
455               << "unusable proc files";
456     return kErrorUnusableProcFiles;
457   }
458 
459   ErrorType error = ValidateCoreFile(core_path);
460   if (error != kErrorNone) {
461     return error;
462   }
463 
464   // Chown the temp container directory back to the original user/group that
465   // crash_reporter is run as, so that additional files can be written to
466   // the temp folder.
467   if (chown(container_dir.value().c_str(), ruid, rgid) < 0) {
468     PLOG(ERROR) << "Could not set owner for " << container_dir.value();
469   }
470 
471   if (!RunCoreToMinidump(core_path,
472                          container_dir,  // procfs directory
473                          minidump_path,
474                          container_dir)) {  // temporary directory
475     return kErrorCore2MinidumpConversion;
476   }
477 
478   LOG(INFO) << "Stored minidump to " << minidump_path.value();
479   return kErrorNone;
480 }
481 
ConvertAndEnqueueCrash(pid_t pid,const std::string & exec,uid_t supplied_ruid,bool * out_of_capacity)482 UserCollector::ErrorType UserCollector::ConvertAndEnqueueCrash(
483     pid_t pid, const std::string &exec, uid_t supplied_ruid,
484     bool *out_of_capacity) {
485   FilePath crash_path;
486   if (!GetCreatedCrashDirectory(pid, supplied_ruid, &crash_path,
487       out_of_capacity)) {
488     LOG(ERROR) << "Unable to find/create process-specific crash path";
489     return kErrorSystemIssue;
490   }
491 
492   // Directory like /tmp/crash_reporter/1234 which contains the
493   // procfs entries and other temporary files used during conversion.
494   FilePath container_dir(StringPrintf("%s/%d", kCoreTempFolder, pid));
495   // Delete a pre-existing directory from crash reporter that may have
496   // been left around for diagnostics from a failed conversion attempt.
497   // If we don't, existing files can cause forking to fail.
498   base::DeleteFile(container_dir, true);
499   std::string dump_basename = FormatDumpBasename(exec, time(nullptr), pid);
500   FilePath core_path = GetCrashPath(crash_path, dump_basename, "core");
501   FilePath meta_path = GetCrashPath(crash_path, dump_basename, "meta");
502   FilePath minidump_path = GetCrashPath(crash_path, dump_basename, "dmp");
503   FilePath log_path = GetCrashPath(crash_path, dump_basename, "log");
504 
505   if (GetLogContents(FilePath(log_config_path_), exec, log_path))
506     AddCrashMetaData("log", log_path.value());
507 
508   brillo::OsReleaseReader reader;
509   reader.Load();
510   std::string value = "undefined";
511   if (!reader.GetString(kBdkVersionKey, &value)) {
512     LOG(ERROR) << "Could not read " << kBdkVersionKey
513                << " from /etc/os-release.d/";
514   }
515   AddCrashMetaData(kBdkVersionKey, value);
516 
517   value = "undefined";
518   if (!reader.GetString(kProductIDKey, &value)) {
519     LOG(ERROR) << "Could not read " << kProductIDKey
520                << " from /etc/os-release.d/";
521   }
522   AddCrashMetaData(kProductIDKey, value);
523 
524   value = "undefined";
525   if (!reader.GetString(kProductVersionKey, &value)) {
526     LOG(ERROR) << "Could not read " << kProductVersionKey
527                << " from /etc/os-release.d/";
528   }
529   AddCrashMetaData(kProductVersionKey, value);
530 
531   ErrorType error_type =
532       ConvertCoreToMinidump(pid, container_dir, core_path, minidump_path);
533   if (error_type != kErrorNone) {
534     LOG(INFO) << "Leaving core file at " << core_path.value()
535               << " due to conversion error";
536     return error_type;
537   }
538 
539   // Here we commit to sending this file.  We must not return false
540   // after this point or we will generate a log report as well as a
541   // crash report.
542   WriteCrashMetaData(meta_path,
543                      exec,
544                      minidump_path.value());
545 
546   if (!IsDeveloperImage()) {
547     base::DeleteFile(core_path, false);
548   } else {
549     LOG(INFO) << "Leaving core file at " << core_path.value()
550               << " due to developer image";
551   }
552 
553   base::DeleteFile(container_dir, true);
554   return kErrorNone;
555 }
556 
ParseCrashAttributes(const std::string & crash_attributes,pid_t * pid,int * signal,uid_t * uid,gid_t * gid,std::string * kernel_supplied_name)557 bool UserCollector::ParseCrashAttributes(const std::string &crash_attributes,
558                                          pid_t *pid, int *signal, uid_t *uid,
559                                          gid_t *gid,
560                                          std::string *kernel_supplied_name) {
561   pcrecpp::RE re("(\\d+):(\\d+):(\\d+):(\\d+):(.*)");
562   if (re.FullMatch(crash_attributes, pid, signal, uid, gid,
563                    kernel_supplied_name))
564     return true;
565 
566   LOG(INFO) << "Falling back to parsing crash attributes '"
567             << crash_attributes << "' without UID and GID";
568   pcrecpp::RE re_without_uid("(\\d+):(\\d+):(.*)");
569   *uid = kUnknownUid;
570   *gid = kUnknownGid;
571   return re_without_uid.FullMatch(crash_attributes, pid, signal,
572       kernel_supplied_name);
573 }
574 
ShouldDump(bool has_owner_consent,bool is_developer,std::string * reason)575 bool UserCollector::ShouldDump(bool has_owner_consent,
576                                bool is_developer,
577                                std::string *reason) {
578   reason->clear();
579 
580   // For developer builds, we always want to keep the crash reports unless
581   // we're testing the crash facilities themselves.  This overrides
582   // feedback.  Crash sending still obeys consent.
583   if (is_developer) {
584     *reason = "developer build - not testing - always dumping";
585     return true;
586   }
587 
588   if (!has_owner_consent) {
589     *reason = "ignoring - no consent";
590     return false;
591   }
592 
593   *reason = "handling";
594   return true;
595 }
596 
HandleCrash(const std::string & crash_attributes,const char * force_exec)597 bool UserCollector::HandleCrash(const std::string &crash_attributes,
598                                 const char *force_exec) {
599   CHECK(initialized_);
600   pid_t pid = 0;
601   int signal = 0;
602   uid_t supplied_ruid = kUnknownUid;
603   gid_t supplied_rgid = kUnknownGid;
604   std::string kernel_supplied_name;
605 
606   if (!ParseCrashAttributes(crash_attributes, &pid, &signal, &supplied_ruid,
607                             &supplied_rgid, &kernel_supplied_name)) {
608     LOG(ERROR) << "Invalid parameter: --user=" <<  crash_attributes;
609     return false;
610   }
611 
612   // Switch to the group and user that ran the crashing binary in order to
613   // access their /proc files.  Do not set suid/sgid, so that we can switch
614   // back after copying the necessary files.
615   if (setresgid(supplied_rgid, supplied_rgid, -1) != 0) {
616     PLOG(FATAL) << "Unable to set real group ID to access process files";
617   }
618   if (setresuid(supplied_ruid, supplied_ruid, -1) != 0) {
619     PLOG(FATAL) << "Unable to set real user ID to access process files";
620   }
621 
622   std::string exec;
623   if (force_exec) {
624     exec.assign(force_exec);
625   } else if (!GetExecutableBaseNameFromPid(pid, &exec)) {
626     // If we cannot find the exec name, use the kernel supplied name.
627     // We don't always use the kernel's since it truncates the name to
628     // 16 characters.
629     exec = StringPrintf("supplied_%s", kernel_supplied_name.c_str());
630   }
631 
632   // Allow us to test the crash reporting mechanism successfully even if
633   // other parts of the system crash.
634   if (!filter_in_.empty() &&
635       (filter_in_ == "none" ||
636        filter_in_ != exec)) {
637     // We use a different format message to make it more obvious in tests
638     // which crashes are test generated and which are real.
639     LOG(WARNING) << "Ignoring crash from " << exec << "[" << pid << "] while "
640                  << "filter_in=" << filter_in_ << ".";
641     return true;
642   }
643 
644   std::string reason;
645   bool dump = ShouldDump(is_feedback_allowed_function_(),
646                          IsDeveloperImage(),
647                          &reason);
648 
649   LOG(WARNING) << "Received crash notification for " << exec << "[" << pid
650                << "] sig " << signal << ", user " << supplied_ruid
651                << " (" << reason << ")";
652 
653   if (dump) {
654     count_crash_function_();
655 
656     if (generate_diagnostics_) {
657       bool out_of_capacity = false;
658       ErrorType error_type =
659           ConvertAndEnqueueCrash(pid, exec, supplied_ruid, &out_of_capacity);
660       if (error_type != kErrorNone) {
661         if (!out_of_capacity)
662           EnqueueCollectionErrorLog(pid, error_type, exec);
663         return false;
664       }
665     }
666   }
667 
668   return true;
669 }
670