Skip to content

Commit

Permalink
ARM: kprobes: introduces checker
Browse files Browse the repository at this point in the history
This patch introdces 'checker' to decoding phase, and calls checkers
when instruction decoding. This allows further decoding for specific
instructions.  This patch introduces a stub call of checkers in kprobe
arch_prepare_kprobe() as an example and for further expansion.

Signed-off-by: Wang Nan <wangnan0@huawei.com>
Reviewed-by: Jon Medhurst <tixy@linaro.org>
Reviewed-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Signed-off-by: Jon Medhurst <tixy@linaro.org>
  • Loading branch information
Wang Nan authored and Jon Medhurst committed Jan 9, 2015
1 parent 832607e commit 83803d9
Show file tree
Hide file tree
Showing 11 changed files with 95 additions and 20 deletions.
5 changes: 3 additions & 2 deletions arch/arm/probes/decode-arm.c
Original file line number Diff line number Diff line change
Expand Up @@ -726,10 +726,11 @@ static void __kprobes arm_singlestep(probes_opcode_t insn,
*/
enum probes_insn __kprobes
arm_probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi,
bool emulate, const union decode_action *actions)
bool emulate, const union decode_action *actions,
const struct decode_checker *checkers[])
{
asi->insn_singlestep = arm_singlestep;
asi->insn_check_cc = probes_condition_checks[insn>>28];
return probes_decode_insn(insn, asi, probes_decode_arm_table, false,
emulate, actions);
emulate, actions, checkers);
}
3 changes: 2 additions & 1 deletion arch/arm/probes/decode-arm.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ 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,
const union decode_action *actions);
const union decode_action *actions,
const struct decode_checker *checkers[]);

#endif
10 changes: 6 additions & 4 deletions arch/arm/probes/decode-thumb.c
Original file line number Diff line number Diff line change
Expand Up @@ -863,20 +863,22 @@ static void __kprobes thumb32_singlestep(probes_opcode_t opcode,

enum probes_insn __kprobes
thumb16_probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi,
bool emulate, const union decode_action *actions)
bool emulate, const union decode_action *actions,
const struct decode_checker *checkers[])
{
asi->insn_singlestep = thumb16_singlestep;
asi->insn_check_cc = thumb_check_cc;
return probes_decode_insn(insn, asi, probes_decode_thumb16_table, true,
emulate, actions);
emulate, actions, checkers);
}

enum probes_insn __kprobes
thumb32_probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi,
bool emulate, const union decode_action *actions)
bool emulate, const union decode_action *actions,
const struct decode_checker *checkers[])
{
asi->insn_singlestep = thumb32_singlestep;
asi->insn_check_cc = thumb_check_cc;
return probes_decode_insn(insn, asi, probes_decode_thumb32_table, true,
emulate, actions);
emulate, actions, checkers);
}
6 changes: 4 additions & 2 deletions arch/arm/probes/decode-thumb.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,11 @@ extern const union decode_item probes_decode_thumb16_table[];

enum probes_insn __kprobes
thumb16_probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi,
bool emulate, const union decode_action *actions);
bool emulate, const union decode_action *actions,
const struct decode_checker *checkers[]);
enum probes_insn __kprobes
thumb32_probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi,
bool emulate, const union decode_action *actions);
bool emulate, const union decode_action *actions,
const struct decode_checker *checkers[]);

#endif
60 changes: 54 additions & 6 deletions arch/arm/probes/decode.c
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,31 @@ static const int decode_struct_sizes[NUM_DECODE_TYPES] = {
[DECODE_TYPE_REJECT] = sizeof(struct decode_reject)
};

static int run_checkers(const struct decode_checker *checkers[],
int action, probes_opcode_t insn,
struct arch_probes_insn *asi,
const struct decode_header *h)
{
const struct decode_checker **p;

if (!checkers)
return INSN_GOOD;

p = checkers;
while (*p != NULL) {
int retval;
probes_check_t *checker_func = (*p)[action].checker;

retval = INSN_GOOD;
if (checker_func)
retval = checker_func(insn, asi, h);
if (retval == INSN_REJECTED)
return retval;
p++;
}
return INSN_GOOD;
}

/*
* probes_decode_insn operates on data tables in order to decode an ARM
* architecture instruction onto which a kprobe has been placed.
Expand Down Expand Up @@ -388,11 +413,17 @@ static const int decode_struct_sizes[NUM_DECODE_TYPES] = {
int __kprobes
probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi,
const union decode_item *table, bool thumb,
bool emulate, const union decode_action *actions)
bool emulate, const union decode_action *actions,
const struct decode_checker *checkers[])
{
const struct decode_header *h = (struct decode_header *)table;
const struct decode_header *next;
bool matched = false;
/*
* @insn can be modified by decode_regs. Save its original
* value for checkers.
*/
probes_opcode_t origin_insn = insn;

if (emulate)
insn = prepare_emulated_insn(insn, asi, thumb);
Expand Down Expand Up @@ -422,24 +453,41 @@ probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi,
}

case DECODE_TYPE_CUSTOM: {
int err;
struct decode_custom *d = (struct decode_custom *)h;
return actions[d->decoder.action].decoder(insn, asi, h);
int action = d->decoder.action;

err = run_checkers(checkers, action, origin_insn, asi, h);
if (err == INSN_REJECTED)
return INSN_REJECTED;
return actions[action].decoder(insn, asi, h);
}

