summaryrefslogtreecommitdiffstats
path: root/runtime
diff options
context:
space:
mode:
Diffstat (limited to 'runtime')
-rw-r--r--runtime/alloc.c133
-rw-r--r--runtime/stack.c18
-rw-r--r--runtime/uprobes/uprobes.c71
-rw-r--r--runtime/uprobes/uprobes.h4
-rw-r--r--runtime/uprobes2/uprobes.c71
-rw-r--r--runtime/uprobes2/uprobes.h4
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);