[PATCH v7 3.2-rc2 29/30] uprobes: Introduce uprobe flags
From: Srikar Dronamraju
Date: Fri Nov 18 2011 - 06:38:41 EST
While registering a probe, there is a timelag between the time the register
request is given all probes are inserted in different processes. If the probe
register fails after inserting a probe in couple of processes; the installed
probes are reverted. However the probes could have hit and triggered handler
before the probes are reverted.
Avoids running the handler until the register is complete or as soon as the
last unregister kicks in.
Also this patch
- enables skipping singlestep where possible.
- uses a flag to denote if a copy of instruction is made.
Signed-off-by: Srikar Dronamraju <srikar@xxxxxxxxxxxxxxxxxx>
---
include/linux/uprobes.h | 11 ++++++++++-
kernel/uprobes.c | 32 ++++++++++++++++++++++++++------
2 files changed, 36 insertions(+), 7 deletions(-)
diff --git a/include/linux/uprobes.h b/include/linux/uprobes.h
index 6a84332..20bdd0a 100644
--- a/include/linux/uprobes.h
+++ b/include/linux/uprobes.h
@@ -46,6 +46,14 @@ struct uprobe_task_arch_info {}; /* arch specific task info */
/* Adjust the return address of a call insn */
#define UPROBES_FIX_CALL 0x2
+/* flags that denote/change uprobes behaviour */
+/* Have a copy of original instruction */
+#define UPROBES_COPY_INSN 0x1
+/* Dont run handlers when first register/ last unregister in progress*/
+#define UPROBES_RUN_HANDLER 0x2
+/* Can skip singlestep */
+#define UPROBES_SKIP_SSTEP 0x4
+
struct uprobe_consumer {
int (*handler)(struct uprobe_consumer *self, struct pt_regs *regs);
/*
@@ -66,7 +74,7 @@ struct uprobe {
struct uprobe_consumer *consumers;
struct inode *inode; /* Also hold a ref to inode */
loff_t offset;
- int copy;
+ int flags;
u16 fixups;
u8 insn[MAX_UINSN_BYTES];
};
@@ -131,6 +139,7 @@ extern int uprobe_post_notifier(struct pt_regs *regs);
extern int uprobe_bkpt_notifier(struct pt_regs *regs);
extern void uprobe_notify_resume(struct pt_regs *regs);
extern bool uprobe_deny_signal(void);
+extern bool __weak can_skip_xol(struct pt_regs *regs, struct uprobe *u);
#else /* CONFIG_UPROBES is not defined */
static inline int register_uprobe(struct inode *inode, loff_t offset,
struct uprobe_consumer *consumer)
diff --git a/kernel/uprobes.c b/kernel/uprobes.c
index f8c0f7c..2493191 100644
--- a/kernel/uprobes.c
+++ b/kernel/uprobes.c
@@ -436,6 +436,9 @@ static struct uprobe *insert_uprobe(struct uprobe *uprobe)
spin_lock_irqsave(&uprobes_treelock, flags);
u = __insert_uprobe(uprobe);
spin_unlock_irqrestore(&uprobes_treelock, flags);
+
+ /* For now assume that the instruction need not be single-stepped */
+ uprobe->flags |= UPROBES_SKIP_SSTEP;
return u;
}
@@ -475,6 +478,9 @@ static void handler_chain(struct uprobe *uprobe, struct pt_regs *regs)
{
struct uprobe_consumer *consumer;
+ if (!(uprobe->flags & UPROBES_RUN_HANDLER))
+ return;
+
down_read(&uprobe->consumer_rwsem);
consumer = uprobe->consumers;
for (consumer = uprobe->consumers; consumer;
@@ -594,7 +600,7 @@ static int install_breakpoint(struct mm_struct *mm, struct uprobe *uprobe,
return -EEXIST;
addr = (unsigned long)vaddr;
- if (!uprobe->copy) {
+ if (!(uprobe->flags & UPROBES_COPY_INSN)) {
ret = copy_insn(uprobe, vma, addr);
if (ret)
return ret;
@@ -606,7 +612,7 @@ static int install_breakpoint(struct mm_struct *mm, struct uprobe *uprobe,
if (ret)
return ret;
- uprobe->copy = 1;
+ uprobe->flags |= UPROBES_COPY_INSN;
}
ret = set_bkpt(mm, uprobe, addr);
if (!ret)
@@ -850,7 +856,8 @@ int register_uprobe(struct inode *inode, loff_t offset,
if (ret) {
uprobe->consumers = NULL;
__unregister_uprobe(inode, offset, uprobe);
- }
+ } else
+ uprobe->flags |= UPROBES_RUN_HANDLER;
}
mutex_unlock(uprobes_hash(inode));
@@ -886,9 +893,10 @@ void unregister_uprobe(struct inode *inode, loff_t offset,
goto unreg_out;
}
- if (!uprobe->consumers)
+ if (!uprobe->consumers) {
__unregister_uprobe(inode, offset, uprobe);
-
+ uprobe->flags &= ~UPROBES_RUN_HANDLER;
+ }
mutex_unlock(uprobes_hash(inode));
unreg_out:
@@ -1337,6 +1345,12 @@ bool uprobe_deny_signal(void)
return true;
}
+bool __weak can_skip_xol(struct pt_regs *regs, struct uprobe *u)
+{
+ u->flags &= ~UPROBES_SKIP_SSTEP;
+ return false;
+}
+
/*
* uprobe_notify_resume gets called in task context just before returning
* to userspace.
@@ -1378,6 +1392,10 @@ void uprobe_notify_resume(struct pt_regs *regs)
}
utask->active_uprobe = u;
handler_chain(u, regs);
+
+ if (u->flags & UPROBES_SKIP_SSTEP && can_skip_xol(regs, u))
+ goto cleanup_ret;
+
utask->state = UTASK_SSTEP;
if (!pre_ssout(u, regs, probept))
user_enable_single_step(current);
@@ -1411,8 +1429,10 @@ void uprobe_notify_resume(struct pt_regs *regs)
utask->state = UTASK_RUNNING;
}
if (u) {
+ if (!(u->flags & UPROBES_SKIP_SSTEP))
+ set_instruction_pointer(regs, probept);
+
put_uprobe(u);
- set_instruction_pointer(regs, probept);
} else
send_sig(SIGTRAP, current, 0);
}
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/