Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 2929
b: refs/heads/master
c: b94cce9
h: refs/heads/master
i:
  2927: 940c71e
v: v3
  • Loading branch information
Hien Nguyen authored and Linus Torvalds committed Jun 23, 2005
1 parent 7bf2f53 commit 079af4d
Show file tree
Hide file tree
Showing 6 changed files with 416 additions and 9 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: 2fa389c5eb8c97d621653184d2adf5fdbd4a3167
refs/heads/master: b94cce926b2b902b79380ccba370d6f9f2980de0
102 changes: 101 additions & 1 deletion trunk/arch/i386/kernel/kprobes.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@
* Rusty Russell).
* 2004-July Suparna Bhattacharya <suparna@in.ibm.com> added jumper probes
* interface to access function arguments.
* 2005-May Hien Nguyen <hien@us.ibm.com>, Jim Keniston
* <jkenisto@us.ibm.com> and Prasanna S Panchamukhi
* <prasanna@in.ibm.com> added function-return probes.
*/

#include <linux/config.h>
Expand Down Expand Up @@ -91,6 +94,53 @@ static inline void prepare_singlestep(struct kprobe *p, struct pt_regs *regs)
regs->eip = (unsigned long)&p->ainsn.insn;
}

struct task_struct *arch_get_kprobe_task(void *ptr)
{
return ((struct thread_info *) (((unsigned long) ptr) &
(~(THREAD_SIZE -1))))->task;
}

void arch_prepare_kretprobe(struct kretprobe *rp, struct pt_regs *regs)
{
unsigned long *sara = (unsigned long *)&regs->esp;
struct kretprobe_instance *ri;
static void *orig_ret_addr;

/*
* Save the return address when the return probe hits
* the first time, and use it to populate the (krprobe
* instance)->ret_addr for subsequent return probes at
* the same addrress since stack address would have
* the kretprobe_trampoline by then.
*/
if (((void*) *sara) != kretprobe_trampoline)
orig_ret_addr = (void*) *sara;

if ((ri = get_free_rp_inst(rp)) != NULL) {
ri->rp = rp;
ri->stack_addr = sara;
ri->ret_addr = orig_ret_addr;
add_rp_inst(ri);
/* Replace the return addr with trampoline addr */
*sara = (unsigned long) &kretprobe_trampoline;
} else {
rp->nmissed++;
}
}

void arch_kprobe_flush_task(struct task_struct *tk, spinlock_t *kp_lock)
{
unsigned long flags = 0;
struct kretprobe_instance *ri;
spin_lock_irqsave(kp_lock, flags);
while ((ri = get_rp_inst_tsk(tk)) != NULL) {
*((unsigned long *)(ri->stack_addr)) =
(unsigned long) ri->ret_addr;
recycle_rp_inst(ri);
}
spin_unlock_irqrestore(kp_lock, flags);
}

/*
* Interrupts are disabled on entry as trap3 is an interrupt gate and they
* remain disabled thorough out this function.
Expand Down Expand Up @@ -183,6 +233,55 @@ static int kprobe_handler(struct pt_regs *regs)
return ret;
}

/*
* For function-return probes, init_kprobes() establishes a probepoint
* here. When a retprobed function returns, this probe is hit and
* trampoline_probe_handler() runs, calling the kretprobe's handler.
*/
void kretprobe_trampoline_holder(void)
{
asm volatile ( ".global kretprobe_trampoline\n"
"kretprobe_trampoline: \n"
"nop\n");
}

/*
* Called when we hit the probe point at kretprobe_trampoline
*/
int trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs)
{
struct task_struct *tsk;
struct kretprobe_instance *ri;
struct hlist_head *head;
struct hlist_node *node;
unsigned long *sara = ((unsigned long *) &regs->esp) - 1;

tsk = arch_get_kprobe_task(sara);
head = kretprobe_inst_table_head(tsk);

hlist_for_each_entry(ri, node, head, hlist) {
if (ri->stack_addr == sara && ri->rp) {
if (ri->rp->handler)
ri->rp->handler(ri, regs);
}
}
return 0;
}

