mirror of
https://github.com/checkpoint-restore/criu.git
synced 2026-07-17 16:47:50 +00:00
compel: remove hardware breakpoint usage
Hardware breakpoints were originally intended to speed up the resume process by stopping the process at a specific point in the pie code. However, it turned out that they don't provide a significant speedup and, in some cases, can even slow it down. This is especially critical for hosts with a large number of CPUs. Hardware Breakpoint Restore Performance Benchmark ================================================== System: Linux 6.17.0-1007-gcp x86_64 CPU: INTEL(R) XEON(R) PLATINUM 8581C CPU @ 2.10GHz Virt: google CRIU: Version: 4.2 Iterations per data point: 5 Threads With BP (us) Without BP (us) Diff (%) -------- ------------ --------------- -------- >>> Benchmarking with 1 thread(s)... 1 391 326 19.9% >>> Benchmarking with 10 thread(s)... 10 1098 695 58.0% >>> Benchmarking with 50 thread(s)... 50 3772 2344 60.9% >>> Benchmarking with 100 thread(s)... 100 6740 4504 49.6% >>> Benchmarking with 500 thread(s)... 500 31382 19982 57.1% >>> Benchmarking with 1000 thread(s)... 1000 58617 40568 44.5% Notes: 'With BP' = hardware breakpoints enabled (current default) 'Without BP' = CRIU_FAULT=130 (FI_NO_BREAKPOINTS, uses PTRACE_SYSCALL) Positive diff% means breakpoints are slower Signed-off-by: Andrei Vagin <avagin@google.com>
This commit is contained in:
parent
869d481cf2
commit
7daebbe469
34 changed files with 34 additions and 496 deletions
|
|
@ -1,42 +0,0 @@
|
|||
#ifndef __COMPEL_BREAKPOINTS_H__
|
||||
#define __COMPEL_BREAKPOINTS_H__
|
||||
#define ARCH_SI_TRAP TRAP_BRKPT
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
struct hwbp_cap {
|
||||
char arch;
|
||||
char bp_count;
|
||||
};
|
||||
|
||||
/* copied from `linux/arch/arm64/include/asm/hw_breakpoint.h` */
|
||||
/* Lengths */
|
||||
#define ARM_BREAKPOINT_LEN_1 0x1
|
||||
#define ARM_BREAKPOINT_LEN_2 0x3
|
||||
#define ARM_BREAKPOINT_LEN_3 0x7
|
||||
#define ARM_BREAKPOINT_LEN_4 0xf
|
||||
#define ARM_BREAKPOINT_LEN_5 0x1f
|
||||
#define ARM_BREAKPOINT_LEN_6 0x3f
|
||||
#define ARM_BREAKPOINT_LEN_7 0x7f
|
||||
#define ARM_BREAKPOINT_LEN_8 0xff
|
||||
|
||||
/* Privilege Levels */
|
||||
#define AARCH64_BREAKPOINT_EL1 1
|
||||
#define AARCH64_BREAKPOINT_EL0 2
|
||||
|
||||
/* Breakpoint */
|
||||
#define ARM_BREAKPOINT_EXECUTE 0
|
||||
|
||||
/* Watchpoints */
|
||||
#define ARM_BREAKPOINT_LOAD 1
|
||||
#define ARM_BREAKPOINT_STORE 2
|
||||
#define AARCH64_ESR_ACCESS_MASK (1 << 6)
|
||||
|
||||
#define DISABLE_HBP 0
|
||||
#define ENABLE_HBP 1
|
||||
|
||||
int ptrace_set_breakpoint(pid_t pid, void *addr);
|
||||
int ptrace_flush_breakpoints(pid_t pid);
|
||||
|
||||
#endif
|
||||
|
|
@ -12,7 +12,6 @@
|
|||
#include "errno.h"
|
||||
#include "infect.h"
|
||||
#include "infect-priv.h"
|
||||
#include "asm/breakpoints.h"
|
||||
#include "asm/gcs-types.h"
|
||||
#include <linux/prctl.h>
|
||||
|
||||
|
|
@ -255,114 +254,6 @@ unsigned long compel_task_size(void)
|
|||
return task_size;
|
||||
}
|
||||
|
||||
static struct hwbp_cap *ptrace_get_hwbp_cap(pid_t pid)
|
||||
{
|
||||
static struct hwbp_cap info;
|
||||
static int available = -1;
|
||||
|
||||
if (available == -1) {
|
||||
unsigned int val;
|
||||
struct iovec iovec = {
|
||||
.iov_base = &val,
|
||||
.iov_len = sizeof(val),
|
||||
};
|
||||
|
||||
if (ptrace(PTRACE_GETREGSET, pid, NT_ARM_HW_BREAK, &iovec) < 0)
|
||||
available = 0;
|
||||
else {
|
||||
info.arch = (char)((val >> 8) & 0xff);
|
||||
info.bp_count = (char)(val & 0xff);
|
||||
|
||||
available = (info.arch != 0);
|
||||
}
|
||||
}
|
||||
|
||||
return available == 1 ? &info : NULL;
|
||||
}
|
||||
|
||||
int ptrace_set_breakpoint(pid_t pid, void *addr)
|
||||
{
|
||||
k_rtsigset_t block;
|
||||
struct hwbp_cap *info = ptrace_get_hwbp_cap(pid);
|
||||
struct user_hwdebug_state regs = {};
|
||||
unsigned int ctrl = 0;
|
||||
struct iovec iovec;
|
||||
|
||||
if (info == NULL || info->bp_count == 0)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* The struct is copied from `arch/arm64/include/asm/hw_breakpoint.h` in
|
||||
* linux kernel:
|
||||
* struct arch_hw_breakpoint_ctrl {
|
||||
* __u32 __reserved : 19,
|
||||
* len : 8,
|
||||
* type : 2,
|
||||
* privilege : 2,
|
||||
* enabled : 1;
|
||||
* };
|
||||
*
|
||||
* The part of `struct arch_hw_breakpoint_ctrl` bits meaning is defined
|
||||
* in <<ARM Architecture Reference Manual for A-profile architecture>>,
|
||||
* D13.3.2 DBGBCR<n>_EL1, Debug Breakpoint Control Registers.
|
||||
*/
|
||||
ctrl = ARM_BREAKPOINT_LEN_4;
|
||||
ctrl = (ctrl << 2) | ARM_BREAKPOINT_EXECUTE;
|
||||
ctrl = (ctrl << 2) | AARCH64_BREAKPOINT_EL0;
|
||||
ctrl = (ctrl << 1) | ENABLE_HBP;
|
||||
regs.dbg_regs[0].addr = (__u64)addr;
|
||||
regs.dbg_regs[0].ctrl = ctrl;
|
||||
iovec.iov_base = ®s;
|
||||
iovec.iov_len = (offsetof(struct user_hwdebug_state, dbg_regs) + sizeof(regs.dbg_regs[0]));
|
||||
|
||||
if (ptrace(PTRACE_SETREGSET, pid, NT_ARM_HW_BREAK, &iovec))
|
||||
return -1;
|
||||
|
||||
/*
|
||||
* FIXME(issues/1429): SIGTRAP can't be blocked, otherwise its handler
|
||||
* will be reset to the default one.
|
||||
*/
|
||||
ksigfillset(&block);
|
||||
ksigdelset(&block, SIGTRAP);
|
||||
if (ptrace(PTRACE_SETSIGMASK, pid, sizeof(k_rtsigset_t), &block)) {
|
||||
pr_perror("Can't block signals for %d", pid);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ptrace(PTRACE_CONT, pid, NULL, NULL) != 0) {
|
||||
pr_perror("Unable to restart the stopped tracee process %d", pid);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int ptrace_flush_breakpoints(pid_t pid)
|
||||
{
|
||||
struct hwbp_cap *info = ptrace_get_hwbp_cap(pid);
|
||||
struct user_hwdebug_state regs = {};
|
||||
unsigned int ctrl = 0;
|
||||
struct iovec iovec;
|
||||
|
||||
if (info == NULL || info->bp_count == 0)
|
||||
return 0;
|
||||
|
||||
ctrl = ARM_BREAKPOINT_LEN_4;
|
||||
ctrl = (ctrl << 2) | ARM_BREAKPOINT_EXECUTE;
|
||||
ctrl = (ctrl << 2) | AARCH64_BREAKPOINT_EL0;
|
||||
ctrl = (ctrl << 1) | DISABLE_HBP;
|
||||
regs.dbg_regs[0].addr = 0ul;
|
||||
regs.dbg_regs[0].ctrl = ctrl;
|
||||
|
||||
iovec.iov_base = ®s;
|
||||
iovec.iov_len = (offsetof(struct user_hwdebug_state, dbg_regs) + sizeof(regs.dbg_regs[0]));
|
||||
|
||||
if (ptrace(PTRACE_SETREGSET, pid, NT_ARM_HW_BREAK, &iovec))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int inject_gcs_cap_token(struct parasite_ctl *ctl, pid_t pid, struct cr_user_gcs *gcs)
|
||||
{
|
||||
struct iovec gcs_iov = { .iov_base = gcs, .iov_len = sizeof(*gcs) };
|
||||
|
|
|
|||
|
|
@ -1,15 +0,0 @@
|
|||
#ifndef __COMPEL_BREAKPOINTS_H__
|
||||
#define __COMPEL_BREAKPOINTS_H__
|
||||
#define ARCH_SI_TRAP TRAP_BRKPT
|
||||
|
||||
static inline int ptrace_set_breakpoint(pid_t pid, void *addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int ptrace_flush_breakpoints(pid_t pid)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
#define UAPI_COMPEL_ASM_TYPES_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <signal.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#define SIGMAX 64
|
||||
|
|
|
|||
|
|
@ -1,6 +0,0 @@
|
|||
#ifndef __COMPEL_BREAKPOINTS_H__
|
||||
#define __COMPEL_BREAKPOINTS_H__
|
||||
#define ARCH_SI_TRAP TRAP_BRKPT
|
||||
extern int ptrace_set_breakpoint(pid_t pid, void *addr);
|
||||
extern int ptrace_flush_breakpoints(pid_t pid);
|
||||
#endif
|
||||
|
|
@ -2,9 +2,11 @@
|
|||
#define UAPI_COMPEL_ASM_TYPES_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <signal.h>
|
||||
|
||||
#define SIGMAX 64
|
||||
#define SIGMAX_OLD 31
|
||||
#define ARCH_SI_TRAP TRAP_BRKPT
|
||||
|
||||
/*
|
||||
* From the Linux kernel header arch/loongarch/include/uapi/asm/ptrace.h
|
||||
|
|
|
|||
|
|
@ -174,19 +174,6 @@ int arch_fetch_sas(struct parasite_ctl *ctl, struct rt_sigframe *s)
|
|||
return err ? err : ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: add feature
|
||||
*/
|
||||
int ptrace_set_breakpoint(pid_t pid, void *addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ptrace_flush_breakpoints(pid_t pid)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Refer to Linux kernel arch/loongarch/include/asm/processor.h
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,6 +0,0 @@
|
|||
#ifndef __COMPEL_BREAKPOINTS_H__
|
||||
#define __COMPEL_BREAKPOINTS_H__
|
||||
#define ARCH_SI_TRAP TRAP_BRKPT
|
||||
extern int ptrace_set_breakpoint(pid_t pid, void *addr);
|
||||
extern int ptrace_flush_breakpoints(pid_t pid);
|
||||
#endif
|
||||
|
|
@ -8,6 +8,7 @@
|
|||
#include <linux/types.h>
|
||||
#define SIGMAX 64
|
||||
#define SIGMAX_OLD 31
|
||||
#define ARCH_SI_TRAP TRAP_BRKPT
|
||||
|
||||
/*
|
||||
* Copied from the Linux kernel header arch/mips/include/asm/ptrace.h
|
||||
|
|
|
|||
|
|
@ -226,16 +226,6 @@ int arch_fetch_sas(struct parasite_ctl *ctl, struct rt_sigframe *s)
|
|||
return err ? err : ret;
|
||||
}
|
||||
|
||||
int ptrace_set_breakpoint(pid_t pid, void *addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ptrace_flush_breakpoints(pid_t pid)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*refer to kernel linux-3.10/arch/mips/include/asm/processor.h*/
|
||||
#define TASK_SIZE32 0x7fff8000UL
|
||||
#define TASK_SIZE64 0x10000000000UL
|
||||
|
|
|
|||
|
|
@ -1,15 +0,0 @@
|
|||
#ifndef __COMPEL_BREAKPOINTS_H__
|
||||
#define __COMPEL_BREAKPOINTS_H__
|
||||
#define ARCH_SI_TRAP TRAP_BRKPT
|
||||
|
||||
static inline int ptrace_set_breakpoint(pid_t pid, void *addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int ptrace_flush_breakpoints(pid_t pid)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
#ifndef __COMPEL_BREAKPOINTS_H__
|
||||
#define __COMPEL_BREAKPOINTS_H__
|
||||
#define ARCH_SI_TRAP TRAP_BRKPT
|
||||
|
||||
static inline int ptrace_set_breakpoint(pid_t pid, void *addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int ptrace_flush_breakpoints(pid_t pid)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
#ifndef __COMPEL_BREAKPOINTS_H__
|
||||
#define __COMPEL_BREAKPOINTS_H__
|
||||
#define ARCH_SI_TRAP TRAP_BRKPT
|
||||
|
||||
static inline int ptrace_set_breakpoint(pid_t pid, void *addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int ptrace_flush_breakpoints(pid_t pid)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#define SIGMAX 64
|
||||
#define SIGMAX_OLD 31
|
||||
#define ARCH_SI_TRAP TRAP_BRKPT
|
||||
|
||||
/*
|
||||
* Definitions from /usr/include/asm/ptrace.h:
|
||||
|
|
|
|||
|
|
@ -1,6 +0,0 @@
|
|||
#ifndef __COMPEL_BREAKPOINTS_H__
|
||||
#define __COMPEL_BREAKPOINTS_H__
|
||||
#define ARCH_SI_TRAP SI_KERNEL
|
||||
extern int ptrace_set_breakpoint(pid_t pid, void *addr);
|
||||
extern int ptrace_flush_breakpoints(pid_t pid);
|
||||
#endif
|
||||
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#define SIGMAX 64
|
||||
#define SIGMAX_OLD 31
|
||||
#define ARCH_SI_TRAP SI_KERNEL
|
||||
|
||||
#define ARCH_HAS_PTRACE_GET_THREAD_AREA
|
||||
|
||||
|
|
|
|||
|
|
@ -633,70 +633,6 @@ int arch_fetch_sas(struct parasite_ctl *ctl, struct rt_sigframe *s)
|
|||
return err ? err : ret;
|
||||
}
|
||||
|
||||
/* Copied from the gdb header gdb/nat/x86-dregs.h */
|
||||
|
||||
/* Debug registers' indices. */
|
||||
#define DR_FIRSTADDR 0
|
||||
#define DR_LASTADDR 3
|
||||
#define DR_NADDR 4 /* The number of debug address registers. */
|
||||
#define DR_STATUS 6 /* Index of debug status register (DR6). */
|
||||
#define DR_CONTROL 7 /* Index of debug control register (DR7). */
|
||||
|
||||
#define DR_LOCAL_ENABLE_SHIFT 0 /* Extra shift to the local enable bit. */
|
||||
#define DR_GLOBAL_ENABLE_SHIFT 1 /* Extra shift to the global enable bit. */
|
||||
#define DR_ENABLE_SIZE 2 /* Two enable bits per debug register. */
|
||||
|
||||
/* Locally enable the break/watchpoint in the I'th debug register. */
|
||||
#define X86_DR_LOCAL_ENABLE(i) (1 << (DR_LOCAL_ENABLE_SHIFT + DR_ENABLE_SIZE * (i)))
|
||||
|
||||
int ptrace_set_breakpoint(pid_t pid, void *addr)
|
||||
{
|
||||
k_rtsigset_t block;
|
||||
int ret;
|
||||
|
||||
/* Set a breakpoint */
|
||||
if (ptrace(PTRACE_POKEUSER, pid, offsetof(struct user, u_debugreg[DR_FIRSTADDR]), addr)) {
|
||||
pr_perror("Unable to setup a breakpoint into %d", pid);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Enable the breakpoint */
|
||||
if (ptrace(PTRACE_POKEUSER, pid, offsetof(struct user, u_debugreg[DR_CONTROL]),
|
||||
X86_DR_LOCAL_ENABLE(DR_FIRSTADDR))) {
|
||||
pr_perror("Unable to enable the breakpoint for %d", pid);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* FIXME(issues/1429): SIGTRAP can't be blocked, otherwise its handler
|
||||
* will be reset to the default one.
|
||||
*/
|
||||
ksigfillset(&block);
|
||||
ksigdelset(&block, SIGTRAP);
|
||||
if (ptrace(PTRACE_SETSIGMASK, pid, sizeof(k_rtsigset_t), &block)) {
|
||||
pr_perror("Can't block signals for %d", pid);
|
||||
return -1;
|
||||
}
|
||||
ret = ptrace(PTRACE_CONT, pid, NULL, NULL);
|
||||
if (ret) {
|
||||
pr_perror("Unable to restart the stopped tracee process %d", pid);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int ptrace_flush_breakpoints(pid_t pid)
|
||||
{
|
||||
/* Disable the breakpoint */
|
||||
if (ptrace(PTRACE_POKEUSER, pid, offsetof(struct user, u_debugreg[DR_CONTROL]), 0)) {
|
||||
pr_perror("Unable to disable the breakpoint for %d", pid);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ptrace_get_regs(pid_t pid, user_regs_struct_t *regs)
|
||||
{
|
||||
struct iovec iov;
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ struct parasite_ctl {
|
|||
int rpid; /* Real pid of the victim */
|
||||
void *remote_map;
|
||||
void *local_map;
|
||||
void *sigreturn_addr; /* A place for the breakpoint */
|
||||
unsigned long map_length;
|
||||
|
||||
struct infect_ctx ictx;
|
||||
|
|
|
|||
|
|
@ -41,7 +41,6 @@ struct parasite_init_args {
|
|||
int32_t h_addr_len;
|
||||
struct sockaddr_un h_addr;
|
||||
int32_t log_level;
|
||||
uint64_t sigreturn_addr;
|
||||
uint64_t sigframe; /* pointer to sigframe */
|
||||
futex_t daemon_connected;
|
||||
#ifdef ARCH_HAS_LONG_PAGES
|
||||
|
|
|
|||
|
|
@ -95,8 +95,6 @@ enum trace_flags {
|
|||
extern int __must_check compel_stop_on_syscall(int tasks, int sys_nr, int sys_nr_compat);
|
||||
extern int compel_stop_tasks_on_syscall(int tasks, pid_t *pids, const int sys_nr, const int sys_nr_compat);
|
||||
|
||||
extern int __must_check compel_stop_pie(pid_t pid, void *addr, bool no_bp);
|
||||
|
||||
extern int __must_check compel_unmap(struct parasite_ctl *ctl, unsigned long addr);
|
||||
|
||||
extern int compel_mode_native(struct parasite_ctl *ctl);
|
||||
|
|
@ -139,8 +137,6 @@ extern struct infect_ctx *compel_infect_ctx(struct parasite_ctl *);
|
|||
#define INFECT_NO_MEMFD (1UL << 0)
|
||||
/* Make parasite connect() fail */
|
||||
#define INFECT_FAIL_CONNECT (1UL << 1)
|
||||
/* No breakpoints in pie tracking */
|
||||
#define INFECT_NO_BREAKPOINTS (1UL << 2)
|
||||
/* Can run parasite inside compat tasks */
|
||||
#define INFECT_COMPATIBLE (1UL << 3)
|
||||
/* Workaround for ptrace bug on Skylake CPUs with kernels older than v4.14 */
|
||||
|
|
|
|||
|
|
@ -11,8 +11,6 @@
|
|||
#include <sys/ptrace.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <compel/asm/breakpoints.h>
|
||||
|
||||
/*
|
||||
* Some constants for ptrace that might be missing from the
|
||||
* standard library includes due to being (relatively) new.
|
||||
|
|
|
|||
|
|
@ -153,7 +153,6 @@ static noinline __used unsigned long parasite_init_daemon(void *data)
|
|||
struct parasite_init_args *args = data;
|
||||
int ret;
|
||||
|
||||
args->sigreturn_addr = (uint64_t)(uintptr_t)fini_sigreturn;
|
||||
sigframe = (void *)(uintptr_t)args->sigframe;
|
||||
#ifdef ARCH_HAS_LONG_PAGES
|
||||
__page_size = args->page_size;
|
||||
|
|
|
|||
|
|
@ -725,7 +725,6 @@ static int parasite_init_daemon(struct parasite_ctl *ctl)
|
|||
goto err;
|
||||
}
|
||||
|
||||
ctl->sigreturn_addr = (void *)(uintptr_t)args->sigreturn_addr;
|
||||
ctl->daemonized = true;
|
||||
pr_info("Parasite %d has been switched to daemon mode\n", pid);
|
||||
return 0;
|
||||
|
|
@ -1435,9 +1434,10 @@ static int parasite_fini_seized(struct parasite_ctl *ctl)
|
|||
return -1;
|
||||
|
||||
/* Go to sigreturn as closer as we can */
|
||||
ret = compel_stop_pie(pid, ctl->sigreturn_addr, ctl->ictx.flags & INFECT_NO_BREAKPOINTS);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ptrace(PTRACE_SYSCALL, pid, NULL, NULL)) {
|
||||
pr_perror("Unable to restart the %d process", pid);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (compel_stop_on_syscall(1, __NR(rt_sigreturn, 0), __NR(rt_sigreturn, 1)))
|
||||
return -1;
|
||||
|
|
@ -1588,38 +1588,6 @@ err:
|
|||
return ret;
|
||||
}
|
||||
|
||||
int compel_stop_pie(pid_t pid, void *addr, bool no_bp)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (no_bp) {
|
||||
pr_debug("Force no-breakpoints restore of %d\n", pid);
|
||||
ret = 0;
|
||||
} else
|
||||
ret = ptrace_set_breakpoint(pid, addr);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (ret > 0) {
|
||||
/*
|
||||
* PIE will stop on a breakpoint, next
|
||||
* stop after that will be syscall enter.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* No breakpoints available -- start tracing it
|
||||
* in a per-syscall manner.
|
||||
*/
|
||||
ret = ptrace(PTRACE_SYSCALL, pid, NULL, NULL);
|
||||
if (ret) {
|
||||
pr_perror("Unable to restart the %d process", pid);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool task_is_trapped(int status, pid_t pid)
|
||||
{
|
||||
if (WIFSTOPPED(status) && (WSTOPSIG(status) & ~PTRACE_SYSCALL_TRAP) == SIGTRAP)
|
||||
|
|
@ -1675,22 +1643,19 @@ int compel_stop_on_syscall(int tasks, const int sys_nr, const int sys_nr_compat)
|
|||
|
||||
pr_debug("%d was trapped\n", pid);
|
||||
|
||||
if ((WSTOPSIG(status) & PTRACE_SYSCALL_TRAP) == 0) {
|
||||
/*
|
||||
* On some platforms such as ARM64, it is impossible to
|
||||
* pass through a breakpoint, so let's clear it right
|
||||
* after it has been triggered.
|
||||
*/
|
||||
if (ptrace_flush_breakpoints(pid)) {
|
||||
pr_err("Unable to clear breakpoints\n");
|
||||
return -1;
|
||||
}
|
||||
goto goon;
|
||||
if (!(WSTOPSIG(status) & PTRACE_SYSCALL_TRAP)) {
|
||||
pr_err("Task %d is in unexpected state: %x\n", pid, status);
|
||||
return -1;
|
||||
}
|
||||
if (trace == TRACE_EXIT) {
|
||||
trace = TRACE_ENTER;
|
||||
pr_debug("`- Expecting exit\n");
|
||||
goto goon;
|
||||
ret = ptrace(PTRACE_SYSCALL, pid, NULL, NULL);
|
||||
if (ret) {
|
||||
pr_perror("ptrace");
|
||||
return -1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (trace == TRACE_ENTER)
|
||||
trace = TRACE_EXIT;
|
||||
|
|
@ -1721,12 +1686,17 @@ int compel_stop_on_syscall(int tasks, const int sys_nr, const int sys_nr_compat)
|
|||
if (!task_is_trapped(status, pid))
|
||||
return -1;
|
||||
|
||||
if (!(WSTOPSIG(status) & PTRACE_SYSCALL_TRAP)) {
|
||||
pr_err("Task %d is in unexpected state: %x\n", pid, status);
|
||||
return -1;
|
||||
}
|
||||
|
||||
pr_debug("%d was stopped\n", pid);
|
||||
tasks--;
|
||||
continue;
|
||||
}
|
||||
goon:
|
||||
ret = ptrace(PTRACE_SYSCALL, pid, NULL, NULL);
|
||||
|
||||
if (ret) {
|
||||
pr_perror("ptrace");
|
||||
return -1;
|
||||
|
|
@ -1785,18 +1755,11 @@ int compel_stop_tasks_on_syscall(int nr_tasks, pid_t *pids, const int sys_nr, co
|
|||
|
||||
pr_debug("%d was trapped\n", pid);
|
||||
|
||||
if ((WSTOPSIG(status) & PTRACE_SYSCALL_TRAP) == 0) {
|
||||
/*
|
||||
* On some platforms such as ARM64, it is impossible to
|
||||
* pass through a breakpoint, so let's clear it right
|
||||
* after it has been triggered.
|
||||
*/
|
||||
if (ptrace_flush_breakpoints(pid)) {
|
||||
pr_err("Unable to clear breakpoints\n");
|
||||
goto err;
|
||||
}
|
||||
goto goon;
|
||||
if (!(WSTOPSIG(status) & PTRACE_SYSCALL_TRAP)) {
|
||||
pr_err("Task %d is in unexpected state: %x\n", pid, status);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (done[i] == SYS_TRAP_ENTER) {
|
||||
done[i] = SYS_TRAP_EXIT;
|
||||
continue;
|
||||
|
|
@ -1810,7 +1773,7 @@ int compel_stop_tasks_on_syscall(int nr_tasks, pid_t *pids, const int sys_nr, co
|
|||
|
||||
if (is_required_syscall(®s, pid, sys_nr, sys_nr_compat))
|
||||
done[i] = SYS_TRAP_ENTER;
|
||||
goon:
|
||||
|
||||
/* Let this task run while updating the others. */
|
||||
ret = ptrace(PTRACE_SYSCALL, pid, NULL, NULL);
|
||||
if (ret) {
|
||||
|
|
|
|||
|
|
@ -199,9 +199,6 @@ static inline void _setup_sas(struct rt_sigframe *sigframe, ThreadSasEntry *sas)
|
|||
int restore_gpregs(struct rt_sigframe *f, UserX86RegsEntry *r);
|
||||
int restore_nonsigframe_gpregs(UserX86RegsEntry *r);
|
||||
|
||||
int ptrace_set_breakpoint(pid_t pid, void *addr);
|
||||
int ptrace_flush_breakpoints(pid_t pid);
|
||||
|
||||
extern int arch_map_vdso(unsigned long map_at, bool compatible);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1589,16 +1589,6 @@ static int check_overlayfs_maps(void)
|
|||
return status == 0 ? 0 : -1;
|
||||
}
|
||||
|
||||
static int check_breakpoints(void)
|
||||
{
|
||||
if (!kdat.has_breakpoints) {
|
||||
pr_warn("Hardware breakpoints don't seem to work\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int check_pagemap_scan_guard_pages(void)
|
||||
{
|
||||
kerndat_warn_about_madv_guards();
|
||||
|
|
@ -1746,7 +1736,6 @@ int cr_check(void)
|
|||
/*
|
||||
* Category 4 - optional.
|
||||
*/
|
||||
check_breakpoints();
|
||||
|
||||
pr_msg("%s\n", ret ? CHECK_MAYBE : CHECK_GOOD);
|
||||
return ret;
|
||||
|
|
@ -1869,7 +1858,6 @@ static struct feature_list feature_list[] = {
|
|||
{ "pagemap_scan", check_pagemap_scan },
|
||||
{ "timer_cr_ids", check_timer_cr_ids },
|
||||
{ "overlayfs_maps", check_overlayfs_maps },
|
||||
{ "breakpoints", check_breakpoints },
|
||||
{ "pagemap_scan_guard_pages", check_pagemap_scan_guard_pages },
|
||||
{ "binfmt_misc_sandboxing", check_binfmt_misc_sandboxing },
|
||||
{ NULL, NULL },
|
||||
|
|
|
|||
|
|
@ -1834,7 +1834,6 @@ static int restore_rseq_cs(void)
|
|||
static int catch_tasks(pid_t *pids, int nr_tasks)
|
||||
{
|
||||
struct pstree_item *item;
|
||||
bool nobp = fault_injected(FI_NO_BREAKPOINTS) || !kdat.has_breakpoints;
|
||||
int npids = 0;
|
||||
|
||||
for_each_pstree_item(item) {
|
||||
|
|
@ -1859,7 +1858,7 @@ static int catch_tasks(pid_t *pids, int nr_tasks)
|
|||
}
|
||||
}
|
||||
for_each_pstree_item(item) {
|
||||
int status, i, ret;
|
||||
int status, i;
|
||||
|
||||
if (!task_alive(item))
|
||||
continue;
|
||||
|
|
@ -1872,9 +1871,10 @@ static int catch_tasks(pid_t *pids, int nr_tasks)
|
|||
return -1;
|
||||
}
|
||||
|
||||
ret = compel_stop_pie(pid, rsti(item)->breakpoint, nobp);
|
||||
if (ret < 0)
|
||||
if (ptrace(PTRACE_SYSCALL, pid, NULL, NULL)) {
|
||||
pr_perror("Unable to resume the %d process", pid);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -3334,7 +3334,6 @@ static int sigreturn_restore(pid_t pid, struct task_restore_args *task_args, uns
|
|||
goto err;
|
||||
}
|
||||
|
||||
task_args->breakpoint = &rsti(current)->breakpoint;
|
||||
task_args->fault_strategy = fi_strategy;
|
||||
|
||||
sigemptyset(&blockmask);
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ enum faults {
|
|||
FI_VDSO_TRAMPOLINES = 127,
|
||||
FI_CHECK_OPEN_HANDLE = 128,
|
||||
FI_NO_MEMFD = 129,
|
||||
FI_NO_BREAKPOINTS = 130,
|
||||
FI_PARTIAL_PAGES = 131,
|
||||
FI_HUGE_ANON_SHMEM_ID = 132,
|
||||
FI_CANNOT_MAP_VDSO = 133,
|
||||
|
|
|
|||
|
|
@ -90,7 +90,6 @@ struct kerndat_s {
|
|||
bool has_shstk;
|
||||
bool has_close_range;
|
||||
bool has_timer_cr_ids;
|
||||
bool has_breakpoints;
|
||||
bool has_madv_guard;
|
||||
bool has_pagemap_scan_guard_pages;
|
||||
bool has_binfmt_misc_sandboxing;
|
||||
|
|
|
|||
|
|
@ -229,7 +229,6 @@ struct task_restore_args {
|
|||
unsigned long vdso_rt_size;
|
||||
struct vdso_maps vdso_maps_rt; /* runtime vdso symbols */
|
||||
unsigned long vdso_rt_parked_at; /* safe place to keep vdso */
|
||||
void **breakpoint;
|
||||
|
||||
enum faults fault_strategy;
|
||||
#ifdef ARCH_HAS_LONG_PAGES
|
||||
|
|
|
|||
|
|
@ -85,8 +85,6 @@ struct rst_info {
|
|||
futex_t shstk_enable;
|
||||
futex_t shstk_unlock;
|
||||
|
||||
void *breakpoint;
|
||||
|
||||
struct rst_arch_info arch_info;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1736,83 +1736,6 @@ static int kerndat_has_timer_cr_ids(void)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void breakpoint_func(void)
|
||||
{
|
||||
if (raise(SIGSTOP))
|
||||
pr_perror("Unable to kill itself with SIGSTOP");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/*
|
||||
* kerndat_breakpoints checks that hardware breakpoints work as they should.
|
||||
* In some cases, they might not work in virtual machines if the hypervisor
|
||||
* doesn't virtualize them. For example, they don't work in AMD SEV virtual
|
||||
* machines if the Debug Virtualization extension isn't supported or isn't
|
||||
* enabled in SEV_FEATURES.
|
||||
*/
|
||||
static int kerndat_breakpoints(void)
|
||||
{
|
||||
int status, ret, exit_code = -1;
|
||||
pid_t pid;
|
||||
|
||||
pid = fork();
|
||||
if (pid == -1) {
|
||||
pr_perror("fork");
|
||||
return -1;
|
||||
}
|
||||
if (pid == 0) {
|
||||
if (ptrace(PTRACE_TRACEME, 0, 0, 0)) {
|
||||
pr_perror("ptrace(PTRACE_TRACEME)");
|
||||
exit(1);
|
||||
}
|
||||
raise(SIGSTOP);
|
||||
breakpoint_func();
|
||||
exit(1);
|
||||
}
|
||||
if (waitpid(pid, &status, 0) == -1) {
|
||||
pr_perror("waitpid for initial stop");
|
||||
goto err;
|
||||
}
|
||||
if (!WIFSTOPPED(status) || WSTOPSIG(status) != SIGSTOP) {
|
||||
pr_err("Child didn't stop as expected: status=%x\n", status);
|
||||
goto err;
|
||||
}
|
||||
ret = ptrace_set_breakpoint(pid, &breakpoint_func);
|
||||
if (ret < 0) {
|
||||
pr_err("Failed to set breakpoint\n");
|
||||
goto err;
|
||||
}
|
||||
if (ret == 0) {
|
||||
pr_debug("Hardware breakpoints appear to be disabled\n");
|
||||
goto out;
|
||||
}
|
||||
if (waitpid(pid, &status, 0) == -1) {
|
||||
pr_perror("waitpid for breakpoint trigger");
|
||||
goto err;
|
||||
}
|
||||
if (!WIFSTOPPED(status) || WSTOPSIG(status) != SIGTRAP) {
|
||||
pr_warn("Hardware breakpoints don't seem to work (status=%x)\n", status);
|
||||
goto out;
|
||||
}
|
||||
kdat.has_breakpoints = true;
|
||||
out:
|
||||
exit_code = 0;
|
||||
err:
|
||||
if (kill(pid, SIGKILL)) {
|
||||
pr_perror("Failed to kill the child process");
|
||||
exit_code = -1;
|
||||
}
|
||||
if (waitpid(pid, &status, 0) == -1) {
|
||||
pr_perror("Failed to wait for the child process");
|
||||
exit_code = -1;
|
||||
}
|
||||
if (!WIFSIGNALED(status) || WTERMSIG(status) != SIGKILL) {
|
||||
pr_err("The child exited with unexpected code: %x\n", status);
|
||||
exit_code = -1;
|
||||
}
|
||||
return exit_code;
|
||||
}
|
||||
|
||||
static int kerndat_has_madv_guard(void)
|
||||
{
|
||||
void *map;
|
||||
|
|
@ -2199,10 +2122,6 @@ int kerndat_init(void)
|
|||
pr_err("kerndat_has_timer_cr_ids has failed when initializing kerndat.\n");
|
||||
ret = -1;
|
||||
}
|
||||
if (!ret && kerndat_breakpoints()) {
|
||||
pr_err("kerndat_breakpoints has failed when initializing kerndat.\n");
|
||||
ret = -1;
|
||||
}
|
||||
if (!ret && kerndat_has_madv_guard()) {
|
||||
pr_err("kerndat_has_madv_guard has failed when initializing kerndat.\n");
|
||||
ret = -1;
|
||||
|
|
|
|||
|
|
@ -421,8 +421,6 @@ struct parasite_ctl *parasite_infect_seized(pid_t pid, struct pstree_item *item,
|
|||
ictx->flags |= INFECT_NO_MEMFD;
|
||||
if (fault_injected(FI_PARASITE_CONNECT))
|
||||
ictx->flags |= INFECT_FAIL_CONNECT;
|
||||
if (fault_injected(FI_NO_BREAKPOINTS) || !kdat.has_breakpoints)
|
||||
ictx->flags |= INFECT_NO_BREAKPOINTS;
|
||||
if (kdat.compat_cr)
|
||||
ictx->flags |= INFECT_COMPATIBLE;
|
||||
if (kdat.x86_has_ptrace_fpu_xsave_bug)
|
||||
|
|
|
|||
|
|
@ -1747,7 +1747,6 @@ __visible long __export_restore_task(struct task_restore_args *args)
|
|||
n_helpers = args->helpers_n;
|
||||
zombies = args->zombies;
|
||||
n_zombies = args->zombies_n;
|
||||
*args->breakpoint = rst_sigreturn;
|
||||
#ifdef ARCH_HAS_LONG_PAGES
|
||||
__page_size = args->page_size;
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ if [ $NOBTRFS -eq 1 ] ; then
|
|||
fi
|
||||
|
||||
./test/zdtm.py run -t zdtm/static/env00 --fault 129 -f uns || fail
|
||||
./test/zdtm.py run -t zdtm/transition/fork --fault 130 -f h || fail
|
||||
./test/zdtm.py run -t zdtm/static/vdso01 --fault 127 || fail
|
||||
./test/zdtm.py run -t zdtm/static/vdso-proxy --fault 127 --iters 3 || fail
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue