Home > Enterprise >  WIFSIGNALED not catching SEGFAULT signal
WIFSIGNALED not catching SEGFAULT signal

Time:06-18

Im fairly certain of what WIFEXITED and WIFSIGNALED are and how they work. I dont know why its not working since the program does a segfault, and I can very well see(debugged) that that signal on the third while iteration is a SEGFAULT. Thanks for any info.

segfault_prog

#include <stdlib.h>
#include <stdio.h>
int main() {
        int *i = 0x478734;
        printf("%d", *i);
}
int main(void) {
    int status;
    int ret = fork();

    if (ret == 0) {
        ptrace(PTRACE_TRACEME, ret, NULL, NULL);
        raise(SIGSTOP);
        execve(argv[1], &argv[1], NULL);
        return 1;
    }
    else {
        while (1) {
            pid_t val = waitpid(ret, &status, 0);
            ptrace(PTRACE_SETOPTIONS, ret, 0,PTRACE_O_EXITKILL | PTRACE_O_TRACEEXEC);

            if (WIFEXITED(status) && val == ret){
                printf("exited normally");
                return WEXITSTATUS (status);
            }
            else if (WIFSIGNALED(status)){
//if I change for WEXITSTATUS(status) == SIGSEGV. then it works. First while iteration wIFEXITVAL(SIGSTOP/19), 2nd (SIGTRAP/5), 3rd (SIGSEGV 11);
                printf("signal error");
                return WEXITSTATUS (status);
            }

            ptrace(PTRACE_CONT, val, 0, 0);
        }
    }
    return (0);
}

CodePudding user response:

A few issues ...

  1. When tracee/child is stopped (i.e. WIFSTOPPED), the signal (i.e. WSTOPSIG) must be sent down to the tracee. This is the last arg for PTRACE_CONT

  2. Doing PTRACE_SETOPTIONS is only needed after the first waitpid

  3. waitpid should have a first arg of -1 to catch all children

By not sending the signal down via the PTRACE_CONT [AFAICT], the signal is deferred [until the tracer elects to send it down]. So, the tracee/child generated the signal but it was never sent to the tracee/child.

Also, as I mentioned, you may want to have a look at my recent answer: ptrace options not working in parent process


Here is the refactored code. It is annotated with the bugs and fixes:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ptrace.h>
#include <sys/wait.h>

int
main(int argc,char **argv)
{
    int status;
    int ret = fork();

    if (ret == 0) {
        ptrace(PTRACE_TRACEME, ret, NULL, NULL);
// NOTE/BUG: not needed
#if 0
        raise(SIGSTOP);
#endif
        execve(argv[1], &argv[1], NULL);
#if 0
        return 1;
#else
        exit(1);
#endif
    }

#if TEST
    int iter = 20;
#else
    int iter = 0x7FFFFFFF;
#endif

    int first = 1;

    for (;  iter > 0;  --iter) {
// NOTE/BUG: we should catch all children (tracee might do fork but not wait
// and orphan/zombie its child (our grandchild) and we'd want to see that)
#if 0
        pid_t val = waitpid(ret, &status, 0);
#else
        pid_t val = waitpid(-1, &status, 0);
#endif

#if 1
        if (first) {
            ptrace(PTRACE_SETOPTIONS, ret, 0,
                PTRACE_O_EXITKILL | PTRACE_O_TRACEEXEC);
            first = 0;
        }
#endif

        printf("pt: status=%8.8X\n",status);

        int signo = 0;

        if (WIFEXITED(status)) {
            printf("pt: WIFEXITED %d\n",WEXITSTATUS(status));
        }

        if (WIFSTOPPED(status)) {
            signo = WSTOPSIG(status);
            printf("pt: WIFSTOPPED %d\n",signo);
            if (signo == SIGTRAP)
                signo = 0;
        }

        if (WIFSIGNALED(status)) {
            printf("pt: WIFSIGNALED %d\n",WTERMSIG(status));
        }

        if (WIFEXITED(status) && val == ret) {
            printf("exited normally");
            return WEXITSTATUS(status);
        }
        else if (WIFSIGNALED(status)) {
            // if I change for WEXITSTATUS(status) == SIGSEGV. then it works.
            // First while iteration wIFEXITVAL(SIGSTOP/19), 2nd (SIGTRAP/5),
            // 3rd (SIGSEGV 11);
#if 0
            printf("signal error");
#else
            printf("signal error -- %d\n",WTERMSIG(status));
#endif
            return WEXITSTATUS(status);
        }

// NOTE/BUG: must send signo from WIFSTOPPED/WSTOPSIG to tracee
#if SHOWBUG
        ptrace(PTRACE_CONT, val, 0, 0);
#else
        ptrace(PTRACE_CONT, val, 0, signo);
#endif
    }

    if (iter <= 0)
        printf("pt: fault -- not seen\n");

    return (0);
}

In the above code, I've used cpp conditionals to denote old vs new code:

#if 0
// old code
#else
// new code
#endif

#if 1
// new code
#endif

Here is the original behavior (compiled with -DTEST -DSHOWBUG):

pt: status=0000057F
pt: WIFSTOPPED 5
pt: status=00000B7F
pt: WIFSTOPPED 11
pt: status=00000B7F
pt: WIFSTOPPED 11
pt: status=00000B7F
pt: WIFSTOPPED 11
pt: status=00000B7F
pt: WIFSTOPPED 11
pt: status=00000B7F
pt: WIFSTOPPED 11
pt: status=00000B7F
pt: WIFSTOPPED 11
pt: status=00000B7F
pt: WIFSTOPPED 11
pt: status=00000B7F
pt: WIFSTOPPED 11
pt: status=00000B7F
pt: WIFSTOPPED 11
pt: status=00000B7F
pt: WIFSTOPPED 11
pt: status=00000B7F
pt: WIFSTOPPED 11
pt: status=00000B7F
pt: WIFSTOPPED 11
pt: status=00000B7F
pt: WIFSTOPPED 11
pt: status=00000B7F
pt: WIFSTOPPED 11
pt: status=00000B7F
pt: WIFSTOPPED 11
pt: status=00000B7F
pt: WIFSTOPPED 11
pt: status=00000B7F
pt: WIFSTOPPED 11
pt: status=00000B7F
pt: WIFSTOPPED 11
pt: status=00000B7F
pt: WIFSTOPPED 11
pt: fault -- not seen

Here is the output with the fix:

pt: status=0000057F
pt: WIFSTOPPED 5
pt: status=00000B7F
pt: WIFSTOPPED 11
pt: status=0000008B
pt: WIFSIGNALED 11
signal error -- 11
  • Related