summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorZhang Yanmin <yanmin.zhang@intel.com>2006-01-13 14:45:21 -0800
committerTony Luck <tony.luck@intel.com>2006-01-13 14:45:21 -0800
commitd3ef1f5aafcf7a4129eb2078c70bc9e577bc3af1 (patch)
tree619647d90b0a51d39e80bbc20b59e7e7dccbf510
parente026cca0f2c09c4c28c902db6384fd8a412671d6 (diff)
downloadop-kernel-dev-d3ef1f5aafcf7a4129eb2078c70bc9e577bc3af1.zip
op-kernel-dev-d3ef1f5aafcf7a4129eb2078c70bc9e577bc3af1.tar.gz
[IA64] prevent accidental modification of args in jprobe handler
When jprobe is hit, the function parameters of the original function should be saved before jprobe handler is executed, and restored it after jprobe handler is executed, because jprobe handler might change the register values due to tail call optimization by the gcc. Signed-off-by: Zhang Yanmin <yanmin.zhang@intel.com> Signed-off-by: Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com> Signed-off-by: Tony Luck <tony.luck@intel.com>
-rw-r--r--arch/ia64/kernel/jprobes.S27
-rw-r--r--arch/ia64/kernel/kprobes.c57
-rw-r--r--include/asm-ia64/kprobes.h6
3 files changed, 90 insertions, 0 deletions
diff --git a/arch/ia64/kernel/jprobes.S b/arch/ia64/kernel/jprobes.S
index 2323377..5cd6226f 100644
--- a/arch/ia64/kernel/jprobes.S
+++ b/arch/ia64/kernel/jprobes.S
@@ -60,3 +60,30 @@ END(jprobe_break)
GLOBAL_ENTRY(jprobe_inst_return)
br.call.sptk.many b0=jprobe_break
END(jprobe_inst_return)
+
+GLOBAL_ENTRY(invalidate_stacked_regs)
+ movl r16=invalidate_restore_cfm
+ ;;
+ mov b6=r16
+ ;;
+ br.ret.sptk.many b6
+ ;;
+invalidate_restore_cfm:
+ mov r16=ar.rsc
+ ;;
+ mov ar.rsc=r0
+ ;;
+ loadrs
+ ;;
+ mov ar.rsc=r16
+ ;;
+ br.cond.sptk.many rp
+END(invalidate_stacked_regs)
+
+GLOBAL_ENTRY(flush_register_stack)
+ // flush dirty regs to backing store (must be first in insn group)
+ flushrs
+ ;;
+ br.ret.sptk.many rp
+END(flush_register_stack)
+
diff --git a/arch/ia64/kernel/kprobes.c b/arch/ia64/kernel/kprobes.c
index 346fedf..50ae8c7 100644
--- a/arch/ia64/kernel/kprobes.c
+++ b/arch/ia64/kernel/kprobes.c
@@ -766,11 +766,56 @@ int __kprobes kprobe_exceptions_notify(struct notifier_block *self,
return ret;
}
+struct param_bsp_cfm {
+ unsigned long ip;
+ unsigned long *bsp;
+ unsigned long cfm;
+};
+
+static void ia64_get_bsp_cfm(struct unw_frame_info *info, void *arg)
+{
+ unsigned long ip;
+ struct param_bsp_cfm *lp = arg;
+
+ do {
+ unw_get_ip(info, &ip);
+ if (ip == 0)
+ break;
+ if (ip == lp->ip) {
+ unw_get_bsp(info, (unsigned long*)&lp->bsp);
+ unw_get_cfm(info, (unsigned long*)&lp->cfm);
+ return;
+ }
+ } while (unw_unwind(info) >= 0);
+ lp->bsp = 0;
+ lp->cfm = 0;
+ return;
+}
+
int __kprobes setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs)
{
struct jprobe *jp = container_of(p, struct jprobe, kp);
unsigned long addr = ((struct fnptr *)(jp->entry))->ip;
struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
+ struct param_bsp_cfm pa;
+ int bytes;
+
+ /*
+ * Callee owns the argument space and could overwrite it, eg
+ * tail call optimization. So to be absolutely safe
+ * we save the argument space before transfering the control
+ * to instrumented jprobe function which runs in
+ * the process context
+ */
+ pa.ip = regs->cr_iip;
+ unw_init_running(ia64_get_bsp_cfm, &pa);
+ bytes = (char *)ia64_rse_skip_regs(pa.bsp, pa.cfm & 0x3f)
+ - (char *)pa.bsp;
+ memcpy( kcb->jprobes_saved_stacked_regs,
+ pa.bsp,
+ bytes );
+ kcb->bsp = pa.bsp;
+ kcb->cfm = pa.cfm;
/* save architectural state */
kcb->jprobe_saved_regs = *regs;
@@ -792,8 +837,20 @@ int __kprobes setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs)
int __kprobes longjmp_break_handler(struct kprobe *p, struct pt_regs *regs)
{
struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
+ int bytes;
+ /* restoring architectural state */
*regs = kcb->jprobe_saved_regs;
+
+ /* restoring the original argument space */
+ flush_register_stack();
+ bytes = (char *)ia64_rse_skip_regs(kcb->bsp, kcb->cfm & 0x3f)
+ - (char *)kcb->bsp;
+ memcpy( kcb->bsp,
+ kcb->jprobes_saved_stacked_regs,
+ bytes );
+ invalidate_stacked_regs();
+
preempt_enable_no_resched();
return 1;
}
diff --git a/include/asm-ia64/kprobes.h b/include/asm-ia64/kprobes.h
index a74b681..8c0fc22 100644
--- a/include/asm-ia64/kprobes.h
+++ b/include/asm-ia64/kprobes.h
@@ -68,10 +68,14 @@ struct prev_kprobe {
unsigned long status;
};
+#define MAX_PARAM_RSE_SIZE (0x60+0x60/0x3f)
/* per-cpu kprobe control block */
struct kprobe_ctlblk {
unsigned long kprobe_status;
struct pt_regs jprobe_saved_regs;
+ unsigned long jprobes_saved_stacked_regs[MAX_PARAM_RSE_SIZE];
+ unsigned long *bsp;
+ unsigned long cfm;
struct prev_kprobe prev_kprobe;
};
@@ -118,5 +122,7 @@ extern int kprobe_exceptions_notify(struct notifier_block *self,
static inline void jprobe_return(void)
{
}
+extern void invalidate_stacked_regs(void);
+extern void flush_register_stack(void);
#endif /* _ASM_KPROBES_H */
OpenPOWER on IntegriCloud