[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>

authored by Zhang Yanmin and committed by Tony Luck d3ef1f5a e026cca0

+90
+27
arch/ia64/kernel/jprobes.S
··· 60 GLOBAL_ENTRY(jprobe_inst_return) 61 br.call.sptk.many b0=jprobe_break 62 END(jprobe_inst_return)
··· 60 GLOBAL_ENTRY(jprobe_inst_return) 61 br.call.sptk.many b0=jprobe_break 62 END(jprobe_inst_return) 63 + 64 + GLOBAL_ENTRY(invalidate_stacked_regs) 65 + movl r16=invalidate_restore_cfm 66 + ;; 67 + mov b6=r16 68 + ;; 69 + br.ret.sptk.many b6 70 + ;; 71 + invalidate_restore_cfm: 72 + mov r16=ar.rsc 73 + ;; 74 + mov ar.rsc=r0 75 + ;; 76 + loadrs 77 + ;; 78 + mov ar.rsc=r16 79 + ;; 80 + br.cond.sptk.many rp 81 + END(invalidate_stacked_regs) 82 + 83 + GLOBAL_ENTRY(flush_register_stack) 84 + // flush dirty regs to backing store (must be first in insn group) 85 + flushrs 86 + ;; 87 + br.ret.sptk.many rp 88 + END(flush_register_stack) 89 +
+57
arch/ia64/kernel/kprobes.c
··· 766 return ret; 767 } 768 769 int __kprobes setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs) 770 { 771 struct jprobe *jp = container_of(p, struct jprobe, kp); 772 unsigned long addr = ((struct fnptr *)(jp->entry))->ip; 773 struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); 774 775 /* save architectural state */ 776 kcb->jprobe_saved_regs = *regs; ··· 837 int __kprobes longjmp_break_handler(struct kprobe *p, struct pt_regs *regs) 838 { 839 struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); 840 841 *regs = kcb->jprobe_saved_regs; 842 preempt_enable_no_resched(); 843 return 1; 844 }
··· 766 return ret; 767 } 768 769 + struct param_bsp_cfm { 770 + unsigned long ip; 771 + unsigned long *bsp; 772 + unsigned long cfm; 773 + }; 774 + 775 + static void ia64_get_bsp_cfm(struct unw_frame_info *info, void *arg) 776 + { 777 + unsigned long ip; 778 + struct param_bsp_cfm *lp = arg; 779 + 780 + do { 781 + unw_get_ip(info, &ip); 782 + if (ip == 0) 783 + break; 784 + if (ip == lp->ip) { 785 + unw_get_bsp(info, (unsigned long*)&lp->bsp); 786 + unw_get_cfm(info, (unsigned long*)&lp->cfm); 787 + return; 788 + } 789 + } while (unw_unwind(info) >= 0); 790 + lp->bsp = 0; 791 + lp->cfm = 0; 792 + return; 793 + } 794 + 795 int __kprobes setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs) 796 { 797 struct jprobe *jp = container_of(p, struct jprobe, kp); 798 unsigned long addr = ((struct fnptr *)(jp->entry))->ip; 799 struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); 800 + struct param_bsp_cfm pa; 801 + int bytes; 802 + 803 + /* 804 + * Callee owns the argument space and could overwrite it, eg 805 + * tail call optimization. So to be absolutely safe 806 + * we save the argument space before transfering the control 807 + * to instrumented jprobe function which runs in 808 + * the process context 809 + */ 810 + pa.ip = regs->cr_iip; 811 + unw_init_running(ia64_get_bsp_cfm, &pa); 812 + bytes = (char *)ia64_rse_skip_regs(pa.bsp, pa.cfm & 0x3f) 813 + - (char *)pa.bsp; 814 + memcpy( kcb->jprobes_saved_stacked_regs, 815 + pa.bsp, 816 + bytes ); 817 + kcb->bsp = pa.bsp; 818 + kcb->cfm = pa.cfm; 819 820 /* save architectural state */ 821 kcb->jprobe_saved_regs = *regs; ··· 792 int __kprobes longjmp_break_handler(struct kprobe *p, struct pt_regs *regs) 793 { 794 struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); 795 + int bytes; 796 797 + /* restoring architectural state */ 798 *regs = kcb->jprobe_saved_regs; 799 + 800 + /* restoring the original argument space */ 801 + flush_register_stack(); 802 + bytes = (char *)ia64_rse_skip_regs(kcb->bsp, kcb->cfm & 0x3f) 803 + - (char *)kcb->bsp; 804 + memcpy( kcb->bsp, 805 + kcb->jprobes_saved_stacked_regs, 806 + bytes ); 807 + invalidate_stacked_regs(); 808 + 809 preempt_enable_no_resched(); 810 return 1; 811 }
+6
include/asm-ia64/kprobes.h
··· 68 unsigned long status; 69 }; 70 71 /* per-cpu kprobe control block */ 72 struct kprobe_ctlblk { 73 unsigned long kprobe_status; 74 struct pt_regs jprobe_saved_regs; 75 struct prev_kprobe prev_kprobe; 76 }; 77 ··· 122 static inline void jprobe_return(void) 123 { 124 } 125 126 #endif /* _ASM_KPROBES_H */
··· 68 unsigned long status; 69 }; 70 71 + #define MAX_PARAM_RSE_SIZE (0x60+0x60/0x3f) 72 /* per-cpu kprobe control block */ 73 struct kprobe_ctlblk { 74 unsigned long kprobe_status; 75 struct pt_regs jprobe_saved_regs; 76 + unsigned long jprobes_saved_stacked_regs[MAX_PARAM_RSE_SIZE]; 77 + unsigned long *bsp; 78 + unsigned long cfm; 79 struct prev_kprobe prev_kprobe; 80 }; 81 ··· 118 static inline void jprobe_return(void) 119 { 120 } 121 + extern void invalidate_stacked_regs(void); 122 + extern void flush_register_stack(void); 123 124 #endif /* _ASM_KPROBES_H */