diff options
Diffstat (limited to 'runtime')
-rw-r--r-- | runtime/staprun/common.c | 7 | ||||
-rw-r--r-- | runtime/staprun/staprun.c | 77 | ||||
-rw-r--r-- | runtime/staprun/staprun.h | 4 | ||||
-rw-r--r-- | runtime/staprun/staprun_funcs.c | 27 | ||||
-rw-r--r-- | runtime/uprobes/Makefile | 10 | ||||
-rw-r--r-- | runtime/uprobes/uprobes.c | 112 | ||||
-rw-r--r-- | runtime/uprobes/uprobes.h | 8 | ||||
-rw-r--r-- | runtime/uprobes/uprobes_i386.c | 6 |
8 files changed, 136 insertions, 115 deletions
diff --git a/runtime/staprun/common.c b/runtime/staprun/common.c index 9056c710..d3f8835a 100644 --- a/runtime/staprun/common.c +++ b/runtime/staprun/common.c @@ -23,6 +23,7 @@ char *target_cmd; char *outfile_name; int attach_mod; int load_only; +int need_uprobes; /* module variables */ char *modname = NULL; @@ -44,9 +45,13 @@ void parse_args(int argc, char **argv) outfile_name = NULL; attach_mod = 0; load_only = 0; + need_uprobes = 0; - while ((c = getopt(argc, argv, "ALvb:t:d:c:o:x:")) != EOF) { + while ((c = getopt(argc, argv, "ALuvb:t:d:c:o:x:")) != EOF) { switch (c) { + case 'u': + need_uprobes = 1; + break; case 'v': verbose++; break; diff --git a/runtime/staprun/staprun.c b/runtime/staprun/staprun.c index 71039e0b..67b01abb 100644 --- a/runtime/staprun/staprun.c +++ b/runtime/staprun/staprun.c @@ -35,6 +35,16 @@ run_as(uid_t uid, gid_t gid, const char *path, char *const argv[]) pid_t pid; int rstatus; + if (verbose >= 2) { + int i = 0; + err("execing: "); + while (argv[i]) { + err("%s ", argv[i]); + i++; + } + err("\n"); + } + if ((pid = fork()) < 0) { _perr("fork"); return -1; @@ -77,18 +87,63 @@ static int run_stapio(char **argv) gid_t gid = getgid(); argv[0] = PKGLIBDIR "/stapio"; - if (verbose >= 2) { - int i = 0; - err("execing: "); - while (argv[i]) { - err("%s ", argv[i]); - i++; - } - err("\n"); - } return run_as(uid, gid, argv[0], argv); } +/* + * Module to be inserted has one or more user-space probes. Make sure + * uprobes is enabled. + * If /proc/kallsyms lists a symbol in uprobes (e.g. unregister_uprobe), + * we're done. + * Else try "modprobe uprobes" to load the uprobes module (if any) + * built with the kernel. + * If that fails, load the uprobes module built in runtime/uprobes. + */ +static int enable_uprobes(void) +{ + int i; + char *argv[10]; + uid_t uid = getuid(); + gid_t gid = getgid(); + + i = 0; + argv[i++] = "/bin/grep"; + argv[i++] = "-q"; + argv[i++] = "unregister_uprobe"; + argv[i++] = "/proc/kallsyms"; + argv[i] = NULL; + if (run_as(uid, gid, argv[0], argv) == 0) + return 0; + + /* + * TODO: If user can't setresuid to root here, staprun will exit. + * Is there a situation where that would fail but the subsequent + * attempt to use CAP_SYS_MODULE privileges (in insert_module()) + * would succeed? + */ + dbug(2, "Inserting uprobes module from /lib/modules, if any.\n"); + i = 0; + argv[i++] = "/sbin/modprobe"; + argv[i++] = "-q"; + argv[i++] = "uprobes"; + argv[i] = NULL; + if (run_as(0, 0, argv[0], argv) == 0) + return 0; + + dbug(2, "Inserting uprobes module from SystemTap runtime.\n"); + argv[0] = NULL; + return insert_module(PKGDATADIR "/runtime/uprobes/uprobes.ko", + NULL, argv); +} + +static int insert_stap_module(void) +{ + char bufsize_option[128]; + if (snprintf_chk(bufsize_option, 128, "_stp_bufsize=%d", buffer_size)) + return -1; + return insert_module(modpath, bufsize_option, modoptions); +} + int init_staprun(void) { @@ -101,7 +156,9 @@ int init_staprun(void) drop_cap(CAP_SYS_ADMIN); if (!attach_mod) { - if (insert_module() < 0) + if (need_uprobes && enable_uprobes() != 0) + return -1; + if (insert_stap_module() < 0) return -1; else inserted_module = 1; diff --git a/runtime/staprun/staprun.h b/runtime/staprun/staprun.h index cde44922..1b0f3221 100644 --- a/runtime/staprun/staprun.h +++ b/runtime/staprun/staprun.h @@ -141,7 +141,8 @@ void drop_cap(cap_value_t cap); /* staprun_funcs.c */ void setup_staprun_signals(void); const char *moderror(int err); -int insert_module(void); +int insert_module(const char *path, const char *special_options, + char **options); int mountfs(void); int check_permissions(void); void handle_symbols(void); @@ -171,6 +172,7 @@ extern char *target_cmd; extern char *outfile_name; extern int attach_mod; extern int load_only; +extern int need_uprobes; /* getopt variables */ extern char *optarg; diff --git a/runtime/staprun/staprun_funcs.c b/runtime/staprun/staprun_funcs.c index 0747b530..eec4ae63 100644 --- a/runtime/staprun/staprun_funcs.c +++ b/runtime/staprun/staprun_funcs.c @@ -47,7 +47,7 @@ const char *moderror(int err) } } -int insert_module(void) +int insert_module(const char *path, const char *special_options, char **options) { int i; long ret; @@ -58,34 +58,35 @@ int insert_module(void) dbug(2, "inserting module\n"); - opts = malloc(128); + if (special_options) + opts = strdup(special_options); + else + opts = strdup(""); if (opts == NULL) { _perr("allocating memory failed"); return -1; } - if (snprintf_chk(opts, 128, "_stp_bufsize=%d", buffer_size)) - return -1; - for (i = 0; modoptions[i] != NULL; i++) { - opts = realloc(opts, strlen(opts) + strlen(modoptions[i]) + 2); + for (i = 0; options[i] != NULL; i++) { + opts = realloc(opts, strlen(opts) + strlen(options[i]) + 2); if (opts == NULL) { - _perr("reallocating memory failed"); + _perr("[re]allocating memory failed"); return -1; } strcat(opts, " "); - strcat(opts, modoptions[i]); + strcat(opts, options[i]); } dbug(2, "module options: %s\n", opts); /* Open the module file. */ - fd = open(modpath, O_RDONLY); + fd = open(path, O_RDONLY); if (fd < 0) { - perr("Error opening '%s'", modpath); + perr("Error opening '%s'", path); return -1; } /* Now that the file is open, figure out how big it is. */ if (fstat(fd, &sbuf) < 0) { - _perr("Error stat'ing '%s'", modpath); + _perr("Error stat'ing '%s'", path); close(fd); return -1; } @@ -93,7 +94,7 @@ int insert_module(void) /* mmap in the entire module. */ file = mmap(NULL, sbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0); if (file == MAP_FAILED) { - _perr("Error mapping '%s'", modpath); + _perr("Error mapping '%s'", path); close(fd); free(opts); return -1; @@ -109,7 +110,7 @@ int insert_module(void) close(fd); if (ret != 0) { - err("Error inserting module '%s': %s\n", modpath, moderror(saved_errno)); + err("Error inserting module '%s': %s\n", path, moderror(saved_errno)); return -1; } return 0; diff --git a/runtime/uprobes/Makefile b/runtime/uprobes/Makefile new file mode 100644 index 00000000..806f7c48 --- /dev/null +++ b/runtime/uprobes/Makefile @@ -0,0 +1,10 @@ +obj-m := uprobes.o +KDIR := /lib/modules/$(shell uname -r)/build +PWD := $(shell pwd) + +default: + $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules + +clean: + rm -f *.mod.c *.ko *.o .*.cmd *~ + rm -rf .tmp_versions diff --git a/runtime/uprobes/uprobes.c b/runtime/uprobes/uprobes.c index 41d0ef11..16c5e046 100644 --- a/runtime/uprobes/uprobes.c +++ b/runtime/uprobes/uprobes.c @@ -59,36 +59,22 @@ typedef void (*uprobe_handler_t)(struct uprobe*, struct pt_regs*); /* Point utask->active_probe at this while running uretprobe handler. */ static struct uprobe_probept uretprobe_trampoline_dummy_probe; -/* - * These data structures are shared by all SystemTap-generated modules - * that use uprobes. - */ -struct uprobe_globals { - struct hlist_head uproc_table[UPROBE_TABLE_SIZE]; - struct mutex uproc_mutex; - struct hlist_head utask_table[UPROBE_TABLE_SIZE]; - spinlock_t utask_table_lock; -}; -static struct uprobe_globals *globals; -static struct hlist_head *uproc_table; /* = globals->uproc_table */ -static struct hlist_head *utask_table; /* = globals->utask_table */ +/* Table of currently probed processes, hashed by tgid. */ +static struct hlist_head uproc_table[UPROBE_TABLE_SIZE]; -#define lock_uproc_table() mutex_lock(&globals->uproc_mutex) -#define unlock_uproc_table() mutex_unlock(&globals->uproc_mutex) +/* Protects uproc_table during uprobe (un)registration */ +static DEFINE_MUTEX(uproc_mutex); -#define lock_utask_table(flags) \ - spin_lock_irqsave(&globals->utask_table_lock, (flags)) -#define unlock_utask_table(flags) \ - spin_unlock_irqrestore(&globals->utask_table_lock, (flags)) +/* Table of uprobe_tasks, hashed by task_struct pointer. */ +static struct hlist_head utask_table[UPROBE_TABLE_SIZE]; +static DEFINE_SPINLOCK(utask_table_lock); -/* - * uprobes_data and uprobes_mutex are the only uprobes hooks in the kernel. - * A pointer to the uprobes_global area is stored in uprobe_data. - */ -extern void *uprobes_data; -extern struct mutex uprobes_mutex; +#define lock_uproc_table() mutex_lock(&uproc_mutex) +#define unlock_uproc_table() mutex_unlock(&uproc_mutex) -static int verify_uprobes(void); +#define lock_utask_table(flags) spin_lock_irqsave(&utask_table_lock, (flags)) +#define unlock_utask_table(flags) \ + spin_unlock_irqrestore(&utask_table_lock, (flags)) /* p_uprobe_utrace_ops = &uprobe_utrace_ops. Fwd refs are a pain w/o this. */ static const struct utrace_engine_ops *p_uprobe_utrace_ops; @@ -824,7 +810,6 @@ static int defer_registration(struct uprobe *u, int regflag, } /* See Documentation/uprobes.txt. */ -static int register_uprobe(struct uprobe *u) { struct task_struct *p; @@ -833,9 +818,6 @@ int register_uprobe(struct uprobe *u) struct uprobe_probept *ppt; struct uprobe_task *cur_utask, *cur_utask_quiescing = NULL; int survivors, ret = 0, uproc_is_new = 0; - if ((ret = verify_uprobes()) < 0) - return ret; - if (!u || !u->handler) return -EINVAL; @@ -982,9 +964,9 @@ fail_tsk: put_task_struct(p); return ret; } +EXPORT_SYMBOL_GPL(register_uprobe); /* See Documentation/uprobes.txt. */ -static void unregister_uprobe(struct uprobe *u) { struct task_struct *p; @@ -993,8 +975,6 @@ void unregister_uprobe(struct uprobe *u) struct uprobe_probept *ppt; struct uprobe_task *cur_utask, *cur_utask_quiescing = NULL; - if (verify_uprobes() < 0) - return; if (!u) return; p = uprobe_get_task(u->pid); @@ -1069,6 +1049,7 @@ done: up_write(&uproc->rwsem); uprobe_put_process(uproc); } +EXPORT_SYMBOL_GPL(unregister_uprobe); /* Find a surviving thread in uproc. Runs with uproc->rwsem locked. */ static struct task_struct *find_surviving_thread(struct uprobe_process *uproc) @@ -2071,64 +2052,26 @@ static const struct utrace_engine_ops uprobe_utrace_ops = .report_exec = uprobe_report_exec }; -/* - * Initialize the uprobes global data area, and return a pointer - * to it. Caller will initialize uprobes_data pointer afterward, to - * ensure that no other module sees a non-null uprobes_data until it's - * completely initialized. - */ -static struct uprobe_globals *init_uprobes(void) +static int __init init_uprobes(void) { int i; - struct uprobe_globals *g = kmalloc(sizeof(*g), GFP_USER); - if (!g) - return ERR_PTR(-ENOMEM); + for (i = 0; i < UPROBE_TABLE_SIZE; i++) { - INIT_HLIST_HEAD(&g->uproc_table[i]); - INIT_HLIST_HEAD(&g->utask_table[i]); + INIT_HLIST_HEAD(&uproc_table[i]); + INIT_HLIST_HEAD(&utask_table[i]); } - mutex_init(&g->uproc_mutex); - spin_lock_init(&g->utask_table_lock); - return g; + + p_uprobe_utrace_ops = &uprobe_utrace_ops; + return 0; } -/* - * Verify that the uprobes globals area has been set up, and that the - * current module's globals variable points at it. Returns 0 if the - * area is successfully set up, or a negative erro otherwise. - */ -static int verify_uprobes(void) +static void __exit exit_uprobes(void) { - if (unlikely(!globals)) { - /* - * First time through for this instrumentation module. - * uprobes_mutex protects both the global uprobes - * initialization and this module's local initialization. - */ - struct uprobe_globals *g; - - mutex_lock(&uprobes_mutex); - if (!uprobes_data) { - /* First time through since boot time */ - g = init_uprobes(); - uprobes_data = g; - } else - g = uprobes_data; - if (!IS_ERR(g)) { - p_uprobe_utrace_ops = &uprobe_utrace_ops; - uproc_table = g->uproc_table; - utask_table = g->utask_table; - } - - /* Set globals pointer to signify all is initialized. */ - globals = g; - mutex_unlock(&uprobes_mutex); - } - if (unlikely(IS_ERR(globals))) - return (int) PTR_ERR(globals); - return 0; } +module_init(init_uprobes); +module_exit(exit_uprobes); + #ifdef CONFIG_URETPROBES /* Called when the entry-point probe u is hit. */ @@ -2209,7 +2152,6 @@ static void uretprobe_handle_return(struct pt_regs *regs, arch_restore_uret_addr(orig_ret_addr, regs); } -static int register_uretprobe(struct uretprobe *rp) { if (!rp || !rp->handler) @@ -2217,6 +2159,7 @@ int register_uretprobe(struct uretprobe *rp) rp->u.handler = URETPROBE_HANDLE_ENTRY; return register_uprobe(&rp->u); } +EXPORT_SYMBOL_GPL(register_uretprobe); /* * The uretprobe containing u is being unregistered. Its uretprobe_instances @@ -2244,13 +2187,13 @@ static void zap_uretprobe_instances(struct uprobe *u, } } -static void unregister_uretprobe(struct uretprobe *rp) { if (!rp) return; unregister_uprobe(&rp->u); } +EXPORT_SYMBOL_GPL(unregister_uretprobe); /* * uproc->ssol_area has been successfully set up. Establish the @@ -2295,3 +2238,4 @@ static void zap_uretprobe_instances(struct uprobe *u, #endif /* CONFIG_URETPROBES */ #include "uprobes_arch.c" +MODULE_LICENSE("GPL"); diff --git a/runtime/uprobes/uprobes.h b/runtime/uprobes/uprobes.h index 574bee62..84dd0b2a 100644 --- a/runtime/uprobes/uprobes.h +++ b/runtime/uprobes/uprobes.h @@ -72,11 +72,11 @@ struct uretprobe_instance { unsigned long reserved2; }; -static int register_uprobe(struct uprobe *u); -static void unregister_uprobe(struct uprobe *u); +extern int register_uprobe(struct uprobe *u); +extern void unregister_uprobe(struct uprobe *u); /* For runtime, assume uprobes support includes uretprobes. */ -static int register_uretprobe(struct uretprobe *rp); -static void unregister_uretprobe(struct uretprobe *rp); +extern int register_uretprobe(struct uretprobe *rp); +extern void unregister_uretprobe(struct uretprobe *rp); #ifdef UPROBES_IMPLEMENTATION diff --git a/runtime/uprobes/uprobes_i386.c b/runtime/uprobes/uprobes_i386.c index 90d50ba0..21420681 100644 --- a/runtime/uprobes/uprobes_i386.c +++ b/runtime/uprobes/uprobes_i386.c @@ -18,8 +18,10 @@ * * Copyright (C) IBM Corporation, 2006 */ -#define UPROBES_IMPLEMENTATION 1 -#include "uprobes.h" +/* + * In versions of uprobes built in the SystemTap runtime, this file + * is #included at the end of uprobes.c. + */ #include <linux/uaccess.h> /* Adapted from arch/x86_64/kprobes.c */ |