Commit 22ea950f authored by yonghong-song's avatar yonghong-song Committed by GitHub

Merge pull request #1682 from hMcLauchlan/inject-qol

QoL improvements for inject
parents 6f53be35 45bcfb7c
...@@ -33,6 +33,7 @@ ...@@ -33,6 +33,7 @@
# 16-Mar-2018 Howard McLauchlan Created this. # 16-Mar-2018 Howard McLauchlan Created this.
import argparse import argparse
import re
from bcc import BPF from bcc import BPF
...@@ -294,6 +295,21 @@ class Probe: ...@@ -294,6 +295,21 @@ class Probe:
class Tool: class Tool:
examples ="""
EXAMPLES:
# ./inject.py kmalloc -v 'SyS_mount()'
Fails all calls to syscall mount
# ./inject.py kmalloc -v '(true) => SyS_mount()(true)'
Explicit rewriting of above
# ./inject.py kmalloc -v 'mount_subtree() => btrfs_mount()'
Fails btrfs mounts only
# ./inject.py kmalloc -v 'd_alloc_parallel(struct dentry *parent, const struct \\
qstr *name)(STRCMP(name->name, 'bananas'))'
Fails dentry allocations of files named 'bananas'
# ./inject.py kmalloc -v -P 0.01 'SyS_mount()'
Fails calls to syscall mount with 1% probability
"""
# add cases as necessary # add cases as necessary
error_injection_mapping = { error_injection_mapping = {
"kmalloc": "should_failslab(struct kmem_cache *s, gfp_t gfpflags)", "kmalloc": "should_failslab(struct kmem_cache *s, gfp_t gfpflags)",
...@@ -303,8 +319,9 @@ class Tool: ...@@ -303,8 +319,9 @@ class Tool:
def __init__(self): def __init__(self):
parser = argparse.ArgumentParser(description="Fail specified kernel" + parser = argparse.ArgumentParser(description="Fail specified kernel" +
" functionality when call chain and predicates are met", " functionality when call chain and predicates are met",
formatter_class=argparse.RawDescriptionHelpFormatter) formatter_class=argparse.RawDescriptionHelpFormatter,
parser.add_argument(metavar="mode", dest="mode", epilog=Tool.examples)
parser.add_argument(dest="mode", choices=['kmalloc','bio'],
help="indicate which base kernel function to fail") help="indicate which base kernel function to fail")
parser.add_argument(metavar="spec", dest="spec", parser.add_argument(metavar="spec", dest="spec",
help="specify call chain") help="specify call chain")
...@@ -315,7 +332,7 @@ class Tool: ...@@ -315,7 +332,7 @@ class Tool:
metavar="probability", type=float, metavar="probability", type=float,
help="probability that this call chain will fail") help="probability that this call chain will fail")
parser.add_argument("-v", "--verbose", action="store_true", parser.add_argument("-v", "--verbose", action="store_true",
help="print BPF program") help="print BPF program")
self.args = parser.parse_args() self.args = parser.parse_args()
self.program = "" self.program = ""
...@@ -350,6 +367,7 @@ class Tool: ...@@ -350,6 +367,7 @@ class Tool:
frames = [] frames = []
cur_frame = [] cur_frame = []
i = 0 i = 0
last_frame_added = 0
while i < len(data): while i < len(data):
# improper input # improper input
...@@ -360,6 +378,10 @@ class Tool: ...@@ -360,6 +378,10 @@ class Tool:
count -= c == ')' count -= c == ')'
if not count: if not count:
if c == '\0' or (c == '=' and data[i + 1] == '>'): if c == '\0' or (c == '=' and data[i + 1] == '>'):
# This block is closing a chunk. This means cur_frame must
# have something in it.
if not cur_frame:
raise Exception("Cannot parse spec, missing parens")
if len(cur_frame) == 2: if len(cur_frame) == 2:
frame = tuple(cur_frame) frame = tuple(cur_frame)
elif cur_frame[0][0] == '(': elif cur_frame[0][0] == '(':
...@@ -373,7 +395,12 @@ class Tool: ...@@ -373,7 +395,12 @@ class Tool:
elif c == ')': elif c == ')':
cur_frame.append(data[start:i + 1].strip()) cur_frame.append(data[start:i + 1].strip())
start = i + 1 start = i + 1
last_frame_added = start
i += 1 i += 1
# We only permit spaces after the last frame
if self.spec[last_frame_added:].strip():
raise Exception("Invalid characters found after last frame");
# improper input # improper input
if count: if count:
raise Exception("Check your parentheses") raise Exception("Check your parentheses")
...@@ -389,7 +416,9 @@ class Tool: ...@@ -389,7 +416,9 @@ class Tool:
func, pred = f[0], f[1] func, pred = f[0], f[1]
if not self._validate_predicate(pred): if not self._validate_predicate(pred):
raise Exception raise Exception("Invalid predicate")
if not self._validate_identifier(func):
raise Exception("Invalid function identifier")
tup = (pred, absolute_order) tup = (pred, absolute_order)
if func not in self.map: if func not in self.map:
...@@ -405,6 +434,16 @@ class Tool: ...@@ -405,6 +434,16 @@ class Tool:
self.length = absolute_order self.length = absolute_order
def _validate_identifier(self, func):
# We've already established paren balancing. We will only look for
# identifier validity here.
paren_index = func.find("(")
potential_id = func[:paren_index]
pattern = '[_a-zA-z][_a-zA-Z0-9]*$'
if re.match(pattern, potential_id):
return True
return False
def _validate_predicate(self, pred): def _validate_predicate(self, pred):
if len(pred) > 0 and pred[0] == "(": if len(pred) > 0 and pred[0] == "(":
......
...@@ -115,12 +115,12 @@ fail our mounts half the time: ...@@ -115,12 +115,12 @@ fail our mounts half the time:
# ./inject.py kmalloc -v -P 0.01 'SyS_mount()' # ./inject.py kmalloc -v -P 0.01 'SyS_mount()'
USAGE message: USAGE message:
usage: inject.py [-h] [-I header] [-P probability] [-v] mode spec usage: inject.py [-h] [-I header] [-P probability] [-v] {kmalloc,bio} spec
Fail specified kernel functionality when call chain and predicates are met Fail specified kernel functionality when call chain and predicates are met
positional arguments: positional arguments:
mode indicate which base kernel function to fail {kmalloc,bio} indicate which base kernel function to fail
spec specify call chain spec specify call chain
optional arguments: optional arguments:
...@@ -130,3 +130,16 @@ optional arguments: ...@@ -130,3 +130,16 @@ optional arguments:
-P probability, --probability probability -P probability, --probability probability
probability that this call chain will fail probability that this call chain will fail
-v, --verbose print BPF program -v, --verbose print BPF program
EXAMPLES:
# ./inject.py kmalloc -v 'SyS_mount()'
Fails all calls to syscall mount
# ./inject.py kmalloc -v '(true) => SyS_mount()(true)'
Explicit rewriting of above
# ./inject.py kmalloc -v 'mount_subtree() => btrfs_mount()'
Fails btrfs mounts only
# ./inject.py kmalloc -v 'd_alloc_parallel(struct dentry *parent, const struct \
qstr *name)(STRCMP(name->name, 'bananas'))'
Fails dentry allocations of files named 'bananas'
# ./inject.py kmalloc -v -P 0.01 'SyS_mount()'
Fails calls to syscall mount with 1% probability
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment