-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy patherror-injection-stress.py
116 lines (93 loc) · 3.1 KB
/
error-injection-stress.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
from bcc import BPF
from time import sleep
from subprocess import Popen
import argparse
import sys
import os
import ctypes as ct
bpf_text = """
#include <uapi/linux/ptrace.h>
#include <linux/bio.h>
BPF_CGROUP_ARRAY(cgroup, 1);
BPF_HASH(seen, u64);
BPF_ARRAY(enabled, u64, 1);
BPF_PERF_OUTPUT(events);
BPF_STACK_TRACE(stack_traces, 10240);
int override_function(struct pt_regs *ctx)
{
/* Filter on our cgroup. */
if (cgroup.check_current_task(0) <= 0)
return 0;
/* Make sure we're ready to inject errors. */
int index = 0;
u64 *e = enabled.lookup(&index);
if (!e || *e == 0)
return 0;
/* Have we seen this stacktrace yet? */
u64 key = stack_traces.get_stackid(ctx, BPF_F_REUSE_STACKID);
u64 zero = 0;
u64 *val = seen.lookup_or_init(&key, &zero);
if (*val == 1)
return 0;
lock_xadd(val, 1);
lock_xadd(e, 1);
events.perf_submit(ctx, &key, sizeof(key));
bpf_trace_printk("overrding something\\n");
unsigned long rc = RCVAL;
bpf_override_return(ctx, rc);
return 0;
}
"""
error_tripped = 0
parser = argparse.ArgumentParser()
parser.add_argument("-o", "--override", required=True,
help="The function to override")
parser.add_argument("-r", "--retval", type=str, help="The return value to use")
parser.add_argument("-e", "--executable", type=str, required=True,
help="The command to run")
parser.add_argument("-c", "--cgroup", type=str, required=True,
help="Path to the cgroup we'll be using for this")
args = parser.parse_args()
retval = "NULL"
if args.retval is not None:
retval = args.retval
bpf_text = bpf_text.replace("RCVAL", retval)
fd = os.open(args.cgroup, os.O_RDONLY)
print("Loading error injection")
b = BPF(text=bpf_text)
# Load the cgroup id into the table
t = b.get_table("cgroup")
t[0] = fd
# Load the kretprobe first, because we want the delete guy to be in place before
# the add guy is in place, otherwise we could error out pids that are no longer
# in our path and cause unfortunate things to happen.
b.attach_kprobe(event=args.override, fn_name="override_function")
def handle_error(cpu, data, size):
stackid = ct.cast(data, ct.POINTER(ct.c_ulonglong)).contents
stack_traces = b.get_table("stack_traces")
stack = stack_traces.walk(stackid.value)
print("Injected error here")
for addr in stack:
print(" %s" % b.ksym(addr))
globals()['error_tripped'] = 1
b["events"].open_perf_buffer(handle_error)
while 1:
print("Running command")
p = Popen(args.executable)
error_tripped = 0
t = b.get_table("enabled")
t[0] = ct.c_int(1)
while error_tripped == 0:
b.kprobe_poll(timeout=30)
if p.poll() is not None:
print("The command exited, breaking")
break
print("Waiting for the command to exit")
p.wait()
if error_tripped == 0:
print("Error injection didn't trip anything, exiting")
break
# We have to remove in this order otherwise we could end up with a half
# populated hasmap and overrding legitimate things.
b.detach_kprobe(args.override)
print("Exiting")