diff options
author | Jin Wen <wenx.jin@intel.com> | 2018-03-13 14:52:02 -0700 |
---|---|---|
committer | Tony Luck <tony.luck@intel.com> | 2018-03-13 14:52:02 -0700 |
commit | 2c2218baced104f89db8f244a9cfe8b3ff63ecec (patch) | |
tree | f9a08d68b185aac1983eb351f9d17095a8c371b4 | |
parent | 68301496e8ef826c3061279d70be832b4d8d7266 (diff) | |
download | ras-tools-2c2218baced104f89db8f244a9cfe8b3ff63ecec.tar.gz |
hornet: Add "-P pid" flag to stop process using ptrace
Picking instruction addresses inside running processes is rather
hit or miss. We may pick an address for injection that is never
executed.
Using ptrace(2) to stop the process we can find the precise address
of the next instruction to be executed and thus guarantee that we
will immediately hit the injected address when we resume running the
process.
Signed-off-by: Jin Wen <wenx.jin@intel.com>
Signed-off-by: Tony Luck <tony.luck@intel.com>
-rw-r--r-- | hornet.8 | 10 | ||||
-rw-r--r-- | hornet.c | 54 |
2 files changed, 54 insertions, 10 deletions
@@ -27,6 +27,13 @@ hornet \- inject an uncorrectable memory error into a process ] \-p .I PID +.br +.B hornet +[ +.B \-v +] +\-P +.I PID .SH CONFIGURATION .B hornet requires that error injection be enabled in ACPI 5.0 mode by the BIOS and that the @@ -75,6 +82,9 @@ in the process. .TP .BI \-p " PID" inject errors into an already running process +.TP +.BI \-P " PID" +stop the process using ptrace(2), inject at the next instruction to be executed, then continue. .SH EXIT STATUS .SH FILES .PP @@ -16,20 +16,28 @@ #include <unistd.h> #include <stdio.h> #include <stdlib.h> +#include <string.h> #include <fcntl.h> +#include <errno.h> +#include <time.h> #include <sys/types.h> #include <sys/signal.h> #include <sys/wait.h> +#include <sys/user.h> +#include <sys/ptrace.h> static char *progname; +static int pagesize; long addr; double delay; int pid; int tflag, dflag, bflag, sflag, mflag, pflag, vflag; +int trace; static void usage(void) { + fprintf(stderr, "Usage: %s -P PID\n", progname); fprintf(stderr, "Usage: %s [hornetopts] -p PID\n", progname); fprintf(stderr, "Usage: %s [hornetopts] command args ...\n", progname); fprintf(stderr, " hornetopts = [-D delay][ -a ADDRESS][-t|-d|-b|-s|-m]\n"); @@ -42,6 +50,15 @@ static void usage(void) #define EINJ_NOTRIGGER "/sys/kernel/debug/apei/einj/notrigger" #define EINJ_DOIT "/sys/kernel/debug/apei/einj/error_inject" +#define check_ptrace(req, pid, addr, data) \ + do { \ + if (ptrace(req, pid, addr, data) == -1) { \ + fprintf(stderr, "Failed to run #req: %s\n", \ + strerror(errno)); \ + return 1; \ + } \ + } while (0) + static void wfile(char *file, unsigned long val) { FILE *fp; @@ -139,7 +156,6 @@ static long randaddr(long lo, long hi) return a & ~0x3ful; } - static long pickaddr(int pid, long lo, long hi, long *phys) { int pagesize = getpagesize(); @@ -192,10 +208,12 @@ int main(int argc, char **argv) int c; int status; long lo, hi, phys, virt; + struct user_regs_struct regs; progname = argv[0]; + pagesize = getpagesize(); - while ((c = getopt(argc, argv, "D:a:tdbsmp:v")) != -1) switch (c) { + while ((c = getopt(argc, argv, "D:P:a:tdbsmp:v")) != -1) switch (c) { case 'D': delay = atof(optarg); break; case 'a': addr = strtol(optarg, NULL, 0); break; case 't': tflag = 1; break; @@ -204,6 +222,7 @@ int main(int argc, char **argv) case 's': sflag = 1; break; case 'm': mflag = 1; break; case 'p': pflag = 1; pid = atoi(optarg); break; + case 'P': trace = 1; pid = atoi(optarg); break; case 'v': vflag++; break; default: usage(); break; } @@ -212,16 +231,26 @@ int main(int argc, char **argv) wfile(EINJ_MASK, ~0x0ul); wfile(EINJ_NOTRIGGER, 1); - if (!pflag) + if (!pflag && !trace) pid = startproc(&argv[optind]); if (delay != 0.0) usleep((useconds_t)(delay * 1.0e6)); - if (kill(pid, SIGSTOP) == -1) { - fprintf(stderr, "%s: cannot stop process\n", progname); - return 1; - } + if (trace) { + check_ptrace(PTRACE_ATTACH, pid, NULL, NULL); + waitpid(pid, NULL, 0); + check_ptrace(PTRACE_GETREGS, pid, NULL, ®s); + virt = regs.rip; + check_ptrace(PTRACE_PEEKTEXT, pid, virt, NULL); + addr = virt; + } else { + if (kill(pid, SIGSTOP) == -1) { + fprintf(stderr, "%s: cannot stop process\n", progname); + return 1; + } + + parsemaps(pid, &lo, &hi); - parsemaps(pid, &lo, &hi); + } if ((virt = pickaddr(pid, lo, hi, &phys)) == -1) { kill(pid, SIGKILL); @@ -231,9 +260,14 @@ int main(int argc, char **argv) wfile(EINJ_ADDR, phys); wfile(EINJ_DOIT, 1); - if (vflag) printf("%s: injected UC error at virt=%lx phys=%lx to pid=%d\n", - progname, virt, phys, pid); + if (vflag) printf("%s: injected UC error at virt=%lx phys=%lx to pid=%d%s\n", + progname, virt, phys, pid, trace == 1 ? "(ptrace)" : NULL); + if (trace) { + sleep(1); + check_ptrace(PTRACE_DETACH, pid, NULL, NULL); + return 0; + } if (kill(pid, SIGCONT) == -1) { fprintf(stderr, "%s: cannot resume process\n", progname); return 1; |