diff options
| -rw-r--r-- | arch/arm/probes/decode-arm.c | 5 | ||||
| -rw-r--r-- | arch/arm/probes/decode-arm.h | 3 | ||||
| -rw-r--r-- | arch/arm/probes/decode-thumb.c | 10 | ||||
| -rw-r--r-- | arch/arm/probes/decode-thumb.h | 6 | ||||
| -rw-r--r-- | arch/arm/probes/decode.c | 60 | ||||
| -rw-r--r-- | arch/arm/probes/decode.h | 11 | ||||
| -rw-r--r-- | arch/arm/probes/kprobes/actions-arm.c | 2 | ||||
| -rw-r--r-- | arch/arm/probes/kprobes/actions-thumb.c | 3 | ||||
| -rw-r--r-- | arch/arm/probes/kprobes/core.c | 6 | ||||
| -rw-r--r-- | arch/arm/probes/kprobes/core.h | 7 | ||||
| -rw-r--r-- | arch/arm/probes/uprobes/core.c | 2 |
11 files changed, 95 insertions, 20 deletions
diff --git a/arch/arm/probes/decode-arm.c b/arch/arm/probes/decode-arm.c index 04114f74a2d2..f72c33a2dcfb 100644 --- a/arch/arm/probes/decode-arm.c +++ b/arch/arm/probes/decode-arm.c | |||
| @@ -726,10 +726,11 @@ static void __kprobes arm_singlestep(probes_opcode_t insn, | |||
| 726 | */ | 726 | */ |
| 727 | enum probes_insn __kprobes | 727 | enum probes_insn __kprobes |
| 728 | arm_probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi, | 728 | arm_probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi, |
| 729 | bool emulate, const union decode_action *actions) | 729 | bool emulate, const union decode_action *actions, |
| 730 | const struct decode_checker *checkers[]) | ||
| 730 | { | 731 | { |
| 731 | asi->insn_singlestep = arm_singlestep; | 732 | asi->insn_singlestep = arm_singlestep; |
| 732 | asi->insn_check_cc = probes_condition_checks[insn>>28]; | 733 | asi->insn_check_cc = probes_condition_checks[insn>>28]; |
| 733 | return probes_decode_insn(insn, asi, probes_decode_arm_table, false, | 734 | return probes_decode_insn(insn, asi, probes_decode_arm_table, false, |
| 734 | emulate, actions); | 735 | emulate, actions, checkers); |
| 735 | } | 736 | } |
diff --git a/arch/arm/probes/decode-arm.h b/arch/arm/probes/decode-arm.h index cb0b26331930..b3b80f6d414b 100644 --- a/arch/arm/probes/decode-arm.h +++ b/arch/arm/probes/decode-arm.h | |||
| @@ -68,6 +68,7 @@ extern const union decode_item probes_decode_arm_table[]; | |||
| 68 | 68 | ||
| 69 | enum probes_insn arm_probes_decode_insn(probes_opcode_t, | 69 | enum probes_insn arm_probes_decode_insn(probes_opcode_t, |
| 70 | struct arch_probes_insn *, bool emulate, | 70 | struct arch_probes_insn *, bool emulate, |
| 71 | const union decode_action *actions); | 71 | const union decode_action *actions, |
| 72 | const struct decode_checker *checkers[]); | ||
| 72 | 73 | ||
| 73 | #endif | 74 | #endif |
diff --git a/arch/arm/probes/decode-thumb.c b/arch/arm/probes/decode-thumb.c index 2f0453a895dc..985e7dd4cac6 100644 --- a/arch/arm/probes/decode-thumb.c +++ b/arch/arm/probes/decode-thumb.c | |||
| @@ -863,20 +863,22 @@ static void __kprobes thumb32_singlestep(probes_opcode_t opcode, | |||
| 863 | 863 | ||
| 864 | enum probes_insn __kprobes | 864 | enum probes_insn __kprobes |
| 865 | thumb16_probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi, | 865 | thumb16_probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi, |
| 866 | bool emulate, const union decode_action *actions) | 866 | bool emulate, const union decode_action *actions, |
| 867 | const struct decode_checker *checkers[]) | ||
| 867 | { | 868 | { |
| 868 | asi->insn_singlestep = thumb16_singlestep; | 869 | asi->insn_singlestep = thumb16_singlestep; |
| 869 | asi->insn_check_cc = thumb_check_cc; | 870 | asi->insn_check_cc = thumb_check_cc; |
| 870 | return probes_decode_insn(insn, asi, probes_decode_thumb16_table, true, | 871 | return probes_decode_insn(insn, asi, probes_decode_thumb16_table, true, |
| 871 | emulate, actions); | 872 | emulate, actions, checkers); |
| 872 | } | 873 | } |
| 873 | 874 | ||
| 874 | enum probes_insn __kprobes | 875 | enum probes_insn __kprobes |
| 875 | thumb32_probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi, | 876 | thumb32_probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi, |
| 876 | bool emulate, const union decode_action *actions) | 877 | bool emulate, const union decode_action *actions, |
| 878 | const struct decode_checker *checkers[]) | ||
| 877 | { | 879 | { |
| 878 | asi->insn_singlestep = thumb32_singlestep; | 880 | asi->insn_singlestep = thumb32_singlestep; |
| 879 | asi->insn_check_cc = thumb_check_cc; | 881 | asi->insn_check_cc = thumb_check_cc; |
| 880 | return probes_decode_insn(insn, asi, probes_decode_thumb32_table, true, | 882 | return probes_decode_insn(insn, asi, probes_decode_thumb32_table, true, |
| 881 | emulate, actions); | 883 | emulate, actions, checkers); |
| 882 | } | 884 | } |
diff --git a/arch/arm/probes/decode-thumb.h b/arch/arm/probes/decode-thumb.h index 039013c7131d..8457add0a2d8 100644 --- a/arch/arm/probes/decode-thumb.h +++ b/arch/arm/probes/decode-thumb.h | |||
| @@ -91,9 +91,11 @@ extern const union decode_item probes_decode_thumb16_table[]; | |||
| 91 | 91 | ||
| 92 | enum probes_insn __kprobes | 92 | enum probes_insn __kprobes |
| 93 | thumb16_probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi, | 93 | thumb16_probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi, |
| 94 | bool emulate, const union decode_action *actions); | 94 | bool emulate, const union decode_action *actions, |
| 95 | const struct decode_checker *checkers[]); | ||
| 95 | enum probes_insn __kprobes | 96 | enum probes_insn __kprobes |
| 96 | thumb32_probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi, | 97 | thumb32_probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi, |
| 97 | bool emulate, const union decode_action *actions); | 98 | bool emulate, const union decode_action *actions, |
| 99 | const struct decode_checker *checkers[]); | ||
| 98 | 100 | ||
| 99 | #endif | 101 | #endif |
diff --git a/arch/arm/probes/decode.c b/arch/arm/probes/decode.c index 3b05d5742359..c7d442018902 100644 --- a/arch/arm/probes/decode.c +++ b/arch/arm/probes/decode.c | |||
| @@ -342,6 +342,31 @@ static const int decode_struct_sizes[NUM_DECODE_TYPES] = { | |||
| 342 | [DECODE_TYPE_REJECT] = sizeof(struct decode_reject) | 342 | [DECODE_TYPE_REJECT] = sizeof(struct decode_reject) |
| 343 | }; | 343 | }; |
| 344 | 344 | ||
| 345 | static int run_checkers(const struct decode_checker *checkers[], | ||
| 346 | int action, probes_opcode_t insn, | ||
| 347 | struct arch_probes_insn *asi, | ||
| 348 | const struct decode_header *h) | ||
| 349 | { | ||
| 350 | const struct decode_checker **p; | ||
| 351 | |||
| 352 | if (!checkers) | ||
| 353 | return INSN_GOOD; | ||
| 354 | |||
| 355 | p = checkers; | ||
| 356 | while (*p != NULL) { | ||
| 357 | int retval; | ||
| 358 | probes_check_t *checker_func = (*p)[action].checker; | ||
| 359 | |||
| 360 | retval = INSN_GOOD; | ||
| 361 | if (checker_func) | ||
| 362 | retval = checker_func(insn, asi, h); | ||
| 363 | if (retval == INSN_REJECTED) | ||
| 364 | return retval; | ||
| 365 | p++; | ||
| 366 | } | ||
| 367 | return INSN_GOOD; | ||
| 368 | } | ||
| 369 | |||
| 345 | /* | 370 | /* |
| 346 | * probes_decode_insn operates on data tables in order to decode an ARM | 371 | * probes_decode_insn operates on data tables in order to decode an ARM |
| 347 | * architecture instruction onto which a kprobe has been placed. | 372 | * architecture instruction onto which a kprobe has been placed. |
| @@ -388,11 +413,17 @@ static const int decode_struct_sizes[NUM_DECODE_TYPES] = { | |||
| 388 | int __kprobes | 413 | int __kprobes |
| 389 | probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi, | 414 | probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi, |
| 390 | const union decode_item *table, bool thumb, | 415 | const union decode_item *table, bool thumb, |
| 391 | bool emulate, const union decode_action *actions) | 416 | bool emulate, const union decode_action *actions, |
| 417 | const struct decode_checker *checkers[]) | ||
| 392 | { | 418 | { |
| 393 | const struct decode_header *h = (struct decode_header *)table; | 419 | const struct decode_header *h = (struct decode_header *)table; |
| 394 | const struct decode_header *next; | 420 | const struct decode_header *next; |
| 395 | bool matched = false; | 421 | bool matched = false; |
| 422 | /* | ||
| 423 | * @insn can be modified by decode_regs. Save its original | ||
| 424 | * value for checkers. | ||
| 425 | */ | ||
| 426 | probes_opcode_t origin_insn = insn; | ||
| 396 | 427 | ||
| 397 | if (emulate) | 428 | if (emulate) |
| 398 | insn = prepare_emulated_insn(insn, asi, thumb); | 429 | insn = prepare_emulated_insn(insn, asi, thumb); |
| @@ -422,24 +453,41 @@ probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi, | |||
| 422 | } | 453 | } |
| 423 | 454 | ||
| 424 | case DECODE_TYPE_CUSTOM: { | 455 | case DECODE_TYPE_CUSTOM: { |
| 456 | int err; | ||
| 425 | struct decode_custom *d = (struct decode_custom *)h; | 457 | struct decode_custom *d = (struct decode_custom *)h; |
| 426 | return actions[d->decoder.action].decoder(insn, asi, h); | 458 | int action = d->decoder.action; |
| 459 | |||
| 460 | err = run_checkers(checkers, action, origin_insn, asi, h); | ||
| 461 | if (err == INSN_REJECTED) | ||
| 462 | return INSN_REJECTED; | ||
| 463 | return actions[action].decoder(insn, asi, h); | ||
| 427 | } | 464 | } |
| 428 | 465 | ||
| 429 | case DECODE_TYPE_SIMULATE: { | 466 | case DECODE_TYPE_SIMULATE: { |
| 467 | int err; | ||
| 430 | struct decode_simulate *d = (struct decode_simulate *)h; | 468 | struct decode_simulate *d = (struct decode_simulate *)h; |
| 431 | asi->insn_handler = actions[d->handler.action].handler; | 469 | int action = d->handler.action; |
| 470 | |||
| 471 | err = run_checkers(checkers, action, origin_insn, asi, h); | ||
| 472 | if (err == INSN_REJECTED) | ||
| 473 | return INSN_REJECTED; | ||
| 474 | asi->insn_handler = actions[action].handler; | ||
| 432 | return INSN_GOOD_NO_SLOT; | 475 | return INSN_GOOD_NO_SLOT; |
| 433 | } | 476 | } |
| 434 | 477 | ||
| 435 | case DECODE_TYPE_EMULATE: { | 478 | case DECODE_TYPE_EMULATE: { |
| 479 | int err; | ||
| 436 | struct decode_emulate *d = (struct decode_emulate *)h; | 480 | struct decode_emulate *d = (struct decode_emulate *)h; |
| 481 | int action = d->handler.action; | ||
| 482 | |||
| 483 | err = run_checkers(checkers, action, origin_insn, asi, h); | ||
| 484 | if (err == INSN_REJECTED) | ||
| 485 | return INSN_REJECTED; | ||
| 437 | 486 | ||
| 438 | if (!emulate) | 487 | if (!emulate) |
| 439 | return actions[d->handler.action].decoder(insn, | 488 | return actions[action].decoder(insn, asi, h); |
| 440 | asi, h); | ||
| 441 | 489 | ||
| 442 | asi->insn_handler = actions[d->handler.action].handler; | 490 | asi->insn_handler = actions[action].handler; |
| 443 | set_emulated_insn(insn, asi, thumb); | 491 | set_emulated_insn(insn, asi, thumb); |
| 444 | return INSN_GOOD; | 492 | return INSN_GOOD; |
| 445 | } | 493 | } |
diff --git a/arch/arm/probes/decode.h b/arch/arm/probes/decode.h index 1d0b53169080..f9b08ba7fe73 100644 --- a/arch/arm/probes/decode.h +++ b/arch/arm/probes/decode.h | |||
| @@ -314,6 +314,14 @@ union decode_action { | |||
| 314 | probes_custom_decode_t *decoder; | 314 | probes_custom_decode_t *decoder; |
| 315 | }; | 315 | }; |
| 316 | 316 | ||
| 317 | typedef enum probes_insn (probes_check_t)(probes_opcode_t, | ||
| 318 | struct arch_probes_insn *, | ||
| 319 | const struct decode_header *); | ||
| 320 | |||
| 321 | struct decode_checker { | ||
| 322 | probes_check_t *checker; | ||
| 323 | }; | ||
| 324 | |||
| 317 | #define DECODE_END \ | 325 | #define DECODE_END \ |
| 318 | {.bits = DECODE_TYPE_END} | 326 | {.bits = DECODE_TYPE_END} |
| 319 | 327 | ||
| @@ -402,6 +410,7 @@ probes_insn_handler_t probes_emulate_none; | |||
| 402 | int __kprobes | 410 | int __kprobes |
| 403 | probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi, | 411 | probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi, |
| 404 | const union decode_item *table, bool thumb, bool emulate, | 412 | const union decode_item *table, bool thumb, bool emulate, |
| 405 | const union decode_action *actions); | 413 | const union decode_action *actions, |
| 414 | const struct decode_checker **checkers); | ||
| 406 | 415 | ||
| 407 | #endif | 416 | #endif |
diff --git a/arch/arm/probes/kprobes/actions-arm.c b/arch/arm/probes/kprobes/actions-arm.c index 2206f2d80c76..fbd93a9ada75 100644 --- a/arch/arm/probes/kprobes/actions-arm.c +++ b/arch/arm/probes/kprobes/actions-arm.c | |||
| @@ -339,3 +339,5 @@ const union decode_action kprobes_arm_actions[NUM_PROBES_ARM_ACTIONS] = { | |||
| 339 | [PROBES_BRANCH] = {.handler = simulate_bbl}, | 339 | [PROBES_BRANCH] = {.handler = simulate_bbl}, |
| 340 | [PROBES_LDMSTM] = {.decoder = kprobe_decode_ldmstm} | 340 | [PROBES_LDMSTM] = {.decoder = kprobe_decode_ldmstm} |
| 341 | }; | 341 | }; |
| 342 | |||
| 343 | const struct decode_checker *kprobes_arm_checkers[] = {NULL}; | ||
diff --git a/arch/arm/probes/kprobes/actions-thumb.c b/arch/arm/probes/kprobes/actions-thumb.c index 6c4e60b62826..2796121fe90e 100644 --- a/arch/arm/probes/kprobes/actions-thumb.c +++ b/arch/arm/probes/kprobes/actions-thumb.c | |||
| @@ -664,3 +664,6 @@ const union decode_action kprobes_t32_actions[NUM_PROBES_T32_ACTIONS] = { | |||
| 664 | [PROBES_T32_MUL_ADD_LONG] = { | 664 | [PROBES_T32_MUL_ADD_LONG] = { |
| 665 | .handler = t32_emulate_rdlo12rdhi8rn16rm0_noflags}, | 665 | .handler = t32_emulate_rdlo12rdhi8rn16rm0_noflags}, |
| 666 | }; | 666 | }; |
| 667 | |||
| 668 | const struct decode_checker *kprobes_t32_checkers[] = {NULL}; | ||
| 669 | const struct decode_checker *kprobes_t16_checkers[] = {NULL}; | ||
diff --git a/arch/arm/probes/kprobes/core.c b/arch/arm/probes/kprobes/core.c index 701f49d74c35..74f3dc3ac212 100644 --- a/arch/arm/probes/kprobes/core.c +++ b/arch/arm/probes/kprobes/core.c | |||
| @@ -61,6 +61,7 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p) | |||
| 61 | kprobe_decode_insn_t *decode_insn; | 61 | kprobe_decode_insn_t *decode_insn; |
| 62 | const union decode_action *actions; | 62 | const union decode_action *actions; |
| 63 | int is; | 63 | int is; |
| 64 | const struct decode_checker **checkers; | ||
| 64 | 65 | ||
| 65 | if (in_exception_text(addr)) | 66 | if (in_exception_text(addr)) |
| 66 | return -EINVAL; | 67 | return -EINVAL; |
| @@ -74,9 +75,11 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p) | |||
| 74 | insn = __opcode_thumb32_compose(insn, inst2); | 75 | insn = __opcode_thumb32_compose(insn, inst2); |
| 75 | decode_insn = thumb32_probes_decode_insn; | 76 | decode_insn = thumb32_probes_decode_insn; |
| 76 | actions = kprobes_t32_actions; | 77 | actions = kprobes_t32_actions; |
| 78 | checkers = kprobes_t32_checkers; | ||
| 77 | } else { | 79 | } else { |
| 78 | decode_insn = thumb16_probes_decode_insn; | 80 | decode_insn = thumb16_probes_decode_insn; |
| 79 | actions = kprobes_t16_actions; | 81 | actions = kprobes_t16_actions; |
| 82 | checkers = kprobes_t16_checkers; | ||
| 80 | } | 83 | } |
| 81 | #else /* !CONFIG_THUMB2_KERNEL */ | 84 | #else /* !CONFIG_THUMB2_KERNEL */ |
| 82 | thumb = false; | 85 | thumb = false; |
| @@ -85,12 +88,13 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p) | |||
| 85 | insn = __mem_to_opcode_arm(*p->addr); | 88 | insn = __mem_to_opcode_arm(*p->addr); |
| 86 | decode_insn = arm_probes_decode_insn; | 89 | decode_insn = arm_probes_decode_insn; |
| 87 | actions = kprobes_arm_actions; | 90 | actions = kprobes_arm_actions; |
| 91 | checkers = kprobes_arm_checkers; | ||
| 88 | #endif | 92 | #endif |
| 89 | 93 | ||
| 90 | p->opcode = insn; | 94 | p->opcode = insn; |
| 91 | p->ainsn.insn = tmp_insn; | 95 | p->ainsn.insn = tmp_insn; |
| 92 | 96 | ||
| 93 | switch ((*decode_insn)(insn, &p->ainsn, true, actions)) { | 97 | switch ((*decode_insn)(insn, &p->ainsn, true, actions, checkers)) { |
| 94 | case INSN_REJECTED: /* not supported */ | 98 | case INSN_REJECTED: /* not supported */ |
| 95 | return -EINVAL; | 99 | return -EINVAL; |
| 96 | 100 | ||
diff --git a/arch/arm/probes/kprobes/core.h b/arch/arm/probes/kprobes/core.h index 2e1e5a3d9155..f88c79fe632a 100644 --- a/arch/arm/probes/kprobes/core.h +++ b/arch/arm/probes/kprobes/core.h | |||
| @@ -37,16 +37,19 @@ kprobe_decode_ldmstm(kprobe_opcode_t insn, struct arch_probes_insn *asi, | |||
| 37 | typedef enum probes_insn (kprobe_decode_insn_t)(probes_opcode_t, | 37 | typedef enum probes_insn (kprobe_decode_insn_t)(probes_opcode_t, |
| 38 | struct arch_probes_insn *, | 38 | struct arch_probes_insn *, |
| 39 | bool, | 39 | bool, |
| 40 | const union decode_action *); | 40 | const union decode_action *, |
| 41 | const struct decode_checker *[*]); | ||
| 41 | 42 | ||
| 42 | #ifdef CONFIG_THUMB2_KERNEL | 43 | #ifdef CONFIG_THUMB2_KERNEL |
| 43 | 44 | ||
| 44 | extern const union decode_action kprobes_t32_actions[]; | 45 | extern const union decode_action kprobes_t32_actions[]; |
| 45 | extern const union decode_action kprobes_t16_actions[]; | 46 | extern const union decode_action kprobes_t16_actions[]; |
| 46 | 47 | extern const struct decode_checker *kprobes_t32_checkers[]; | |
| 48 | extern const struct decode_checker *kprobes_t16_checkers[]; | ||
| 47 | #else /* !CONFIG_THUMB2_KERNEL */ | 49 | #else /* !CONFIG_THUMB2_KERNEL */ |
| 48 | 50 | ||
| 49 | extern const union decode_action kprobes_arm_actions[]; | 51 | extern const union decode_action kprobes_arm_actions[]; |
| 52 | extern const struct decode_checker *kprobes_arm_checkers[]; | ||
| 50 | 53 | ||
| 51 | #endif | 54 | #endif |
| 52 | 55 | ||
diff --git a/arch/arm/probes/uprobes/core.c b/arch/arm/probes/uprobes/core.c index b2954f6d3abe..d1329f1ba4e4 100644 --- a/arch/arm/probes/uprobes/core.c +++ b/arch/arm/probes/uprobes/core.c | |||
| @@ -88,7 +88,7 @@ int arch_uprobe_analyze_insn(struct arch_uprobe *auprobe, struct mm_struct *mm, | |||
| 88 | auprobe->ixol[1] = __opcode_to_mem_arm(UPROBE_SS_ARM_INSN); | 88 | auprobe->ixol[1] = __opcode_to_mem_arm(UPROBE_SS_ARM_INSN); |
| 89 | 89 | ||
| 90 | ret = arm_probes_decode_insn(insn, &auprobe->asi, false, | 90 | ret = arm_probes_decode_insn(insn, &auprobe->asi, false, |
| 91 | uprobes_probes_actions); | 91 | uprobes_probes_actions, NULL); |
| 92 | switch (ret) { | 92 | switch (ret) { |
| 93 | case INSN_REJECTED: | 93 | case INSN_REJECTED: |
| 94 | return -EINVAL; | 94 | return -EINVAL; |
