mirror of
https://github.com/checkpoint-restore/criu
synced 2025-08-22 18:07:57 +00:00
kerndat: check that hardware breakpoints work
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. Fixes #2658 Signed-off-by: Andrei Vagin <avagin@gmail.com>
This commit is contained in:
parent
0e9d0767fb
commit
f6c14eece3
@ -1589,6 +1589,17 @@ 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 (*chk_feature)(void);
|
||||
|
||||
/*
|
||||
@ -1616,6 +1627,7 @@ static int (*chk_feature)(void);
|
||||
return ret; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
int cr_check(void)
|
||||
{
|
||||
struct ns_id *ns;
|
||||
@ -1724,6 +1736,10 @@ int cr_check(void)
|
||||
ret |= check_autofs();
|
||||
ret |= check_compat_cr();
|
||||
}
|
||||
/*
|
||||
* Category 4 - optional.
|
||||
*/
|
||||
check_breakpoints();
|
||||
|
||||
pr_msg("%s\n", ret ? CHECK_MAYBE : CHECK_GOOD);
|
||||
return ret;
|
||||
@ -1836,6 +1852,7 @@ 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 },
|
||||
{ NULL, NULL },
|
||||
};
|
||||
|
||||
|
@ -1820,6 +1820,7 @@ static int restore_rseq_cs(void)
|
||||
static int catch_tasks(bool root_seized)
|
||||
{
|
||||
struct pstree_item *item;
|
||||
bool nobp = fault_injected(FI_NO_BREAKPOINTS) || !kdat.has_breakpoints;
|
||||
|
||||
for_each_pstree_item(item) {
|
||||
int status, i, ret;
|
||||
@ -1847,7 +1848,7 @@ static int catch_tasks(bool root_seized)
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = compel_stop_pie(pid, rsti(item)->breakpoint, fault_injected(FI_NO_BREAKPOINTS));
|
||||
ret = compel_stop_pie(pid, rsti(item)->breakpoint, nobp);
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
}
|
||||
|
@ -90,6 +90,7 @@ struct kerndat_s {
|
||||
bool has_shstk;
|
||||
bool has_close_range;
|
||||
bool has_timer_cr_ids;
|
||||
bool has_breakpoints;
|
||||
};
|
||||
|
||||
extern struct kerndat_s kdat;
|
||||
|
@ -1736,6 +1736,83 @@ 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;
|
||||
}
|
||||
|
||||
/*
|
||||
* Some features depend on resource that can be dynamically changed
|
||||
* at the OS runtime. There are cases that we cannot determine the
|
||||
@ -1999,6 +2076,9 @@ int kerndat_init(void)
|
||||
}
|
||||
if (!ret && kerndat_has_timer_cr_ids()) {
|
||||
pr_err("kerndat_has_timer_cr_ids has failed when initializing kerndat.\n");
|
||||
}
|
||||
if (!ret && kerndat_breakpoints()) {
|
||||
pr_err("kerndat_breakpoints has failed when initializing kerndat.\n");
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
|
@ -421,7 +421,7 @@ 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))
|
||||
if (fault_injected(FI_NO_BREAKPOINTS) || !kdat.has_breakpoints)
|
||||
ictx->flags |= INFECT_NO_BREAKPOINTS;
|
||||
if (kdat.compat_cr)
|
||||
ictx->flags |= INFECT_COMPATIBLE;
|
||||
|
Loading…
x
Reference in New Issue
Block a user