diff options
Diffstat (limited to 'runtime')
-rw-r--r-- | runtime/alloc.c | 133 | ||||
-rw-r--r-- | runtime/stack.c | 18 | ||||
-rw-r--r-- | runtime/uprobes/uprobes.c | 71 | ||||
-rw-r--r-- | runtime/uprobes/uprobes.h | 4 | ||||
-rw-r--r-- | runtime/uprobes2/uprobes.c | 71 | ||||
-rw-r--r-- | runtime/uprobes2/uprobes.h | 4 |
6 files changed, 224 insertions, 77 deletions
diff --git a/runtime/alloc.c b/runtime/alloc.c index fa85fc41..b2578e06 100644 --- a/runtime/alloc.c +++ b/runtime/alloc.c @@ -172,18 +172,100 @@ static void _stp_mem_debug_free(void *addr, enum _stp_memtype type) return; } + +static void _stp_mem_debug_validate(void *addr) +{ + int found = 0; + struct list_head *p, *tmp; + struct _stp_mem_entry *m = NULL; + + spin_lock(&_stp_mem_lock); + list_for_each_safe(p, tmp, &_stp_mem_list) { + m = list_entry(p, struct _stp_mem_entry, list); + if (m->addr == addr) { + found = 1; + break; + } + } + spin_unlock(&_stp_mem_lock); + if (!found) { + printk("SYSTEMTAP ERROR: Couldn't validate memory %p\n", + addr); + return; + } + if (m->magic != MEM_MAGIC) { + printk("SYSTEMTAP ERROR: Memory at %p corrupted!!\n", addr); + return; + } + + switch (m->type) { + case MEM_KMALLOC: + _stp_check_mem_fence(addr, m->len); + break; + case MEM_PERCPU: + /* do nothing */ + break; + case MEM_VMALLOC: + _stp_check_mem_fence(addr, m->len); + break; + default: + printk("SYSTEMTAP ERROR: Attempted to validate memory at addr %p len=%d with unknown allocation type.\n", addr, (int)m->len); + } + + return; +} +#endif + +/* #define STP_MAXMEMORY 8192 */ +/* + * If STP_MAXMEMORY is defined to a value (stap -DSTP_MAXMEMORY=8192 + * ...) then every memory allocation is checked to make sure the + * systemtap module doesn't use more than STP_MAXMEMORY of memory. + * STP_MAXMEMORY is specified in kilobytes, so, for example, '8192' + * means that the systemtap module won't use more than 8 megabytes of + * memory. + * + * Note 1: This size does include the size of the module itself, plus + * any additional allocations. + * + * Note 2: Since we can't be ensured that the module transport is set + * up when a memory allocation problem happens, this code can't + * directly report an error back to a user (so instead it uses + * 'printk'). If the modules transport has been set up, the code that + * calls the memory allocation functions + * (_stp_kmalloc/_stp_kzalloc/etc.) should report an error directly to + * the user. + * + * Note 3: This only tracks direct allocations by the systemtap + * runtime. This does not track indirect allocations (such as done by + * kprobes/uprobes/etc. internals). + */ + +#ifdef STP_MAXMEMORY +#ifndef STAPCONF_GRSECURITY +#define _STP_MODULE_CORE_SIZE (THIS_MODULE->core_size) +#else +#define _STP_MODULE_CORE_SIZE (THIS_MODULE->core_size_rw) +#endif #endif static void *_stp_kmalloc(size_t size) { + void *ret; +#ifdef STP_MAXMEMORY + if ((_STP_MODULE_CORE_SIZE + _stp_allocated_memory + size) + > (STP_MAXMEMORY * 1024)) { + return NULL; + } +#endif #ifdef DEBUG_MEM - void *ret = kmalloc(size + MEM_DEBUG_SIZE, STP_ALLOC_FLAGS); + ret = kmalloc(size + MEM_DEBUG_SIZE, STP_ALLOC_FLAGS); if (likely(ret)) { _stp_allocated_memory += size; ret = _stp_mem_debug_setup(ret, size, MEM_KMALLOC); } #else - void *ret = kmalloc(size, STP_ALLOC_FLAGS); + ret = kmalloc(size, STP_ALLOC_FLAGS); if (likely(ret)) { _stp_allocated_memory += size; } @@ -194,15 +276,22 @@ static void *_stp_kmalloc(size_t size) static void *_stp_kzalloc(size_t size) #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15) { + void *ret; +#ifdef STP_MAXMEMORY + if ((_STP_MODULE_CORE_SIZE + _stp_allocated_memory + size) + > (STP_MAXMEMORY * 1024)) { + return NULL; + } +#endif #ifdef DEBUG_MEM - void *ret = kmalloc(size + MEM_DEBUG_SIZE, STP_ALLOC_FLAGS); + ret = kmalloc(size + MEM_DEBUG_SIZE, STP_ALLOC_FLAGS); if (likely(ret)) { _stp_allocated_memory += size; ret = _stp_mem_debug_setup(ret, size, MEM_KMALLOC); memset (ret, 0, size); } #else - void *ret = kmalloc(size, STP_ALLOC_FLAGS); + ret = kmalloc(size, STP_ALLOC_FLAGS); if (likely(ret)) { _stp_allocated_memory += size; memset (ret, 0, size); @@ -212,14 +301,21 @@ static void *_stp_kzalloc(size_t size) } #else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15) */ { + void *ret; +#ifdef STP_MAXMEMORY + if ((_STP_MODULE_CORE_SIZE + _stp_allocated_memory + size) + > (STP_MAXMEMORY * 1024)) { + return NULL; + } +#endif #ifdef DEBUG_MEM - void *ret = kzalloc(size + MEM_DEBUG_SIZE, STP_ALLOC_FLAGS); + ret = kzalloc(size + MEM_DEBUG_SIZE, STP_ALLOC_FLAGS); if (likely(ret)) { _stp_allocated_memory += size; ret = _stp_mem_debug_setup(ret, size, MEM_KMALLOC); } #else - void *ret = kzalloc(size, STP_ALLOC_FLAGS); + ret = kzalloc(size, STP_ALLOC_FLAGS); if (likely(ret)) { _stp_allocated_memory += size; } @@ -230,14 +326,21 @@ static void *_stp_kzalloc(size_t size) static void *_stp_vmalloc(unsigned long size) { + void *ret; +#ifdef STP_MAXMEMORY + if ((_STP_MODULE_CORE_SIZE + _stp_allocated_memory + size) + > (STP_MAXMEMORY * 1024)) { + return NULL; + } +#endif #ifdef DEBUG_MEM - void *ret = __vmalloc(size + MEM_DEBUG_SIZE, STP_ALLOC_FLAGS, PAGE_KERNEL); + ret = __vmalloc(size + MEM_DEBUG_SIZE, STP_ALLOC_FLAGS, PAGE_KERNEL); if (likely(ret)) { _stp_allocated_memory += size; ret = _stp_mem_debug_setup(ret, size, MEM_VMALLOC); } #else - void *ret = __vmalloc(size, STP_ALLOC_FLAGS, PAGE_KERNEL); + ret = __vmalloc(size, STP_ALLOC_FLAGS, PAGE_KERNEL); if (likely(ret)) { _stp_allocated_memory += size; } @@ -258,6 +361,14 @@ static void *_stp_alloc_percpu(size_t size) if (size > _STP_MAX_PERCPU_SIZE) return NULL; +#ifdef STP_MAXMEMORY + if ((_STP_MODULE_CORE_SIZE + _stp_allocated_memory + + (size * num_online_cpus())) + > (STP_MAXMEMORY * 1024)) { + return NULL; + } +#endif + #ifdef STAPCONF_ALLOC_PERCPU_ALIGN ret = __alloc_percpu(size, 8); #else @@ -287,6 +398,12 @@ static void *_stp_alloc_percpu(size_t size) static void *_stp_kmalloc_node(size_t size, int node) { void *ret; +#ifdef STP_MAXMEMORY + if ((_STP_MODULE_CORE_SIZE + _stp_allocated_memory + size) + > (STP_MAXMEMORY * 1024)) { + return NULL; + } +#endif #ifdef DEBUG_MEM ret = kmalloc_node(size + MEM_DEBUG_SIZE, STP_ALLOC_FLAGS, node); if (likely(ret)) { diff --git a/runtime/stack.c b/runtime/stack.c index 50dde6e1..612fa010 100644 --- a/runtime/stack.c +++ b/runtime/stack.c @@ -132,25 +132,29 @@ static void _stp_stack_print(struct pt_regs *regs, int verbose, struct kretprobe _stp_print("\nReturning to : "); } _stp_symbol_print((unsigned long)_stp_ret_addr_r(pi)); - } else if (ri) { -#ifdef CONFIG_UTRACE /* as a proxy for presence of uprobes */ + _stp_print_char('\n'); +#ifdef CONFIG_UTRACE /* as a proxy for presence of uprobes... */ + } else if (ri && ri != GET_PC_URETPROBE_NONE) { if (verbose == SYM_VERBOSE_FULL) { _stp_print("Returning from: "); - _stp_usymbol_print(ri->rp->u.vaddr, tsk); /* otherwise this dereference fails */ + /* ... otherwise this dereference fails */ + _stp_usymbol_print(ri->rp->u.vaddr, tsk); _stp_print("\nReturning to : "); _stp_usymbol_print(ri->ret_addr, tsk); + _stp_print_char('\n'); } else _stp_func_print(ri->ret_addr, verbose, 0, tsk); #endif + } else if (verbose == SYM_VERBOSE_BRIEF) { + _stp_func_print(REG_IP(regs), verbose, 0, tsk); } else { _stp_print_char(' '); if (tsk) - _stp_usymbol_print(REG_IP(regs), tsk); + _stp_usymbol_print(REG_IP(regs), tsk); else - _stp_symbol_print(REG_IP(regs)); - } - if (verbose != SYM_VERBOSE_BRIEF) + _stp_symbol_print(REG_IP(regs)); _stp_print_char('\n'); + } } else if (pi) _stp_printf("%p %p ", (int64_t)(long)_stp_ret_addr_r(pi), (int64_t) REG_IP(regs)); else diff --git a/runtime/uprobes/uprobes.c b/runtime/uprobes/uprobes.c index cdb98707..5ccc7102 100644 --- a/runtime/uprobes/uprobes.c +++ b/runtime/uprobes/uprobes.c @@ -2599,37 +2599,46 @@ static void uretprobe_set_trampoline(struct uprobe_process *uproc, unsigned long uprobe_get_pc(struct uretprobe_instance *ri, unsigned long pc, unsigned long sp) { - struct uretprobe *rp; - struct uprobe_kimg *uk; - struct uprobe_process *uproc; - unsigned long trampoline_addr; - struct hlist_node *r; - struct uretprobe_instance *ret_inst; - - if (!ri) - return 0; - rp = ri->rp; - uk = (struct uprobe_kimg *)rp->u.kdata; - if (!uk) - return 0; - uproc = uk->ppt->uproc; - if (IS_ERR(uproc->uretprobe_trampoline_addr)) - return pc; - trampoline_addr = (unsigned long)uproc->uretprobe_trampoline_addr; - if (pc != trampoline_addr) - return pc; - r = &ri->hlist; - hlist_for_each_entry_from(ret_inst, r, hlist) { - if (ret_inst->ret_addr == trampoline_addr) - continue; - /* First handler with a stack pointer lower than the - address (or equal) must be the one. */ - if (ret_inst->sp == sp || compare_stack_ptrs(ret_inst->sp, sp)) - return ret_inst->ret_addr; - } - printk(KERN_ERR "Original return address for trampoline not found at " - "0x%lx pid/tgid=%d/%d\n", sp, current->pid, current->tgid); - return 0; + struct uretprobe *rp; + struct uprobe_kimg *uk; + struct uprobe_task *utask; + struct uprobe_process *uproc; + unsigned long trampoline_addr; + struct hlist_node *r; + struct uretprobe_instance *ret_inst; + + if (!ri) + return 0; + if (ri == GET_PC_URETPROBE_NONE) { + utask = uprobe_find_utask(current); + if (!utask) + return 0; + uproc = utask->uproc; + r = utask->uretprobe_instances.first; + } else { + rp = ri->rp; + uk = (struct uprobe_kimg *)rp->u.kdata; + if (!uk) + return 0; + uproc = uk->ppt->uproc; + r = &ri->hlist; + } + if (IS_ERR(uproc->uretprobe_trampoline_addr)) + return pc; + trampoline_addr = (unsigned long)uproc->uretprobe_trampoline_addr; + if (pc != trampoline_addr) + return pc; + hlist_for_each_entry_from(ret_inst, r, hlist) { + if (ret_inst->ret_addr == trampoline_addr) + continue; + /* First handler with a stack pointer lower than the + address (or equal) must be the one. */ + if (ret_inst->sp == sp || compare_stack_ptrs(ret_inst->sp, sp)) + return ret_inst->ret_addr; + } + printk(KERN_ERR "Original return address for trampoline not found at " + "0x%lx pid/tgid=%d/%d\n", sp, current->pid, current->tgid); + return 0; } EXPORT_SYMBOL_GPL(uprobe_get_pc); diff --git a/runtime/uprobes/uprobes.h b/runtime/uprobes/uprobes.h index e888f9e8..80725f23 100644 --- a/runtime/uprobes/uprobes.h +++ b/runtime/uprobes/uprobes.h @@ -99,7 +99,11 @@ extern void unmap_uretprobe(struct uretprobe *rp); * Given a program counter, translate it back to the original address * if it is the address of the trampoline. sp is the stack pointer for * the frame that corresponds to the address. + * + * When not called from a uretprobe hander, pass GET_PC_URETPROBE_NONE + * instead of a uretprobe_instance. */ +#define GET_PC_URETPROBE_NONE ((struct uretprobe_instance *)-1L) extern unsigned long uprobe_get_pc(struct uretprobe_instance *ri, unsigned long pc, unsigned long sp); diff --git a/runtime/uprobes2/uprobes.c b/runtime/uprobes2/uprobes.c index 02941e26..623855ff 100644 --- a/runtime/uprobes2/uprobes.c +++ b/runtime/uprobes2/uprobes.c @@ -2840,37 +2840,46 @@ static void uretprobe_set_trampoline(struct uprobe_process *uproc, unsigned long uprobe_get_pc(struct uretprobe_instance *ri, unsigned long pc, unsigned long sp) { - struct uretprobe *rp; - struct uprobe_kimg *uk; - struct uprobe_process *uproc; - unsigned long trampoline_addr; - struct hlist_node *r; - struct uretprobe_instance *ret_inst; - - if (!ri) - return 0; - rp = ri->rp; - uk = (struct uprobe_kimg *)rp->u.kdata; - if (!uk) - return 0; - uproc = uk->ppt->uproc; - if (IS_ERR(uproc->uretprobe_trampoline_addr)) - return pc; - trampoline_addr = (unsigned long)uproc->uretprobe_trampoline_addr; - if (pc != trampoline_addr) - return pc; - r = &ri->hlist; - hlist_for_each_entry_from(ret_inst, r, hlist) { - if (ret_inst->ret_addr == trampoline_addr) - continue; - /* First handler with a stack pointer lower than the - address (or equal) must be the one. */ - if (ret_inst->sp == sp || compare_stack_ptrs(ret_inst->sp, sp)) - return ret_inst->ret_addr; - } - printk(KERN_ERR "Original return address for trampoline not found at " - "0x%lx pid/tgid=%d/%d\n", sp, current->pid, current->tgid); - return 0; + struct uretprobe *rp; + struct uprobe_kimg *uk; + struct uprobe_task *utask; + struct uprobe_process *uproc; + unsigned long trampoline_addr; + struct hlist_node *r; + struct uretprobe_instance *ret_inst; + + if (!ri) + return 0; + if (ri == GET_PC_URETPROBE_NONE) { + utask = uprobe_find_utask(current); + if (!utask) + return 0; + uproc = utask->uproc; + r = utask->uretprobe_instances.first; + } else { + rp = ri->rp; + uk = (struct uprobe_kimg *)rp->u.kdata; + if (!uk) + return 0; + uproc = uk->ppt->uproc; + r = &ri->hlist; + } + if (IS_ERR(uproc->uretprobe_trampoline_addr)) + return pc; + trampoline_addr = (unsigned long)uproc->uretprobe_trampoline_addr; + if (pc != trampoline_addr) + return pc; + hlist_for_each_entry_from(ret_inst, r, hlist) { + if (ret_inst->ret_addr == trampoline_addr) + continue; + /* First handler with a stack pointer lower than the + address (or equal) must be the one. */ + if (ret_inst->sp == sp || compare_stack_ptrs(ret_inst->sp, sp)) + return ret_inst->ret_addr; + } + printk(KERN_ERR "Original return address for trampoline not found at " + "0x%lx pid/tgid=%d/%d\n", sp, current->pid, current->tgid); + return 0; } EXPORT_SYMBOL_GPL(uprobe_get_pc); diff --git a/runtime/uprobes2/uprobes.h b/runtime/uprobes2/uprobes.h index 5d2a826e..c4e1f59c 100644 --- a/runtime/uprobes2/uprobes.h +++ b/runtime/uprobes2/uprobes.h @@ -92,7 +92,11 @@ extern void unmap_uretprobe(struct uretprobe *rp); * Given a program counter, translate it back to the original address * if it is the address of the trampoline. sp is the stack pointer for * the frame that corresponds to the address. + * + * When not called from a uretprobe hander, pass GET_PC_URETPROBE_NONE + * instead of a uretprobe_instance. */ +#define GET_PC_URETPROBE_NONE ((struct uretprobe_instance *)-1L) extern unsigned long uprobe_get_pc(struct uretprobe_instance *ri, unsigned long pc, unsigned long sp); |