Index: components/crash/content/app/breakpad_linux.cc |
diff --git a/components/crash/content/app/breakpad_linux.cc b/components/crash/content/app/breakpad_linux.cc |
index 6e5058e9563ebfbe932835c763dc04712d682979..b621143ecbf2d3fd153272fbfe3b35b49efc4376 100644 |
--- a/components/crash/content/app/breakpad_linux.cc |
+++ b/components/crash/content/app/breakpad_linux.cc |
@@ -100,6 +100,7 @@ ExceptionHandler* g_breakpad = nullptr; |
#if defined(ADDRESS_SANITIZER) |
const char* g_asan_report_str = nullptr; |
#endif |
+ |
#if defined(OS_ANDROID) |
#define G_DUMPS_SUPPRESSED_MAGIC 0x5AFECEDE |
uint32_t g_dumps_suppressed = 0; |
@@ -1157,16 +1158,41 @@ void LoadDataFromFile(google_breakpad::PageAllocator& allocator, |
LoadDataFromFD(allocator, *fd, true, file_data, size); |
} |
+// Concatenates a |prefix| and a |number| to get a string like "--foo=123" or |
+// "/dev/fd/4". Safe to run in a compromised context. The returned memory is |
+// internally owned by |allocator|. |
+char* StringFromPrefixAndUint(const char* prefix, |
+ uint64_t number, |
+ google_breakpad::PageAllocator* allocator) { |
+ // Convert the number to a string. |
+ char number_buf[kUint64StringSize]; |
+ const unsigned number_len = my_uint64_len(number); |
+ my_uint64tos(number_buf, number, number_len); |
+ number_buf[number_len] = '\0'; |
+ |
+ // Concatenate the prefix and number. |
+ size_t output_len = my_strlen(prefix) + my_strlen(number_buf) + 1; |
+ char* output = reinterpret_cast<char*>(allocator->Alloc(output_len)); |
+ my_strlcpy(output, prefix, output_len); |
+ my_strlcat(output, number_buf, output_len); |
+ return output; |
+} |
+ |
// Spawn the appropriate upload process for the current OS: |
// - generic Linux invokes wget. |
-// - ChromeOS invokes crash_reporter. |
+// - Chrome OS invokes crash_reporter. Crashes are uploaded by a separate |
+// crash_sender script that runs periodically. |
// |dumpfile| is the path to the dump data file. |
// |mime_boundary| is only used on Linux. |
// |exe_buf| is only used on CrOS and is the crashing process' name. |
+// |upload_status_fd| is the file descriptor of a pipe that will receive: |
+// - On Linux, the crash report id |
+// - On Chrome OS, the magic crash complete string. |
void ExecUploadProcessOrTerminate(const BreakpadInfo& info, |
const char* dumpfile, |
const char* mime_boundary, |
const char* exe_buf, |
+ int upload_status_fd, |
google_breakpad::PageAllocator* allocator) { |
#if defined(OS_CHROMEOS) |
// CrOS uses crash_reporter instead of wget to report crashes, |
@@ -1174,16 +1200,13 @@ void ExecUploadProcessOrTerminate(const BreakpadInfo& info, |
// crashing process. |
static const char kCrashReporterBinary[] = "/sbin/crash_reporter"; |
- char pid_buf[kUint64StringSize]; |
- uint64_t pid_str_length = my_uint64_len(info.pid); |
- my_uint64tos(pid_buf, info.pid, pid_str_length); |
- pid_buf[pid_str_length] = '\0'; |
- |
- char uid_buf[kUint64StringSize]; |
- uid_t uid = geteuid(); |
- uint64_t uid_str_length = my_uint64_len(uid); |
- my_uint64tos(uid_buf, uid, uid_str_length); |
- uid_buf[uid_str_length] = '\0'; |
+ // crash_reporter writes output to stdout. Connect it to the status pipe fd. |
+ if (sys_dup2(upload_status_fd, STDOUT_FILENO) == -1) { |
+ const char err[] = "dup2 failed\n"; |
+ WriteLog(err, sizeof(err) - 1); |
+ // Continue anyway, as crash_report may succeed even if we can't read its |
+ // status. |
+ } |
const char kChromeFlag[] = "--chrome="; |
size_t buf_len = my_strlen(dumpfile) + sizeof(kChromeFlag); |
@@ -1192,19 +1215,8 @@ void ExecUploadProcessOrTerminate(const BreakpadInfo& info, |
my_strlcat(chrome_flag, kChromeFlag, buf_len); |
my_strlcat(chrome_flag, dumpfile, buf_len); |
- const char kPidFlag[] = "--pid="; |
- buf_len = my_strlen(pid_buf) + sizeof(kPidFlag); |
- char* pid_flag = reinterpret_cast<char*>(allocator->Alloc(buf_len)); |
- pid_flag[0] = '\0'; |
- my_strlcat(pid_flag, kPidFlag, buf_len); |
- my_strlcat(pid_flag, pid_buf, buf_len); |
- |
- const char kUidFlag[] = "--uid="; |
- buf_len = my_strlen(uid_buf) + sizeof(kUidFlag); |
- char* uid_flag = reinterpret_cast<char*>(allocator->Alloc(buf_len)); |
- uid_flag[0] = '\0'; |
- my_strlcat(uid_flag, kUidFlag, buf_len); |
- my_strlcat(uid_flag, uid_buf, buf_len); |
+ char* pid_flag = StringFromPrefixAndUint("--pid=", info.pid, allocator); |
+ char* uid_flag = StringFromPrefixAndUint("--uid=", geteuid(), allocator); |
const char kExeBuf[] = "--exe="; |
buf_len = my_strlen(exe_buf) + sizeof(kExeBuf); |
@@ -1223,7 +1235,9 @@ void ExecUploadProcessOrTerminate(const BreakpadInfo& info, |
}; |
static const char msg[] = "Cannot upload crash dump: cannot exec " |
"/sbin/crash_reporter\n"; |
-#else |
+ |
+#else // defined(OS_CHROMEOS) |
+ |
// Compress |dumpfile| with gzip. |
const pid_t gzip_child = sys_fork(); |
if (gzip_child < 0) { |
@@ -1293,6 +1307,10 @@ void ExecUploadProcessOrTerminate(const BreakpadInfo& info, |
my_strlcpy(post_file, post_file_msg, post_file_size); |
my_strlcat(post_file, dumpfile, post_file_size); |
+ // Write the wget status output to the status pipe file descriptor path. |
+ char* status_fd_path = |
+ StringFromPrefixAndUint("/dev/fd/", upload_status_fd, allocator); |
+ |
static const char kWgetBinary[] = "/usr/bin/wget"; |
const char* args[] = { |
kWgetBinary, |
@@ -1302,13 +1320,14 @@ void ExecUploadProcessOrTerminate(const BreakpadInfo& info, |
kUploadURL, |
"--timeout=10", // Set a timeout so we don't hang forever. |
"--tries=1", // Don't retry if the upload fails. |
- "-O", // output reply to fd 3 |
- "/dev/fd/3", |
+ "-O", // Output reply to the file descriptor path. |
+ status_fd_path, |
nullptr, |
}; |
static const char msg[] = "Cannot upload crash dump: cannot exec " |
"/usr/bin/wget\n"; |
-#endif |
+#endif // defined(OS_CHROMEOS) |
+ |
execve(args[0], const_cast<char**>(args), environ); |
WriteLog(msg, sizeof(msg) - 1); |
sys__exit(1); |
@@ -1352,9 +1371,13 @@ size_t WaitForCrashReportUploadProcess(int fd, size_t bytes_to_read, |
// |buf| should be |expected_len| + 1 characters in size and nullptr terminated. |
bool IsValidCrashReportId(const char* buf, size_t bytes_read, |
size_t expected_len) { |
- if (bytes_read != expected_len) |
+ if (bytes_read != expected_len) { |
+ static const char msg[] = "Unexpected crash report id length\n"; |
+ WriteLog(msg, sizeof(msg) - 1); |
return false; |
+ } |
#if defined(OS_CHROMEOS) |
+ // See kSuccessMagic in platform2/crash-reporter/chrome_collector.cc. |
return my_strcmp(buf, "_sys_cr_finished") == 0; |
#else |
for (size_t i = 0; i < bytes_read; ++i) { |
@@ -1372,7 +1395,7 @@ void HandleCrashReportId(const char* buf, size_t bytes_read, |
if (!IsValidCrashReportId(buf, bytes_read, expected_len)) { |
#if defined(OS_CHROMEOS) |
static const char msg[] = |
- "System crash-reporter failed to process crash report."; |
+ "System crash_reporter failed to process crash report."; |
#else |
static const char msg[] = "Failed to get crash dump id."; |
#endif |
@@ -1447,6 +1470,27 @@ const char* GetCrashingProcessName(const BreakpadInfo& info, |
} |
#endif |
+// Attempts to close all open file descriptors other than stdin, stdout and |
+// stderr (0, 1, and 2). |
+void CloseAllFileDescriptors() { |
+ const int fd = sys_open("/proc/self/fd", O_DIRECTORY | O_RDONLY, 0); |
+ if (fd < 0) { |
+ for (unsigned i = 3; i < 8192; ++i) |
+ IGNORE_RET(sys_close(i)); |
+ } else { |
+ google_breakpad::DirectoryReader reader(fd); |
+ const char* name; |
+ while (reader.GetNextEntry(&name)) { |
+ int i; |
+ if (my_strtoui(&i, name) && i > 2 && i != fd) |
+ IGNORE_RET(sys_close(i)); |
+ reader.PopEntry(); |
+ } |
+ |
+ IGNORE_RET(sys_close(fd)); |
+ } |
+} |
+ |
void HandleCrashDump(const BreakpadInfo& info) { |
int dumpfd; |
bool keep_fd = false; |
@@ -1779,22 +1823,7 @@ void HandleCrashDump(const BreakpadInfo& info) { |
// hold them open for too long. |
// |
// Thus, we have to loop and try and close everything. |
- const int fd = sys_open("/proc/self/fd", O_DIRECTORY | O_RDONLY, 0); |
- if (fd < 0) { |
- for (unsigned i = 3; i < 8192; ++i) |
- IGNORE_RET(sys_close(i)); |
- } else { |
- google_breakpad::DirectoryReader reader(fd); |
- const char* name; |
- while (reader.GetNextEntry(&name)) { |
- int i; |
- if (my_strtoui(&i, name) && i > 2 && i != fd) |
- IGNORE_RET(sys_close(i)); |
- reader.PopEntry(); |
- } |
- |
- IGNORE_RET(sys_close(fd)); |
- } |
+ CloseAllFileDescriptors(); |
IGNORE_RET(sys_setsid()); |
@@ -1805,15 +1834,15 @@ void HandleCrashDump(const BreakpadInfo& info) { |
const pid_t upload_child = sys_fork(); |
if (!upload_child) { |
// Upload process. |
- IGNORE_RET(sys_close(fds[0])); |
- IGNORE_RET(sys_dup2(fds[1], 3)); |
+ IGNORE_RET(sys_close(fds[0])); // Close read end of pipe. |
+ // Write status to the pipe. |
ExecUploadProcessOrTerminate(info, temp_file, mime_boundary, exe_buf, |
- &allocator); |
+ fds[1], &allocator); |
} |
// Helper process. |
if (upload_child > 0) { |
- IGNORE_RET(sys_close(fds[1])); |
+ IGNORE_RET(sys_close(fds[1])); // Close write end of pipe. |
const size_t kCrashIdLength = 16; |
char id_buf[kCrashIdLength + 1]; |