Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 126343
b: refs/heads/master
c: e8386a0
h: refs/heads/master
i:
  126341: aa5223e
  126339: a748b6b
  126335: 7c9003f
v: v3
  • Loading branch information
Masami Hiramatsu authored and Linus Torvalds committed Jan 6, 2009
1 parent 0a38664 commit 0463b87
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 45 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 017c39bdb1b3ac1da6db339474a77b528043c05a
refs/heads/master: e8386a0cb22f4a2d439384212c494ad0bda848fe
5 changes: 4 additions & 1 deletion trunk/Documentation/kprobes.txt
Original file line number Diff line number Diff line change
Expand Up @@ -497,7 +497,10 @@ The first column provides the kernel address where the probe is inserted.
The second column identifies the type of probe (k - kprobe, r - kretprobe
and j - jprobe), while the third column specifies the symbol+offset of
the probe. If the probed function belongs to a module, the module name
is also specified.
is also specified. Following columns show probe status. If the probe is on
a virtual address that is no longer valid (module init sections, module
virtual addresses that correspond to modules that've been unloaded),
such probes are marked with [GONE].

/debug/kprobes/enabled: Turn kprobes ON/OFF

Expand Down
14 changes: 11 additions & 3 deletions trunk/include/linux/kprobes.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,6 @@ struct kprobe {
/* list of kprobes for multi-handler support */
struct list_head list;

/* Indicates that the corresponding module has been ref counted */
unsigned int mod_refcounted;

/*count the number of times this probe was temporarily disarmed */
unsigned long nmissed;

Expand Down Expand Up @@ -103,8 +100,19 @@ struct kprobe {

/* copy of the original instruction */
struct arch_specific_insn ainsn;

/* Indicates various status flags. Protected by kprobe_mutex. */
u32 flags;
};

/* Kprobe status flags */
#define KPROBE_FLAG_GONE 1 /* breakpoint has already gone */

static inline int kprobe_gone(struct kprobe *p)
{
return p->flags & KPROBE_FLAG_GONE;
}

/*
* Special probe type that uses setjmp-longjmp type tricks to resume
* execution at a specified entry with a matching prototype corresponding
Expand Down
159 changes: 119 additions & 40 deletions trunk/kernel/kprobes.c
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,7 @@ static int __kprobes aggr_pre_handler(struct kprobe *p, struct pt_regs *regs)
struct kprobe *kp;

list_for_each_entry_rcu(kp, &p->list, list) {
if (kp->pre_handler) {
if (kp->pre_handler && !kprobe_gone(kp)) {
set_kprobe_instance(kp);
if (kp->pre_handler(kp, regs))
return 1;
Expand All @@ -343,7 +343,7 @@ static void __kprobes aggr_post_handler(struct kprobe *p, struct pt_regs *regs,
struct kprobe *kp;

list_for_each_entry_rcu(kp, &p->list, list) {
if (kp->post_handler) {
if (kp->post_handler && !kprobe_gone(kp)) {
set_kprobe_instance(kp);
kp->post_handler(kp, regs, flags);
reset_kprobe_instance();
Expand Down Expand Up @@ -545,9 +545,10 @@ static inline void add_aggr_kprobe(struct kprobe *ap, struct kprobe *p)
ap->addr = p->addr;
ap->pre_handler = aggr_pre_handler;
ap->fault_handler = aggr_fault_handler;
if (p->post_handler)
/* We don't care the kprobe which has gone. */
if (p->post_handler && !kprobe_gone(p))
ap->post_handler = aggr_post_handler;
if (p->break_handler)
if (p->break_handler && !kprobe_gone(p))
ap->break_handler = aggr_break_handler;

INIT_LIST_HEAD(&ap->list);
Expand All @@ -566,17 +567,41 @@ static int __kprobes register_aggr_kprobe(struct kprobe *old_p,
int ret = 0;
struct kprobe *ap;

if (kprobe_gone(old_p)) {
/*
* Attempting to insert new probe at the same location that
* had a probe in the module vaddr area which already
* freed. So, the instruction slot has already been
* released. We need a new slot for the new probe.
*/
ret = arch_prepare_kprobe(old_p);
if (ret)
return ret;
}
if (old_p->pre_handler == aggr_pre_handler) {
copy_kprobe(old_p, p);
ret = add_new_kprobe(old_p, p);
ap = old_p;
} else {
ap = kzalloc(sizeof(struct kprobe), GFP_KERNEL);
if (!ap)
if (!ap) {
if (kprobe_gone(old_p))
arch_remove_kprobe(old_p);
return -ENOMEM;
}
add_aggr_kprobe(ap, old_p);
copy_kprobe(ap, p);
ret = add_new_kprobe(ap, p);
}
if (kprobe_gone(old_p)) {
/*
* If the old_p has gone, its breakpoint has been disarmed.
* We have to arm it again after preparing real kprobes.
*/
ap->flags &= ~KPROBE_FLAG_GONE;
if (kprobe_enabled)
arch_arm_kprobe(ap);
}
return ret;
}

Expand Down Expand Up @@ -639,8 +664,7 @@ static int __kprobes __register_kprobe(struct kprobe *p,
return -EINVAL;
}

p->mod_refcounted = 0;

p->flags = 0;
/*
* Check if are we probing a module.
*/
Expand All @@ -649,16 +673,14 @@ static int __kprobes __register_kprobe(struct kprobe *p,
struct module *calling_mod;
calling_mod = __module_text_address(called_from);
/*
* We must allow modules to probe themself and in this case
* avoid incrementing the module refcount, so as to allow
* unloading of self probing modules.
* We must hold a refcount of the probed module while updating
* its code to prohibit unexpected unloading.
*/
if (calling_mod != probed_mod) {
if (unlikely(!try_module_get(probed_mod))) {
preempt_enable();
return -EINVAL;
}
p->mod_refcounted = 1;
} else
probed_mod = NULL;
}
Expand Down Expand Up @@ -687,8 +709,9 @@ static int __kprobes __register_kprobe(struct kprobe *p,
out:
mutex_unlock(&kprobe_mutex);

if (ret && probed_mod)
if (probed_mod)
module_put(probed_mod);

return ret;
}

Expand Down Expand Up @@ -716,16 +739,16 @@ static int __kprobes __unregister_kprobe_top(struct kprobe *p)
list_is_singular(&old_p->list))) {
/*
* Only probe on the hash list. Disarm only if kprobes are
* enabled - otherwise, the breakpoint would already have
* been removed. We save on flushing icache.
* enabled and not gone - otherwise, the breakpoint would
* already have been removed. We save on flushing icache.
*/
if (kprobe_enabled)
if (kprobe_enabled && !kprobe_gone(old_p))
arch_disarm_kprobe(p);
hlist_del_rcu(&old_p->hlist);
} else {
if (p->break_handler)
if (p->break_handler && !kprobe_gone(p))
old_p->break_handler = NULL;
if (p->post_handler) {
if (p->post_handler && !kprobe_gone(p)) {
list_for_each_entry_rcu(list_p, &old_p->list, list) {
if ((list_p != p) && (list_p->post_handler))
goto noclean;
Expand All @@ -740,27 +763,16 @@ static int __kprobes __unregister_kprobe_top(struct kprobe *p)

static void __kprobes __unregister_kprobe_bottom(struct kprobe *p)
{
struct module *mod;
struct kprobe *old_p;

if (p->mod_refcounted) {
/*
* Since we've already incremented refcount,
* we don't need to disable preemption.
*/
mod = module_text_address((unsigned long)p->addr);
if (mod)
module_put(mod);
}

if (list_empty(&p->list) || list_is_singular(&p->list)) {
if (!list_empty(&p->list)) {
/* "p" is the last child of an aggr_kprobe */
old_p = list_entry(p->list.next, struct kprobe, list);
list_del(&p->list);
kfree(old_p);
}
if (list_empty(&p->list))
arch_remove_kprobe(p);
else if (list_is_singular(&p->list)) {
/* "p" is the last child of an aggr_kprobe */
old_p = list_entry(p->list.next, struct kprobe, list);
list_del(&p->list);
arch_remove_kprobe(old_p);
kfree(old_p);
}
}

Expand Down Expand Up @@ -1074,6 +1086,67 @@ static int __kprobes pre_handler_kretprobe(struct kprobe *p,

#endif /* CONFIG_KRETPROBES */

/* Set the kprobe gone and remove its instruction buffer. */
static void __kprobes kill_kprobe(struct kprobe *p)
{
struct kprobe *kp;
p->flags |= KPROBE_FLAG_GONE;
if (p->pre_handler == aggr_pre_handler) {
/*
* If this is an aggr_kprobe, we have to list all the
* chained probes and mark them GONE.
*/
list_for_each_entry_rcu(kp, &p->list, list)
kp->flags |= KPROBE_FLAG_GONE;
p->post_handler = NULL;
p->break_handler = NULL;
}
/*
* Here, we can remove insn_slot safely, because no thread calls
* the original probed function (which will be freed soon) any more.
*/
arch_remove_kprobe(p);
}

/* Module notifier call back, checking kprobes on the module */
static int __kprobes kprobes_module_callback(struct notifier_block *nb,
unsigned long val, void *data)
{
struct module *mod = data;
struct hlist_head *head;
struct hlist_node *node;
struct kprobe *p;
unsigned int i;

if (val != MODULE_STATE_GOING)
return NOTIFY_DONE;

/*
* module .text section will be freed. We need to
* disable kprobes which have been inserted in the section.
*/
mutex_lock(&kprobe_mutex);
for (i = 0; i < KPROBE_TABLE_SIZE; i++) {
head = &kprobe_table[i];
hlist_for_each_entry_rcu(p, node, head, hlist)
if (within_module_core((unsigned long)p->addr, mod)) {
/*
* The vaddr this probe is installed will soon
* be vfreed buy not synced to disk. Hence,
* disarming the breakpoint isn't needed.
*/
kill_kprobe(p);
}
}
mutex_unlock(&kprobe_mutex);
return NOTIFY_DONE;
}

static struct notifier_block kprobe_module_nb = {
.notifier_call = kprobes_module_callback,
.priority = 0
};

static int __init init_kprobes(void)
{
int i, err = 0;
Expand Down Expand Up @@ -1130,6 +1203,9 @@ static int __init init_kprobes(void)
err = arch_init_kprobes();
if (!err)
err = register_die_notifier(&kprobe_exceptions_nb);
if (!err)
err = register_module_notifier(&kprobe_module_nb);

kprobes_initialized = (err == 0);

if (!err)
Expand All @@ -1150,10 +1226,12 @@ static void __kprobes report_probe(struct seq_file *pi, struct kprobe *p,
else
kprobe_type = "k";
if (sym)
seq_printf(pi, "%p %s %s+0x%x %s\n", p->addr, kprobe_type,
sym, offset, (modname ? modname : " "));
seq_printf(pi, "%p %s %s+0x%x %s %s\n", p->addr, kprobe_type,
sym, offset, (modname ? modname : " "),
(kprobe_gone(p) ? "[GONE]" : ""));
else
seq_printf(pi, "%p %s %p\n", p->addr, kprobe_type, p->addr);
seq_printf(pi, "%p %s %p %s\n", p->addr, kprobe_type, p->addr,
(kprobe_gone(p) ? "[GONE]" : ""));
}

static void __kprobes *kprobe_seq_start(struct seq_file *f, loff_t *pos)
Expand Down Expand Up @@ -1234,7 +1312,8 @@ static void __kprobes enable_all_kprobes(void)
for (i = 0; i < KPROBE_TABLE_SIZE; i++) {
head = &kprobe_table[i];
hlist_for_each_entry_rcu(p, node, head, hlist)
arch_arm_kprobe(p);
if (!kprobe_gone(p))
arch_arm_kprobe(p);
}

kprobe_enabled = true;
Expand Down Expand Up @@ -1263,7 +1342,7 @@ static void __kprobes disable_all_kprobes(void)
for (i = 0; i < KPROBE_TABLE_SIZE; i++) {
head = &kprobe_table[i];
hlist_for_each_entry_rcu(p, node, head, hlist) {
if (!arch_trampoline_kprobe(p))
if (!arch_trampoline_kprobe(p) && !kprobe_gone(p))
arch_disarm_kprobe(p);
}
}
Expand Down

0 comments on commit 0463b87

Please sign in to comment.