cregit-Linux how code gets into the kernel

Release 4.11 arch/arm/probes/kprobes/actions-arm.c

/*
 * arch/arm/probes/kprobes/actions-arm.c
 *
 * Copyright (C) 2006, 2007 Motorola Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 */

/*
 * We do not have hardware single-stepping on ARM, This
 * effort is further complicated by the ARM not having a
 * "next PC" register.  Instructions that change the PC
 * can't be safely single-stepped in a MP environment, so
 * we have a lot of work to do:
 *
 * In the prepare phase:
 *   *) If it is an instruction that does anything
 *      with the CPU mode, we reject it for a kprobe.
 *      (This is out of laziness rather than need.  The
 *      instructions could be simulated.)
 *
 *   *) Otherwise, decode the instruction rewriting its
 *      registers to take fixed, ordered registers and
 *      setting a handler for it to run the instruction.
 *
 * In the execution phase by an instruction's handler:
 *
 *   *) If the PC is written to by the instruction, the
 *      instruction must be fully simulated in software.
 *
 *   *) Otherwise, a modified form of the instruction is
 *      directly executed.  Its handler calls the
 *      instruction in insn[0].  In insn[1] is a
 *      "mov pc, lr" to return.
 *
 *      Before calling, load up the reordered registers
 *      from the original instruction's registers.  If one
 *      of the original input registers is the PC, compute
 *      and adjust the appropriate input register.
 *
 *      After call completes, copy the output registers to
 *      the original instruction's original registers.
 *
 * We don't use a real breakpoint instruction since that
 * would have us in the kernel go from SVC mode to SVC
 * mode losing the link register.  Instead we use an
 * undefined instruction.  To simplify processing, the
 * undefined instruction used for kprobes must be reserved
 * exclusively for kprobes use.
 *
 * TODO: ifdef out some instruction decoding based on architecture.
 */

#include <linux/kernel.h>
#include <linux/kprobes.h>
#include <linux/ptrace.h>

#include "../decode-arm.h"
#include "core.h"
#include "checkers.h"

#if  __LINUX_ARM_ARCH__ >= 6

#define BLX(reg)	"blx    "reg"           \n\t"
#else

#define BLX(reg)	"mov    lr, pc          \n\t"   \
                        "mov    pc, "reg"       \n\t"
#endif


static void __kprobes emulate_ldrdstrd(probes_opcode_t insn, struct arch_probes_insn *asi, struct pt_regs *regs) { unsigned long pc = regs->ARM_pc + 4; int rt = (insn >> 12) & 0xf; int rn = (insn >> 16) & 0xf; int rm = insn & 0xf; register unsigned long rtv asm("r0") = regs->uregs[rt]; register unsigned long rt2v asm("r1") = regs->uregs[rt+1]; register unsigned long rnv asm("r2") = (rn == 15) ? pc : regs->uregs[rn]; register unsigned long rmv asm("r3") = regs->uregs[rm]; __asm__ __volatile__ ( BLX("%[fn]") : "=r" (rtv), "=r" (rt2v), "=r" (rnv) : "0" (rtv), "1" (rt2v), "2" (rnv), "r" (rmv), [fn] "r" (asi->insn_fn) : "lr", "memory", "cc" ); regs->uregs[rt] = rtv; regs->uregs[rt+1] = rt2v; if (is_writeback(insn)) regs->uregs[rn] = rnv; }

Contributors

PersonTokensPropCommitsCommitProp
David A. Long10862.79%571.43%
Jon Medhurst (Tixy)4928.49%114.29%
Quentin Barnes158.72%114.29%
Total172100.00%7100.00%


