diff options
author | Anton Arapov <anton@redhat.com> | 2012-11-01 00:00:00 +0200 |
---|---|---|
committer | Anton Arapov <anton@redhat.com> | 2012-11-01 13:43:12 +0100 |
commit | 0ab1ca172ef0277d88b15f71679a744aa2b2425c (patch) | |
tree | 3f426733144e1e3cf20102c5241b2e4727e41d78 | |
parent | 0128b2f1d3884966ccad73d901f31d544ff73302 (diff) | |
download | kernel-uprobes-0ab1ca172ef0277d88b15f71679a744aa2b2425c.tar.gz kernel-uprobes-0ab1ca172ef0277d88b15f71679a744aa2b2425c.tar.xz kernel-uprobes-0ab1ca172ef0277d88b15f71679a744aa2b2425c.zip |
uretprobes: register, unregister uretprobesf17_uretprobes
A uretprobe is specified by a file:offset:handler. A uprobe is created
at the offset specified. When this uprobe is hit a uretprobe instance
would be created and a probe placed instead of the return address of the
current function. The handler specified is invoked before replacing the
return address. A uretprobe therefore places two probes, firstly a
uprobe at offset, whose handler in turn places the second probe at the
return address at task runtime.
Signed-off-by: Anithra P Janakiraman <anithra@linux.vnet.ibm.com>
Signed-off-by: Anton Arapov <anton@redhat.com>
-rw-r--r-- | include/linux/uprobes.h | 11 | ||||
-rw-r--r-- | kernel/events/uprobes.c | 147 |
2 files changed, 158 insertions, 0 deletions
diff --git a/include/linux/uprobes.h b/include/linux/uprobes.h index 6c1cf58ae28..c73d6c5b0d0 100644 --- a/include/linux/uprobes.h +++ b/include/linux/uprobes.h @@ -122,6 +122,8 @@ extern void uprobe_notify_resume(struct pt_regs *regs); extern bool uprobe_deny_signal(void); extern bool __weak arch_uprobe_skip_sstep(struct arch_uprobe *aup, struct pt_regs *regs); extern void uprobe_clear_state(struct mm_struct *mm); +extern int uretprobe_register(struct inode *inode, loff_t offset, struct uretprobe_consumer *consumer); +extern void uretprobe_unregister(struct inode *inode, loff_t offset, struct uretprobe_consumer *consumer); #else /* !CONFIG_UPROBES */ struct uprobes_state { }; @@ -166,5 +168,14 @@ static inline void uprobe_copy_process(struct task_struct *t) static inline void uprobe_clear_state(struct mm_struct *mm) { } +static inline int +uretprobe_register(struct inode *inode, loff_t offset, struct uretprobe_consumer *consumer) +{ + return -ENOSYS; +} +static inline void +uretprobe_unregister(struct inode *inode, loff_t offset, struct uretprobe_consumer *consumer) +{ +} #endif /* !CONFIG_UPROBES */ #endif /* _LINUX_UPROBES_H */ diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c index 6b619a3144c..776fd622e75 100644 --- a/kernel/events/uprobes.c +++ b/kernel/events/uprobes.c @@ -650,6 +650,45 @@ static bool consumer_del(struct uprobe *uprobe, struct uprobe_consumer *uc) return ret; } +/* Returns the previous uretprobe consumer */ +static struct uretprobe_consumer * +uretprobe_consumer_add(struct uprobe *uprobe, struct uretprobe_consumer *rpc) +{ + struct uretprobe *rp; + + rp = uprobe->rp; + + down_write(&rp->consumer_rwsem); + rpc->next = rp->consumers; + rp->consumers = rpc; + up_write(&rp->consumer_rwsem); + + return rpc->next; +} + +/* + * For uretprobe @uretprobe, delete the consumer @urc. + * Return true if the @urc is deleted successfully or return false. + */ +static bool +uretprobe_consumer_del(struct uretprobe *rp, struct uretprobe_consumer *rpc) +{ + struct uretprobe_consumer **con; + bool ret = false; + + down_write(&rp->consumer_rwsem); + for (con = &rp->consumers; *con; con = &(*con)->next) { + if (*con == rpc) { + *con = rpc->next; + ret = true; + break; + } + } + up_write(&rp->consumer_rwsem); + + return ret; +} + static int __copy_insn(struct address_space *mapping, struct file *filp, char *insn, unsigned long nbytes, loff_t offset) @@ -1032,6 +1071,114 @@ void uprobe_unregister(struct inode *inode, loff_t offset, struct uprobe_consume put_uprobe(uprobe); } +/* + * uretprobe_register - register return probe + * @inode: the file in which the probe has to be placed. + * @offset: offset from the start of the file. + * @urc: information on howto handle the probe. + * + * Return errno if it cannot successully install probes else + * return 0 (success) + */ +int uretprobe_register(struct inode *inode, loff_t offset, struct uretprobe_consumer *urc) +{ + struct uretprobe *rp; + struct uprobe_consumer *uc; + struct uprobe *uprobe; + + if (!inode || !urc || urc->next) + return -EINVAL; + + uprobe = find_uprobe(inode, offset); + if (uprobe) { + uc = uprobe->consumers; + if (!uc) + return -EINVAL; + + /* + * uprobe already exists, it could have uprobe and or uretprobe + * consumers. If there is a uretprobe consumer and uretprobe + * structure already exists. + */ + while (uc) { + if (uc == uretprobe_dummy_consumer) { + rp = uprobe->rp; + goto uretprobe_consumer_add; + } + uc = uc->next; + } + } + + /* + * We have reached here because either there is no uprobe or the uprobe + * does not have a uretprobe consumer yet + */ + if (!uretprobe_dummy_consumer) { + uretprobe_dummy_consumer = + kzalloc(sizeof(struct uprobe_consumer), GFP_KERNEL); + uretprobe_dummy_consumer->handler = NULL; + uretprobe_dummy_consumer->filter = NULL; + } + if (uprobe_register(inode, offset, uretprobe_dummy_consumer)) + return -EINVAL; + + + uprobe = find_uprobe(inode, offset); + rp = kzalloc(sizeof(struct uretprobe), GFP_KERNEL); + rp->consumers = NULL; + init_rwsem(&rp->consumer_rwsem); + uprobe->rp = rp; + + uretprobe_consumer_add: + uretprobe_consumer_add(uprobe, urc); + put_uprobe(uprobe); + + return 0; +} + +void uretprobe_unregister(struct inode *inode, loff_t offset, struct uretprobe_consumer *rpc) +{ + struct uprobe *uprobe; + struct uretprobe *rp = NULL; + struct uprobe_consumer *uc; + + if (!inode || !rpc) + return; + + uprobe = find_uprobe(inode, offset); + if (!uprobe) { + printk(KERN_ERR "No uprobe found with inode:offset %p %lld\n", + inode, offset); + return; + } + + down_read(&uprobe->consumer_rwsem); + uc = uprobe->consumers; + while (uc) { + if (uc == uretprobe_dummy_consumer) { + rp = uprobe->rp; + break; + } + uc = uc->next; + } + up_read(&uprobe->consumer_rwsem); + + if (!rp) { + printk(KERN_ERR "No uretprobe found with inode:offset %p %lld\n", + inode, offset); + goto unlock_uretprobe; + } + if (!uretprobe_consumer_del(rp, rpc)) + goto unlock_uretprobe; + if (rp->consumers) + goto unlock_uretprobe; + + uprobe_unregister(inode, offset, uretprobe_dummy_consumer); + + unlock_uretprobe: + put_uprobe(uprobe); +} + static struct rb_node * find_node_in_range(struct inode *inode, loff_t min, loff_t max) { |