summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAnton Arapov <anton@redhat.com>2012-11-01 00:00:00 +0200
committerAnton Arapov <anton@redhat.com>2012-11-01 13:43:12 +0100
commit0ab1ca172ef0277d88b15f71679a744aa2b2425c (patch)
tree3f426733144e1e3cf20102c5241b2e4727e41d78
parent0128b2f1d3884966ccad73d901f31d544ff73302 (diff)
downloadkernel-uprobes-f17_uretprobes.zip
kernel-uprobes-f17_uretprobes.tar.gz
kernel-uprobes-f17_uretprobes.tar.xz
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.h11
-rw-r--r--kernel/events/uprobes.c147
2 files changed, 158 insertions, 0 deletions
diff --git a/include/linux/uprobes.h b/include/linux/uprobes.h
index 6c1cf58..c73d6c5 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 6b619a3..776fd62 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)
{