case DECODE_TYPE_SIMULATE: {
int err;
struct decode_simulate *d = (struct decode_simulate *)h;
asi->insn_handler = actions[d->handler.action].handler;
int action = d->handler.action;

err = run_checkers(checkers, action, origin_insn, asi, h);
if (err == INSN_REJECTED)
return INSN_REJECTED;
asi->insn_handler = actions[action].handler;
return INSN_GOOD_NO_SLOT;
}

case DECODE_TYPE_EMULATE: {
int err;
struct decode_emulate *d = (struct decode_emulate *)h;
int action = d->handler.action;

err = run_checkers(checkers, action, origin_insn, asi, h);
if (err == INSN_REJECTED)
return INSN_REJECTED;

if (!emulate)
return actions[d->handler.action].decoder(insn,
asi, h);
return actions[action].decoder(insn, asi, h);

asi->insn_handler = actions[d->handler.action].handler;
asi->insn_handler = actions[action].handler;
set_emulated_insn(insn, asi, thumb);
return INSN_GOOD;
}
Expand Down
11 changes: 10 additions & 1 deletion arch/arm/probes/decode.h
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,14 @@ union decode_action {
probes_custom_decode_t *decoder;
};

typedef enum probes_insn (probes_check_t)(probes_opcode_t,
struct arch_probes_insn *,
const struct decode_header *);

struct decode_checker {
probes_check_t *checker;
};

#define DECODE_END \
{.bits = DECODE_TYPE_END}

Expand Down Expand Up @@ -402,6 +410,7 @@ probes_insn_handler_t probes_emulate_none;
int __kprobes
probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi,
const union decode_item *table, bool thumb, bool emulate,
const union decode_action *actions);
const union decode_action *actions,
const struct decode_checker **checkers);

#endif
2 changes: 2 additions & 0 deletions arch/arm/probes/kprobes/actions-arm.c
Original file line number Diff line number Diff line change
Expand Up @@ -339,3 +339,5 @@ const union decode_action kprobes_arm_actions[NUM_PROBES_ARM_ACTIONS] = {
[PROBES_BRANCH] = {.handler = simulate_bbl},
[PROBES_LDMSTM] = {.decoder = kprobe_decode_ldmstm}
};

const struct decode_checker *kprobes_arm_checkers[] = {NULL};
3 changes: 3 additions & 0 deletions arch/arm/probes/kprobes/actions-thumb.c
Original file line number Diff line number Diff line change
Expand Up @@ -664,3 +664,6 @@ const union decode_action kprobes_t32_actions[NUM_PROBES_T32_ACTIONS] = {
[PROBES_T32_MUL_ADD_LONG] = {
.handler = t32_emulate_rdlo12rdhi8rn16rm0_noflags},
};

const struct decode_checker *kprobes_t32_checkers[] = {NULL};
const struct decode_checker *kprobes_t16_checkers[] = {NULL};
6 changes: 5 additions & 1 deletion arch/arm/probes/kprobes/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
kprobe_decode_insn_t *decode_insn;
const union decode_action *actions;
int is;
const struct decode_checker **checkers;

if (in_exception_text(addr))
return -EINVAL;
Expand All @@ -74,9 +75,11 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
insn = __opcode_thumb32_compose(insn, inst2);
decode_insn = thumb32_probes_decode_insn;
actions = kprobes_t32_actions;
checkers = kprobes_t32_checkers;
} else {
decode_insn = thumb16_probes_decode_insn;
actions = kprobes_t16_actions;
checkers = kprobes_t16_checkers;
}
#else /* !CONFIG_THUMB2_KERNEL */
thumb = false;
Expand All @@ -85,12 +88,13 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
insn = __mem_to_opcode_arm(*p->addr);
decode_insn = arm_probes_decode_insn;
actions = kprobes_arm_actions;
checkers = kprobes_arm_checkers;
#endif

p->opcode = insn;
p->ainsn.insn = tmp_insn;

switch ((*decode_insn)(insn, &p->ainsn, true, actions)) {
switch ((*decode_insn)(insn, &p->ainsn, true, actions, checkers)) {
case INSN_REJECTED: /* not supported */
return -EINVAL;

Expand Down
7 changes: 5 additions & 2 deletions arch/arm/probes/kprobes/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,16 +37,19 @@ kprobe_decode_ldmstm(kprobe_opcode_t insn, struct arch_probes_insn *asi,
typedef enum probes_insn (kprobe_decode_insn_t)(probes_opcode_t,
struct arch_probes_insn *,
bool,
const union decode_action *);
const union decode_action *,
const struct decode_checker *[*]);

#ifdef CONFIG_THUMB2_KERNEL

extern const union decode_action kprobes_t32_actions[];
extern const union decode_action kprobes_t16_actions[];

extern const struct decode_checker *kprobes_t32_checkers[];
extern const struct decode_checker *kprobes_t16_checkers[];
#else /* !CONFIG_THUMB2_KERNEL */

extern const union decode_action kprobes_arm_actions[];
extern const struct decode_checker *kprobes_arm_checkers[];

#endif

Expand Down
2 changes: 1 addition & 1 deletion arch/arm/probes/uprobes/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ int arch_uprobe_analyze_insn(struct arch_uprobe *auprobe, struct mm_struct *mm,
auprobe->ixol[1] = __opcode_to_mem_arm(UPROBE_SS_ARM_INSN);

ret = arm_probes_decode_insn(insn, &auprobe->asi, false,
uprobes_probes_actions);
uprobes_probes_actions, NULL);
switch (ret) {
case INSN_REJECTED:
return -EINVAL;
Expand Down

0 comments on commit 83803d9

Please sign in to comment.