arm64: uprobes - ARM32 instruction probing
Subsystems (6)
A message that is not in the archive (sent off-list or not yet received): CGME20180124123450eucas1p23a79f56cd4a65c790468cfc2f3e1339b@eucas1p2.samsung.com
cover arm64: uprobes - ARM32 instruction probing
The uprobe feature on ARM64 kernel does not support ARM A32 instruction probing, making 32 bit apps running on 64 bit kernel unprobeable.
This patchset utilizes ARM32 uprobe code in ARM64 tree with following modifications:
- moves ARM32 uprobes code form arch/arm to lib/uprobes/arm to be reused by ARM64 code
- makes adjustments in api so that it suits the one used by ARM64 kernel, e.g. struct pt_regs, register numbers
- takes into account differences between 32 and 64 bit ARM architectures. A64 instruction handling is left unchanged.
Detect what kind of instruction is being probed and depending on the result:
- if an A64 instruction handle it the old way, using existing A64 instructions probing code,
- if an A32 instruction decode it and handle using the new code, moved from 32 bit arm kernel tree.
Currently following test cases have been carried out with positive results:
- simulation-able instructions, like: STM, LDM, BLX, BL
- xol-able instructions, like: STR, LDR, ADD, CMN, CMP
- branching
- uretprobes.
This patchset does not implement T32 (Thumb) instructions probing (which is also unsupported on "native" ARM32 arch).
Maciej Slodczyk (7):
arm64: move arm uprobes code to be reused by arm64
arm64: uprobes - fix checkpatch.pl issues
arm64: introduce get_swbp_insn() instead of static assignment
arm64: change arm64 probes handler prototype
arm64: make arm uprobes code reusable by arm64
arm64: change arm_probe_decode_insn() function name
arm64: uprobes - ARM32 instruction probing
arch/arm/include/asm/probes.h | 8 ++
arch/arm/include/asm/ptrace.h | 32 +++++
arch/arm/include/asm/uprobes.h | 2 +-
arch/arm/probes/Makefile | 8 +-
arch/arm/probes/kprobes/actions-arm.c | 2 +-
arch/arm/probes/kprobes/checkers-arm.c | 4 +-
arch/arm/probes/kprobes/checkers-common.c | 4 +-
arch/arm/probes/kprobes/checkers.h | 2 +-
arch/arm/probes/kprobes/core.c | 2 +-
arch/arm/probes/kprobes/core.h | 2 +-
arch/arm/probes/uprobes/Makefile | 2 +-
arch/arm/probes/uprobes/core.c | 10 +-
arch/arm64/include/asm/probes.h | 24 +++-
arch/arm64/include/asm/ptrace.h | 21 +++
arch/arm64/include/asm/uprobes.h | 21 ++-
arch/arm64/kernel/debug-monitors.c | 8 ++
arch/arm64/kernel/probes/Makefile | 2 +
arch/arm64/kernel/probes/decode-insn.c | 30 ++--
arch/arm64/kernel/probes/decode-insn.h | 15 +-
arch/arm64/kernel/probes/kprobes.c | 4 +-
arch/arm64/kernel/probes/simulate-insn.c | 32 +++--
arch/arm64/kernel/probes/simulate-insn.h | 24 ++--
arch/arm64/kernel/probes/uprobes.c | 121 ++++++++++++++--
include/linux/uprobes.h | 1 +
kernel/events/uprobes.c | 15 +-
lib/Makefile | 2 +
lib/probes/Makefile | 2 +
lib/probes/arm/Makefile | 11 ++
.../uprobes => lib/probes/arm}/actions-arm.c | 153 ++++++++++++++++++---
{arch/arm/probes => lib/probes/arm}/decode-arm.c | 57 +++++---
{arch/arm/probes => lib/probes/arm}/decode-arm.h | 4 +-
{arch/arm/probes => lib/probes/arm}/decode.c | 18 ++-
{arch/arm/probes => lib/probes/arm}/decode.h | 48 ++++---
33 files changed, 551 insertions(+), 140 deletions(-)
create mode 100644 lib/probes/Makefile
create mode 100644 lib/probes/arm/Makefile
rename {arch/arm/probes/uprobes => lib/probes/arm}/actions-arm.c (61%)
rename {arch/arm/probes => lib/probes/arm}/decode-arm.c (95%)
rename {arch/arm/probes => lib/probes/arm}/decode-arm.h (94%)
rename {arch/arm/probes => lib/probes/arm}/decode.c (98%)
rename {arch/arm/probes => lib/probes/arm}/decode.h (94%)Signature · 1 line
2.7.4
PATCH 1/7 arm64: move arm uprobes code to be reused by arm64
Move ARM32 uprobes code from arch/arm/probes/ to a more common location - lib/probes/arm/. This code will be used by ARM64 code when uprobing 32-bit applications.
---
arch/arm/probes/Makefile | 8 ++++----
arch/arm/probes/kprobes/actions-arm.c | 2 +-
arch/arm/probes/kprobes/checkers-arm.c | 4 ++--
arch/arm/probes/kprobes/checkers-common.c | 4 ++--
arch/arm/probes/kprobes/checkers.h | 2 +-
arch/arm/probes/kprobes/core.c | 2 +-
arch/arm/probes/kprobes/core.h | 2 +-
arch/arm/probes/uprobes/Makefile | 2 +-
arch/arm/probes/uprobes/core.c | 4 ++--
lib/Makefile | 2 ++
lib/probes/Makefile | 1 +
lib/probes/arm/Makefile | 6 ++++++
{arch/arm/probes/uprobes => lib/probes/arm}/actions-arm.c | 4 ++--
{arch/arm/probes => lib/probes/arm}/decode-arm.c | 0
{arch/arm/probes => lib/probes/arm}/decode-arm.h | 0
{arch/arm/probes => lib/probes/arm}/decode.c | 0
{arch/arm/probes => lib/probes/arm}/decode.h | 0
17 files changed, 26 insertions(+), 17 deletions(-)
create mode 100644 lib/probes/Makefile
create mode 100644 lib/probes/arm/Makefile
rename {arch/arm/probes/uprobes => lib/probes/arm}/actions-arm.c (99%)
rename {arch/arm/probes => lib/probes/arm}/decode-arm.c (100%)
rename {arch/arm/probes => lib/probes/arm}/decode-arm.h (100%)
rename {arch/arm/probes => lib/probes/arm}/decode.c (100%)
rename {arch/arm/probes => lib/probes/arm}/decode.h (100%)arch/arm/probes/Makefile +4 -4
@@ -1,8 +1,8 @@ # SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_UPROBES) += decode.o decode-arm.o uprobes/ -obj-$(CONFIG_KPROBES) += decode.o kprobes/ +subdir-ccflags-y := -I$(srctree)/lib/probes/arm/ + +obj-$(CONFIG_UPROBES) += uprobes/ +obj-$(CONFIG_KPROBES) += kprobes/ ifdef CONFIG_THUMB2_KERNEL obj-$(CONFIG_KPROBES) += decode-thumb.o -else -obj-$(CONFIG_KPROBES) += decode-arm.o endif
arch/arm/probes/kprobes/actions-arm.c +1 -1
@@ -62,7 +62,7 @@#include <linux/kprobes.h> #include <linux/ptrace.h> -#include "../decode-arm.h" +#include "decode-arm.h" #include "core.h" #include "checkers.h"
arch/arm/probes/kprobes/checkers-arm.c +2 -2
@@ -14,8 +14,8 @@*/ #include <linux/kernel.h> -#include "../decode.h" -#include "../decode-arm.h" +#include "decode.h" +#include "decode-arm.h" #include "checkers.h" static enum probes_insn __kprobes arm_check_stack(probes_opcode_t insn,
arch/arm/probes/kprobes/checkers-common.c +2 -2
@@ -14,8 +14,8 @@*/ #include <linux/kernel.h> -#include "../decode.h" -#include "../decode-arm.h" +#include "decode.h" +#include "decode-arm.h" #include "checkers.h" enum probes_insn checker_stack_use_none(probes_opcode_t insn,
arch/arm/probes/kprobes/checkers.h +1 -1
@@ -17,7 +17,7 @@#include <linux/kernel.h> #include <linux/types.h> -#include "../decode.h" +#include "decode.h" extern probes_check_t checker_stack_use_none;extern probes_check_t checker_stack_use_unknown;
arch/arm/probes/kprobes/core.c +1 -1
@@ -33,7 +33,7 @@#include <linux/bug.h> #include <asm/patch.h> -#include "../decode-arm.h" +#include "decode-arm.h" #include "../decode-thumb.h" #include "core.h"
arch/arm/probes/kprobes/core.h +1 -1
@@ -20,7 +20,7 @@#define _ARM_KERNEL_KPROBES_H#include <asm/kprobes.h> -#include "../decode.h" +#include "decode.h" /* * These undefined instructions must be unique and
arch/arm/probes/uprobes/Makefile +1 -1
@@ -1 +1 @@ -obj-$(CONFIG_UPROBES) += core.o actions-arm.o +obj-$(CONFIG_UPROBES) += core.o
arch/arm/probes/uprobes/core.c +2 -2
@@ -17,8 +17,8 @@#include <asm/opcodes.h> #include <asm/traps.h> -#include "../decode.h" -#include "../decode-arm.h" +#include "decode.h" +#include "decode-arm.h" #include "core.h" #define UPROBE_TRAP_NR UINT_MAX
lib/Makefile +2 -0
@@ -124,6 +124,8 @@ obj-$(CONFIG_ZSTD_COMPRESS) += zstd/ obj-$(CONFIG_ZSTD_DECOMPRESS) += zstd/ obj-$(CONFIG_XZ_DEC) += xz/ obj-$(CONFIG_RAID6_PQ) += raid6/ +obj-$(CONFIG_UPROBES) += probes/ +obj-$(CONFIG_KPROBES) += probes/ lib-$(CONFIG_DECOMPRESS_GZIP) += decompress_inflate.o lib-$(CONFIG_DECOMPRESS_BZIP2) += decompress_bunzip2.o
lib/probes/Makefile new file +1 -0
@@ -0,0 +1 @@ +obj-$(CONFIG_ARM) += arm/
lib/probes/arm/Makefile new file +6 -0
@@ -0,0 +1,6 @@ +ccflags-y += -I$(srctree)/arch/arm/probes/uprobes/ +obj-$(CONFIG_UPROBES) += decode.o decode-arm.o actions-arm.o +obj-$(CONFIG_KPROBES) += decode.o +ifndef CONFIG_THUMB2_KERNEL +obj-$(CONFIG_KPROBES) += decode-arm.o +endif
lib/probes/arm/actions-arm.c renamed from arch/arm/probes/uprobes/actions-arm.c +2 -2
@@ -13,8 +13,8 @@#include <linux/uprobes.h> #include <linux/module.h> -#include "../decode.h" -#include "../decode-arm.h" +#include "decode.h" +#include "decode-arm.h" #include "core.h" static int uprobes_substitute_pc (unsigned long *pinsn,u32 oregs)
lib/probes/arm/decode-arm.c renamed from arch/arm/probes/decode-arm.c +0 -0
lib/probes/arm/decode-arm.h renamed from arch/arm/probes/decode-arm.h +0 -0
lib/probes/arm/decode.c renamed from arch/arm/probes/decode.c +0 -0
lib/probes/arm/decode.h renamed from arch/arm/probes/decode.h +0 -0
Signature · 1 line
2.7.4
PATCH 2/7 arm64: uprobes - fix checkpatch.pl issues
In reply to Maciej SlodczykFix checkpatch.pl issues in moved arm uprobes code.
--- lib/probes/arm/actions-arm.c | 4 ++-- lib/probes/arm/decode-arm.c | 1 + lib/probes/arm/decode-arm.h | 4 ++-- lib/probes/arm/decode.c | 6 ++++++ lib/probes/arm/decode.h | 7 +++++-- 5 files changed, 16 insertions(+), 6 deletions(-)
lib/probes/arm/actions-arm.c +2 -2
@@ -165,10 +165,10 @@ uprobe_decode_ldmstm(probes_opcode_t insn, { struct arch_uprobe *auprobe = container_of(asi, struct arch_uprobe, asi); -unsigned reglist = insn &0xffff ; +unsigned int reglist = insn &0xffff ;int rn = (insn >>16 ) &0xf ;int lbit = insn & (1 <<20 ); -unsigned used = reglist | (1 << rn); +unsigned int used = reglist | (1 << rn);if (rn ==15 )return INSN_REJECTED ;
lib/probes/arm/decode-arm.c +1 -0
@@ -101,6 +101,7 @@ void __kprobes simulate_mrs(probes_opcode_t insn, {int rd = (insn >>12 ) &0xf ;unsigned long mask =0xf8ff03df ;/* Mask out execution state */ + regs->uregs[rd] = regs->ARM_cpsr & mask; }
lib/probes/arm/decode-arm.h +2 -2
@@ -66,8 +66,8 @@ void __kprobes simulate_mov_ipsp(probes_opcode_t opcode,extern const union decode_item probes_decode_arm_table[]; -enum probes_insn arm_probes_decode_insn (probes_opcode_t , -struct arch_probes_insn *,bool emulate, +enum probes_insn arm_probes_decode_insn (probes_opcode_t opcode, +struct arch_probes_insn *api,bool emulate,const union decode_action *actions,const struct decode_checker *checkers[]);
lib/probes/arm/decode.c +6 -0
@@ -55,6 +55,7 @@ bool load_write_pc_interworks;void __init test_load_write_pc_interworking (void ) {int arch =cpu_architecture (); +BUG_ON (arch ==CPU_ARCH_UNKNOWN ); load_write_pc_interworks = arch >= CPU_ARCH_ARMv5T; }
@@ -69,6 +70,7 @@ bool alu_write_pc_interworks;void __init test_alu_write_pc_interworking (void ) {int arch =cpu_architecture (); +BUG_ON (arch ==CPU_ARCH_UNKNOWN ); alu_write_pc_interworks = arch >= CPU_ARCH_ARMv7; }
@@ -151,6 +153,7 @@ static unsigned long __kprobes __check_lt(unsigned long cpsr)static unsigned long __kprobes __check_gt (unsigned long cpsr) {unsigned long temp = cpsr ^ (cpsr <<3 );/* PSR_N_BIT ^= PSR_V_BIT */ + temp |= (cpsr <<1 );/* PSR_N_BIT |= PSR_Z_BIT */ return (~temp) &PSR_N_BIT ; }
@@ -158,6 +161,7 @@ static unsigned long __kprobes __check_gt(unsigned long cpsr)static unsigned long __kprobes __check_le (unsigned long cpsr) {unsigned long temp = cpsr ^ (cpsr <<3 );/* PSR_N_BIT ^= PSR_V_BIT */ + temp |= (cpsr <<1 );/* PSR_N_BIT |= PSR_Z_BIT */ return temp &PSR_N_BIT ; }
@@ -228,6 +232,7 @@ set_emulated_insn(probes_opcode_t insn, struct arch_probes_insn *asi,#ifdef CONFIG_THUMB2_KERNEL if (thumb) {u16 *ip = (u16 *)asi->insn; +if (is_wide_instruction (insn)) *ip++ =__opcode_to_mem_thumb16 (insn >>16 ); *ip++ =__opcode_to_mem_thumb16 (insn);
@@ -465,6 +470,7 @@ probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi,case DECODE_TYPE_TABLE : {struct decode_table *d = (struct decode_table *)h; + next = (struct decode_header *)d->table.table;break ; }
lib/probes/arm/decode.h +5 -2
@@ -59,6 +59,7 @@ static inline unsigned long it_advance(unsigned long cpsr)/* We need to shift left ITSTATE<4:0> */ const unsigned long mask =0x06001c00 ;/* Mask ITSTATE<4:0> */ unsigned long it = cpsr & mask; + it <<=1 ; it |= it >> (27 -10 );/* Carry ITSTATE<2> to correct place */ it &= mask;
@@ -71,6 +72,7 @@ static inline unsigned long it_advance(unsigned long cpsr)static inline void __kprobes bx_write_pc (long pcv,struct pt_regs *regs) {long cpsr = regs->ARM_cpsr; +if (pcv &0x1 ) { cpsr |=PSR_T_BIT ; pcv &= ~0x1 ;
@@ -253,7 +255,8 @@ static inline void __kprobes alu_write_pc(long pcv, struct pt_regs *regs)* instruction will be modified to "AND R0, R2, R3, ASL R1" and then placed into * the kprobes instruction slot. This can then be called later by the handler * function emulate_rd12rn16rm0rs8_rwflags (a pointer to which is retrieved from -* the indicated slot in the action array), in order to simulate the instruction. +* the indicated slot in the action array), in order to simulate the +* instruction. */ enum decode_type {
@@ -282,7 +285,7 @@ enum decode_reg_type {REG_TYPE_NOPCWB ,/* No PC if load/store write-back flag also set */ /* The following types are used when the encoding for PC indicates -* another instruction form. This distiction only matters for test +* another instruction form. This distinction only matters for test * case coverage checks. */ REG_TYPE_NOPCX, /* Register must not be PC */
Signature · 1 line
2.7.4
PATCH 3/7 arm64: introduce get_swbp_insn() instead of static assignment
In reply to Maciej SlodczykIn uprobes generic code, an arch specific software breakpoint instruction is statically assigned with a #define statement. It does not allow to examine the context and set the proper arch on runtime, which is the case of uprobing either a 32 or 64 bit app on a 64-bit kernel. Introduce get_swbp_insn() with weak attribute that will allow to be redefined in ARM64 uprobes code.
--- include/linux/uprobes.h | 1 + kernel/events/uprobes.c | 12 +++++++++++- 2 files changed, 12 insertions(+), 1 deletion(-)
include/linux/uprobes.h +1 -0
@@ -117,6 +117,7 @@ struct uprobes_state {extern int set_swbp (struct arch_uprobe *aup,struct mm_struct *mm,unsigned long vaddr);extern int set_orig_insn (struct arch_uprobe *aup,struct mm_struct *mm,unsigned long vaddr); +extern uprobe_opcode_t get_swbp_insn (void );extern bool is_swbp_insn (uprobe_opcode_t *insn);extern bool is_trap_insn (uprobe_opcode_t *insn);extern unsigned long uprobe_get_swbp_addr (struct pt_regs *regs);
kernel/events/uprobes.c +11 -1
@@ -216,6 +216,16 @@ static int __replace_page(struct vm_area_struct *vma, unsigned long addr, }/** +* get_swbp_insn - return breakpoint instruction. +* Default implementation of get_swbp_insn +* Returns architecture-dependent breakpoint instruction opcode +*/ +uprobe_opcode_t __weak get_swbp_insn (void ) +{ +return UPROBE_SWBP_INSN ; +} + +/** * is_swbp_insn - check if instruction is breakpoint instruction. * @insn: instruction to be checked. * Default implementation of is_swbp_insn
@@ -1178,7 +1188,7 @@ static int xol_add_vma(struct mm_struct *mm, struct xol_area *area)static struct xol_area *__create_xol_area (unsigned long vaddr) {struct mm_struct *mm = current->mm; -uprobe_opcode_t insn =UPROBE_SWBP_INSN ; +uprobe_opcode_t insn =get_swbp_insn ();struct xol_area *area; area =kmalloc (sizeof (*area),GFP_KERNEL );
Signature · 1 line
2.7.4
PATCH 4/7 arm64: change arm64 probes handler prototype
In reply to Maciej SlodczykA probes_handler_t() handler function prototype differ between ARM32 and ARM64 arch subtrees. Make ARM64 prototype the same as ARM32 prototype and adjust the ARM64 code to work with the new prototype.
--- arch/arm64/include/asm/probes.h | 6 +++++- arch/arm64/kernel/probes/kprobes.c | 2 +- arch/arm64/kernel/probes/simulate-insn.c | 32 ++++++++++++++++++++++++-------- arch/arm64/kernel/probes/simulate-insn.h | 24 ++++++++++++++++-------- arch/arm64/kernel/probes/uprobes.c | 3 +-- 5 files changed, 47 insertions(+), 20 deletions(-)
arch/arm64/include/asm/probes.h +5 -1
@@ -16,7 +16,11 @@#define _ARM_PROBES_Htypedef u32 probe_opcode_t ; -typedef void (probes_handler_t ) (u32 opcode,long addr,struct pt_regs *); +struct arch_probe_insn ; + +typedef void (probes_handler_t ) (u32 opcode, +struct arch_probe_insn *api, +struct pt_regs *);/* architecture specific copy of original instruction */ struct arch_probe_insn {
arch/arm64/kernel/probes/kprobes.c +1 -1
@@ -69,7 +69,7 @@ static void __kprobes arch_simulate_insn(struct kprobe *p, struct pt_regs *regs)struct kprobe_ctlblk *kcb =get_kprobe_ctlblk ();if (p->ainsn.api.handler) - p->ainsn.api.handler ((u32 )p->opcode, (long )p->addr, regs); + p->ainsn.api.handler ((u32 )p->opcode, &p->ainsn.api, regs);/* single step simulated, now go for post processing */ post_kprobe_handler (kcb, regs);
arch/arm64/kernel/probes/simulate-insn.c +24 -8
@@ -92,9 +92,11 @@ static bool __kprobes check_tbnz(u32 opcode, struct pt_regs *regs)* instruction simulation functions */ void __kprobes -simulate_adr_adrp (u32 opcode,long addr,struct pt_regs *regs) +simulate_adr_adrp (u32 opcode,struct arch_probe_insn *api, +struct pt_regs *regs) {long imm, xn, val; +long addr =instruction_pointer (regs); xn = opcode &0x1f ; imm = ((opcode >>3 ) &0x1ffffc ) | ((opcode >>29 ) &0x3 );
@@ -110,9 +112,11 @@ simulate_adr_adrp(u32 opcode, long addr, struct pt_regs *regs) }void __kprobes -simulate_b_bl (u32 opcode,long addr,struct pt_regs *regs) +simulate_b_bl (u32 opcode,struct arch_probe_insn *api, +struct pt_regs *regs) {int disp =bbl_displacement (opcode); +long addr =instruction_pointer (regs);/* Link register is x30 */ if (opcode & (1 <<31 ))
@@ -122,9 +126,11 @@ simulate_b_bl(u32 opcode, long addr, struct pt_regs *regs) }void __kprobes -simulate_b_cond (u32 opcode,long addr,struct pt_regs *regs) +simulate_b_cond (u32 opcode,struct arch_probe_insn *api, +struct pt_regs *regs) {int disp =4 ; +long addr =instruction_pointer (regs);if (aarch32_opcode_cond_checks[opcode &0xf ](regs->pstate &0xffffffff )) disp =bcond_displacement (opcode);
@@ -133,9 +139,11 @@ simulate_b_cond(u32 opcode, long addr, struct pt_regs *regs) }void __kprobes -simulate_br_blr_ret (u32 opcode,long addr,struct pt_regs *regs) +simulate_br_blr_ret (u32 opcode,struct arch_probe_insn *api, +struct pt_regs *regs) {int xn = (opcode >>5 ) &0x1f ; +long addr =instruction_pointer (regs);/* update pc first in case we're doing a "blr lr" */ instruction_pointer_set (regs,get_x_reg (regs, xn));
@@ -146,9 +154,11 @@ simulate_br_blr_ret(u32 opcode, long addr, struct pt_regs *regs) }void __kprobes -simulate_cbz_cbnz (u32 opcode,long addr,struct pt_regs *regs) +simulate_cbz_cbnz (u32 opcode,struct arch_probe_insn *api, +struct pt_regs *regs) {int disp =4 ; +long addr =instruction_pointer (regs);if (opcode & (1 <<24 )) {if (check_cbnz (opcode, regs))
@@ -161,9 +171,11 @@ simulate_cbz_cbnz(u32 opcode, long addr, struct pt_regs *regs) }void __kprobes -simulate_tbz_tbnz (u32 opcode,long addr,struct pt_regs *regs) +simulate_tbz_tbnz (u32 opcode,struct arch_probe_insn *api, +struct pt_regs *regs) {int disp =4 ; +long addr =instruction_pointer (regs);if (opcode & (1 <<24 )) {if (check_tbnz (opcode, regs))
@@ -176,11 +188,13 @@ simulate_tbz_tbnz(u32 opcode, long addr, struct pt_regs *regs) }void __kprobes -simulate_ldr_literal (u32 opcode,long addr,struct pt_regs *regs) +simulate_ldr_literal (u32 opcode,struct arch_probe_insn *api, +struct pt_regs *regs) {u64 *load_addr;int xn = opcode &0x1f ;int disp; +long addr =instruction_pointer (regs); disp =ldr_displacement (opcode); load_addr = (u64 *) (addr + disp);
@@ -194,11 +208,13 @@ simulate_ldr_literal(u32 opcode, long addr, struct pt_regs *regs) }void __kprobes -simulate_ldrsw_literal (u32 opcode,long addr,struct pt_regs *regs) +simulate_ldrsw_literal (u32 opcode,struct arch_probe_insn *api, +struct pt_regs *regs) {s32 *load_addr;int xn = opcode &0x1f ;int disp; +long addr =instruction_pointer (regs); disp =ldr_displacement (opcode); load_addr = (s32 *) (addr + disp);
arch/arm64/kernel/probes/simulate-insn.h +16 -8
@@ -16,13 +16,21 @@#ifndef _ARM_KERNEL_KPROBES_SIMULATE_INSN_H#define _ARM_KERNEL_KPROBES_SIMULATE_INSN_H -void simulate_adr_adrp (u32 opcode,long addr,struct pt_regs *regs); -void simulate_b_bl (u32 opcode,long addr,struct pt_regs *regs); -void simulate_b_cond (u32 opcode,long addr,struct pt_regs *regs); -void simulate_br_blr_ret (u32 opcode,long addr,struct pt_regs *regs); -void simulate_cbz_cbnz (u32 opcode,long addr,struct pt_regs *regs); -void simulate_tbz_tbnz (u32 opcode,long addr,struct pt_regs *regs); -void simulate_ldr_literal (u32 opcode,long addr,struct pt_regs *regs); -void simulate_ldrsw_literal (u32 opcode,long addr,struct pt_regs *regs); +void simulate_adr_adrp (u32 opcode,struct arch_probe_insn *api, +struct pt_regs *regs); +void simulate_b_bl (u32 opcode,struct arch_probe_insn *api, +struct pt_regs *regs); +void simulate_b_cond (u32 opcode,struct arch_probe_insn *api, +struct pt_regs *regs); +void simulate_br_blr_ret (u32 opcode,struct arch_probe_insn *api, +struct pt_regs *regs); +void simulate_cbz_cbnz (u32 opcode,struct arch_probe_insn *api, +struct pt_regs *regs); +void simulate_tbz_tbnz (u32 opcode,struct arch_probe_insn *api, +struct pt_regs *regs); +void simulate_ldr_literal (u32 opcode,struct arch_probe_insn *api, +struct pt_regs *regs); +void simulate_ldrsw_literal (u32 opcode,struct arch_probe_insn *api, +struct pt_regs *regs);#endif /* _ARM_KERNEL_KPROBES_SIMULATE_INSN_H */
arch/arm64/kernel/probes/uprobes.c +1 -2
@@ -112,10 +112,9 @@ bool arch_uprobe_skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs)return false; insn = *(probe_opcode_t *)(&auprobe->insn[0 ]); - addr =instruction_pointer (regs);if (auprobe->api.handler) - auprobe->api.handler (insn, addr, regs); + auprobe->api.handler (insn, &auprobe->api, regs);return true; }
Signature · 1 line
2.7.4
PATCH 5/7 arm64: make arm uprobes code reusable by arm64
In reply to Maciej SlodczykThere are many segments of ARM32 uprobes code that is very specific to 32-bit ARM arch and many differences between the two architectures that could be made portable (e.g. register numbers). Exclude the ARM32 specific code from ARM64 compilation process and make adjustments in ARM32 uprobes code to be accessible by ARM64 code.
--- arch/arm/include/asm/probes.h | 8 ++ arch/arm/include/asm/ptrace.h | 32 ++++++++ arch/arm/include/asm/uprobes.h | 2 +- arch/arm/probes/uprobes/core.c | 6 +- arch/arm64/include/asm/probes.h | 24 ++++-- arch/arm64/include/asm/ptrace.h | 21 +++++ arch/arm64/include/asm/uprobes.h | 21 ++++- arch/arm64/kernel/probes/Makefile | 2 + arch/arm64/kernel/probes/decode-insn.c | 28 +++---- arch/arm64/kernel/probes/decode-insn.h | 15 ++-- arch/arm64/kernel/probes/kprobes.c | 4 +- arch/arm64/kernel/probes/simulate-insn.c | 16 ++-- arch/arm64/kernel/probes/simulate-insn.h | 16 ++-- arch/arm64/kernel/probes/uprobes.c | 12 +-- lib/probes/Makefile | 1 + lib/probes/arm/Makefile | 5 ++ lib/probes/arm/actions-arm.c | 130 ++++++++++++++++++++++++++++--- lib/probes/arm/decode-arm.c | 47 ++++++----- lib/probes/arm/decode.c | 12 ++- lib/probes/arm/decode.h | 41 ++++++---- 20 files changed, 335 insertions(+), 108 deletions(-)
arch/arm/include/asm/probes.h +8 -0
@@ -21,6 +21,13 @@#ifndef __ASSEMBLY__ +enum probes_insn { +INSN_REJECTED , +INSN_GOOD_NO_SLOT , +INSN_GOOD , +}; + +typedef u32 probes_opcode_t ;struct arch_probes_insn ;
@@ -45,6 +52,7 @@ struct arch_probes_insn { bool kprobe_direct_exec; }; +extern probes_check_cc *const probes_condition_checks[];#endif /* __ASSEMBLY__ */ /*
arch/arm/include/asm/ptrace.h +32 -0
@@ -93,6 +93,8 @@ static inline long regs_return_value(struct pt_regs *regs) }#define instruction_pointer (regs) (regs)->ARM_pc +#define link_register (regs) ((regs)->ARM_lr) +#define state_register (regs) ((regs)->ARM_cpsr)#ifdef CONFIG_THUMB2_KERNEL #define frame_pointer (regs) (regs)->ARM_r7
@@ -106,6 +108,35 @@ static inline void instruction_pointer_set(struct pt_regs *regs,instruction_pointer (regs) = val; } +static inline void link_register_set (struct pt_regs *regs, +unsigned long val) +{ +link_register (regs) = val; +} + +static inline void state_register_set (struct pt_regs *regs, +unsigned long val) +{ +state_register (regs) = val; +} + +/* +* Read a register given an architectural register index r. +*/ +static inline unsigned long pt_regs_read_reg (const struct pt_regs *regs,int r) +{ +return regs->uregs[r]; +} + +/* +* Write a register given an architectural register index r. +*/ +static inline void pt_regs_write_reg (struct pt_regs *regs,int r, +unsigned long val) +{ + regs->uregs[r] = val; +} +#ifdef CONFIG_SMP extern unsigned long profile_pc (struct pt_regs *regs);#else
@@ -167,5 +198,6 @@ static inline unsigned long user_stack_pointer(struct pt_regs *regs) ((current_stack_pointer | (THREAD_SIZE -1 )) -7 ) -1 ; \ }) +#define ARM_COMPAT_LR_OFFSET 0#endif /* __ASSEMBLY__ */ #endif
arch/arm/include/asm/uprobes.h +1 -1
@@ -39,7 +39,7 @@ struct arch_uprobe {void (*posthandler)(struct arch_uprobe *auprobe,struct arch_uprobe_task *autask,struct pt_regs *regs); - struct arch_probes_insn asi; + struct arch_probes_insn api; };#endif
arch/arm/probes/uprobes/core.c +3 -3
@@ -38,7 +38,7 @@ int set_swbp(struct arch_uprobe *auprobe, struct mm_struct *mm,bool arch_uprobe_ignore (struct arch_uprobe *auprobe,struct pt_regs *regs) { -if (!auprobe->asi.insn_check_cc (regs->ARM_cpsr)) { +if (!auprobe->api.insn_check_cc (regs->ARM_cpsr)) { regs->ARM_pc +=4 ;return true; }
@@ -55,7 +55,7 @@ bool arch_uprobe_skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs) opcode =__mem_to_opcode_arm (*(unsigned int *) auprobe->insn); - auprobe->asi.insn_singlestep (opcode, &auprobe->asi, regs); + auprobe->api.insn_singlestep (opcode, &auprobe->api, regs);return true; }
@@ -87,7 +87,7 @@ int arch_uprobe_analyze_insn(struct arch_uprobe *auprobe, struct mm_struct *mm, auprobe->ixol[0 ] =__opcode_to_mem_arm (insn); auprobe->ixol[1 ] =__opcode_to_mem_arm (UPROBE_SS_ARM_INSN ); - ret =arm_probes_decode_insn (insn, &auprobe->asi, false, + ret =arm_probes_decode_insn (insn, &auprobe->api, false, uprobes_probes_actions,NULL );switch (ret) {case INSN_REJECTED :
arch/arm64/include/asm/probes.h +16 -8
@@ -15,25 +15,33 @@#ifndef _ARM_PROBES_H#define _ARM_PROBES_H -typedef u32 probe_opcode_t ; -struct arch_probe_insn ; +enum probes_insn { +INSN_REJECTED , +INSN_GOOD_NO_SLOT , +INSN_GOOD , +}; + +typedef u32 probes_opcode_t ; +struct arch_probes_insn ; -typedef void (probes_handler_t ) (u32 opcode, -struct arch_probe_insn *api, +typedef void (probes_insn_handler_t ) (u32 opcode, +struct arch_probes_insn *api,struct pt_regs *); +typedef unsigned long (probes_check_cc )(unsigned long ); +/* architecture specific copy of original instruction */ -struct arch_probe_insn { -probe_opcode_t *insn; +struct arch_probes_insn { +probes_opcode_t *insn;pstate_check_t *pstate_cc; -probes_handler_t *handler; +probes_insn_handler_t *insn_handler;/* restore address after step xol */ unsigned long restore; };#ifdef CONFIG_KPROBES typedef u32 kprobe_opcode_t ;struct arch_specific_insn { -struct arch_probe_insn api; +struct arch_probes_insn api; };#endif
arch/arm64/include/asm/ptrace.h +21 -0
@@ -274,8 +274,29 @@ static inline void procedure_link_pointer_set(struct pt_regs *regs,procedure_link_pointer (regs) = val; } + +#define link_register (regs) ((regs)->compat_lr) + +static inline void link_register_set (struct pt_regs *regs, +unsigned long val) +{ +link_register (regs) = val; +} + +#define state_register (regs) ((regs)->pstate) + +static inline void state_register_set (struct pt_regs *regs, +unsigned long val) +{ +state_register (regs) = val; +} + +#undef profile_pcextern unsigned long profile_pc (struct pt_regs *regs); + +#define ARM_COMPAT_LR_OFFSET 4 +#endif /* __ASSEMBLY__ */ #endif
arch/arm64/include/asm/uprobes.h +20 -1
@@ -22,6 +22,12 @@typedef u32 uprobe_opcode_t ;struct arch_uprobe_task { +u64 backup; +}; + +enum uprobe_arch { +UPROBE_AARCH64 , +UPROBE_AARCH32 };struct arch_uprobe {
@@ -29,8 +35,21 @@ struct arch_uprobe {u8 insn[MAX_UINSN_BYTES ]; u8 ixol[MAX_UINSN_BYTES]; }; -struct arch_probe_insn api; + +probes_opcode_t orig_insn; +probes_opcode_t bp_insn; + +struct arch_probes_insn api;bool simulate; +u64 pcreg; +enum uprobe_arch arch; + +void (*prehandler)(struct arch_uprobe *auprobe, +struct arch_uprobe_task *autask, +struct pt_regs *regs); +void (*posthandler)(struct arch_uprobe *auprobe, +struct arch_uprobe_task *autask, +struct pt_regs *regs); };#endif
arch/arm64/kernel/probes/Makefile +2 -0
@@ -1,4 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 +ccflags-y += -I$(srctree)/lib/probes/arm/ +ccflags-y += -I$(srctree)/arch/arm64/kernel/probes/ obj-$(CONFIG_KPROBES) += kprobes.o decode-insn.o \ kprobes_trampoline.o \ simulate-insn.o
arch/arm64/kernel/probes/decode-insn.c +14 -14
@@ -77,8 +77,8 @@ static bool __kprobes aarch64_insn_is_steppable(u32 insn)* INSN_GOOD If instruction is supported and uses instruction slot, * INSN_GOOD_NO_SLOT If instruction is supported but doesn't use its slot. */ -enum probe_insn __kprobes -arm_probe_decode_insn (probe_opcode_t insn,struct arch_probe_insn *api) +enum probes_insn __kprobes +arm_probe_decode_insn (probes_opcode_t insn,struct arch_probes_insn *api) {/* * Instructions reading or modifying the PC won't work from the XOL
@@ -88,26 +88,26 @@ arm_probe_decode_insn(probe_opcode_t insn, struct arch_probe_insn *api)return INSN_GOOD ;if (aarch64_insn_is_bcond (insn)) { - api->handler = simulate_b_cond; + api->insn_handler = simulate_b_cond; }else if (aarch64_insn_is_cbz (insn) ||aarch64_insn_is_cbnz (insn)) { - api->handler = simulate_cbz_cbnz; + api->insn_handler = simulate_cbz_cbnz; }else if (aarch64_insn_is_tbz (insn) ||aarch64_insn_is_tbnz (insn)) { - api->handler = simulate_tbz_tbnz; + api->insn_handler = simulate_tbz_tbnz; }else if (aarch64_insn_is_adr_adrp (insn)) { - api->handler = simulate_adr_adrp; + api->insn_handler = simulate_adr_adrp; }else if (aarch64_insn_is_b (insn) ||aarch64_insn_is_bl (insn)) { - api->handler = simulate_b_bl; + api->insn_handler = simulate_b_bl; }else if (aarch64_insn_is_br (insn) ||aarch64_insn_is_blr (insn) ||aarch64_insn_is_ret (insn)) { - api->handler = simulate_br_blr_ret; + api->insn_handler = simulate_br_blr_ret; }else if (aarch64_insn_is_ldr_lit (insn)) { - api->handler = simulate_ldr_literal; + api->insn_handler = simulate_ldr_literal; }else if (aarch64_insn_is_ldrsw_lit (insn)) { - api->handler = simulate_ldrsw_literal; + api->insn_handler = simulate_ldrsw_literal; }else {/* * Instruction cannot be stepped out-of-line and we don't
@@ -138,12 +138,12 @@ is_probed_address_atomic(kprobe_opcode_t *scan_start, kprobe_opcode_t *scan_end)return false; } -enum probe_insn __kprobes +enum probes_insn __kprobes arm_kprobe_decode_insn (kprobe_opcode_t *addr,struct arch_specific_insn *asi) { -enum probe_insn decoded; -probe_opcode_t insn =le32_to_cpu (*addr); -probe_opcode_t *scan_end =NULL ; +enum probes_insn decoded; +probes_opcode_t insn =le32_to_cpu (*addr); +probes_opcode_t *scan_end =NULL ;unsigned long size =0 , offset =0 ;/*
arch/arm64/kernel/probes/decode-insn.h +6 -9
@@ -16,7 +16,9 @@#ifndef _ARM_KERNEL_KPROBES_ARM64_H#define _ARM_KERNEL_KPROBES_ARM64_H +#include <asm/probes.h> #include <asm/kprobes.h> +#include "decode.h" /* * ARM strongly recommends a limit of 128 bytes between LoadExcl and
@@ -25,17 +27,12 @@*/ #define MAX_ATOMIC_CONTEXT_SIZE (128 / sizeof(kprobe_opcode_t)) -enum probe_insn { -INSN_REJECTED , -INSN_GOOD_NO_SLOT , -INSN_GOOD , -}; -#ifdef CONFIG_KPROBES -enum probe_insn __kprobes +enum probes_insn __kprobes arm_kprobe_decode_insn (kprobe_opcode_t *addr,struct arch_specific_insn *asi);#endif -enum probe_insn __kprobes -arm_probe_decode_insn (probe_opcode_t insn,struct arch_probe_insn *asi); +enum probes_insn __kprobes +arm_probe_decode_insn (probes_opcode_t insn,struct arch_probes_insn *asi); +extern const union decode_action uprobes_probes_actions[];#endif /* _ARM_KERNEL_KPROBES_ARM64_H */
arch/arm64/kernel/probes/kprobes.c +2 -2
@@ -68,8 +68,8 @@ static void __kprobes arch_simulate_insn(struct kprobe *p, struct pt_regs *regs) {struct kprobe_ctlblk *kcb =get_kprobe_ctlblk (); -if (p->ainsn.api.handler) - p->ainsn.api.handler ((u32 )p->opcode, &p->ainsn.api, regs); +if (p->ainsn.api.insn_handler) + p->ainsn.api.insn_handler ((u32 )p->opcode, &p->ainsn.api, regs);/* single step simulated, now go for post processing */ post_kprobe_handler (kcb, regs);
arch/arm64/kernel/probes/simulate-insn.c +8 -8
@@ -92,7 +92,7 @@ static bool __kprobes check_tbnz(u32 opcode, struct pt_regs *regs)* instruction simulation functions */ void __kprobes -simulate_adr_adrp (u32 opcode,struct arch_probe_insn *api, +simulate_adr_adrp (u32 opcode,struct arch_probes_insn *api,struct pt_regs *regs) {long imm, xn, val;
@@ -112,7 +112,7 @@ simulate_adr_adrp(u32 opcode, struct arch_probe_insn *api, }void __kprobes -simulate_b_bl (u32 opcode,struct arch_probe_insn *api, +simulate_b_bl (u32 opcode,struct arch_probes_insn *api,struct pt_regs *regs) {int disp =bbl_displacement (opcode);
@@ -126,7 +126,7 @@ simulate_b_bl(u32 opcode, struct arch_probe_insn *api, }void __kprobes -simulate_b_cond (u32 opcode,struct arch_probe_insn *api, +simulate_b_cond (u32 opcode,struct arch_probes_insn *api,struct pt_regs *regs) {int disp =4 ;
@@ -139,7 +139,7 @@ simulate_b_cond(u32 opcode, struct arch_probe_insn *api, }void __kprobes -simulate_br_blr_ret (u32 opcode,struct arch_probe_insn *api, +simulate_br_blr_ret (u32 opcode,struct arch_probes_insn *api,struct pt_regs *regs) {int xn = (opcode >>5 ) &0x1f ;
@@ -154,7 +154,7 @@ simulate_br_blr_ret(u32 opcode, struct arch_probe_insn *api, }void __kprobes -simulate_cbz_cbnz (u32 opcode,struct arch_probe_insn *api, +simulate_cbz_cbnz (u32 opcode,struct arch_probes_insn *api,struct pt_regs *regs) {int disp =4 ;
@@ -171,7 +171,7 @@ simulate_cbz_cbnz(u32 opcode, struct arch_probe_insn *api, }void __kprobes -simulate_tbz_tbnz (u32 opcode,struct arch_probe_insn *api, +simulate_tbz_tbnz (u32 opcode,struct arch_probes_insn *api,struct pt_regs *regs) {int disp =4 ;
@@ -188,7 +188,7 @@ simulate_tbz_tbnz(u32 opcode, struct arch_probe_insn *api, }void __kprobes -simulate_ldr_literal (u32 opcode,struct arch_probe_insn *api, +simulate_ldr_literal (u32 opcode,struct arch_probes_insn *api,struct pt_regs *regs) {u64 *load_addr;
@@ -208,7 +208,7 @@ simulate_ldr_literal(u32 opcode, struct arch_probe_insn *api, }void __kprobes -simulate_ldrsw_literal (u32 opcode,struct arch_probe_insn *api, +simulate_ldrsw_literal (u32 opcode,struct arch_probes_insn *api,struct pt_regs *regs) {s32 *load_addr;
arch/arm64/kernel/probes/simulate-insn.h +8 -8
@@ -16,21 +16,21 @@#ifndef _ARM_KERNEL_KPROBES_SIMULATE_INSN_H#define _ARM_KERNEL_KPROBES_SIMULATE_INSN_H -void simulate_adr_adrp (u32 opcode,struct arch_probe_insn *api, +void simulate_adr_adrp (u32 opcode,struct arch_probes_insn *api,struct pt_regs *regs); -void simulate_b_bl (u32 opcode,struct arch_probe_insn *api, +void simulate_b_bl (u32 opcode,struct arch_probes_insn *api,struct pt_regs *regs); -void simulate_b_cond (u32 opcode,struct arch_probe_insn *api, +void simulate_b_cond (u32 opcode,struct arch_probes_insn *api,struct pt_regs *regs); -void simulate_br_blr_ret (u32 opcode,struct arch_probe_insn *api, +void simulate_br_blr_ret (u32 opcode,struct arch_probes_insn *api,struct pt_regs *regs); -void simulate_cbz_cbnz (u32 opcode,struct arch_probe_insn *api, +void simulate_cbz_cbnz (u32 opcode,struct arch_probes_insn *api,struct pt_regs *regs); -void simulate_tbz_tbnz (u32 opcode,struct arch_probe_insn *api, +void simulate_tbz_tbnz (u32 opcode,struct arch_probes_insn *api,struct pt_regs *regs); -void simulate_ldr_literal (u32 opcode,struct arch_probe_insn *api, +void simulate_ldr_literal (u32 opcode,struct arch_probes_insn *api,struct pt_regs *regs); -void simulate_ldrsw_literal (u32 opcode,struct arch_probe_insn *api, +void simulate_ldrsw_literal (u32 opcode,struct arch_probes_insn *api,struct pt_regs *regs);#endif /* _ARM_KERNEL_KPROBES_SIMULATE_INSN_H */
arch/arm64/kernel/probes/uprobes.c +6 -6
@@ -37,7 +37,7 @@ unsigned long uprobe_get_swbp_addr(struct pt_regs *regs)int arch_uprobe_analyze_insn (struct arch_uprobe *auprobe,struct mm_struct *mm,unsigned long addr) { -probe_opcode_t insn; +probes_opcode_t insn;/* TODO: Currently we do not support AARCH32 instruction probing */ if (mm->context.flags &MMCF_AARCH32 )
@@ -45,7 +45,7 @@ int arch_uprobe_analyze_insn(struct arch_uprobe *auprobe, struct mm_struct *mm,else if (!IS_ALIGNED (addr,AARCH64_INSN_SIZE ))return -EINVAL ; - insn = *(probe_opcode_t *)(&auprobe->insn[0 ]); + insn = *(probes_opcode_t *)(&auprobe->insn[0 ]);switch (arm_probe_decode_insn (insn, &auprobe->api)) {case INSN_REJECTED :
@@ -105,16 +105,16 @@ bool arch_uprobe_xol_was_trapped(struct task_struct *t)bool arch_uprobe_skip_sstep (struct arch_uprobe *auprobe,struct pt_regs *regs) { -probe_opcode_t insn; +probes_opcode_t insn;unsigned long addr;if (!auprobe->simulate)return false; - insn = *(probe_opcode_t *)(&auprobe->insn[0 ]); + insn = *(probes_opcode_t *)(&auprobe->insn[0 ]); -if (auprobe->api.handler) - auprobe->api.handler (insn, &auprobe->api, regs); +if (auprobe->api.insn_handler) + auprobe->api.insn_handler (insn, &auprobe->api, regs);return true; }
lib/probes/Makefile +1 -0
@@ -1 +1,2 @@ obj-$(CONFIG_ARM) += arm/ +obj-$(CONFIG_ARM64) += arm/
lib/probes/arm/Makefile +5 -0
@@ -1,4 +1,9 @@ +ccflags-y += -I$(srctree)/lib/uprobes/arm/ +ifdef CONFIG_ARM64 +ccflags-y += -I$(srctree)/arch/arm64/kernel/probes/ +else ccflags-y += -I$(srctree)/arch/arm/probes/uprobes/ +endif obj-$(CONFIG_UPROBES) += decode.o decode-arm.o actions-arm.o obj-$(CONFIG_KPROBES) += decode.o ifndef CONFIG_THUMB2_KERNEL
lib/probes/arm/actions-arm.c +118 -12
193 lines not on this page · raw message
lib/probes/arm/decode-arm.c +28 -19
108 lines not on this page · raw message
lib/probes/arm/decode.c +10 -2
@@ -13,13 +13,18 @@#include <linux/kernel.h> #include <linux/types.h> +#ifdef CONFIG_ARM64 +#include <asm/insn.h> +#include <../../../arm/include/asm/opcodes.h> +#else /* CONFIG_ARM64 */ #include <asm/system_info.h> #include <asm/ptrace.h> +#endif /* CONFIG_ARM64 */ #include <linux/bug.h> -#include "decode.h" +#ifndef find_str_pc_offset/*
@@ -189,7 +194,9 @@ void __kprobes probes_emulate_none(probes_opcode_t opcode,struct arch_probes_insn *asi, struct pt_regs *regs) { +#ifndef CONFIG_ARM64 asi->insn_fn(); +#endif /* CONFIG_ARM64 */ }/*
@@ -430,6 +437,7 @@ probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi,*/ probes_opcode_t origin_insn = insn; +#ifndef CONFIG_ARM64 /* * stack_space is initialized to 0 here. Checker functions * should update is value if they find this is a stack store
@@ -446,7 +454,7 @@ probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi,* registers are used', to prevent any potential optimization. */ asi->register_usage_flags = ~0UL ; - +#endif /* CONFIG_ARM64 */ if (emulate) insn =prepare_emulated_insn (insn, asi, thumb);
lib/probes/arm/decode.h +26 -15
104 lines not on this page · raw message
Signature · 1 line
2.7.4
PATCH 6/7 arm64: change arm_probe_decode_insn() function name
In reply to Maciej SlodczykARM probe decoding function has similar name in arm32 - arm_probes_decode_insn(), and arm64 - arm_probe_decode_insn(). Change arm64 probes decoding function name from arm_probe_decode_insn() to arm64_probes_decode_insn() to minimize the risk of confusion.
--- arch/arm64/kernel/probes/decode-insn.c | 4 ++-- arch/arm64/kernel/probes/decode-insn.h | 2 +- arch/arm64/kernel/probes/uprobes.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-)
arch/arm64/kernel/probes/decode-insn.c +2 -2
@@ -78,7 +78,7 @@ static bool __kprobes aarch64_insn_is_steppable(u32 insn)* INSN_GOOD_NO_SLOT If instruction is supported but doesn't use its slot. */ enum probes_insn __kprobes -arm_probe_decode_insn (probes_opcode_t insn,struct arch_probes_insn *api) +arm64_probes_decode_insn (probes_opcode_t insn,struct arch_probes_insn *api) {/* * Instructions reading or modifying the PC won't work from the XOL
@@ -162,7 +162,7 @@ arm_kprobe_decode_insn(kprobe_opcode_t *addr, struct arch_specific_insn *asi)else scan_end = addr -MAX_ATOMIC_CONTEXT_SIZE ; } - decoded =arm_probe_decode_insn (insn, &asi->api); + decoded =arm64_probes_decode_insn (insn, &asi->api);if (decoded !=INSN_REJECTED && scan_end)if (is_probed_address_atomic (addr -1 , scan_end))
arch/arm64/kernel/probes/decode-insn.h +1 -1
@@ -32,7 +32,7 @@ enum probes_insn __kprobes arm_kprobe_decode_insn(kprobe_opcode_t *addr, struct arch_specific_insn *asi);#endif enum probes_insn __kprobes -arm_probe_decode_insn (probes_opcode_t insn,struct arch_probes_insn *asi); +arm64_probes_decode_insn (probes_opcode_t insn,struct arch_probes_insn *asi);extern const union decode_action uprobes_probes_actions[];#endif /* _ARM_KERNEL_KPROBES_ARM64_H */
arch/arm64/kernel/probes/uprobes.c +1 -1
@@ -47,7 +47,7 @@ int arch_uprobe_analyze_insn(struct arch_uprobe *auprobe, struct mm_struct *mm, insn = *(probes_opcode_t *)(&auprobe->insn[0 ]); -switch (arm_probe_decode_insn (insn, &auprobe->api)) { +switch (arm64_probes_decode_insn (insn, &auprobe->api)) {case INSN_REJECTED :return -EINVAL ;
Signature · 1 line
2.7.4
PATCH 7/7 arm64: uprobes - ARM32 instruction probing
In reply to Maciej SlodczykDetect what kind of instruction is being probed and depending on the result:
- if an A64 instruction handle it the old way, using existing A64 instructions probing code,
- if an A32 instruction decode it and handle using the new code, moved from 32 bit arm kernel tree.
--- arch/arm64/kernel/debug-monitors.c | 8 +++ arch/arm64/kernel/probes/uprobes.c | 110 ++++++++++++++++++++++++++++++++++--- kernel/events/uprobes.c | 3 + lib/probes/arm/actions-arm.c | 35 ++++++++---- lib/probes/arm/decode-arm.c | 15 ++++- 5 files changed, 149 insertions(+), 22 deletions(-)
arch/arm64/kernel/debug-monitors.c +8 -0
@@ -364,6 +364,14 @@ int aarch32_break_handler(struct pt_regs *regs)if (!bp)return -EFAULT ; +/* +* Since bp != false, a sofware breakpoint instruction is being handled. +* If in user mode (compat_user_mode() few lines above), +* try to handle it by an uprobe handler, if registered. +*/ +if (!brk_handler ((unsigned long )pc,BRK64_ESR_UPROBES , regs)) +return 0 ; +send_user_sigtrap (TRAP_BRKPT );return 0 ; }
arch/arm64/kernel/probes/uprobes.c +101 -9
157 lines not on this page · raw message
kernel/events/uprobes.c +3 -0
@@ -1566,6 +1566,9 @@ static void prepare_uretprobe(struct uprobe *uprobe, struct pt_regs *regs)unsigned long orig_ret_vaddr, trampoline_vaddr;bool chained; +if (!current->mm) +return ; +if (!get_xol_area ())return ;
lib/probes/arm/actions-arm.c +25 -10
@@ -15,10 +15,7 @@#include "decode.h" #include "decode-arm.h" - -#ifdef CONFIG_ARM64 #include <../../../arm/include/asm/opcodes.h> -#endif /* CONFIG_ARM64 */ static int uprobes_substitute_pc (unsigned long *pinsn,u32 oregs) {
@@ -75,8 +72,13 @@ static void uprobe_set_pc(struct arch_uprobe *auprobe, {u32 pcreg = auprobe->pcreg; - autask->backup =pt_regs_read_reg (regs, pcreg); -pt_regs_write_reg (regs, pcreg,instruction_pointer (regs) +8 ); +if (pcreg ==15 ) { + autask->backup =instruction_pointer (regs); +instruction_pointer_set (regs,instruction_pointer (regs) +8 ); + }else { + autask->backup =pt_regs_read_reg (regs, pcreg); +pt_regs_write_reg (regs, pcreg,instruction_pointer (regs) +8 ); + } } static void uprobe_unset_pc(struct arch_uprobe *auprobe,
@@ -84,7 +86,10 @@ static void uprobe_unset_pc(struct arch_uprobe *auprobe, struct pt_regs *regs) {/* PC will be taken care of by common code */ -pt_regs_write_reg (regs, auprobe->pcreg, autask->backup); +if (auprobe->pcreg ==15 ) +instruction_pointer_set (regs, autask->backup); +else +pt_regs_write_reg (regs, auprobe->pcreg, autask->backup); } static void uprobe_aluwrite_pc(struct arch_uprobe *auprobe,
@@ -93,8 +98,13 @@ static void uprobe_aluwrite_pc(struct arch_uprobe *auprobe, {u32 pcreg = auprobe->pcreg; -alu_write_pc (pt_regs_read_reg (regs, pcreg), regs); -pt_regs_write_reg (regs, pcreg, autask->backup); +if (pcreg ==15 ) { +alu_write_pc (instruction_pointer (regs), regs); +instruction_pointer_set (regs, autask->backup); + }else { +alu_write_pc (pt_regs_read_reg (regs, pcreg), regs); +pt_regs_write_reg (regs, pcreg, autask->backup); + } } static void uprobe_write_pc(struct arch_uprobe *auprobe,
@@ -103,8 +113,13 @@ static void uprobe_write_pc(struct arch_uprobe *auprobe, {u32 pcreg = auprobe->pcreg; -load_write_pc (pt_regs_read_reg (regs, pcreg), regs); -pt_regs_write_reg (regs, pcreg, autask->backup); +if (pcreg ==15 ) { +load_write_pc (instruction_pointer (regs), regs); +instruction_pointer_set (regs, autask->backup); + }else { +load_write_pc (pt_regs_read_reg (regs, pcreg), regs); +pt_regs_write_reg (regs, pcreg, autask->backup); + } }enum probes_insn
lib/probes/arm/decode-arm.c +12 -3
@@ -87,11 +87,17 @@ void __kprobes simulate_blx2bx(probes_opcode_t insn, struct arch_probes_insn *asi, struct pt_regs *regs) { int rm = insn & 0xf; -long rmv =pt_regs_read_reg (regs, rm); +long rmv;long cpsr; +if (rm ==15 ) + rmv = (long )instruction_pointer (regs); +else + rmv =pt_regs_read_reg (regs, rm); +if (insn & (1 <<5 )) -link_register_set (regs, (long )instruction_pointer (regs)); +link_register_set (regs, + (long )instruction_pointer (regs) +ARM_COMPAT_LR_OFFSET );instruction_pointer_set (regs, rmv & ~0x1 ); cpsr =state_register (regs) & ~PSR_T_BIT ;
@@ -108,7 +114,10 @@ void __kprobes simulate_mrs(probes_opcode_t insn,int rd = (insn >>12 ) &0xf ;unsigned long mask =0xf8ff03df ;/* Mask out execution state */ -pt_regs_write_reg (regs, rd,state_register (regs) & mask); +if (rd ==15 ) +instruction_pointer_set (regs,state_register (regs) & mask); +else +pt_regs_write_reg (regs, rd,state_register (regs) & mask); } void __kprobes simulate_mov_ipsp(probes_opcode_t insn,
Signature · 1 line
2.7.4
Oleg Nesterov
maintainerPATCH 7/7 arm64: uprobes - ARM32 instruction probing
Quoted 6 lines · Maciej Slodczyk
Detect what kind of instruction is being probed and depending on the result:
- if an A64 instruction handle it the old way, using existing A64 instructions probing code,
- if an A32 instruction decode it and handle using the new code, moved from 32 bit arm kernel tree.
how this connects to the change in prepare_uretprobe(),
kernel/events/uprobes.c static void prepare_uretprobe(struct uprobe *uprobe, struct pt_regs *regs) · line 1571
Quoted 9 lines
kernel/events/uprobes.c +3
-0@@ -1566,6 +1566,9 @@ static void prepare_uretprobe(struct uprobe *uprobe, struct pt_regs *regs)unsigned long orig_ret_vaddr, trampoline_vaddr;bool chained; +if (!current->mm) +return ; +
?
and how it is possible to hit ->mm == NULL here?
Oleg.
PATCH 7/7 arm64: uprobes - ARM32 instruction probing
Quoted 16 lines · Oleg Nesterov
Quoted 6 lines · Maciej Slodczyk
Detect what kind of instruction is being probed and depending on the result:
- if an A64 instruction handle it the old way, using existing A64 instructions probing code,
- if an A32 instruction decode it and handle using the new code, moved from 32 bit arm kernel tree.how this connects to the change in
prepare_uretprobe(),Quoted 6 lines
kernel/events/uprobes.c +0
-0@@ -1566,6 +1566,9 @@ static void prepare_uretprobe(struct uprobe *uprobe, struct pt_regs *regs)unsigned long orig_ret_vaddr, trampoline_vaddr;bool chained;
Quoted 8 lines
kernel/events/uprobes.c static void prepare_uretprobe(struct uprobe *uprobe, struct pt_regs *regs) · line 1571
Quoted 3 lines
kernel/events/uprobes.c +3
-0+if (!current->mm) +return ; +?
and how it is possible to hit ->mm == NULL here?
That's obviously something that shouldn't have been left here. NULL was there because of other bug that's been fixed. I put it for testing only and for some reason forgot to remove. Thanks for pointing that out!
Regards,
Maciej