【问题标题】:Ptrace prevents signal from interrupting pselect() in traced processPtrace 防止信号在被跟踪的进程中中断 pselect()
【发布时间】:2016-07-13 09:24:59
【问题描述】:

我正在尝试使用 ptrace 监控二进制文件的系统调用。二进制文件在 pselect() 中休眠,并且没有 ptrace,SIGQUIT 使其从 pselect 返回。传递给 pselect 的阻塞信号掩码包括 SIGQUIT。

当使用 ptrace 执行时,它会从 sys_pselect6 中退出,但不会完全退出 glibc 的 pselect。我在做什么阻止 sys_pselect6 退出到用户代码?

追踪器:

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

int main(int argc, char *argv[])
{
    int pid = fork(), sys_in = 1, status;

    if (pid == 0) {
        if (ptrace(PTRACE_TRACEME, getppid(), NULL, NULL) < 0)
            err(1, "TRACEME()");

        execl("./child", "./child", NULL);
        err(1, "execl()");
    }

    if (waitpid(pid, &status, 0) != pid) err(1, "wait()");

    for (;; sys_in ^= 1) {
        if (ptrace(PTRACE_SYSCALL, pid, NULL, NULL) < 0) err(1, "SYSCALL");

        if (waitpid(pid, &status, 0) != pid) err(1, "wait()");

        if (sys_in) {
            long long sys_no = ptrace(PTRACE_PEEKUSER, pid, 8 * ORIG_RAX, NULL);
            printf("syscall entry %lld\n", sys_no);
        }
        else printf("syscall exit\n");
    }
    return 0;
}

孩子:

#include <stdio.h>
#include <sys/select.h>
#include <signal.h>
#include <err.h>

void handle_sigquit(int sig, siginfo_t* info, void *ctx)
{
}

int main()
{
    sigset_t mask;
    sigset_t orig_mask;
    struct sigaction sa = {};

    sa.sa_flags = SA_SIGINFO;
    sa.sa_sigaction = handle_sigquit;
    sigaction(SIGQUIT, &sa, NULL);

    sigemptyset(&mask);
    sigaddset(&mask, SIGQUIT);

    if (sigprocmask(SIG_BLOCK, &mask, &orig_mask) < 0) err(1, "sigprocmask()");

    pselect(0, NULL, NULL, NULL, NULL, &orig_mask);
    warn("pselect()");
    return 0;
}

【问题讨论】:

    标签: linux unix select signals ptrace


    【解决方案1】:
    ptrace(PTRACE_SYSCALL, pid, NULL, NULL)
    

    每当您的调试器收到通知时,您只需假定该通知是关于系统调用的,并相应地处理它。事实并非如此。

    您使用wait 收到的一些通知是针对您的被调试者收到的信号。当这些发生时,PTRACE_SYSCALL 调用中的最后一个 NULL 会消除(有效地屏蔽)到达被调试进程的信号。

    在处理ptrace 结果时,您需要检查导致调试器唤醒的信号。至少,检查它是否是SIGTRAP 或其他东西。如果是别的东西,最好的办法是把它传递给被调试进程。

    查看此small program 以了解一种简单的方法。

    【讨论】:

      最近更新 更多