void trampoline_post_handler(struct kprobe *p, struct pt_regs *regs,
unsigned long flags)
{
struct kretprobe_instance *ri;
/* RA already popped */
unsigned long *sara = ((unsigned long *)&regs->esp) - 1;

while ((ri = get_rp_inst(sara))) {
regs->eip = (unsigned long)ri->ret_addr;
recycle_rp_inst(ri);
}
regs->eflags &= ~TF_MASK;
}

/*
* Called after single-stepping. p->addr is the address of the
* instruction whose first byte has been replaced by the "int 3"
Expand Down Expand Up @@ -266,7 +365,8 @@ static inline int post_kprobe_handler(struct pt_regs *regs)
if (current_kprobe->post_handler)
current_kprobe->post_handler(current_kprobe, regs, 0);

resume_execution(current_kprobe, regs);
if (current_kprobe->post_handler != trampoline_post_handler)
resume_execution(current_kprobe, regs);
regs->eflags |= kprobe_saved_eflags;

unlock_kprobes();
Expand Down
15 changes: 15 additions & 0 deletions trunk/arch/i386/kernel/process.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
#include <linux/kallsyms.h>
#include <linux/ptrace.h>
#include <linux/random.h>
#include <linux/kprobes.h>

#include <asm/uaccess.h>
#include <asm/pgtable.h>
Expand Down Expand Up @@ -339,6 +340,13 @@ void exit_thread(void)
struct task_struct *tsk = current;
struct thread_struct *t = &tsk->thread;

/*
* Remove function-return probe instances associated with this task
* and put them back on the free list. Do not insert an exit probe for
* this function, it will be disabled by kprobe_flush_task if you do.
*/
kprobe_flush_task(tsk);