static void __kprobes emulate_ldr(probes_opcode_t insn, struct arch_probes_insn *asi, struct pt_regs *regs) { unsigned long pc = regs->ARM_pc + 4; int rt = (insn >> 12) & 0xf; int rn = (insn >> 16) & 0xf; int rm = insn & 0xf; register unsigned long rtv asm("r0"); register unsigned long rnv asm("r2") = (rn == 15) ? pc : regs->uregs[rn]; register unsigned long rmv asm("r3") = regs->uregs[rm]; __asm__ __volatile__ ( BLX("%[fn]") : "=r" (rtv), "=r" (rnv) : "1" (rnv), "r" (rmv), [fn] "r" (asi->insn_fn) : "lr", "memory", "cc" ); if (rt == 15) load_write_pc(rtv, regs); else regs->uregs[rt] = rtv; if (is_writeback(insn)) regs->uregs[rn] = rnv; }

Contributors

PersonTokensPropCommitsCommitProp
Jon Medhurst (Tixy)11778.00%225.00%
David A. Long2617.33%562.50%
Quentin Barnes74.67%112.50%
Total150100.00%8100.00%


static void __kprobes emulate_str(probes_opcode_t insn, struct arch_probes_insn *asi, struct pt_regs *regs) { unsigned long rtpc = regs->ARM_pc - 4 + str_pc_offset; unsigned long rnpc = regs->ARM_pc + 4; int rt = (insn >> 12) & 0xf; int rn = (insn >> 16) & 0xf; int rm = insn & 0xf; register unsigned long rtv asm("r0") = (rt == 15) ? rtpc : regs->uregs[rt]; register unsigned long rnv asm("r2") = (rn == 15) ? rnpc : regs->uregs[rn]; register unsigned long rmv asm("r3") = regs->uregs[rm]; __asm__ __volatile__ ( BLX("%[fn]") : "=r" (rnv) : "r" (rtv), "0" (rnv), "r" (rmv), [fn] "r" (asi->insn_fn) : "lr", "memory", "cc" ); if (is_writeback(insn)) regs->uregs[rn] = rnv; }

Contributors

PersonTokensPropCommitsCommitProp
Jon Medhurst (Tixy)11474.03%228.57%
David A. Long4025.97%571.43%
Total154100.00%7100.00%


static void __kprobes emulate_rd12rn16rm0rs8_rwflags(probes_opcode_t insn, struct arch_probes_insn *asi, struct pt_regs *regs) { unsigned long pc = regs->ARM_pc + 4; int rd = (insn >> 12) & 0xf; int rn = (insn >> 16) & 0xf; int rm = insn & 0xf; int rs = (insn >> 8) & 0xf; register unsigned long rdv asm("r0") = regs->uregs[rd]; register unsigned long rnv asm("r2") = (rn == 15) ? pc : regs->uregs[rn]; register unsigned long rmv asm("r3") = (rm == 15) ? pc : regs->uregs[rm]; register unsigned long rsv asm("r1") = regs->uregs[rs]; unsigned long cpsr = regs->ARM_cpsr; __asm__ __volatile__ ( "msr cpsr_fs, %[cpsr] \n\t" BLX("%[fn]") "mrs %[cpsr], cpsr \n\t" : "=r" (rdv), [cpsr] "=r" (cpsr) : "0" (rdv), "r" (rnv), "r" (rmv), "r" (rsv), "1" (cpsr), [fn] "r" (asi->insn_fn) : "lr", "memory", "cc" ); if (rd == 15) alu_write_pc(rdv, regs); else regs->uregs[rd] = rdv; regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK); }

Contributors

PersonTokensPropCommitsCommitProp
Jon Medhurst (Tixy)16882.76%337.50%
David A. Long3517.24%562.50%
Total203100.00%8100.00%


