aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJin Wen <wenx.jin@intel.com>2018-03-13 14:52:02 -0700
committerTony Luck <tony.luck@intel.com>2018-03-13 14:52:02 -0700
commit2c2218baced104f89db8f244a9cfe8b3ff63ecec (patch)
treef9a08d68b185aac1983eb351f9d17095a8c371b4
parent68301496e8ef826c3061279d70be832b4d8d7266 (diff)
downloadras-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.810
-rw-r--r--hornet.c54
2 files changed, 54 insertions, 10 deletions
diff --git a/hornet.8 b/hornet.8
index 0488915..dbd5306 100644
--- a/hornet.8
+++ b/hornet.8
@@ -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
diff --git a/hornet.c b/hornet.c
index 2c1b1fa..49e3a5e 100644
--- a/hornet.c
+++ b/hornet.c
@@ -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, &regs);
+ 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;