diff options
-rw-r--r-- | base/linux_util.cc | 47 | ||||
-rw-r--r-- | base/linux_util.h | 6 | ||||
-rw-r--r-- | chrome/app/breakpad_linux.cc | 14 | ||||
-rw-r--r-- | chrome/browser/crash_handler_host_linux.cc | 54 |
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"); |