static void __kprobes emulate_rd12rn16rm0_rwflags_nopc(probes_opcode_t insn, struct arch_probes_insn *asi, struct pt_regs *regs) { int rd = (insn >> 12) & 0xf; int rn = (insn >> 16) & 0xf; int rm = insn & 0xf; register unsigned long rdv asm("r0") = regs->uregs[rd]; register unsigned long rnv asm("r2") = regs->uregs[rn]; register unsigned long rmv asm("r3") = regs->uregs[rm]; unsigned long cpsr = regs->ARM_cpsr; __asm__ __volatile__ ( "msr cpsr_fs, %[cpsr] \n\t" BLX("%[fn]") "mrs %[cpsr], cpsr \n\t" : "=r" (rdv), [cpsr] "=r" (cpsr) : "0" (rdv), "r" (rnv), "r" (rmv), "1" (cpsr), [fn] "r" (asi->insn_fn) : "lr", "memory", "cc" ); regs->uregs[rd] = rdv; regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK); }

Contributors

PersonTokensPropCommitsCommitProp
Jon Medhurst (Tixy)12994.85%233.33%
David A. Long75.15%466.67%
Total136100.00%6100.00%


static void __kprobes emulate_rd16rn12rm0rs8_rwflags_nopc(probes_opcode_t insn, struct arch_probes_insn *asi, struct pt_regs *regs) { int rd = (insn >> 16) & 0xf; int rn = (insn >> 12) & 0xf; int rm = insn & 0xf; int rs = (insn >> 8) & 0xf; register unsigned long rdv asm("r2") = regs->uregs[rd]; register unsigned long rnv asm("r0") = regs->uregs[rn]; register unsigned long rmv asm("r3") = regs->uregs[rm]; register unsigned long rsv asm("r1") = regs->uregs[rs]; unsigned long cpsr = regs->ARM_cpsr; __asm__ __volatile__ ( "msr cpsr_fs, %[cpsr] \n\t" BLX("%[fn]") "mrs %[cpsr], cpsr \n\t" : "=r" (rdv), [cpsr] "=r" (cpsr) : "0" (rdv), "r" (rnv), "r" (rmv), "r" (rsv), "1" (cpsr), [fn] "r" (asi->insn_fn) : "lr", "memory", "cc" ); regs->uregs[rd] = rdv; regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK); }

Contributors

PersonTokensPropCommitsCommitProp
Jon Medhurst (Tixy)15695.71%120.00%
David A. Long74.29%480.00%
Total163100.00%5100.00%


static void __kprobes emulate_rd12rm0_noflags_nopc(probes_opcode_t insn, struct arch_probes_insn *asi, struct pt_regs *regs) { int rd = (insn >> 12) & 0xf; int rm = insn & 0xf; register unsigned long rdv asm("r0") = regs->uregs[rd]; register unsigned long rmv asm("r3") = regs->uregs[rm]; __asm__ __volatile__ ( BLX("%[fn]") : "=r" (rdv) : "0" (rdv), "r" (rmv), [fn] "r" (asi->insn_fn) : "lr", "memory", "cc" ); regs->uregs[rd] = rdv; }

Contributors

PersonTokensPropCommitsCommitProp
Jon Medhurst (Tixy)7591.46%120.00%
David A. Long78.54%480.00%
Total82100.00%5100.00%


static void __kprobes emulate_rdlo12rdhi16rn0rm8_rwflags_nopc(probes_opcode_t insn, struct arch_probes_insn *asi, struct pt_regs *regs) { int rdlo = (insn >> 12) & 0xf; int rdhi = (insn >> 16) & 0xf; int rn = insn & 0xf; int rm = (insn >> 8) & 0xf; register unsigned long rdlov asm("r0") = regs->uregs[rdlo]; register unsigned long rdhiv asm("r2") = regs->uregs[rdhi]; register unsigned long rnv asm("r3") = regs->uregs[rn]; register unsigned long rmv asm("r1") = regs->uregs[rm]; unsigned long cpsr = regs->ARM_cpsr; __asm__ __volatile__ ( "msr cpsr_fs, %[cpsr] \n\t" BLX("%[fn]") "mrs %[cpsr], cpsr \n\t" : "=r" (rdlov), "=r" (rdhiv), [cpsr] "=r" (cpsr) : "0" (rdlov), "1" (rdhiv), "r" (rnv), "r" (rmv), "2" (cpsr), [fn] "r" (asi->insn_fn) : "lr", "memory", "cc" ); regs->uregs[rdlo] = rdlov; regs->uregs[rdhi] = rdhiv; regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK); }

