Messages in this thread | | | Date | Mon, 23 May 2016 16:05:47 -0700 | Subject | x86: A process doesn't stop on hw breakpoints sometimes | From | Andrei Vagin <> |
| |
Hi,
We use breakpoints on CRIU to stop a processes before calling rt_sigreturn and we found that sometimes a process runs through a break-point without stopping on it.
https://github.com/xemul/criu/issues/162
A small reproducer is attached. It forks a child, stops it, sets a breakpoint, executes a child, waits when it stops on the breakpoint. I execute it a few times concurrently and wait a few minutes.
https://asciinema.org/a/006l3u5v82ubbkfy9fto07agd
I know that it can be reproduced on: AMD A10 Micro-6700T Intel(R) Core(TM) i5-5200U CPU @ 2.20GHz Intel(R) Core(TM) i7-4600U CPU @ 2.10GHz
so It doesn't look like a bug in a processor.
Thanks, Andrew #include <sys/ptrace.h> #include <stdio.h> #include <sys/wait.h> #include <sys/user.h> #include <asm/debugreg.h> #include <stdlib.h> #include <unistd.h> #include <assert.h>
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
unsigned long encode_dr7(int drnum, int enable, unsigned int type, unsigned int len) { unsigned long dr7;
dr7 = ((len | type) & 0xf) << (DR_CONTROL_SHIFT + drnum * DR_CONTROL_SIZE); if (enable) dr7 |= (DR_GLOBAL_ENABLE << (drnum * DR_ENABLE_SIZE));
return dr7; }
int write_dr(int pid, int dr, unsigned long val) { return ptrace(PTRACE_POKEUSER, pid, offsetof (struct user, u_debugreg[dr]), val); }
void set_bp(pid_t pid, void *addr) { unsigned long dr7; assert(write_dr(pid, 0, (long)addr) == 0); dr7 = encode_dr7(0, 1, DR_RW_EXECUTE, DR_LEN_1); assert(write_dr(pid, 7, dr7) == 0); }
# define noinline __attribute__((noinline)) static noinline void bp1(void) {} static noinline void bp2(void) {}
static void child1() { int nr = 0;
for (;;) { bp1(); printf("fail1 %d %d\n", getpid(), ++nr); } } static void child2() { int nr = 0;
for (;;) { bp2(); printf("fail2 %d %d\n", getpid(), ++nr); } }
# define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) int main(int argc, char *argv[]) { pid_t pid; int status, i;
for (i = 0; ; i++ ) { pid = fork(); assert(pid != -1); if (pid == 0) { assert(ptrace(PTRACE_TRACEME, 0, 0, 0) == 0); kill(getpid(), SIGSTOP); if (i % 2) child1(); else child2(); return 1; }
assert(waitpid(pid, NULL, 0) == pid);
set_bp(pid, i % 2 ? bp1 : bp2);
assert(ptrace(PTRACE_CONT, pid, NULL, NULL) == 0); assert(waitpid(pid, &status, 0) == pid); if (WIFEXITED(status)) return 1; assert(ptrace(PTRACE_KILL, pid, 0, 0) == 0); assert(waitpid(pid, &status, 0) == pid); }
return 0; }
| |