1

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);
}
273K
  • 29,503
  • 10
  • 41
  • 64
Olivia22
  • 131
  • 7
  • 1
    Please show _actual_ code. What is `ptrace_me` ? – Employed Russian Jun 17 '22 at 15:04
  • @EmployedRussian added the ptrace clarification. – Olivia22 Jun 17 '22 at 15:07
  • 1
    The code does not compile. There is an `int statut`, but later on `status` is used. `pe` is not defined - and so on. A minimal, reproducible example would allow people to help: https://stackoverflow.com/help/minimal-reproducible-example – Stephan Schlecht Jun 17 '22 at 15:11
  • 1
    I'd leave off the `raise`--it's not needed. You may need other `PTRACE_O_*` options. Do you see _any_ action by the target child program? My recent answer may be of some use: [ptrace options not working in parent process](https://stackoverflow.com/a/72620471) – Craig Estey Jun 17 '22 at 15:13
  • 4
    Please post code that I can copy, paste to an online compiler such as [godbolt](https://godbolt.org/), and run. This means *you* should copy it *from the draft of your question*, paste to an online compiler, abd run. If it does not run, do not post it. – n. m. could be an AI Jun 17 '22 at 15:16
  • @CraigEstey Hi, Yes well the action comes from the execve which will trigger a Segfault. Your code was super helpful btw. However the way I was proceding forced me to use raise(sigtstop). Thanks for your explanation again its always insightful – Olivia22 Jun 17 '22 at 17:00

1 Answers1

2

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
Craig Estey
  • 30,627
  • 4
  • 24
  • 48