Contributors

PersonTokensPropCommitsCommitProp
Jon Medhurst (Tixy)16595.93%120.00%
David A. Long74.07%480.00%
Total172100.00%5100.00%

const union decode_action kprobes_arm_actions[NUM_PROBES_ARM_ACTIONS] = { [PROBES_PRELOAD_IMM] = {.handler = probes_simulate_nop}, [PROBES_PRELOAD_REG] = {.handler = probes_simulate_nop}, [PROBES_BRANCH_IMM] = {.handler = simulate_blx1}, [PROBES_MRS] = {.handler = simulate_mrs}, [PROBES_BRANCH_REG] = {.handler = simulate_blx2bx}, [PROBES_CLZ] = {.handler = emulate_rd12rm0_noflags_nopc}, [PROBES_SATURATING_ARITHMETIC] = { .handler = emulate_rd12rn16rm0_rwflags_nopc}, [PROBES_MUL1] = {.handler = emulate_rdlo12rdhi16rn0rm8_rwflags_nopc}, [PROBES_MUL2] = {.handler = emulate_rd16rn12rm0rs8_rwflags_nopc}, [PROBES_SWP] = {.handler = emulate_rd12rn16rm0_rwflags_nopc}, [PROBES_LDRSTRD] = {.handler = emulate_ldrdstrd}, [PROBES_LOAD_EXTRA] = {.handler = emulate_ldr}, [PROBES_LOAD] = {.handler = emulate_ldr}, [PROBES_STORE_EXTRA] = {.handler = emulate_str}, [PROBES_STORE] = {.handler = emulate_str}, [PROBES_MOV_IP_SP] = {.handler = simulate_mov_ipsp}, [PROBES_DATA_PROCESSING_REG] = { .handler = emulate_rd12rn16rm0rs8_rwflags}, [PROBES_DATA_PROCESSING_IMM] = { .handler = emulate_rd12rn16rm0rs8_rwflags}, [PROBES_MOV_HALFWORD] = {.handler = emulate_rd12rm0_noflags_nopc}, [PROBES_SEV] = {.handler = probes_emulate_none}, [PROBES_WFE] = {.handler = probes_simulate_nop}, [PROBES_SATURATE] = {.handler = emulate_rd12rn16rm0_rwflags_nopc}, [PROBES_REV] = {.handler = emulate_rd12rm0_noflags_nopc}, [PROBES_MMI] = {.handler = emulate_rd12rn16rm0_rwflags_nopc}, [PROBES_PACK] = {.handler = emulate_rd12rn16rm0_rwflags_nopc}, [PROBES_EXTEND] = {.handler = emulate_rd12rm0_noflags_nopc}, [PROBES_EXTEND_ADD] = {.handler = emulate_rd12rn16rm0_rwflags_nopc}, [PROBES_MUL_ADD_LONG] = { .handler = emulate_rdlo12rdhi16rn0rm8_rwflags_nopc}, [PROBES_MUL_ADD] = {.handler = emulate_rd16rn12rm0rs8_rwflags_nopc}, [PROBES_BITFIELD] = {.handler = emulate_rd12rm0_noflags_nopc}, [PROBES_BRANCH] = {.handler = simulate_bbl}, [PROBES_LDMSTM] = {.decoder = kprobe_decode_ldmstm} }; const struct decode_checker *kprobes_arm_checkers[] = {arm_stack_checker, arm_regs_checker, NULL};

Overall Contributors

PersonTokensPropCommitsCommitProp
Jon Medhurst (Tixy)100160.59%1252.17%
David A. Long60236.44%626.09%
Quentin Barnes281.69%14.35%
Wang Nan211.27%417.39%
Total1652100.00%23100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.