Add atomic usage counter to uprobe struct and check it before remove uprobe.
Change-Id: Idc7804d38028413d68d4fbac41813ca3899e5443
Signed-off-by: Vyacheslav Cherkashin <v.cherkashin@samsung.com>
static int urp_handler(struct pt_regs *regs, pid_t tgid)
{
struct uprobe *p;
+ unsigned long flags;
unsigned long vaddr = regs->ARM_pc;
unsigned long offset_bp = thumb_mode(regs) ?
0x1a :
4 * UPROBES_TRAMP_RET_BREAK_IDX;
unsigned long tramp_addr = vaddr - offset_bp;
+ local_irq_save(flags);
p = get_uprobe_by_insn_slot((void *)tramp_addr, tgid, regs);
- if (p == NULL) {
- printk(KERN_INFO
- "no_uprobe: Not one of ours: let kernel handle it %lx\n",
- vaddr);
+ if (unlikely(p == NULL)) {
+ local_irq_restore(flags);
+
+ pr_info("no_uprobe: Not one of ours: let kernel handle it %lx\n",
+ vaddr);
return 1;
}
+ get_up(p);
+ local_irq_restore(flags);
trampoline_uprobe_handler(p, regs);
+ put_up(p);
return 0;
}
{
int ret = 0;
struct uprobe *p;
+ unsigned long flags;
unsigned long vaddr = regs->ARM_pc | !!thumb_mode(regs);
pid_t tgid = current->tgid;
+ local_irq_save(flags);
p = get_uprobe((uprobe_opcode_t *)vaddr, tgid);
if (p) {
+ get_up(p);
+ local_irq_restore(flags);
if (!p->pre_handler || !p->pre_handler(p, regs))
arch_prepare_singlestep(p, regs);
+ put_up(p);
} else {
+ local_irq_restore(flags);
ret = urp_handler(regs, tgid);
/* check ARM/THUMB CPU mode matches installed probe mode */
if (ret) {
vaddr ^= 1;
+
+ local_irq_save(flags);
p = get_uprobe((uprobe_opcode_t *)vaddr, tgid);
if (p) {
+ get_up(p);
+ local_irq_restore(flags);
pr_err("invalid mode: thumb=%d addr=%p insn=%08lx\n",
!!thumb_mode(regs), p->addr, p->opcode);
ret = 0;
disarm_uprobe(p, current);
+ put_up(p);
+ } else {
+ local_irq_restore(flags);
}
}
}
return 1;
}
+static int uprobe_handler_retprobe(struct uprobe *p, struct pt_regs *regs)
+{
+ int ret;
+
+ ret = trampoline_uprobe_handler(p, regs);
+ set_current_probe(NULL);
+ put_up(p);
+
+ return ret;
+}
+
static int uprobe_handler_part2(struct uprobe *p, struct pt_regs *regs)
{
if (!p->pre_handler(p, regs)) {
prepare_tramp(p, regs);
if (p->ainsn.boostable == 1 && !p->post_handler)
- return 1;
+ goto exit_and_put_up;
save_current_flags(regs);
set_current_probe(p);
prepare_ss(regs);
+
+ return 1;
}
+exit_and_put_up:
+ set_current_probe(NULL);
+ put_up(p);
return 1;
}
struct uprobe *p = get_uprobe((void *)vaddr, tgid);
if (p) {
+ get_up(p);
if (p->pre_handler) {
set_current_probe(p);
exceptions_handler(regs, uprobe_handler_part2);
}
set_current_probe(p);
- exceptions_handler(regs, trampoline_uprobe_handler);
+ get_up(p);
+ exceptions_handler(regs, uprobe_handler_retprobe);
}
return 1;
/* reset current probe */
set_current_probe(NULL);
+ put_up(p);
return 1;
}
.priority = INT_MAX
};
+struct up_valid_struct {
+ struct uprobe *p;
+ bool found;
+};
+
+static int __uprobe_is_valid(struct uprobe *p, void *data)
+{
+ struct up_valid_struct *valid = (struct up_valid_struct *)data;
+
+ if (valid->p == p) {
+ valid->found = true;
+ return 1;
+ }
+
+ return 0;
+}
+
+static bool uprobe_is_valid(struct uprobe *p)
+{
+ struct up_valid_struct valid = {
+ .p = p,
+ .found = false,
+ };
+
+ for_each_uprobe(__uprobe_is_valid, (void *)&valid);
+
+ return valid.found;
+}
+
+static int do_exit_handler(struct kprobe *kp, struct pt_regs *regs)
+{
+ struct uprobe *p;
+
+ p = get_current_probe();
+ if (p && uprobe_is_valid(p)) {
+ set_current_probe(NULL);
+ put_up(p);
+ }
+
+ return 0;
+}
+
+static struct kprobe kp_do_exit = {
+ .pre_handler = do_exit_handler
+};
+
/**
* @brief Registers notify.
*
int swap_arch_init_uprobes(void)
{
int ret;
+ const char *sym;
- resume_userspace_addr = swap_ksyms("resume_userspace");
- if (resume_userspace_addr == 0) {
- pr_err("symbol 'resume_userspace' not found\n");
- return -ESRCH;
- }
+ sym = "resume_userspace";
+ resume_userspace_addr = swap_ksyms(sym);
+ if (resume_userspace_addr == 0)
+ goto not_found;
+
+ sym = "do_exit";
+ kp_do_exit.addr = (void *)swap_ksyms(sym);
+ if (kp_do_exit.addr == NULL)
+ goto not_found;
ret = swap_td_raw_reg(&td_raw, sizeof(struct uprobe_ctlblk));
if (ret)
ret = register_die_notifier(&uprobe_exceptions_nb);
if (ret)
- swap_td_raw_unreg(&td_raw);
+ goto unreg_td;
+
+ ret = swap_register_kprobe(&kp_do_exit);
+ if (ret)
+ goto unreg_exeption;
+ return 0;
+
+unreg_exeption:
+ unregister_die_notifier(&uprobe_exceptions_nb);
+unreg_td:
+ swap_td_raw_unreg(&td_raw);
return ret;
+
+not_found:
+ pr_err("symbol '%s' not found\n", sym);
+ return -ESRCH;
}
/**
*/
void swap_arch_exit_uprobes(void)
{
+ swap_unregister_kprobe(&kp_do_exit);
unregister_die_notifier(&uprobe_exceptions_nb);
swap_td_raw_unreg(&td_raw);
}
}
/* =================================================================== */
+
+void for_each_uprobe(int (*func)(struct uprobe *, void *), void *data)
+{
+ int i;
+ struct uprobe *p;
+ struct hlist_head *head;
+ struct hlist_node *tnode;
+ DECLARE_NODE_PTR_FOR_HLIST(node);
+
+ for (i = 0; i < UPROBE_TABLE_SIZE; ++i) {
+ head = &uprobe_table[i];
+ swap_hlist_for_each_entry_safe(p, node, tnode, head, hlist) {
+ if (func(p, data))
+ return;
+ }
+ }
+}
+
+static int wait_up_action(atomic_t *val)
+{
+ BUG_ON(atomic_read(val));
+ schedule();
+ return 0;
+}
+
+static void wait_up(struct uprobe *p)
+{
+ wait_on_atomic_t(&p->usage, wait_up_action, TASK_UNINTERRUPTIBLE);
+}
+
/**
* @brief Registers uprobe.
*
p->ainsn.insn = NULL;
INIT_LIST_HEAD(&p->list);
+ atomic_set(&p->usage, 1);
/* get the first item */
old_p = get_uprobe(p->addr, p->task->tgid);
kfree(old_p);
}
- if (!in_atomic())
+ if (!in_atomic()) {
synchronize_sched();
+ atomic_dec(&p->usage);
+ wait_up(p);
+ }
+
remove_uprobe(p);
} else {
if (p->break_handler)
#define _SWAP_UPROBES_H
+#include <master/wait.h>
#include <kprobe/swap_kprobes.h>
-
#include <swap-asm/swap_uprobes.h>
/**
uprobe_break_handler_t break_handler;
/** Saved opcode (which has been replaced with breakpoint).*/
uprobe_opcode_t opcode;
+ atomic_t usage;
#ifdef CONFIG_ARM
/** Safe/unsafe to use probe on ARM.*/
unsigned safe_arm:1;
char data[0]; /**< Custom data */
};
+
+static void inline get_up(struct uprobe *p)
+{
+ atomic_inc(&p->usage);
+}
+
+static void inline put_up(struct uprobe *p)
+{
+ if (atomic_dec_and_test(&p->usage))
+ wake_up_atomic_t(&p->usage);
+}
+
+void for_each_uprobe(int (*func)(struct uprobe *, void *), void *data);
int swap_register_uprobe(struct uprobe *p);
void swap_unregister_uprobe(struct uprobe *p);
void __swap_unregister_uprobe(struct uprobe *up, int disarm);