/* The process may have allocated an io port bitmap... nuke it. */
if (unlikely(NULL != t->io_bitmap_ptr)) {
int cpu = get_cpu();
Expand All @@ -362,6 +370,13 @@ void flush_thread(void)
{
struct task_struct *tsk = current;

/*
* Remove function-return probe instances associated with this task
* and put them back on the free list. Do not insert an exit probe for
* this function, it will be disabled by kprobe_flush_task if you do.
*/
kprobe_flush_task(tsk);

memset(tsk->thread.debugreg, 0, sizeof(unsigned long)*8);
memset(tsk->thread.tls_array, 0, sizeof(tsk->thread.tls_array));
/*
Expand Down
3 changes: 3 additions & 0 deletions trunk/include/asm-i386/kprobes.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ typedef u8 kprobe_opcode_t;
: (((unsigned long)current_thread_info()) + THREAD_SIZE - (ADDR)))

#define JPROBE_ENTRY(pentry) (kprobe_opcode_t *)pentry
#define ARCH_SUPPORTS_KRETPROBES

void kretprobe_trampoline(void);

/* Architecture specific copy of original instruction*/
struct arch_specific_insn {
Expand Down
90 changes: 88 additions & 2 deletions trunk/include/linux/kprobes.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,21 +25,31 @@
* Rusty Russell).
* 2004-July Suparna Bhattacharya <suparna@in.ibm.com> added jumper probes
* interface to access function arguments.
* 2005-May Hien Nguyen <hien@us.ibm.com> and Jim Keniston
* <jkenisto@us.ibm.com> and Prasanna S Panchamukhi
* <prasanna@in.ibm.com> added function-return probes.
*/
#include <linux/config.h>
#include <linux/list.h>
#include <linux/notifier.h>
#include <linux/smp.h>
#include <linux/spinlock.h>

#include <asm/kprobes.h>

struct kprobe;
struct pt_regs;
struct kretprobe;
struct kretprobe_instance;
typedef int (*kprobe_pre_handler_t) (struct kprobe *, struct pt_regs *);
typedef int (*kprobe_break_handler_t) (struct kprobe *, struct pt_regs *);
typedef void (*kprobe_post_handler_t) (struct kprobe *, struct pt_regs *,
unsigned long flags);
typedef int (*kprobe_fault_handler_t) (struct kprobe *, struct pt_regs *,
int trapnr);
typedef int (*kretprobe_handler_t) (struct kretprobe_instance *,
struct pt_regs *);

struct kprobe {
struct hlist_node hlist;

Expand Down Expand Up @@ -85,6 +95,62 @@ struct jprobe {
kprobe_opcode_t *entry; /* probe handling code to jump to */
};

#ifdef ARCH_SUPPORTS_KRETPROBES
extern int trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs);
extern void trampoline_post_handler(struct kprobe *p, struct pt_regs *regs,
unsigned long flags);
extern struct task_struct *arch_get_kprobe_task(void *ptr);
extern void arch_prepare_kretprobe(struct kretprobe *rp, struct pt_regs *regs);
extern void arch_kprobe_flush_task(struct task_struct *tk, spinlock_t *kp_lock);
#else /* ARCH_SUPPORTS_KRETPROBES */
static inline void kretprobe_trampoline(void)
{
}
static inline int trampoline_probe_handler(struct kprobe *p,
struct pt_regs *regs)
{
return 0;
}
static inline void trampoline_post_handler(struct kprobe *p,
struct pt_regs *regs, unsigned long flags)
{
}
static inline void arch_prepare_kretprobe(struct kretprobe *rp,
struct pt_regs *regs)
{
}
static inline void arch_kprobe_flush_task(struct task_struct *tk)
{
}
#define arch_get_kprobe_task(ptr) ((struct task_struct *)NULL)
#endif /* ARCH_SUPPORTS_KRETPROBES */
/*
* Function-return probe -
* Note:
* User needs to provide a handler function, and initialize maxactive.
* maxactive - The maximum number of instances of the probed function that
* can be active concurrently.
* nmissed - tracks the number of times the probed function's return was
* ignored, due to maxactive being too low.
*
*/
struct kretprobe {
struct kprobe kp;
kretprobe_handler_t handler;
int maxactive;
int nmissed;
struct hlist_head free_instances;
struct hlist_head used_instances;
};

struct kretprobe_instance {
struct hlist_node uflist; /* either on free list or used list */
struct hlist_node hlist;
struct kretprobe *rp;
void *ret_addr;
void *stack_addr;
};

#ifdef CONFIG_KPROBES
/* Locks kprobe: irq must be disabled */
void lock_kprobes(void);
Expand All @@ -104,6 +170,7 @@ extern void show_registers(struct pt_regs *regs);

/* Get the kprobe at this addr (if any). Must have called lock_kprobes */
struct kprobe *get_kprobe(void *addr);
struct hlist_head * kretprobe_inst_table_head(struct task_struct *tsk);

int register_kprobe(struct kprobe *p);
void unregister_kprobe(struct kprobe *p);
Expand All @@ -113,7 +180,16 @@ int register_jprobe(struct jprobe *p);
void unregister_jprobe(struct jprobe *p);
void jprobe_return(void);

#else
int register_kretprobe(struct kretprobe *rp);
void unregister_kretprobe(struct kretprobe *rp);

struct kretprobe_instance *get_free_rp_inst(struct kretprobe *rp);
struct kretprobe_instance *get_rp_inst(void *sara);
struct kretprobe_instance *get_rp_inst_tsk(struct task_struct *tk);
void add_rp_inst(struct kretprobe_instance *ri);
void kprobe_flush_task(struct task_struct *tk);
void recycle_rp_inst(struct kretprobe_instance *ri);
#else /* CONFIG_KPROBES */
static inline int kprobe_running(void)
{
return 0;
Expand All @@ -135,5 +211,15 @@ static inline void unregister_jprobe(struct jprobe *p)
static inline void jprobe_return(void)
{
}
#endif
static inline int register_kretprobe(struct kretprobe *rp)
{
return -ENOSYS;
}
static inline void unregister_kretprobe(struct kretprobe *rp)
{
}
static inline void kprobe_flush_task(struct task_struct *tk)
{
}
#endif /* CONFIG_KPROBES */
#endif /* _LINUX_KPROBES_H */
Loading

0 comments on commit 079af4d

Please sign in to comment.