Skip to content

Commit

Permalink
bcc: Add __user attribute to support bpf_probe_read_user in argdist
Browse files Browse the repository at this point in the history
argdist traces probe functions and its parameter values.

Add functionality to convert:
- All userspace probes char * read to bpf_probe_read_user()
- Syscall/kprobes char* params with __user attribute to bpf_probe_read_user()

Signed-off-by: Sumanth Korikkar <sumanthk@linux.ibm.com>
Acked-by: Ilya Leoshkevich <iii@linux.ibm.com>
  • Loading branch information
sumanthkorikkar committed Apr 29, 2020
1 parent 99739b2 commit 306080b
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 26 deletions.
47 changes: 21 additions & 26 deletions tools/argdist.py
Expand Up @@ -9,7 +9,7 @@
# Licensed under the Apache License, Version 2.0 (the "License")
# Copyright (C) 2016 Sasha Goldshtein.

from bcc import BPF, USDT
from bcc import BPF, USDT, StrcmpRewrite
from time import sleep, strftime
import argparse
import re
Expand Down Expand Up @@ -41,6 +41,10 @@ def _parse_signature(self):
param_type = param[0:index + 1].strip()
param_name = param[index + 1:].strip()
self.param_types[param_name] = param_type
# Maintain list of user params. Then later decide to
# switch to bpf_probe_read or bpf_probe_read_user.
if "__user" in param_type.split():
self.probe_user_list.add(param_name)

def _generate_entry(self):
self.entry_probe_func = self.probe_func_name + "_entry"
Expand Down Expand Up @@ -182,6 +186,8 @@ def __init__(self, tool, type, specifier):
self.pid = tool.args.pid
self.cumulative = tool.args.cumulative or False
self.raw_spec = specifier
self.probe_user_list = set()
self.bin_cmp = False
self._validate_specifier()

spec_and_label = specifier.split('#')
Expand Down Expand Up @@ -250,32 +256,16 @@ def _enable_usdt_probe(self):
self.usdt_ctx.enable_probe(
self.function, self.probe_func_name)

def _generate_streq_function(self, string):
fname = "streq_%d" % Probe.streq_index
Probe.streq_index += 1
self.streq_functions += """
static inline bool %s(char const *ignored, char const *str) {
char needle[] = %s;
char haystack[sizeof(needle)];
bpf_probe_read(&haystack, sizeof(haystack), (void *)str);
for (int i = 0; i < sizeof(needle) - 1; ++i) {
if (needle[i] != haystack[i]) {
return false;
}
}
return true;
}
""" % (fname, string)
return fname

def _substitute_exprs(self):
def repl(expr):
expr = self._substitute_aliases(expr)
matches = re.finditer('STRCMP\\(("[^"]+\\")', expr)
for match in matches:
string = match.group(1)
fname = self._generate_streq_function(string)
expr = expr.replace("STRCMP", fname, 1)
rdict = StrcmpRewrite.rewrite_expr(expr,
self.bin_cmp, self.library,
self.probe_user_list, self.streq_functions,
Probe.streq_index)
expr = rdict["expr"]
self.streq_functions = rdict["streq_functions"]
Probe.streq_index = rdict["probeid"]
return expr.replace("$retval", "PT_REGS_RC(ctx)")
for i in range(0, len(self.exprs)):
self.exprs[i] = repl(self.exprs[i])
Expand Down Expand Up @@ -305,9 +295,14 @@ def _generate_usdt_arg_assignment(self, i):
def _generate_field_assignment(self, i):
text = self._generate_usdt_arg_assignment(i)
if self._is_string(self.expr_types[i]):
return (text + " bpf_probe_read(&__key.v%d.s," +
if self.is_user or \
self.exprs[i] in self.probe_user_list:
probe_readfunc = "bpf_probe_read_user"
else:
probe_readfunc = "bpf_probe_read"
return (text + " %s(&__key.v%d.s," +
" sizeof(__key.v%d.s), (void *)%s);\n") % \
(i, i, self.exprs[i])
(probe_readfunc, i, i, self.exprs[i])
else:
return text + " __key.v%d = %s;\n" % \
(i, self.exprs[i])
Expand Down
9 changes: 9 additions & 0 deletions tools/argdist_example.txt
Expand Up @@ -449,3 +449,12 @@ argdist -I 'kernel/sched/sched.h' \
in kernel/sched/sched.h which is in kernel source tree and not in kernel-devel
package. So this command needs to run at the kernel source tree root directory
so that the added header file can be found by the compiler.

argdist -C 'p::do_sys_open(int dfd, const char __user *filename, int flags,
umode_t mode):char*:filename:STRCMP("sample.txt", filename)'
Trace open of the file "sample.txt". It should be noted that 'filename'
passed to the do_sys_open is a char * user pointer. Hence parameter
'filename' should be tagged with __user for kprobes (const char __user
*filename). This information distinguishes if the 'filename' should be
copied from userspace to the bpf stack or from kernel space to the bpf
stack.

0 comments on commit 306080b

Please sign in to comment.