From: David Long <hidden> Date: 2016-06-03 03:26:31
From: "David A. Long" <redacted>
This patchset is heavily based on Sandeepa Prabhu's ARM v8 kprobes patches,
first seen in October 2013. This version attempts to address concerns raised by
reviewers and also fixes problems discovered during testing.
This patchset adds support for kernel probes(kprobes), jump probes(jprobes)
and return probes(kretprobes) support for ARM64.
The kprobes mechanism makes use of software breakpoint and single stepping
support available in the ARM v8 kernel.
Changes since v2 include:
1) Removal of NOP padding in kprobe XOL slots. Slots are now exactly one
instruction long.
2) Disabling of interrupts during execution in single-step mode.
3) Fixing of numerous problems in instruction simulation code (mostly
thanks to Will Cohen).
4) Support for the HAVE_REGS_AND_STACK_ACCESS_API feature is added, to allow
access to kprobes through debugfs.
5) kprobes is *not* enabled in defconfig.
6) Numerous complaints from checkpatch have been cleaned up, although a couple
remain as removing the function pointer typedefs results in ugly code.
Changes since v3 include:
1) Remove table-driven instruction parsing and replace with an if statement
calling out to old and new instruction test functions in insn.c.
2) I removed the addition of orig_x0 to ptrace.h.
3) Reorder the patches.
4) Replace the previous interrupt disabling (from Will Cohen) with
an improved solution (from Steve Capper).
Changes since v4 include:
1) Added insn.c functions to detect exception instructions and DAIF
read/write instructions, and use them to reject probing same.
2) Changed adr detect function to also recognize adrp. Reject both.
3) Added missing __kprobes for some new functions.
4) Added call to kprobes_fault_handler from mm do_page_fault.
5) Reject all non-simulated branch/ret instructions, not just those
that use an immediate offset.
6) Moved software breakpoint definitions into debug-monitors.h.
7) Removed "!XIP_KERNEL" from Kconfig.
8) changed kprobes_condition_check_t and kprobes_prepare_t to probes_*,
for future sharing with uprobes.
9) Removed bogus call to kprobes_restore_local_irqflag() from
trampoline_probe_handler().
Changes since v5 include:
1) Replaced installation of breakpoint hook with direct call from the
handlers in debug-monitors.c, as requested.
2) Reject probing of instructions that read the interrupt mask, in
addition to instructions that set it.
3) Cleaned up comments describing usage of Debug Mask.
4) Added KPROBE_REENTER case in reenter_kprobe.
5) Corrected the ifdef'd definitions for notify_page_fault() to be
consistent when KPROBES is not configed.
6) Changed "cpsr" to "pstate" for HAVE_REGS_AND_STACK_ACCESS_API feature.
7) Added back in missing new files in previous patch.
8) Changed two instances of pr_warning() to pr_warn().
Note that there seems to be at least a potential issue with kprobes
on multiple (possibly all) platforms having to do with use of kfree
inside of the kretprobes trampoline handler. This has manifested
occasionally in systemtap testing on arm64. There does not appear to
be an simple solution to the problem.
Changes since v6 include:
1) New trampoline code from Will Cohen fixes the occasional failure seen
when processing kretprobes by replacing the software breakpoint with
assembly code to implement the return to the original execution stream.
2) Changed ip0, ip1, fp, and lr to plain numbered registers for purposes
of recognizing them as an ascii string in the stack/reg access code.
3) Removed orig_x0.
4) Moved ARM_x* defines from arch/arm64/include/uapi/asm/ptrace.h to
arch/arm64/kernel/ptrace.c.
Changes since v7 include:
1) Move trampoline entry/return code into separate ".S" file instead
of making it a macro in a header file.
2) Add missing register name definitions in asm-offsets.c and use them
in place of hard-coded integer offsets in the trampoline code.
3) Correct the values used to decode MSR immediate instructions, in insn.h.
4) Remove the currently unused simulate_none() function.
Changes since v8 include:
1) Replaced use of REG_OFFSET_NAME with GPR_OFFSET_NAME for numbered
registers.
2) Added an alias for "lr" in the register name lookup table, which perf
tools need to be able to recognize.
3) Changed the code for checking instruction types for probeability and
steppability as per review feedback.
4) Fixed the size of cache being flushed when filling single-step slot.
5) Fixed big-endian issues.
6) Blacklisted copy_to/from_user to avoid aborts while single-stepping.
7) Record conditional instructions that fail the conditional test just
like any other probed (non-conditional) instruction.
8) Removed use of magic number for detecting jprobe return and just
check the breakpoint address instead.
9) Got rid of the unnecessary arch/arm64/kprobes.h.
10) The PSTATE and SP are now properly saved in the kretprobe trampoline
code.
11) This patch no longer depends on the "Consolidate redundant
register/stack access code" patch set.
12) Remove call to fixup_exception from kprobe_fault_handler.
Changes since v9 include:
1) Remove arch/arm/opcodes.c from the arm64 build and move the renamed
arm64_check_condition() function to armv8_deprecated.c. Remove the
asmlinkage.
2) Various other type and style changes suggested by Marc Zyngier.
3) Put back the call to fixup_exception from kprobe_fault_handler.
It proved to be necessary for correct operation.
Changes since v10 include:
1) Rename arm64_check_condition() to arm32_check_condition().
2) Remove redundant define of ARM_OPCODE_CONDITION_UNCOND.
3) Use a accessor functions to read and write registers by number
in the simulation code, to avoid accidentally overriding parts of
the pt_regs structure (e.g.: when the reg is xzr).
4) Remove unused register offset defines.
5) Replace instance of "(void *) 0" with NULL.
6) Rewrite the kretprobe trampoline code using arch/arm64/kvm/hyp/entry.S
as an example. Construct a more complete saved PSTATE in this code.
Changes since v11 include:
1) Add check for address within irq stack, in regs_within_kernel_stack()
2) Replaced inappropriate use of user_pt_regs with pt_regs.
3) Added comments to opcode_condition_checks table explaining equivalence of
"nv" and "al" condition codes.
4) Cleaned up some subtle problems in the instruction simulation code.
5) Readability improvements in kprobes_trampoline.S.
6) Additional blacklisting for entry code, exception handling code, and
select debug functions.
7) Check address to be probed for proper alignment.
8) Add rodata section to areas where kprobes may not be placed.
Changes since v12 include:
1) Changed regs_get_register() to expicitly reference pt_regs structure fields
instead of just using an address offset.
2) Reject probing of eret.
3) Correctly handle addresses on the interrupt stack
4) Add kprobe_ctlblk argument to static irqflag handling functions to avoid
doing extra calls to get_kprobe_ctlblk().
5) Removed a couple of logically redundant assignments to kprobe_status.
6) Added calls to pause_graph_tracing/unpause_graph_tracing to avoid
disaster when kprobe'ing and tracing at the same time.
7) Added idmap and hypervisor text sections to blacklisted regions
8) Numerous additional comments, formatting changes, and rearranging
of if-else statements.
David A. Long (3):
arm64: Add HAVE_REGS_AND_STACK_ACCESS_API feature
arm64: Add more test functions to insn.c
arm64: add conditional instruction simulation support
Pratyush Anand (2):
arm64: Blacklist non-kprobe-able symbol
arm64: Treat all entry code as non-kprobe-able
Sandeepa Prabhu (4):
arm64: Kprobes with single stepping support
arm64: kprobes instruction simulation support
arm64: Add kernel return probes support (kretprobes)
kprobes: Add arm64 case in kprobe example module
William Cohen (1):
arm64: Add trampoline code for kretprobes
arch/arm64/Kconfig | 3 +
arch/arm64/include/asm/debug-monitors.h | 5 +
arch/arm64/include/asm/insn.h | 42 ++
arch/arm64/include/asm/kprobes.h | 62 +++
arch/arm64/include/asm/probes.h | 45 +++
arch/arm64/include/asm/ptrace.h | 54 ++-
arch/arm64/kernel/Makefile | 6 +-
arch/arm64/kernel/arm64ksyms.c | 2 +
arch/arm64/kernel/armv8_deprecated.c | 19 +-
arch/arm64/kernel/asm-offsets.c | 11 +
arch/arm64/kernel/debug-monitors.c | 36 +-
arch/arm64/kernel/entry.S | 3 +
arch/arm64/kernel/hw_breakpoint.c | 8 +
arch/arm64/kernel/insn.c | 133 +++++++
arch/arm64/kernel/kgdb.c | 4 +
arch/arm64/kernel/kprobes-arm64.c | 174 ++++++++
arch/arm64/kernel/kprobes-arm64.h | 35 ++
arch/arm64/kernel/kprobes.c | 659 +++++++++++++++++++++++++++++++
arch/arm64/kernel/kprobes_trampoline.S | 85 ++++
arch/arm64/kernel/probes-simulate-insn.c | 218 ++++++++++
arch/arm64/kernel/probes-simulate-insn.h | 28 ++
arch/arm64/kernel/ptrace.c | 118 ++++++
arch/arm64/kernel/vmlinux.lds.S | 2 +
arch/arm64/mm/fault.c | 26 ++
samples/kprobes/kprobe_example.c | 8 +
25 files changed, 1778 insertions(+), 8 deletions(-)
create mode 100644 arch/arm64/include/asm/kprobes.h
create mode 100644 arch/arm64/include/asm/probes.h
create mode 100644 arch/arm64/kernel/kprobes-arm64.c
create mode 100644 arch/arm64/kernel/kprobes-arm64.h
create mode 100644 arch/arm64/kernel/kprobes.c
create mode 100644 arch/arm64/kernel/kprobes_trampoline.S
create mode 100644 arch/arm64/kernel/probes-simulate-insn.c
create mode 100644 arch/arm64/kernel/probes-simulate-insn.h
--
2.5.0
From: David Long <hidden> Date: 2016-06-03 03:26:39
From: "David A. Long" <redacted>
Certain instructions are hard to execute correctly out-of-line (as in
kprobes). Test functions are added to insn.[hc] to identify these. The
instructions include any that use PC-relative addressing, change the PC,
or change interrupt masking. For efficiency and simplicity test
functions are also added for small collections of related instructions.
Signed-off-by: David A. Long <redacted>
---
arch/arm64/include/asm/insn.h | 36 ++++++++++++++++++++++++++++++++++++
arch/arm64/kernel/insn.c | 34 ++++++++++++++++++++++++++++++++++
2 files changed, 70 insertions(+)
From: David Long <hidden> Date: 2016-06-03 03:26:41
From: Pratyush Anand <redacted>
Add all function symbols which are called from do_debug_exception under
NOKPROBE_SYMBOL, as they can not kprobed.
Signed-off-by: Pratyush Anand <redacted>
---
arch/arm64/kernel/arm64ksyms.c | 2 ++
arch/arm64/kernel/debug-monitors.c | 18 ++++++++++++++++++
arch/arm64/kernel/hw_breakpoint.c | 8 ++++++++
arch/arm64/kernel/kgdb.c | 4 ++++
arch/arm64/mm/fault.c | 1 +
5 files changed, 33 insertions(+)
@@ -225,6 +232,7 @@ static int call_step_hook(struct pt_regs *regs, unsigned int esr)returnretval;}+NOKPROBE_SYMBOL(call_step_hook);staticvoidsend_user_sigtrap(intsi_code){
@@ -279,6 +287,7 @@ static int single_step_handler(unsigned long addr, unsigned int esr,return0;}+NOKPROBE_SYMBOL(single_step_handler);/**Breakpointhandlerisre-entrantasanotherbreakpointcan
@@ -316,6 +325,7 @@ static int call_break_hook(struct pt_regs *regs, unsigned int esr)returnfn?fn(regs,esr):DBG_HOOK_ERROR;}+NOKPROBE_SYMBOL(call_break_hook);staticintbrk_handler(unsignedlongaddr,unsignedintesr,structpt_regs*regs)
@@ -329,6 +339,7 @@ static int brk_handler(unsigned long addr, unsigned int esr,return0;}+NOKPROBE_SYMBOL(brk_handler);intaarch32_break_handler(structpt_regs*regs){
@@ -365,6 +376,7 @@ int aarch32_break_handler(struct pt_regs *regs)send_user_sigtrap(TRAP_BRKPT);return0;}+NOKPROBE_SYMBOL(aarch32_break_handler);staticint__initdebug_traps_init(void){
@@ -46,6 +46,10 @@ static int handler_pre(struct kprobe *p, struct pt_regs *regs)" ex1 = 0x%lx\n",p->symbol_name,p->addr,regs->pc,regs->ex1);#endif+#ifdef CONFIG_ARM64+pr_info("pre_handler: p->addr = 0x%p, pc = 0x%lx\n",+p->addr,(long)regs->pc);+#endif/* A dump_stack() here will give a stack backtrace */return0;
From: David Long <hidden> Date: 2016-06-03 03:27:34
From: Sandeepa Prabhu <redacted>
Add support for basic kernel probes(kprobes) and jump probes
(jprobes) for ARM64.
Kprobes utilizes software breakpoint and single step debug
exceptions supported on ARM v8.
A software breakpoint is placed at the probe address to trap the
kernel execution into the kprobe handler.
ARM v8 supports enabling single stepping before the break exception
return (ERET), with next PC in exception return address (ELR_EL1). The
kprobe handler prepares an executable memory slot for out-of-line
execution with a copy of the original instruction being probed, and
enables single stepping. The PC is set to the out-of-line slot address
before the ERET. With this scheme, the instruction is executed with the
exact same register context except for the PC (and DAIF) registers.
Debug mask (PSTATE.D) is enabled only when single stepping a recursive
kprobe, e.g.: during kprobes reenter so that probed instruction can be
single stepped within the kprobe handler -exception- context.
The recursion depth of kprobe is always 2, i.e. upon probe re-entry,
any further re-entry is prevented by not calling handlers and the case
counted as a missed kprobe).
Single stepping from the x-o-l slot has a drawback for PC-relative accesses
like branching and symbolic literals access as the offset from the new PC
(slot address) may not be ensured to fit in the immediate value of
the opcode. Such instructions need simulation, so reject
probing them.
Instructions generating exceptions or cpu mode change are rejected
for probing.
Exclusive load/store instructions are rejected too. Additionally, the
code is checked to see if it is inside an exclusive load/store sequence
(code from Pratyush).
System instructions are mostly enabled for stepping, except MSR/MRS
accesses to "DAIF" flags in PSTATE, which are not safe for
probing.
Thanks to Steve Capper and Pratyush Anand for several suggested
Changes.
Signed-off-by: Sandeepa Prabhu <redacted>
Signed-off-by: David A. Long <redacted>
Signed-off-by: Pratyush Anand <redacted>
---
arch/arm64/Kconfig | 1 +
arch/arm64/include/asm/debug-monitors.h | 5 +
arch/arm64/include/asm/insn.h | 4 +-
arch/arm64/include/asm/kprobes.h | 60 ++++
arch/arm64/include/asm/probes.h | 44 +++
arch/arm64/kernel/Makefile | 1 +
arch/arm64/kernel/debug-monitors.c | 18 +-
arch/arm64/kernel/kprobes-arm64.c | 144 +++++++++
arch/arm64/kernel/kprobes-arm64.h | 35 +++
arch/arm64/kernel/kprobes.c | 526 ++++++++++++++++++++++++++++++++
arch/arm64/kernel/vmlinux.lds.S | 1 +
arch/arm64/mm/fault.c | 27 +-
12 files changed, 861 insertions(+), 5 deletions(-)
create mode 100644 arch/arm64/include/asm/kprobes.h
create mode 100644 arch/arm64/include/asm/probes.h
create mode 100644 arch/arm64/kernel/kprobes-arm64.c
create mode 100644 arch/arm64/kernel/kprobes-arm64.h
create mode 100644 arch/arm64/kernel/kprobes.c
@@ -0,0 +1,44 @@+/*+*arch/arm64/include/asm/probes.h+*+*Copyright(C)2013LinaroLimited+*+*Thisprogramisfreesoftware;youcanredistributeitand/ormodify+*itunderthetermsoftheGNUGeneralPublicLicenseversion2as+*publishedbytheFreeSoftwareFoundation.+*+*Thisprogramisdistributedinthehopethatitwillbeuseful,+*butWITHOUTANYWARRANTY;withouteventheimpliedwarrantyof+*MERCHANTABILITYorFITNESSFORAPARTICULARPURPOSE.SeetheGNU+*GeneralPublicLicenseformoredetails.+*/+#ifndef _ARM_PROBES_H+#define _ARM_PROBES_H++structkprobe;+structarch_specific_insn;++typedefu32kprobe_opcode_t;+typedefunsignedlong(kprobes_pstate_check_t)(unsignedlong);+typedefvoid(kprobes_handler_t)(u32opcode,longaddr,structpt_regs*);++enumpc_restore_type{+NO_RESTORE,+RESTORE_PC,+};++structkprobe_pc_restore{+enumpc_restore_typetype;+unsignedlongaddr;+};++/* architecture specific copy of original instruction */+structarch_specific_insn{+kprobe_opcode_t*insn;+kprobes_pstate_check_t*pstate_cc;+kprobes_handler_t*handler;+/* restore address after step xol */+structkprobe_pc_restorerestore;+};++#endif
@@ -274,10 +275,14 @@ static int single_step_handler(unsigned long addr, unsigned int esr,*/user_rewind_single_step(current);}else{+#ifdef CONFIG_KPROBES+if(kprobe_single_step_handler(regs,esr)==DBG_HOOK_HANDLED)+return0;+#endifif(call_step_hook(regs,esr)==DBG_HOOK_HANDLED)return0;-pr_warning("Unexpected kernel single-step exception at EL1\n");+pr_warn("Unexpected kernel single-step exception at EL1\n");/**Re-enablesteppingsinceweknowthatwewillbe*returningtoregs.
@@ -332,8 +337,15 @@ static int brk_handler(unsigned long addr, unsigned int esr,{if(user_mode(regs)){send_user_sigtrap(TRAP_BRKPT);-}elseif(call_break_hook(regs,esr)!=DBG_HOOK_HANDLED){-pr_warning("Unexpected kernel BRK exception at EL1\n");+}+#ifdef CONFIG_KPROBES+elseif((esr&BRK64_ESR_MASK)==BRK64_ESR_KPROBES){+if(kprobe_breakpoint_handler(regs,esr)!=DBG_HOOK_HANDLED)+return-EFAULT;+}+#endif+elseif(call_break_hook(regs,esr)!=DBG_HOOK_HANDLED){+pr_warn("Unexpected kernel BRK exception at EL1\n");return-EFAULT;}
@@ -0,0 +1,526 @@+/*+*arch/arm64/kernel/kprobes.c+*+*KprobessupportforARM64+*+*Copyright(C)2013LinaroLimited.+*Author:SandeepaPrabhu<sandeepa.prabhu@linaro.org>+*+*Thisprogramisfreesoftware;youcanredistributeitand/ormodify+*itunderthetermsoftheGNUGeneralPublicLicenseversion2as+*publishedbytheFreeSoftwareFoundation.+*+*Thisprogramisdistributedinthehopethatitwillbeuseful,+*butWITHOUTANYWARRANTY;withouteventheimpliedwarrantyof+*MERCHANTABILITYorFITNESSFORAPARTICULARPURPOSE.SeetheGNU+*GeneralPublicLicenseformoredetails.+*+*/+#include<linux/kernel.h>+#include<linux/kprobes.h>+#include<linux/module.h>+#include<linux/slab.h>+#include<linux/stop_machine.h>+#include<linux/stringify.h>+#include<asm/traps.h>+#include<asm/ptrace.h>+#include<asm/cacheflush.h>+#include<asm/debug-monitors.h>+#include<asm/system_misc.h>+#include<asm/insn.h>+#include<asm/uaccess.h>+#include<asm/irq.h>++#include"kprobes-arm64.h"++#define MIN_STACK_SIZE(addr) (on_irq_stack(addr, raw_smp_processor_id()) ? \+min((unsignedlong)IRQ_STACK_SIZE,\+IRQ_STACK_PTR(raw_smp_processor_id())-(addr)):\+min((unsignedlong)MAX_STACK_SIZE,\+(unsignedlong)current_thread_info()+THREAD_START_SP-(addr)))++voidjprobe_return_break(void);++DEFINE_PER_CPU(structkprobe*,current_kprobe)=NULL;+DEFINE_PER_CPU(structkprobe_ctlblk,kprobe_ctlblk);++staticvoid__kprobesarch_prepare_ss_slot(structkprobe*p)+{+/* prepare insn slot */+p->ainsn.insn[0]=cpu_to_le32(p->opcode);++flush_icache_range((uintptr_t)(p->ainsn.insn),+(uintptr_t)(p->ainsn.insn)++MAX_INSN_SIZE*sizeof(kprobe_opcode_t));++/*+*Needsrestoringofreturnaddressaftersteppingxol.+*/+p->ainsn.restore.addr=(unsignedlong)p->addr++sizeof(kprobe_opcode_t);+p->ainsn.restore.type=RESTORE_PC;+}++int__kprobesarch_prepare_kprobe(structkprobe*p)+{+unsignedlongprobe_addr=(unsignedlong)p->addr;+externchar__start_rodata[];+externchar__end_rodata[];++if(probe_addr&0x3)+return-EINVAL;++/* copy instruction */+p->opcode=le32_to_cpu(*p->addr);++if(in_exception_text(probe_addr))+return-EINVAL;+if(probe_addr>=(unsignedlong)__start_rodata&&+probe_addr<=(unsignedlong)__end_rodata)+return-EINVAL;++/* decode instruction */+switch(arm_kprobe_decode_insn(p->addr,&p->ainsn)){+caseINSN_REJECTED:/* insn not supported */+return-EINVAL;++caseINSN_GOOD_NO_SLOT:/* insn need simulation */+return-EINVAL;++caseINSN_GOOD:/* instruction uses slot */+p->ainsn.insn=get_insn_slot();+if(!p->ainsn.insn)+return-ENOMEM;+break;+};++/* prepare the instruction */+arch_prepare_ss_slot(p);++return0;+}++staticint__kprobespatch_text(kprobe_opcode_t*addr,u32opcode)+{+void*addrs[1];+u32insns[1];++addrs[0]=(void*)addr;+insns[0]=(u32)opcode;++returnaarch64_insn_patch_text(addrs,insns,1);+}++/* arm kprobe: install breakpoint in text */+void__kprobesarch_arm_kprobe(structkprobe*p)+{+patch_text(p->addr,BRK64_OPCODE_KPROBES);+}++/* disarm kprobe: remove breakpoint from text */+void__kprobesarch_disarm_kprobe(structkprobe*p)+{+patch_text(p->addr,p->opcode);+}++void__kprobesarch_remove_kprobe(structkprobe*p)+{+if(p->ainsn.insn){+free_insn_slot(p->ainsn.insn,0);+p->ainsn.insn=NULL;+}+}++staticvoid__kprobessave_previous_kprobe(structkprobe_ctlblk*kcb)+{+kcb->prev_kprobe.kp=kprobe_running();+kcb->prev_kprobe.status=kcb->kprobe_status;+}++staticvoid__kprobesrestore_previous_kprobe(structkprobe_ctlblk*kcb)+{+__this_cpu_write(current_kprobe,kcb->prev_kprobe.kp);+kcb->kprobe_status=kcb->prev_kprobe.status;+}++staticvoid__kprobesset_current_kprobe(structkprobe*p)+{+__this_cpu_write(current_kprobe,p);+}++/*+*TheD-flag(Debugmask)isset(masked)upondeugexceptionentry.+*Kprobesneedstoclear(unmask)D-flag-ONLY-incaseofrecursive+*probei.e.whenprobehitfromkprobehandlercontextupon+*executingthepre/posthandlers.Inthiscasewereturnwith+*D-flagclearsothatsingle-steppingcanbecarried-out.+*+*LeaveD-flagsetinallothercases.+*/+staticvoid__kprobes+spsr_set_debug_flag(structpt_regs*regs,intmask)+{+unsignedlongspsr=regs->pstate;++if(mask)+spsr|=PSR_D_BIT;+else+spsr&=~PSR_D_BIT;++regs->pstate=spsr;+}++/*+*Interruptsneedtobedisabledbeforesingle-stepmodeisset,andnot+*reenableduntilaftersingle-stepmodeends.+*WithoutdisablinginterruptonlocalCPU,thereisachanceof+*interruptoccurrenceintheperiodofexceptionreturnandstartof+*out-of-linesingle-step,thatresultinwronglysinglestepping+*intotheinterrupthandler.+*/+staticvoid__kprobeskprobes_save_local_irqflag(structkprobe_ctlblk*kcb,+structpt_regs*regs)+{+kcb->saved_irqflag=regs->pstate;+regs->pstate|=PSR_I_BIT;+}++staticvoid__kprobeskprobes_restore_local_irqflag(structkprobe_ctlblk*kcb,+structpt_regs*regs)+{+if(kcb->saved_irqflag&PSR_I_BIT)+regs->pstate|=PSR_I_BIT;+else+regs->pstate&=~PSR_I_BIT;+}++staticvoid__kprobes+set_ss_context(structkprobe_ctlblk*kcb,unsignedlongaddr)+{+kcb->ss_ctx.ss_pending=true;+kcb->ss_ctx.match_addr=addr+sizeof(kprobe_opcode_t);+}++staticvoid__kprobesclear_ss_context(structkprobe_ctlblk*kcb)+{+kcb->ss_ctx.ss_pending=false;+kcb->ss_ctx.match_addr=0;+}++staticvoid__kprobessetup_singlestep(structkprobe*p,+structpt_regs*regs,+structkprobe_ctlblk*kcb,intreenter)+{+unsignedlongslot;++if(reenter){+save_previous_kprobe(kcb);+set_current_kprobe(p);+kcb->kprobe_status=KPROBE_REENTER;+}else{+kcb->kprobe_status=KPROBE_HIT_SS;+}++BUG_ON(!p->ainsn.insn);++/* prepare for single stepping */+slot=(unsignedlong)p->ainsn.insn;++set_ss_context(kcb,slot);/* mark pending ss */++if(kcb->kprobe_status==KPROBE_REENTER)+spsr_set_debug_flag(regs,0);++/* IRQs and single stepping do not mix well. */+kprobes_save_local_irqflag(kcb,regs);+kernel_enable_single_step(regs);+instruction_pointer(regs)=slot;+}++staticint__kprobesreenter_kprobe(structkprobe*p,+structpt_regs*regs,+structkprobe_ctlblk*kcb)+{+switch(kcb->kprobe_status){+caseKPROBE_HIT_SSDONE:+caseKPROBE_HIT_ACTIVE:+kprobes_inc_nmissed_count(p);+setup_singlestep(p,regs,kcb,1);+break;+caseKPROBE_HIT_SS:+caseKPROBE_REENTER:+pr_warn("Unrecoverable kprobe detected at %p.\n",p->addr);+dump_kprobe(p);+BUG();+break;+default:+WARN_ON(1);+return0;+}++return1;+}++staticvoid__kprobes+post_kprobe_handler(structkprobe_ctlblk*kcb,structpt_regs*regs)+{+structkprobe*cur=kprobe_running();++if(!cur)+return;++/* return addr restore if non-branching insn */+if(cur->ainsn.restore.type==RESTORE_PC){+instruction_pointer(regs)=cur->ainsn.restore.addr;+BUG_ON(!instruction_pointer(regs));+}++/* restore back original saved kprobe variables and continue */+if(kcb->kprobe_status==KPROBE_REENTER){+restore_previous_kprobe(kcb);+return;+}+/* call post handler */+kcb->kprobe_status=KPROBE_HIT_SSDONE;+if(cur->post_handler){+/* post_handler can hit breakpoint and single step+*again,soweenableD-flagforrecursiveexception.+*/+cur->post_handler(cur,regs,0);+}++reset_current_kprobe();+}++int__kprobeskprobe_fault_handler(structpt_regs*regs,unsignedintfsr)+{+structkprobe*cur=kprobe_running();+structkprobe_ctlblk*kcb=get_kprobe_ctlblk();++switch(kcb->kprobe_status){+caseKPROBE_HIT_SS:+caseKPROBE_REENTER:+/*+*Weareherebecausetheinstructionbeingsingle+*steppedcausedapagefault.Weresetthecurrent+*kprobeandtheippointsbacktotheprobeaddress+*andallowthepagefaulthandlertocontinueasa+*normalpagefault.+*/+instruction_pointer(regs)=(unsignedlong)cur->addr;+if(!instruction_pointer(regs))+BUG();+if(kcb->kprobe_status==KPROBE_REENTER)+restore_previous_kprobe(kcb);+else+reset_current_kprobe();++break;+caseKPROBE_HIT_ACTIVE:+caseKPROBE_HIT_SSDONE:+/*+*Weincrementthenmissedcountforaccounting,+*wecanalsousenpre/npostfaultcountforaccounting+*thesespecificfaultcases.+*/+kprobes_inc_nmissed_count(cur);++/*+*Wecomeherebecauseinstructionsinthepre/post+*handlercausedthepage_fault,thiscouldhappen+*ifhandlertriestoaccessuserspaceby+*copy_from_user(),get_user()etc.Letthe+*user-specifiedhandlertrytofixitfirst.+*/+if(cur->fault_handler&&cur->fault_handler(cur,regs,fsr))+return1;++/*+*Incasetheuser-specifiedfaulthandlerreturned+*zero,trytofixup.+*/+if(fixup_exception(regs))+return1;+}+return0;+}++int__kprobeskprobe_exceptions_notify(structnotifier_block*self,+unsignedlongval,void*data)+{+returnNOTIFY_DONE;+}++staticvoid__kprobeskprobe_handler(structpt_regs*regs)+{+structkprobe*p,*cur_kprobe;+structkprobe_ctlblk*kcb;+unsignedlongaddr=instruction_pointer(regs);++kcb=get_kprobe_ctlblk();+cur_kprobe=kprobe_running();++p=get_kprobe((kprobe_opcode_t*)addr);++if(p){+if(cur_kprobe){+if(reenter_kprobe(p,regs,kcb))+return;+}else{+/* Probe hit */+set_current_kprobe(p);+kcb->kprobe_status=KPROBE_HIT_ACTIVE;++/*+*Ifwehavenopre-handleroritreturned0,we+*continuewithnormalprocessing.Ifwehavea+*pre-handleranditreturnednon-zero,itprepped+*forcallingthebreak_handlerbelowonre-entry,+*sogetoutdoingnothingmorehere.+*+*pre_handlercanhitabreakpointandcanstepthru+*beforereturn,keepPSTATED-flagenableduntil+*pre_handlerreturnback.+*/+if(!p->pre_handler||!p->pre_handler(p,regs)){+setup_singlestep(p,regs,kcb,0);+return;+}+}+}elseif((le32_to_cpu(*(kprobe_opcode_t*)addr)==+BRK64_OPCODE_KPROBES)&&cur_kprobe){+/* We probably hit a jprobe. Call its break handler. */+if(cur_kprobe->break_handler&&+cur_kprobe->break_handler(cur_kprobe,regs)){+setup_singlestep(cur_kprobe,regs,kcb,0);+return;+}+}+/*+*Thebreakpointinstructionwasremovedright+*afterwehitit.Anothercpuhasremoved+*eitheraprobepointoradebuggerbreakpoint+*atthisaddress.Ineithercase,nofurther+*handlingofthisinterruptisappropriate.+*Returnbacktooriginalinstruction,andcontinue.+*/+}++staticint__kprobes+kprobe_ss_hit(structkprobe_ctlblk*kcb,unsignedlongaddr)+{+if((kcb->ss_ctx.ss_pending)+&&(kcb->ss_ctx.match_addr==addr)){+clear_ss_context(kcb);/* clear pending ss */+returnDBG_HOOK_HANDLED;+}+/* not ours, kprobes should ignore it */+returnDBG_HOOK_ERROR;+}++int__kprobes+kprobe_single_step_handler(structpt_regs*regs,unsignedintesr)+{+structkprobe_ctlblk*kcb=get_kprobe_ctlblk();+intretval;++/* return error if this is not our step */+retval=kprobe_ss_hit(kcb,instruction_pointer(regs));++if(retval==DBG_HOOK_HANDLED){+kprobes_restore_local_irqflag(kcb,regs);+kernel_disable_single_step();++if(kcb->kprobe_status==KPROBE_REENTER)+spsr_set_debug_flag(regs,1);++post_kprobe_handler(kcb,regs);+}++returnretval;+}++int__kprobes+kprobe_breakpoint_handler(structpt_regs*regs,unsignedintesr)+{+kprobe_handler(regs);+returnDBG_HOOK_HANDLED;+}++int__kprobessetjmp_pre_handler(structkprobe*p,structpt_regs*regs)+{+structjprobe*jp=container_of(p,structjprobe,kp);+structkprobe_ctlblk*kcb=get_kprobe_ctlblk();+longstack_ptr=kernel_stack_pointer(regs);++kcb->jprobe_saved_regs=*regs;+/*+*AsLinuspointedout,gccassumesthatthecallee+*ownstheargumentspaceandcouldoverwriteit,e.g.+*tailcalloptimization.So,tobeabsolutelysafe+*wealsosaveandrestoreenoughstackbytestocover+*theargumentarea.+*/+memcpy(kcb->jprobes_stack,(void*)stack_ptr,+MIN_STACK_SIZE(stack_ptr));++instruction_pointer(regs)=(long)jp->entry;+preempt_disable();+pause_graph_tracing();+return1;+}++void__kprobesjprobe_return(void)+{+structkprobe_ctlblk*kcb=get_kprobe_ctlblk();++/*+*Jprobehandlerreturnbyenteringbreakexception,+*encodedsameaskprobe,butwithfollowingconditions+*-amagicnumberinx0toidentifyfromrestofotherkprobes.+*-restorestackaddrtooriginalsavedpt_regs+*/+asmvolatile("ldr x0, [%0]\n\t"+"mov sp, x0\n\t"+".globl jprobe_return_break\n\t"+"jprobe_return_break:\n\t"+"brk %1\n\t"+:+:"r"(&kcb->jprobe_saved_regs.sp),+"I"(BRK64_ESR_KPROBES)+:"memory");+}++int__kprobeslongjmp_break_handler(structkprobe*p,structpt_regs*regs)+{+structkprobe_ctlblk*kcb=get_kprobe_ctlblk();+longstack_addr=kcb->jprobe_saved_regs.sp;+longorig_sp=kernel_stack_pointer(regs);+structjprobe*jp=container_of(p,structjprobe,kp);++if(instruction_pointer(regs)!=(u64)jprobe_return_break)+return0;++if(orig_sp!=stack_addr){+structpt_regs*saved_regs=+(structpt_regs*)kcb->jprobe_saved_regs.sp;+pr_err("current sp %lx does not match saved sp %lx\n",+orig_sp,stack_addr);+pr_err("Saved registers for jprobe %p\n",jp);+show_regs(saved_regs);+pr_err("Current registers\n");+show_regs(regs);+BUG();+}+unpause_graph_tracing();+*regs=kcb->jprobe_saved_regs;+memcpy((void*)stack_addr,kcb->jprobes_stack,+MIN_STACK_SIZE(stack_addr));+preempt_enable_no_resched();+return1;+}++int__initarch_init_kprobes(void)+{+return0;+}
@@ -259,6 +281,9 @@ static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,unsignedlongvm_flags=VM_READ|VM_WRITE|VM_EXEC;unsignedintmm_flags=FAULT_FLAG_ALLOW_RETRY|FAULT_FLAG_KILLABLE;+if(notify_page_fault(regs,esr))+return0;+tsk=current;mm=tsk->mm;
From: David Long <hidden> Date: 2016-06-03 03:28:18
From: "David A. Long" <redacted>
Cease using the arm32 arm_check_condition() function and replace it with
a local version for use in deprecated instruction support on arm64. Also
make the function table used by this available for future use by kprobes
and/or uprobes.
This function is dervied from code written by Sandeepa Prabhu.
Signed-off-by: Sandeepa Prabhu <redacted>
Signed-off-by: David A. Long <redacted>
---
arch/arm64/include/asm/insn.h | 3 ++
arch/arm64/kernel/Makefile | 3 +-
arch/arm64/kernel/armv8_deprecated.c | 19 ++++++-
arch/arm64/kernel/insn.c | 98 ++++++++++++++++++++++++++++++++++++
4 files changed, 119 insertions(+), 4 deletions(-)
@@ -366,6 +366,21 @@ static int emulate_swpX(unsigned int address, unsigned int *data,returnres;}+#define ARM_OPCODE_CONDITION_UNCOND 0xf++staticunsignedint__kprobesarm32_check_condition(u32opcode,u32psr)+{+u32cc_bits=opcode>>28;++if(cc_bits!=ARM_OPCODE_CONDITION_UNCOND){+if((*opcode_condition_checks[cc_bits])(psr))+returnARM_OPCODE_CONDTEST_PASS;+else+returnARM_OPCODE_CONDTEST_FAIL;+}+returnARM_OPCODE_CONDTEST_UNCOND;+}+/**swp_handlerlogstheidofcallingprocess,dissectstheinstruction,sanity*checksthememorylocation,callsemulate_swpXfortheactualoperationand
From: David Long <hidden> Date: 2016-06-03 03:32:02
From: Sandeepa Prabhu <redacted>
Kprobes needs simulation of instructions that cannot be stepped
from a different memory location, e.g.: those instructions
that uses PC-relative addressing. In simulation, the behaviour
of the instruction is implemented using a copy of pt_regs.
The following instruction categories are simulated:
- All branching instructions(conditional, register, and immediate)
- Literal access instructions(load-literal, adr/adrp)
Conditional execution is limited to branching instructions in
ARM v8. If conditions at PSTATE do not match the condition fields
of opcode, the instruction is effectively NOP.
Thanks to Will Cohen for assorted suggested changes.
Signed-off-by: Sandeepa Prabhu <redacted>
Signed-off-by: William Cohen <redacted>
Signed-off-by: David A. Long <redacted>
---
arch/arm64/include/asm/insn.h | 1 +
arch/arm64/include/asm/probes.h | 5 +-
arch/arm64/kernel/Makefile | 3 +-
arch/arm64/kernel/insn.c | 1 +
arch/arm64/kernel/kprobes-arm64.c | 32 ++++-
arch/arm64/kernel/kprobes.c | 53 ++++++--
arch/arm64/kernel/probes-simulate-insn.c | 218 +++++++++++++++++++++++++++++++
arch/arm64/kernel/probes-simulate-insn.h | 28 ++++
8 files changed, 325 insertions(+), 16 deletions(-)
create mode 100644 arch/arm64/kernel/probes-simulate-insn.c
create mode 100644 arch/arm64/kernel/probes-simulate-insn.h
@@ -35,7 +36,7 @@ struct kprobe_pc_restore {/* architecture specific copy of original instruction */structarch_specific_insn{kprobe_opcode_t*insn;-kprobes_pstate_check_t*pstate_cc;+pstate_check_t*pstate_cc;kprobes_handler_t*handler;/* restore address after step xol */structkprobe_pc_restorerestore;
@@ -62,6 +65,24 @@ static void __kprobes arch_prepare_ss_slot(struct kprobe *p)p->ainsn.restore.type=RESTORE_PC;}+staticvoid__kprobesarch_prepare_simulate(structkprobe*p)+{+/* This instructions is not executed xol. No need to adjust the PC */+p->ainsn.restore.addr=0;+p->ainsn.restore.type=NO_RESTORE;+}++staticvoid__kprobesarch_simulate_insn(structkprobe*p,structpt_regs*regs)+{+structkprobe_ctlblk*kcb=get_kprobe_ctlblk();++if(p->ainsn.handler)+p->ainsn.handler((u32)p->opcode,(long)p->addr,regs);++/* single step simulated, now go for post processing */+post_kprobe_handler(kcb,regs);+}+int__kprobesarch_prepare_kprobe(structkprobe*p){unsignedlongprobe_addr=(unsignedlong)p->addr;
@@ -86,7 +107,8 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)return-EINVAL;caseINSN_GOOD_NO_SLOT:/* insn need simulation */-return-EINVAL;+p->ainsn.insn=NULL;+break;caseINSN_GOOD:/* instruction uses slot */p->ainsn.insn=get_insn_slot();
@@ -96,7 +118,10 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)};/* prepare the instruction */-arch_prepare_ss_slot(p);+if(p->ainsn.insn)+arch_prepare_ss_slot(p);+else+arch_prepare_simulate(p);return0;}
@@ -222,20 +247,24 @@ static void __kprobes setup_singlestep(struct kprobe *p,kcb->kprobe_status=KPROBE_HIT_SS;}-BUG_ON(!p->ainsn.insn);-/* prepare for single stepping */-slot=(unsignedlong)p->ainsn.insn;+if(p->ainsn.insn){+/* prepare for single stepping */+slot=(unsignedlong)p->ainsn.insn;-set_ss_context(kcb,slot);/* mark pending ss */+set_ss_context(kcb,slot);/* mark pending ss */-if(kcb->kprobe_status==KPROBE_REENTER)-spsr_set_debug_flag(regs,0);+if(kcb->kprobe_status==KPROBE_REENTER)+spsr_set_debug_flag(regs,0);-/* IRQs and single stepping do not mix well. */-kprobes_save_local_irqflag(kcb,regs);-kernel_enable_single_step(regs);-instruction_pointer(regs)=slot;+/* IRQs and single stepping do not mix well. */+kprobes_save_local_irqflag(kcb,regs);+kernel_enable_single_step(regs);+instruction_pointer(regs)=slot;+}else{+/* insn simulation */+arch_simulate_insn(p,regs);+}}staticint__kprobesreenter_kprobe(structkprobe*p,
@@ -0,0 +1,218 @@+/*+*arch/arm64/kernel/probes-simulate-insn.c+*+*Copyright(C)2013LinaroLimited.+*+*Thisprogramisfreesoftware;youcanredistributeitand/ormodify+*itunderthetermsoftheGNUGeneralPublicLicenseversion2as+*publishedbytheFreeSoftwareFoundation.+*+*Thisprogramisdistributedinthehopethatitwillbeuseful,+*butWITHOUTANYWARRANTY;withouteventheimpliedwarrantyof+*MERCHANTABILITYorFITNESSFORAPARTICULARPURPOSE.SeetheGNU+*GeneralPublicLicenseformoredetails.+*/++#include<linux/kernel.h>+#include<linux/kprobes.h>+#include<linux/module.h>++#include"probes-simulate-insn.h"++#define sign_extend(x, signbit) \+((x)|(0-((x)&(1<<(signbit)))))++#define bbl_displacement(insn) \+sign_extend(((insn)&0x3ffffff)<<2,27)++#define bcond_displacement(insn) \+sign_extend(((insn>>5)&0x7ffff)<<2,20)++#define cbz_displacement(insn) \+sign_extend(((insn>>5)&0x7ffff)<<2,20)++#define tbz_displacement(insn) \+sign_extend(((insn>>5)&0x3fff)<<2,15)++#define ldr_displacement(insn) \+sign_extend(((insn>>5)&0x7ffff)<<2,20)++staticinlinevoidset_x_reg(structpt_regs*regs,intreg,u64val)+{+if(reg<31)+regs->regs[reg]=val;+}++staticinlinevoidset_w_reg(structpt_regs*regs,intreg,u64val)+{+if(reg<31)+regs->regs[reg]=lower_32_bits(val);+}++staticinlineu64get_x_reg(structpt_regs*regs,intreg)+{+if(reg<31)+returnregs->regs[reg];+else+return0;+}++staticinlineu32get_w_reg(structpt_regs*regs,intreg)+{+if(reg<31)+returnlower_32_bits(regs->regs[reg]);+else+return0;+}++staticbool__kprobescheck_cbz(u32opcode,structpt_regs*regs)+{+intxn=opcode&0x1f;++return(opcode&(1<<31))?+(get_x_reg(regs,xn)==0):(get_w_reg(regs,xn)==0);+}++staticbool__kprobescheck_cbnz(u32opcode,structpt_regs*regs)+{+intxn=opcode&0x1f;++return(opcode&(1<<31))?+(get_x_reg(regs,xn)!=0):(get_w_reg(regs,xn)!=0);+}++staticbool__kprobescheck_tbz(u32opcode,structpt_regs*regs)+{+intxn=opcode&0x1f;+intbit_pos=((opcode&(1<<31))>>26)|((opcode>>19)&0x1f);++return((get_x_reg(regs,xn)>>bit_pos)&0x1)==0;+}++staticbool__kprobescheck_tbnz(u32opcode,structpt_regs*regs)+{+intxn=opcode&0x1f;+intbit_pos=((opcode&(1<<31))>>26)|((opcode>>19)&0x1f);++return((get_x_reg(regs,xn)>>bit_pos)&0x1)!=0;+}++/*+*instructionsimulationfunctions+*/+void__kprobes+simulate_adr_adrp(u32opcode,longaddr,structpt_regs*regs)+{+longimm,xn,val;++xn=opcode&0x1f;+imm=((opcode>>3)&0x1ffffc)|((opcode>>29)&0x3);+imm=sign_extend(imm,20);+if(opcode&0x80000000)+val=(imm<<12)+(addr&0xfffffffffffff000);+else+val=imm+addr;++set_x_reg(regs,xn,val);++instruction_pointer(regs)+=4;+}++void__kprobes+simulate_b_bl(u32opcode,longaddr,structpt_regs*regs)+{+intdisp=bbl_displacement(opcode);++/* Link register is x30 */+if(opcode&(1<<31))+set_x_reg(regs,30,addr+4);++instruction_pointer(regs)=addr+disp;+}++void__kprobes+simulate_b_cond(u32opcode,longaddr,structpt_regs*regs)+{+intdisp=4;++if(opcode_condition_checks[opcode&0xf](regs->pstate&0xffffffff))+disp=bcond_displacement(opcode);++instruction_pointer(regs)=addr+disp;+}++void__kprobes+simulate_br_blr_ret(u32opcode,longaddr,structpt_regs*regs)+{+intxn=(opcode>>5)&0x1f;++/* update pc first in case we're doing a "blr lr" */+instruction_pointer(regs)=get_x_reg(regs,xn);++/* Link register is x30 */+if(((opcode>>21)&0x3)==1)+set_x_reg(regs,30,addr+4);+}++void__kprobes+simulate_cbz_cbnz(u32opcode,longaddr,structpt_regs*regs)+{+intdisp=4;++if(opcode&(1<<24)){+if(check_cbnz(opcode,regs))+disp=cbz_displacement(opcode);+}else{+if(check_cbz(opcode,regs))+disp=cbz_displacement(opcode);+}+instruction_pointer(regs)=addr+disp;+}++void__kprobes+simulate_tbz_tbnz(u32opcode,longaddr,structpt_regs*regs)+{+intdisp=4;++if(opcode&(1<<24)){+if(check_tbnz(opcode,regs))+disp=tbz_displacement(opcode);+}else{+if(check_tbz(opcode,regs))+disp=tbz_displacement(opcode);+}+instruction_pointer(regs)=addr+disp;+}++void__kprobes+simulate_ldr_literal(u32opcode,longaddr,structpt_regs*regs)+{+u64*load_addr;+intxn=opcode&0x1f;+intdisp;++disp=ldr_displacement(opcode);+load_addr=(u64*)(addr+disp);++if(opcode&(1<<30))/* x0-x30 */+set_x_reg(regs,xn,*load_addr);+else/* w0-w30 */+set_w_reg(regs,xn,*load_addr);++instruction_pointer(regs)+=4;+}++void__kprobes+simulate_ldrsw_literal(u32opcode,longaddr,structpt_regs*regs)+{+s32*load_addr;+intxn=opcode&0x1f;+intdisp;++disp=ldr_displacement(opcode);+load_addr=(s32*)(addr+disp);++set_x_reg(regs,xn,*load_addr);++instruction_pointer(regs)+=4;+}
From: David Long <hidden> Date: 2016-06-03 03:33:14
From: Sandeepa Prabhu <redacted>
The pre-handler of this special 'trampoline' kprobe executes the return
probe handler functions and restores original return address in ELR_EL1.
This way the saved pt_regs still hold the original register context to be
carried back to the probed kernel function.
Signed-off-by: Sandeepa Prabhu <redacted>
Signed-off-by: David A. Long <redacted>
---
arch/arm64/Kconfig | 1 +
arch/arm64/kernel/kprobes.c | 75 ++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 75 insertions(+), 1 deletion(-)
@@ -577,7 +577,80 @@ bool arch_within_kprobe_blacklist(unsigned long addr)void__kprobes__used*trampoline_probe_handler(structpt_regs*regs){-returnNULL;+structkretprobe_instance*ri=NULL;+structhlist_head*head,empty_rp;+structhlist_node*tmp;+unsignedlongflags,orig_ret_addr=0;+unsignedlongtrampoline_address=+(unsignedlong)&kretprobe_trampoline;++INIT_HLIST_HEAD(&empty_rp);+kretprobe_hash_lock(current,&head,&flags);++/*+*Itispossibletohavemultipleinstancesassociatedwithagiven+*taskeitherbecausemultiplefunctionsinthecallpathhave+*areturnprobeinstalledonthem,and/ormorethanonereturn+*probewasregisteredforatargetfunction.+*+*Wecanhandlethisbecause:+*-instancesarealwaysinsertedattheheadofthelist+*-whenmultiplereturnprobesareregisteredforthesame+*function,thefirstinstance'sret_addrwillpointtothe+*realreturnaddress,andalltherestwillpointto+*kretprobe_trampoline+*/+hlist_for_each_entry_safe(ri,tmp,head,hlist){+if(ri->task!=current)+/* another task is sharing our hash bucket */+continue;++if(ri->rp&&ri->rp->handler){+__this_cpu_write(current_kprobe,&ri->rp->kp);+get_kprobe_ctlblk()->kprobe_status=KPROBE_HIT_ACTIVE;+ri->rp->handler(ri,regs);+__this_cpu_write(current_kprobe,NULL);+}++orig_ret_addr=(unsignedlong)ri->ret_addr;+recycle_rp_inst(ri,&empty_rp);++if(orig_ret_addr!=trampoline_address)+/*+*Thisistherealreturnaddress.Anyother+*instancesassociatedwiththistaskarefor+*othercallsdeeperonthecallstack+*/+break;+}++kretprobe_assert(ri,orig_ret_addr,trampoline_address);+/* restore the original return address */+instruction_pointer(regs)=orig_ret_addr;+reset_current_kprobe();+kretprobe_hash_unlock(current,&flags);++hlist_for_each_entry_safe(ri,tmp,&empty_rp,hlist){+hlist_del(&ri->hlist);+kfree(ri);+}++/* return 1 so that post handlers not called */+return(void*)orig_ret_addr;+}++void__kprobesarch_prepare_kretprobe(structkretprobe_instance*ri,+structpt_regs*regs)+{+ri->ret_addr=(kprobe_opcode_t*)regs->regs[30];++/* replace return addr (x30) with trampoline */+regs->regs[30]=(long)&kretprobe_trampoline;+}++int__kprobesarch_trampoline_kprobe(structkprobe*p)+{+return0;}int__initarch_init_kprobes(void)
From: David Long <hidden> Date: 2016-06-03 03:34:09
From: William Cohen <redacted>
The trampoline code is used by kretprobes to capture a return from a probed
function. This is done by saving the registers, calling the handler, and
restoring the registers. The code then returns to the original saved caller
return address. It is necessary to do this directly instead of using a
software breakpoint because the code used in processing that breakpoint
could itself be kprobe'd and cause a problematic reentry into the debug
exception handler.
Signed-off-by: William Cohen <redacted>
Signed-off-by: David A. Long <redacted>
---
arch/arm64/include/asm/kprobes.h | 2 +
arch/arm64/kernel/Makefile | 1 +
arch/arm64/kernel/asm-offsets.c | 11 +++++
arch/arm64/kernel/kprobes.c | 5 ++
arch/arm64/kernel/kprobes_trampoline.S | 85 ++++++++++++++++++++++++++++++++++
5 files changed, 104 insertions(+)
create mode 100644 arch/arm64/kernel/kprobes_trampoline.S
On Thu, 2 Jun 2016 23:26:17 -0400
David Long [off-list ref] wrote:
From: "David A. Long" <redacted>
Cease using the arm32 arm_check_condition() function and replace it with
a local version for use in deprecated instruction support on arm64. Also
make the function table used by this available for future use by kprobes
and/or uprobes.
This function is dervied from code written by Sandeepa Prabhu.
@@ -366,6 +366,21 @@ static int emulate_swpX(unsigned int address, unsigned int *data,returnres;}+#define ARM_OPCODE_CONDITION_UNCOND 0xf++staticunsignedint__kprobesarm32_check_condition(u32opcode,u32psr)
Would you be OK for using arm32 instead of aarch32 prefix?
On Thu, 2 Jun 2016 23:26:20 -0400
David Long [off-list ref] wrote:
From: Pratyush Anand <redacted>
Entry symbols are not kprobe safe. So blacklist them for kprobing.
Signed-off-by: Pratyush Anand <redacted>
Signed-off-by: David A. Long <redacted>
Looks good to me.
Acked-by: Masami Hiramatsu <mhiramat@kernel.org>
@@ -46,6 +46,10 @@ static int handler_pre(struct kprobe *p, struct pt_regs *regs)" ex1 = 0x%lx\n",p->symbol_name,p->addr,regs->pc,regs->ex1);#endif+#ifdef CONFIG_ARM64+pr_info("pre_handler: p->addr = 0x%p, pc = 0x%lx\n",+p->addr,(long)regs->pc);+#endif/* A dump_stack() here will give a stack backtrace */return0;
On Thu, 2 Jun 2016 23:26:23 -0400
David Long [off-list ref] wrote:
quoted hunk
From: Sandeepa Prabhu <redacted>
The pre-handler of this special 'trampoline' kprobe executes the return
probe handler functions and restores original return address in ELR_EL1.
This way the saved pt_regs still hold the original register context to be
carried back to the probed kernel function.
Signed-off-by: Sandeepa Prabhu <redacted>
Signed-off-by: David A. Long <redacted>
---
arch/arm64/Kconfig | 1 +
arch/arm64/kernel/kprobes.c | 75 ++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 75 insertions(+), 1 deletion(-)
@@ -577,7 +577,80 @@ bool arch_within_kprobe_blacklist(unsigned long addr)void__kprobes__used*trampoline_probe_handler(structpt_regs*regs){-returnNULL;+structkretprobe_instance*ri=NULL;+structhlist_head*head,empty_rp;+structhlist_node*tmp;+unsignedlongflags,orig_ret_addr=0;+unsignedlongtrampoline_address=+(unsignedlong)&kretprobe_trampoline;++INIT_HLIST_HEAD(&empty_rp);+kretprobe_hash_lock(current,&head,&flags);++/*+*Itispossibletohavemultipleinstancesassociatedwithagiven+*taskeitherbecausemultiplefunctionsinthecallpathhave+*areturnprobeinstalledonthem,and/ormorethanonereturn+*probewasregisteredforatargetfunction.+*+*Wecanhandlethisbecause:+*-instancesarealwaysinsertedattheheadofthelist+*-whenmultiplereturnprobesareregisteredforthesame+*function,thefirstinstance'sret_addrwillpointtothe+*realreturnaddress,andalltherestwillpointto+*kretprobe_trampoline+*/+hlist_for_each_entry_safe(ri,tmp,head,hlist){+if(ri->task!=current)+/* another task is sharing our hash bucket */+continue;++if(ri->rp&&ri->rp->handler){+__this_cpu_write(current_kprobe,&ri->rp->kp);+get_kprobe_ctlblk()->kprobe_status=KPROBE_HIT_ACTIVE;+ri->rp->handler(ri,regs);+__this_cpu_write(current_kprobe,NULL);+}++orig_ret_addr=(unsignedlong)ri->ret_addr;+recycle_rp_inst(ri,&empty_rp);++if(orig_ret_addr!=trampoline_address)+/*+*Thisistherealreturnaddress.Anyother+*instancesassociatedwiththistaskarefor+*othercallsdeeperonthecallstack+*/+break;+}
This looks not included an improvement done by Syuhei, see
commit 737480a0d525 ("kprobes/x86: Fix the return address of multiple kretprobes")
Thank you!
On Thu, 2 Jun 2016 23:26:22 -0400
David Long [off-list ref] wrote:
From: William Cohen <redacted>
The trampoline code is used by kretprobes to capture a return from a probed
function. This is done by saving the registers, calling the handler, and
restoring the registers. The code then returns to the original saved caller
return address. It is necessary to do this directly instead of using a
software breakpoint because the code used in processing that breakpoint
could itself be kprobe'd and cause a problematic reentry into the debug
exception handler.
OK, I think we had discussed why this was not included to the next patch.
(Not like to merge patches from different person?)
Acked-by: Masami Hiramatsu <mhiramat@kernel.org>
Thanks,
quoted hunk
Signed-off-by: William Cohen <redacted>
Signed-off-by: David A. Long <redacted>
---
arch/arm64/include/asm/kprobes.h | 2 +
arch/arm64/kernel/Makefile | 1 +
arch/arm64/kernel/asm-offsets.c | 11 +++++
arch/arm64/kernel/kprobes.c | 5 ++
arch/arm64/kernel/kprobes_trampoline.S | 85 ++++++++++++++++++++++++++++++++++
5 files changed, 104 insertions(+)
create mode 100644 arch/arm64/kernel/kprobes_trampoline.S
On Thu, 2 Jun 2016 23:26:19 -0400
David Long [off-list ref] wrote:
From: Sandeepa Prabhu <redacted>
Add support for basic kernel probes(kprobes) and jump probes
(jprobes) for ARM64.
Kprobes utilizes software breakpoint and single step debug
exceptions supported on ARM v8.
A software breakpoint is placed at the probe address to trap the
kernel execution into the kprobe handler.
ARM v8 supports enabling single stepping before the break exception
return (ERET), with next PC in exception return address (ELR_EL1). The
kprobe handler prepares an executable memory slot for out-of-line
execution with a copy of the original instruction being probed, and
enables single stepping. The PC is set to the out-of-line slot address
before the ERET. With this scheme, the instruction is executed with the
exact same register context except for the PC (and DAIF) registers.
Debug mask (PSTATE.D) is enabled only when single stepping a recursive
kprobe, e.g.: during kprobes reenter so that probed instruction can be
single stepped within the kprobe handler -exception- context.
The recursion depth of kprobe is always 2, i.e. upon probe re-entry,
any further re-entry is prevented by not calling handlers and the case
counted as a missed kprobe).
Single stepping from the x-o-l slot has a drawback for PC-relative accesses
like branching and symbolic literals access as the offset from the new PC
(slot address) may not be ensured to fit in the immediate value of
the opcode. Such instructions need simulation, so reject
probing them.
Instructions generating exceptions or cpu mode change are rejected
for probing.
Exclusive load/store instructions are rejected too. Additionally, the
code is checked to see if it is inside an exclusive load/store sequence
(code from Pratyush).
System instructions are mostly enabled for stepping, except MSR/MRS
accesses to "DAIF" flags in PSTATE, which are not safe for
probing.
Thanks to Steve Capper and Pratyush Anand for several suggested
Changes.
Basically looks good to me.
I have some trivial comments.
@@ -274,10 +275,14 @@ static int single_step_handler(unsigned long addr, unsigned int esr,*/user_rewind_single_step(current);}else{+#ifdef CONFIG_KPROBES+if(kprobe_single_step_handler(regs,esr)==DBG_HOOK_HANDLED)+return0;+#endifif(call_step_hook(regs,esr)==DBG_HOOK_HANDLED)return0;-pr_warning("Unexpected kernel single-step exception at EL1\n");+pr_warn("Unexpected kernel single-step exception at EL1\n");
This change would better be splitted, anyway, it depends on the maintainer
of this file (Will and Catalin?)
quoted hunk
/*
* Re-enable stepping since we know that we will be
* returning to regs.
@@ -332,8 +337,15 @@ static int brk_handler(unsigned long addr, unsigned int esr, { if (user_mode(regs)) { send_user_sigtrap(TRAP_BRKPT);- } else if (call_break_hook(regs, esr) != DBG_HOOK_HANDLED) {- pr_warning("Unexpected kernel BRK exception at EL1\n");+ }+#ifdef CONFIG_KPROBES+ else if ((esr & BRK64_ESR_MASK) == BRK64_ESR_KPROBES) {+ if (kprobe_breakpoint_handler(regs, esr) != DBG_HOOK_HANDLED)+ return -EFAULT;+ }+#endif+ else if (call_break_hook(regs, esr) != DBG_HOOK_HANDLED) {+ pr_warn("Unexpected kernel BRK exception at EL1\n"); return -EFAULT; }
From: David Long <hidden> Date: 2016-06-10 19:16:28
On 06/03/2016 11:40 PM, Masami Hiramatsu wrote:
Hi David,
On Thu, 2 Jun 2016 23:26:18 -0400
David Long [off-list ref] wrote:
quoted
From: Pratyush Anand <redacted>
Add all function symbols which are called from do_debug_exception under
NOKPROBE_SYMBOL, as they can not kprobed.
I see, but this patch should be applied after kprobes
are implemented on arm64.
I've gone back to that ordering (I think it was that way in earlier
versions) but it feels odd. Wouldn't you want the unsafe symbols marked
that way before turning on kprobes, like it already is in the generic
kernel code?
From: David Long <hidden> Date: 2016-06-13 04:10:41
On 06/07/2016 09:07 PM, Masami Hiramatsu wrote:
On Thu, 2 Jun 2016 23:26:19 -0400
David Long [off-list ref] wrote:
quoted
From: Sandeepa Prabhu <redacted>
Add support for basic kernel probes(kprobes) and jump probes
(jprobes) for ARM64.
Kprobes utilizes software breakpoint and single step debug
exceptions supported on ARM v8.
A software breakpoint is placed at the probe address to trap the
kernel execution into the kprobe handler.
ARM v8 supports enabling single stepping before the break exception
return (ERET), with next PC in exception return address (ELR_EL1). The
kprobe handler prepares an executable memory slot for out-of-line
execution with a copy of the original instruction being probed, and
enables single stepping. The PC is set to the out-of-line slot address
before the ERET. With this scheme, the instruction is executed with the
exact same register context except for the PC (and DAIF) registers.
Debug mask (PSTATE.D) is enabled only when single stepping a recursive
kprobe, e.g.: during kprobes reenter so that probed instruction can be
single stepped within the kprobe handler -exception- context.
The recursion depth of kprobe is always 2, i.e. upon probe re-entry,
any further re-entry is prevented by not calling handlers and the case
counted as a missed kprobe).
Single stepping from the x-o-l slot has a drawback for PC-relative accesses
like branching and symbolic literals access as the offset from the new PC
(slot address) may not be ensured to fit in the immediate value of
the opcode. Such instructions need simulation, so reject
probing them.
Instructions generating exceptions or cpu mode change are rejected
for probing.
Exclusive load/store instructions are rejected too. Additionally, the
code is checked to see if it is inside an exclusive load/store sequence
(code from Pratyush).
System instructions are mostly enabled for stepping, except MSR/MRS
accesses to "DAIF" flags in PSTATE, which are not safe for
probing.
Thanks to Steve Capper and Pratyush Anand for several suggested
Changes.
Basically looks good to me.
I have some trivial comments.
Not sure why kprobes.c and kprobes-arm64.c are splitted.
This comes from the model of the arm32 kprobes code where handling of
the low-level instruction simulation is implemented in separate files
for 32-bit vs. thumb instructions. It should make a little more sense
in the future when additional instruction simulation code will hopefully
be added for those instructions we cannot currently single-step
out-of-line. It also probably *could* be merged into one file.
@@ -274,10 +275,14 @@ static int single_step_handler(unsigned long addr, unsigned int esr,*/user_rewind_single_step(current);}else{+#ifdef CONFIG_KPROBES+if(kprobe_single_step_handler(regs,esr)==DBG_HOOK_HANDLED)+return0;+#endifif(call_step_hook(regs,esr)==DBG_HOOK_HANDLED)return0;-pr_warning("Unexpected kernel single-step exception at EL1\n");+pr_warn("Unexpected kernel single-step exception at EL1\n");
This change would better be splitted, anyway, it depends on the maintainer
of this file (Will and Catalin?)
I've removed this cleanup from this patch set.
quoted
/*
* Re-enable stepping since we know that we will be
* returning to regs.
@@ -332,8 +337,15 @@ static int brk_handler(unsigned long addr, unsigned int esr, { if (user_mode(regs)) { send_user_sigtrap(TRAP_BRKPT);- } else if (call_break_hook(regs, esr) != DBG_HOOK_HANDLED) {- pr_warning("Unexpected kernel BRK exception at EL1\n");+ }+#ifdef CONFIG_KPROBES+ else if ((esr & BRK64_ESR_MASK) == BRK64_ESR_KPROBES) {+ if (kprobe_breakpoint_handler(regs, esr) != DBG_HOOK_HANDLED)+ return -EFAULT;+ }+#endif+ else if (call_break_hook(regs, esr) != DBG_HOOK_HANDLED) {+ pr_warn("Unexpected kernel BRK exception at EL1\n"); return -EFAULT; }
Ah, that gets used later when simulation support is added. I've removed
this enum value from this commit and will add it to the later one.
Please no one complain about using an enum instead of a bool, it will
eventually have three possible values.
What happen if mod == NULL? it should be return error, isn't it?
No, it should be fine. It just means it didn't have to do either of the
extra checks to limit the end of the search through the code to the
boundary of one of the corresponding module text sections. It means the
instruction is in the regular kernel (non-module) text segment.
From: David Long <hidden> Date: 2016-06-13 04:19:57
On 06/03/2016 11:53 PM, Masami Hiramatsu wrote:
On Thu, 2 Jun 2016 23:26:17 -0400
David Long [off-list ref] wrote:
quoted
From: "David A. Long" <redacted>
Cease using the arm32 arm_check_condition() function and replace it with
a local version for use in deprecated instruction support on arm64. Also
make the function table used by this available for future use by kprobes
and/or uprobes.
This function is dervied from code written by Sandeepa Prabhu.
Are those condition checkers only for aarch32 opcode? or
general for aarch64 too? If it is only for aarch32, we'd better
add aarch32 prefix.
I have this vague recollection there once was a reason for this but I
can't for the life of me remember why. I altered the symbol name to
something that begins with aarch32.
@@ -366,6 +366,21 @@ static int emulate_swpX(unsigned int address, unsigned int *data,returnres;}+#define ARM_OPCODE_CONDITION_UNCOND 0xf++staticunsignedint__kprobesarm32_check_condition(u32opcode,u32psr)
Would you be OK for using arm32 instead of aarch32 prefix?
I think you meant the opposite of that? I guess that would make sense,
and would be simple enough since it's an internal function. I will
change arm32 to aarch32.
From: David Long <hidden> Date: 2016-06-13 04:24:04
On 06/07/2016 06:38 AM, Masami Hiramatsu wrote:
On Thu, 2 Jun 2016 23:26:22 -0400
David Long [off-list ref] wrote:
quoted
From: William Cohen <redacted>
The trampoline code is used by kretprobes to capture a return from a probed
function. This is done by saving the registers, calling the handler, and
restoring the registers. The code then returns to the original saved caller
return address. It is necessary to do this directly instead of using a
software breakpoint because the code used in processing that breakpoint
could itself be kprobe'd and cause a problematic reentry into the debug
exception handler.
OK, I think we had discussed why this was not included to the next patch.
(Not like to merge patches from different person?)
Yes, and adding the trampoline support before making use of it seemed OK
to me even if it wasn't strictly necessary.
Not sure why kprobes.c and kprobes-arm64.c are splitted.
This comes from the model of the arm32 kprobes code where handling of
the low-level instruction simulation is implemented in separate files
for 32-bit vs. thumb instructions. It should make a little more sense
in the future when additional instruction simulation code will hopefully
be added for those instructions we cannot currently single-step
out-of-line. It also probably *could* be merged into one file.
Hmm, at least the name of arch/arm64/kernel/kprobes-arm64.c is
meaningless. As we've done in x86, I think we can make it
arch/arm64/kernel/kprobes/decode-insn.{c,h}
[..]
quoted
quoted
+
+/* Return:
+ * INSN_REJECTED If instruction is one not allowed to kprobe,
+ * INSN_GOOD If instruction is supported and uses instruction slot,
+ * INSN_GOOD_NO_SLOT If instruction is supported but doesn't use its slot.
Is there any chance to return INSN_GOOD_NO_SLOT?
Ah, that gets used later when simulation support is added. I've removed
this enum value from this commit and will add it to the later one.
Please no one complain about using an enum instead of a bool, it will
eventually have three possible values.
What happen if mod == NULL? it should be return error, isn't it?
No, it should be fine. It just means it didn't have to do either of the
extra checks to limit the end of the search through the code to the
boundary of one of the corresponding module text sections. It means the
instruction is in the regular kernel (non-module) text segment.
Ah, I see. It is OK then. :)
Thank you,
--
Masami Hiramatsu [off-list ref]
Not sure why kprobes.c and kprobes-arm64.c are splitted.
This comes from the model of the arm32 kprobes code where handling of
the low-level instruction simulation is implemented in separate files
for 32-bit vs. thumb instructions. It should make a little more sense
in the future when additional instruction simulation code will hopefully
be added for those instructions we cannot currently single-step
out-of-line. It also probably *could* be merged into one file.
Hmm, at least the name of arch/arm64/kernel/kprobes-arm64.c is
meaningless. As we've done in x86, I think we can make it
arch/arm64/kernel/kprobes/decode-insn.{c,h}
I've changed the name to kprobe-decode-insn.[hc], or do you feel
strongly the three kprobes source files in arch/arm64/kernel need their
own subdirectory?
[..]
quoted
quoted
quoted
+
+/* Return:
+ * INSN_REJECTED If instruction is one not allowed to kprobe,
+ * INSN_GOOD If instruction is supported and uses instruction slot,
+ * INSN_GOOD_NO_SLOT If instruction is supported but doesn't use its slot.
Is there any chance to return INSN_GOOD_NO_SLOT?
Ah, that gets used later when simulation support is added. I've removed
this enum value from this commit and will add it to the later one.
Please no one complain about using an enum instead of a bool, it will
eventually have three possible values.
What happen if mod == NULL? it should be return error, isn't it?
No, it should be fine. It just means it didn't have to do either of the
extra checks to limit the end of the search through the code to the
boundary of one of the corresponding module text sections. It means the
instruction is in the regular kernel (non-module) text segment.
Not sure why kprobes.c and kprobes-arm64.c are splitted.
This comes from the model of the arm32 kprobes code where handling of
the low-level instruction simulation is implemented in separate files
for 32-bit vs. thumb instructions. It should make a little more sense
in the future when additional instruction simulation code will hopefully
be added for those instructions we cannot currently single-step
out-of-line. It also probably *could* be merged into one file.
Hmm, at least the name of arch/arm64/kernel/kprobes-arm64.c is
meaningless. As we've done in x86, I think we can make it
arch/arm64/kernel/kprobes/decode-insn.{c,h}
I've changed the name to kprobe-decode-insn.[hc], or do you feel
strongly the three kprobes source files in arch/arm64/kernel need their
own subdirectory?
Yes, especially when we start working on kprobes-on-ftrace support,
it is better to have a separate file for that.
Thank you!
--
Masami Hiramatsu [off-list ref]
Hi David,
I have additional comments on this.
On Thu, 2 Jun 2016 23:26:19 -0400
David Long [off-list ref] wrote:
+/*
+ * The D-flag (Debug mask) is set (masked) upon deug exception entry.
deug -> debug
+ * Kprobes needs to clear (unmask) D-flag -ONLY- in case of recursive
+ * probe i.e. when probe hit from kprobe handler context upon
+ * executing the pre/post handlers. In this case we return with
+ * D-flag clear so that single-stepping can be carried-out.
+ *
+ * Leave D-flag set in all other cases.
+ */
+static void __kprobes
+spsr_set_debug_flag(struct pt_regs *regs, int mask)
+{
+ unsigned long spsr = regs->pstate;
+
+ if (mask)
+ spsr |= PSR_D_BIT;
+ else
+ spsr &= ~PSR_D_BIT;
+
+ regs->pstate = spsr;
+}
+
[..]
+
+int __kprobes kprobe_fault_handler(struct pt_regs *regs, unsigned int fsr)
+{
+ struct kprobe *cur = kprobe_running();
+ struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
+
+ switch (kcb->kprobe_status) {
+ case KPROBE_HIT_SS:
+ case KPROBE_REENTER:
+ /*
+ * We are here because the instruction being single
+ * stepped caused a page fault. We reset the current
+ * kprobe and the ip points back to the probe address
+ * and allow the page fault handler to continue as a
+ * normal page fault.
+ */
+ instruction_pointer(regs) = (unsigned long)cur->addr;
+ if (!instruction_pointer(regs))
+ BUG();
As according to the recent x86 kprobe bug on fault handler
discussion, here this also need kernel_disable_single_stap()
and spsr_set_debug_flag() in case of KPROBE_REENTER as you did
in kprobe_single_step_handler(). (Also, those code should be
a function for reuse)
From: David Long <hidden> Date: 2016-06-22 18:16:32
On 06/07/2016 06:28 AM, Masami Hiramatsu wrote:
On Thu, 2 Jun 2016 23:26:23 -0400
David Long [off-list ref] wrote:
quoted
From: Sandeepa Prabhu <redacted>
The pre-handler of this special 'trampoline' kprobe executes the return
probe handler functions and restores original return address in ELR_EL1.
This way the saved pt_regs still hold the original register context to be
carried back to the probed kernel function.
Signed-off-by: Sandeepa Prabhu <redacted>
Signed-off-by: David A. Long <redacted>
---
arch/arm64/Kconfig | 1 +
arch/arm64/kernel/kprobes.c | 75 ++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 75 insertions(+), 1 deletion(-)
@@ -577,7 +577,80 @@ bool arch_within_kprobe_blacklist(unsigned long addr)void__kprobes__used*trampoline_probe_handler(structpt_regs*regs){-returnNULL;+structkretprobe_instance*ri=NULL;+structhlist_head*head,empty_rp;+structhlist_node*tmp;+unsignedlongflags,orig_ret_addr=0;+unsignedlongtrampoline_address=+(unsignedlong)&kretprobe_trampoline;++INIT_HLIST_HEAD(&empty_rp);+kretprobe_hash_lock(current,&head,&flags);++/*+*Itispossibletohavemultipleinstancesassociatedwithagiven+*taskeitherbecausemultiplefunctionsinthecallpathhave+*areturnprobeinstalledonthem,and/ormorethanonereturn+*probewasregisteredforatargetfunction.+*+*Wecanhandlethisbecause:+*-instancesarealwaysinsertedattheheadofthelist+*-whenmultiplereturnprobesareregisteredforthesame+*function,thefirstinstance'sret_addrwillpointtothe+*realreturnaddress,andalltherestwillpointto+*kretprobe_trampoline+*/+hlist_for_each_entry_safe(ri,tmp,head,hlist){+if(ri->task!=current)+/* another task is sharing our hash bucket */+continue;++if(ri->rp&&ri->rp->handler){+__this_cpu_write(current_kprobe,&ri->rp->kp);+get_kprobe_ctlblk()->kprobe_status=KPROBE_HIT_ACTIVE;+ri->rp->handler(ri,regs);+__this_cpu_write(current_kprobe,NULL);+}++orig_ret_addr=(unsignedlong)ri->ret_addr;+recycle_rp_inst(ri,&empty_rp);++if(orig_ret_addr!=trampoline_address)+/*+*Thisistherealreturnaddress.Anyother+*instancesassociatedwiththistaskarefor+*othercallsdeeperonthecallstack+*/+break;+}
This looks not included an improvement done by Syuhei, see
commit 737480a0d525 ("kprobes/x86: Fix the return address of multiple kretprobes")
Thank you!
I've updated the relevant part of this function with the current x86
version of the code.
Not sure why kprobes.c and kprobes-arm64.c are splitted.
This comes from the model of the arm32 kprobes code where handling of
the low-level instruction simulation is implemented in separate files
for 32-bit vs. thumb instructions. It should make a little more sense
in the future when additional instruction simulation code will hopefully
be added for those instructions we cannot currently single-step
out-of-line. It also probably *could* be merged into one file.
Hmm, at least the name of arch/arm64/kernel/kprobes-arm64.c is
meaningless. As we've done in x86, I think we can make it
arch/arm64/kernel/kprobes/decode-insn.{c,h}
I've changed the name to kprobe-decode-insn.[hc], or do you feel
strongly the three kprobes source files in arch/arm64/kernel need their
own subdirectory?
Yes, especially when we start working on kprobes-on-ftrace support,
it is better to have a separate file for that.
I've reorganized the kprobes source files into their own subdirectory
and changed some of their names.