issue-437 Fixed issues related to new glibc shipped with Ubuntu 10.10

1. ptrace permissions were modifed to be a bit more strict which required
   us to programatically set the permissions while syncing up to the profiling
   thread.

2. Order of destructors registered with atexit changed which was casuing us to
   miss generating the backtrace when heap checker was finished. Seems that we
   initially fixed this for FreeBSD and now linux has changed their behaviour
   to be the same. We are now a bit stricter on the rules here accross all
   platforms.


git-svn-id: http://gperftools.googlecode.com/svn/trunk@152 6b5cf1ce-ec42-a296-1ba9-69fdba395a50
This commit is contained in:
chappedm@gmail.com 2012-09-18 00:00:20 +00:00
parent cd723b43ff
commit fa0209f261
2 changed files with 60 additions and 31 deletions

View File

@ -45,6 +45,8 @@ extern "C" {
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <sys/prctl.h>
#include <semaphore.h>
#include "base/linux_syscall_support.h"
#include "base/thread_lister.h"
@ -240,6 +242,7 @@ struct ListerParams {
ListAllProcessThreadsCallBack callback;
void *parameter;
va_list ap;
sem_t *lock;
};
@ -254,6 +257,13 @@ static void ListerThread(struct ListerParams *args) {
struct kernel_stat marker_sb, proc_sb;
stack_t altstack;
/* Wait for parent thread to set appropriate permissions
* to allow ptrace activity
*/
if (sem_wait(args->lock) < 0) {
goto failure;
}
/* Create "marker" that we can use to detect threads sharing the same
* address space and the same file handles. By setting the FD_CLOEXEC flag
* we minimize the risk of misidentifying child processes as threads;
@ -536,6 +546,7 @@ int ListAllProcessThreads(void *parameter,
pid_t clone_pid;
int dumpable = 1, sig;
struct kernel_sigset_t sig_blocked, sig_old;
sem_t lock;
va_start(args.ap, callback);
@ -565,6 +576,7 @@ int ListAllProcessThreads(void *parameter,
args.altstack_mem = altstack_mem;
args.parameter = parameter;
args.callback = callback;
args.lock = &lock;
/* Before cloning the thread lister, block all asynchronous signals, as we */
/* are not prepared to handle them. */
@ -596,42 +608,59 @@ int ListAllProcessThreads(void *parameter,
#undef SYS_LINUX_SYSCALL_SUPPORT_H
#include "linux_syscall_support.h"
#endif
int clone_errno;
clone_pid = local_clone((int (*)(void *))ListerThread, &args);
clone_errno = errno;
sys_sigprocmask(SIG_SETMASK, &sig_old, &sig_old);
/* Lock before clone so that parent
* can set ptrace permissions prior
* to ListerThread actually executing
*/
if (sem_init(&lock, 0, 0) == 0) {
if (clone_pid >= 0) {
int status, rc;
while ((rc = sys0_waitpid(clone_pid, &status, __WALL)) < 0 &&
ERRNO == EINTR) {
/* Keep waiting */
}
if (rc < 0) {
args.err = ERRNO;
args.result = -1;
} else if (WIFEXITED(status)) {
switch (WEXITSTATUS(status)) {
case 0: break; /* Normal process termination */
case 2: args.err = EFAULT; /* Some fault (e.g. SIGSEGV) detected */
args.result = -1;
break;
case 3: args.err = EPERM; /* Process is already being traced */
args.result = -1;
break;
default:args.err = ECHILD; /* Child died unexpectedly */
args.result = -1;
break;
int clone_errno;
clone_pid = local_clone((int (*)(void *))ListerThread, &args);
clone_errno = errno;
sys_sigprocmask(SIG_SETMASK, &sig_old, &sig_old);
if (clone_pid >= 0) {
/* Allow child process to ptrace us
* and then release lock so that
* ListerThread then executes
*/
prctl(PR_SET_PTRACER, clone_pid, 0, 0, 0);
sem_post(&lock);
int status, rc;
while ((rc = sys0_waitpid(clone_pid, &status, __WALL)) < 0 &&
ERRNO == EINTR) {
/* Keep waiting */
}
} else if (!WIFEXITED(status)) {
args.err = EFAULT; /* Terminated due to an unhandled signal*/
if (rc < 0) {
args.err = ERRNO;
args.result = -1;
} else if (WIFEXITED(status)) {
switch (WEXITSTATUS(status)) {
case 0: break; /* Normal process termination */
case 2: args.err = EFAULT; /* Some fault (e.g. SIGSEGV) detected */
args.result = -1;
break;
case 3: args.err = EPERM; /* Process is already being traced */
args.result = -1;
break;
default:args.err = ECHILD; /* Child died unexpectedly */
args.result = -1;
break;
}
} else if (!WIFEXITED(status)) {
args.err = EFAULT; /* Terminated due to an unhandled signal*/
args.result = -1;
}
sem_destroy(&lock);
} else {
args.result = -1;
args.err = clone_errno;
}
} else {
args.result = -1;
args.err = clone_errno;
args.err = errno;
}
}

View File

@ -2025,9 +2025,9 @@ void HeapLeakChecker_InternalInitStart() {
// at the right time, on FreeBSD we always check after, even in the
// less strict modes. This just means FreeBSD is always a bit
// stricter in its checking than other OSes.
#ifdef __FreeBSD__
// This now appears to be the case in other OSes as well;
// so always check afterwards.
FLAGS_heap_check_after_destructors = true;
#endif
{ SpinLockHolder l(&heap_checker_lock);
RAW_DCHECK(heap_checker_pid == getpid(), "");