From 35b9c6934fc5c1e2ea4cf7e30b91b3b91e48074d Mon Sep 17 00:00:00 2001 From: Jiajie Li Date: Mon, 12 Oct 2020 16:16:57 +0800 Subject: [PATCH 39/89] kpatch_ptrace: Split function kpatch_ptrace_waitpid The function kpatch_ptrace_waitpid is arch related, let's rename it with kpatch_arch_ptrace_waitpid, and make the defination in arch/x86/arch_ptrace.c and arch/aarch64/arch_ptrace.c Signed-off-by: Jiajie Li Signed-off-by: Ying Fang --- src/arch/aarch64/arch_ptrace.c | 145 +++++++++++++++++++++++++++++++++ src/arch/x86/arch_ptrace.c | 140 +++++++++++++++++++++++++++++++ src/include/kpatch_ptrace.h | 20 +++++ src/kpatch_ptrace.c | 130 +---------------------------- 4 files changed, 307 insertions(+), 128 deletions(-) diff --git a/src/arch/aarch64/arch_ptrace.c b/src/arch/aarch64/arch_ptrace.c index e69de29..fb19e86 100644 --- a/src/arch/aarch64/arch_ptrace.c +++ b/src/arch/aarch64/arch_ptrace.c @@ -0,0 +1,145 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "include/kpatch_process.h" +#include "include/kpatch_common.h" +#include "include/kpatch_ptrace.h" +#include "include/kpatch_log.h" + +#include + +int +kpatch_arch_ptrace_waitpid(kpatch_process_t *proc, + struct timespec *timeout, + const sigset_t *sigset) +{ + struct kpatch_ptrace_ctx *pctx; + siginfo_t siginfo; + int ret, status; + pid_t pid; + struct user_regs_struct regs; + struct iovec regs_iov; + + regs_iov.iov_base = ®s; + regs_iov.iov_len = sizeof(regs); + + /* Immediately reap one attached thread */ + pid = waitpid(-1, &status, __WALL | WNOHANG); + + if (pid < 0) { + kplogerror("can't wait for tracees\n"); + return -1; + } + + /* There is none ready, wait for notification via signal */ + if (pid == 0) { + ret = sigtimedwait(sigset, &siginfo, timeout); + if (ret == -1 && errno == EAGAIN) { + /* We have timeouted */ + return -1; + } + + if (ret == -1 && errno == EINVAL) { + kperr("invalid timeout\n"); + return -1; + } + + /* We have got EINTR and must restart */ + if (ret == -1 && errno == EINTR) + return 0; + + /** + * Kernel stacks signals that follow too quickly. + * Deal with it by waiting for any child, not just + * one that is specified in signal + */ + pid = waitpid(-1, &status, __WALL | WNOHANG); + + if (pid == 0) { + kperr("missing waitpid for %d\n", siginfo.si_pid); + return 0; + } + + if (pid < 0) { + kplogerror("can't wait for tracee %d\n", siginfo.si_pid); + return -1; + } + } + + if (!WIFSTOPPED(status) && WIFSIGNALED(status)) { + /* Continue, resending the signal */ + ret = ptrace(PTRACE_CONT, pid, NULL, + (void *)(uintptr_t)WTERMSIG(status)); + if (ret < 0) { + kplogerror("can't start tracee %d\n", pid); + return -1; + } + return 0; + } + + if (WIFEXITED(status)) { + pctx = kpatch_ptrace_find_thread(proc, pid, 0UL); + if (pctx == NULL) { + kperr("got unexpected child '%d' exit\n", pid); + } else { + /* It's dead */ + pctx->pid = pctx->running = 0; + } + return 1; + } + + ret = ptrace(PTRACE_GETREGSET, pid, (void *)NT_PRSTATUS, (void *)®s_iov); + if (ret < 0) { + kplogerror("can't get regs %d\n", pid); + return -1; + } + + pctx = kpatch_ptrace_find_thread(proc, pid, regs.pc); + + if (pctx == NULL) { + /* We either don't know anything about this thread or + * even worse -- we stopped it in the wrong place. + * Bail out. + */ + pctx = kpatch_ptrace_find_thread(proc, pid, 0); + if (pctx != NULL) + pctx->running = 0; + + /* TODO: fix the latter by SINGLESTEPping such a thread with + * the original instruction in place */ + kperr("the thread ran out: %d, pc= %llx, expected = %lx\n", pid, + regs.pc, pctx->execute_until); + errno = ESRCH; + return -1; + } + + pctx->running = 0; + + /* Restore thread registers, pctx is now valid */ + kpdebug("Got thread %d at %llx\n", pctx->pid, + regs.pc - BREAK_INSN_LENGTH); + + regs.pc = pctx->execute_until; + + ret = ptrace(PTRACE_SETREGSET, pctx->pid, (void*)NT_PRSTATUS, (void*)®s_iov); + if (ret < 0) { + kplogerror("can't set regs - %d\n", pctx->pid); + return -1; + } + + return 1; +} diff --git a/src/arch/x86/arch_ptrace.c b/src/arch/x86/arch_ptrace.c index e69de29..6e943fd 100644 --- a/src/arch/x86/arch_ptrace.c +++ b/src/arch/x86/arch_ptrace.c @@ -0,0 +1,140 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "include/kpatch_process.h" +#include "include/kpatch_common.h" +#include "include/kpatch_ptrace.h" +#include "include/kpatch_log.h" + +#include + +int kpatch_arch_ptrace_waitpid(kpatch_process_t *proc, + struct timespec *timeout, + const sigset_t *sigset) +{ + struct kpatch_ptrace_ctx *pctx; + siginfo_t siginfo; + int ret, status; + pid_t pid; + struct user_regs_struct regs; + + /* Immediately reap one attached thread */ + pid = waitpid(-1, &status, __WALL | WNOHANG); + + if (pid < 0) { + kplogerror("can't wait for tracees\n"); + return -1; + } + + /* There is none ready, wait for notification via signal */ + if (pid == 0) { + ret = sigtimedwait(sigset, &siginfo, timeout); + if (ret == -1 && errno == EAGAIN) { + /* We have timeouted */ + return -1; + } + + if (ret == -1 && errno == EINVAL) { + kperr("invalid timeout\n"); + return -1; + } + + /* We have got EINTR and must restart */ + if (ret == -1 && errno == EINTR) + return 0; + + /** + * Kernel stacks signals that follow too quickly. + * Deal with it by waiting for any child, not just + * one that is specified in signal + */ + pid = waitpid(-1, &status, __WALL | WNOHANG); + + if (pid == 0) { + kperr("missing waitpid for %d\n", siginfo.si_pid); + return 0; + } + + if (pid < 0) { + kplogerror("can't wait for tracee %d\n", siginfo.si_pid); + return -1; + } + } + + if (!WIFSTOPPED(status) && WIFSIGNALED(status)) { + /* Continue, resending the signal */ + ret = ptrace(PTRACE_CONT, pid, NULL, + (void *)(uintptr_t)WTERMSIG(status)); + if (ret < 0) { + kplogerror("can't start tracee %d\n", pid); + return -1; + } + return 0; + } + + if (WIFEXITED(status)) { + pctx = kpatch_ptrace_find_thread(proc, pid, 0UL); + if (pctx == NULL) { + kperr("got unexpected child '%d' exit\n", pid); + } else { + /* It's dead */ + pctx->pid = pctx->running = 0; + } + return 1; + } + + ret = ptrace(PTRACE_GETREGS, pid, NULL, ®s); + if (ret < 0) { + kplogerror("can't get regs %d\n", pid); + return -1; + } + + pctx = kpatch_ptrace_find_thread(proc, pid, regs.rip); + + if (pctx == NULL) { + /* We either don't know anything about this thread or + * even worse -- we stopped it in the wrong place. + * Bail out. + */ + pctx = kpatch_ptrace_find_thread(proc, pid, 0); + if (pctx != NULL) + pctx->running = 0; + + /* TODO: fix the latter by SINGLESTEPping such a thread with + * the original instruction in place */ + kperr("the thread ran out: %d, rip = %llx, expected = %lx\n", pid, + regs.rip, pctx->execute_until); + errno = ESRCH; + return -1; + } + + pctx->running = 0; + + /* Restore thread registers, pctx is now valid */ + kpdebug("Got thread %d at %llx\n", pctx->pid, + regs.rip - BREAK_INSN_LENGTH); + + regs.rip = pctx->execute_until; + + ret = ptrace(PTRACE_SETREGS, pctx->pid, NULL, ®s); + if (ret < 0) { + kplogerror("can't set regs - %d\n", pctx->pid); + return -1; + } + + return 1; +} diff --git a/src/include/kpatch_ptrace.h b/src/include/kpatch_ptrace.h index 7557e1f..1c7d33e 100644 --- a/src/include/kpatch_ptrace.h +++ b/src/include/kpatch_ptrace.h @@ -102,4 +102,24 @@ kpatch_process_memcpy(kpatch_process_t *proc, unsigned long dst, unsigned long src, size_t size); + +#define BREAK_INSN_LENGTH 1 +#define BREAK_INSN {0xcc} + +#define SEC_TO_MSEC 1000 +#define MSEC_TO_NSEC 1000000 + +#define for_each_thread(proc, pctx) \ + list_for_each_entry(pctx, &proc->ptrace.pctxs, list) + +struct kpatch_ptrace_ctx * +kpatch_ptrace_find_thread(kpatch_process_t *proc, + pid_t pid, + unsigned long rip); + +int +kpatch_arch_ptrace_waitpid(kpatch_process_t *proc, + struct timespec *timeout, + const sigset_t *sigset); + #endif diff --git a/src/kpatch_ptrace.c b/src/kpatch_ptrace.c index 8910aa8..3c57288 100644 --- a/src/kpatch_ptrace.c +++ b/src/kpatch_ptrace.c @@ -180,16 +180,8 @@ int kpatch_ptrace_get_entry_point(struct kpatch_ptrace_ctx *pctx, return entry[0] == AT_ENTRY ? 0 : -1; } -#define BREAK_INSN_LENGTH 1 -#define BREAK_INSN {0xcc} -#define SEC_TO_MSEC 1000 -#define MSEC_TO_NSEC 1000000 - -#define for_each_thread(proc, pctx) \ - list_for_each_entry(pctx, &proc->ptrace.pctxs, list) - -static struct kpatch_ptrace_ctx * +struct kpatch_ptrace_ctx * kpatch_ptrace_find_thread(kpatch_process_t *proc, pid_t pid, unsigned long rip) @@ -213,124 +205,6 @@ kpatch_ptrace_find_thread(kpatch_process_t *proc, return NULL; } -static inline int -kpatch_ptrace_waitpid(kpatch_process_t *proc, - struct timespec *timeout, - const sigset_t *sigset) -{ - struct kpatch_ptrace_ctx *pctx; - siginfo_t siginfo; - int ret, status; - pid_t pid; - struct user_regs_struct regs; - - /* Immediately reap one attached thread */ - pid = waitpid(-1, &status, __WALL | WNOHANG); - - if (pid < 0) { - kplogerror("can't wait for tracees\n"); - return -1; - } - - /* There is none ready, wait for notification via signal */ - if (pid == 0) { - ret = sigtimedwait(sigset, &siginfo, timeout); - if (ret == -1 && errno == EAGAIN) { - /* We have timeouted */ - return -1; - } - - if (ret == -1 && errno == EINVAL) { - kperr("invalid timeout\n"); - return -1; - } - - /* We have got EINTR and must restart */ - if (ret == -1 && errno == EINTR) - return 0; - - /** - * Kernel stacks signals that follow too quickly. - * Deal with it by waiting for any child, not just - * one that is specified in signal - */ - pid = waitpid(-1, &status, __WALL | WNOHANG); - - if (pid == 0) { - kperr("missing waitpid for %d\n", siginfo.si_pid); - return 0; - } - - if (pid < 0) { - kplogerror("can't wait for tracee %d\n", siginfo.si_pid); - return -1; - } - } - - if (!WIFSTOPPED(status) && WIFSIGNALED(status)) { - /* Continue, resending the signal */ - ret = ptrace(PTRACE_CONT, pid, NULL, - (void *)(uintptr_t)WTERMSIG(status)); - if (ret < 0) { - kplogerror("can't start tracee %d\n", pid); - return -1; - } - return 0; - } - - if (WIFEXITED(status)) { - pctx = kpatch_ptrace_find_thread(proc, pid, 0UL); - if (pctx == NULL) { - kperr("got unexpected child '%d' exit\n", pid); - } else { - /* It's dead */ - pctx->pid = pctx->running = 0; - } - return 1; - } - - ret = ptrace(PTRACE_GETREGS, pid, NULL, ®s); - if (ret < 0) { - kplogerror("can't get regs %d\n", pid); - return -1; - } - - pctx = kpatch_ptrace_find_thread(proc, pid, regs.rip); - - if (pctx == NULL) { - /* We either don't know anything about this thread or - * even worse -- we stopped it in the wrong place. - * Bail out. - */ - pctx = kpatch_ptrace_find_thread(proc, pid, 0); - if (pctx != NULL) - pctx->running = 0; - - /* TODO: fix the latter by SINGLESTEPping such a thread with - * the original instruction in place */ - kperr("the thread ran out: %d, rip = %llx, expected = %lx\n", pid, - regs.rip, pctx->execute_until); - errno = ESRCH; - return -1; - } - - pctx->running = 0; - - /* Restore thread registers, pctx is now valid */ - kpdebug("Got thread %d at %llx\n", pctx->pid, - regs.rip - BREAK_INSN_LENGTH); - - regs.rip = pctx->execute_until; - - ret = ptrace(PTRACE_SETREGS, pctx->pid, NULL, ®s); - if (ret < 0) { - kplogerror("can't set regs - %d\n", pctx->pid); - return -1; - } - - return 1; -} - struct breakpoint { unsigned long addr; unsigned char orig_code[BREAK_INSN_LENGTH]; @@ -441,7 +315,7 @@ kpatch_ptrace_execute_until(kpatch_process_t *proc, break; } - rv = kpatch_ptrace_waitpid(proc, &timeout, &sigset); + rv = kpatch_arch_ptrace_waitpid(proc, &timeout, &sigset); if (rv < 0) break; -- 2.23.0