summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--base/linux_util.cc47
-rw-r--r--base/linux_util.h6
-rw-r--r--chrome/app/breakpad_linux.cc14
-rw-r--r--chrome/browser/crash_handler_host_linux.cc54
4 files changed, 113 insertions, 8 deletions
diff --git a/base/linux_util.cc b/base/linux_util.cc
index d9fd065..dda6333 100644
--- a/base/linux_util.cc
+++ b/base/linux_util.cc
@@ -6,19 +6,24 @@
#include <dirent.h>
#include <errno.h>
+#include <fcntl.h>
#include <glib.h>
#include <stdlib.h>
#include <sys/stat.h>
+#include <sys/stat.h>
+#include <sys/types.h>
#include <unistd.h>
#include <vector>
#include "base/command_line.h"
#include "base/env_var.h"
+#include "base/file_util.h"
#include "base/lock.h"
#include "base/path_service.h"
#include "base/process_util.h"
#include "base/singleton.h"
+#include "base/scoped_ptr.h"
#include "base/string_util.h"
namespace {
@@ -241,4 +246,46 @@ bool FindProcessHoldingSocket(pid_t* pid_out, ino_t socket_inode) {
return already_found;
}
+pid_t FindThreadIDWithSyscall(pid_t pid, const std::string& expected_data) {
+ char buf[256];
+ snprintf(buf, sizeof(buf), "/proc/%d/task", pid);
+ DIR* task = opendir(buf);
+ if (!task) {
+ LOG(WARNING) << "Cannot open " << buf;
+ return -1;
+ }
+
+ std::vector<pid_t> tids;
+ struct dirent* dent;
+ while ((dent = readdir(task))) {
+ char *endptr;
+ const unsigned long int tid_ul = strtoul(dent->d_name, &endptr, 10);
+ if (tid_ul == ULONG_MAX || *endptr)
+ continue;
+ tids.push_back(tid_ul);
+ }
+ closedir(task);
+
+ scoped_array<char> syscall_data(new char[expected_data.length()]);
+ for (std::vector<pid_t>::const_iterator
+ i = tids.begin(); i != tids.end(); ++i) {
+ const pid_t current_tid = *i;
+ snprintf(buf, sizeof(buf), "/proc/%d/task/%d/syscall", pid, current_tid);
+ int fd = open(buf, O_RDONLY);
+ if (fd < 0)
+ continue;
+ bool read_ret =
+ file_util::ReadFromFD(fd, syscall_data.get(), expected_data.length());
+ close(fd);
+ if (!read_ret)
+ continue;
+
+ if (0 == strncmp(expected_data.c_str(), syscall_data.get(),
+ expected_data.length())) {
+ return current_tid;
+ }
+ }
+ return -1;
+}
+
} // namespace base
diff --git a/base/linux_util.h b/base/linux_util.h
index e810674..a4ba9b6 100644
--- a/base/linux_util.h
+++ b/base/linux_util.h
@@ -29,6 +29,12 @@ bool FileDescriptorGetInode(ino_t* inode_out, int fd);
// multiple processes hold the socket, this function returns false.
bool FindProcessHoldingSocket(pid_t* pid_out, ino_t socket_inode);
+// For a given process |pid|, look through all its threads and find the first
+// thread with /proc/[pid]/task/[thread_id]/syscall whose first N bytes matches
+// |expected_data|, where N is the length of |expected_data|.
+// Returns the thread id or -1 on error.
+pid_t FindThreadIDWithSyscall(pid_t pid, const std::string& expected_data);
+
} // namespace base
#endif // BASE_LINUX_UTIL_H_
diff --git a/chrome/app/breakpad_linux.cc b/chrome/app/breakpad_linux.cc
index 9b65724..bedadd9 100644
--- a/chrome/app/breakpad_linux.cc
+++ b/chrome/app/breakpad_linux.cc
@@ -679,12 +679,17 @@ NonBrowserCrashHandler(const void* crash_context, size_t crash_context_size,
memcpy(crash_url, child_process_logging::active_url.data(), crash_url_len);
memcpy(distro, base::linux_distro.data(), distro_len);
+ char b; // Dummy variable for sys_read below.
+ const char* b_addr = &b; // Get the address of |b| so we can create the
+ // expected /proc/[pid]/syscall content in the
+ // browser to convert namespace tids.
+
// The length of the control message:
static const unsigned kControlMsgSize = CMSG_SPACE(sizeof(int));
struct kernel_msghdr msg;
my_memset(&msg, 0, sizeof(struct kernel_msghdr));
- struct kernel_iovec iov[4];
+ struct kernel_iovec iov[6];
iov[0].iov_base = const_cast<void*>(crash_context);
iov[0].iov_len = crash_context_size;
iov[1].iov_base = guid;
@@ -693,9 +698,13 @@ NonBrowserCrashHandler(const void* crash_context, size_t crash_context_size,
iov[2].iov_len = kMaxActiveURLSize + 1;
iov[3].iov_base = distro;
iov[3].iov_len = kDistroSize + 1;
+ iov[4].iov_base = &b_addr;
+ iov[4].iov_len = sizeof(b_addr);
+ iov[5].iov_base = &fds[0];
+ iov[5].iov_len = sizeof(fds[0]);
msg.msg_iov = iov;
- msg.msg_iovlen = 4;
+ msg.msg_iovlen = 6;
char cmsg[kControlMsgSize];
my_memset(cmsg, 0, kControlMsgSize);
msg.msg_control = cmsg;
@@ -710,7 +719,6 @@ NonBrowserCrashHandler(const void* crash_context, size_t crash_context_size,
HANDLE_EINTR(sys_sendmsg(fd, &msg, 0));
sys_close(fds[1]);
- char b;
HANDLE_EINTR(sys_read(fds[0], &b, 1));
return true;
diff --git a/chrome/browser/crash_handler_host_linux.cc b/chrome/browser/crash_handler_host_linux.cc
index b23b7ea..61ffd7d 100644
--- a/chrome/browser/crash_handler_host_linux.cc
+++ b/chrome/browser/crash_handler_host_linux.cc
@@ -7,6 +7,7 @@
#include <stdint.h>
#include <stdlib.h>
#include <sys/socket.h>
+#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>
@@ -27,6 +28,8 @@
#include "chrome/common/chrome_paths.h"
#include "chrome/common/env_vars.h"
+using google_breakpad::ExceptionHandler;
+
// Since classes derived from CrashHandlerHostLinux are singletons, it's only
// destroyed at the end of the processes lifetime, which is greater in span than
// the lifetime of the IO message loop.
@@ -87,17 +90,20 @@ void CrashHandlerHostLinux::OnFileCanReadWithoutBlocking(int fd) {
CMSG_SPACE(sizeof(int)) + CMSG_SPACE(sizeof(struct ucred));
// The length of the regular payload:
static const unsigned kCrashContextSize =
- sizeof(google_breakpad::ExceptionHandler::CrashContext);
+ sizeof(ExceptionHandler::CrashContext);
struct msghdr msg = {0};
- struct iovec iov[4];
+ struct iovec iov[6];
char crash_context[kCrashContextSize];
char guid[kGuidSize + 1];
char crash_url[kMaxActiveURLSize + 1];
char distro[kDistroSize + 1];
+ char* tid_buf_addr = NULL;
+ int tid_fd = -1;
char control[kControlMsgSize];
const ssize_t expected_msg_size = sizeof(crash_context) + sizeof(guid) +
- sizeof(crash_url) + sizeof(distro);
+ sizeof(crash_url) + sizeof(distro) +
+ sizeof(tid_buf_addr) + sizeof(tid_fd);
iov[0].iov_base = crash_context;
iov[0].iov_len = sizeof(crash_context);
@@ -107,8 +113,12 @@ void CrashHandlerHostLinux::OnFileCanReadWithoutBlocking(int fd) {
iov[2].iov_len = sizeof(crash_url);
iov[3].iov_base = distro;
iov[3].iov_len = sizeof(distro);
+ iov[4].iov_base = &tid_buf_addr;
+ iov[4].iov_len = sizeof(tid_buf_addr);
+ iov[5].iov_base = &tid_fd;
+ iov[5].iov_len = sizeof(tid_fd);
msg.msg_iov = iov;
- msg.msg_iovlen = 4;
+ msg.msg_iovlen = 6;
msg.msg_control = control;
msg.msg_controllen = kControlMsgSize;
@@ -183,12 +193,46 @@ void CrashHandlerHostLinux::OnFileCanReadWithoutBlocking(int fd) {
return;
}
- if (!base::FindProcessHoldingSocket(&crashing_pid, inode_number - 1)) {
+ pid_t actual_crashing_pid = -1;
+ if (!base::FindProcessHoldingSocket(&actual_crashing_pid, inode_number - 1)) {
LOG(WARNING) << "Failed to find process holding other end of crash reply "
"socket";
HANDLE_EINTR(close(signal_fd));
return;
}
+ if (actual_crashing_pid != crashing_pid) {
+ crashing_pid = actual_crashing_pid;
+
+ // The crashing TID set inside the compromised context via sys_gettid()
+ // in ExceptionHandler::HandleSignal is also wrong and needs to be
+ // translated.
+ //
+ // We expect the crashing thread to be in sys_read(), waiting for use to
+ // write to |signal_fd|. Most newer kernels where we have the different pid
+ // namespaces also have /proc/[pid]/syscall, so we can look through
+ // |actual_crashing_pid|'s thread group and find the thread that's in the
+ // read syscall with the right arguments.
+
+ std::string expected_syscall_data;
+ // /proc/[pid]/syscall is formatted as follows:
+ // syscall_number arg1 ... arg6 sp pc
+ // but we just check syscall_number through arg3.
+ StringAppendF(&expected_syscall_data, "%d 0x%x %p 0x1 ",
+ SYS_read, tid_fd, tid_buf_addr);
+ pid_t crashing_tid =
+ base::FindThreadIDWithSyscall(crashing_pid, expected_syscall_data);
+ if (crashing_tid == -1) {
+ // We didn't find the thread we want. Maybe it didn't reach sys_read()
+ // yet, or the kernel doesn't support /proc/[pid]/syscall or the thread
+ // went away. We'll just take a guess here and assume the crashing
+ // thread is the thread group leader.
+ crashing_tid = crashing_pid;
+ }
+
+ ExceptionHandler::CrashContext* bad_context =
+ reinterpret_cast<ExceptionHandler::CrashContext*>(crash_context);
+ bad_context->tid = crashing_tid;
+ }
bool upload = true;
FilePath dumps_path("/tmp");