diff options
author | Jon Medhurst <tixy@yxit.co.uk> | 2011-04-08 10:32:55 -0400 |
---|---|---|
committer | Nicolas Pitre <nicolas.pitre@linaro.org> | 2011-04-28 23:40:57 -0400 |
commit | 54823accfcfc715e9e757a621afb40dabc01d033 (patch) | |
tree | 17707c238fbb07f26769b974bec731945015696e /arch/arm | |
parent | 6823fc85fcfba11675f2027aadf2d5291c6f351b (diff) |
ARM: kprobes: Reject probing of LDR/STR instructions which update PC unpredictably
Using PC as an base register with writeback is UNPREDICTABLE, as is non
word-sized loads or stores of PC. (We only really care about preventing
loads to PC but it keeps the code simpler if we also exclude stores.)
Signed-off-by: Jon Medhurst <tixy@yxit.co.uk>
Signed-off-by: Nicolas Pitre <nicolas.pitre@linaro.org>
Diffstat (limited to 'arch/arm')
-rw-r--r-- | arch/arm/kernel/kprobes-decode.c | 17 |
1 files changed, 17 insertions, 0 deletions
diff --git a/arch/arm/kernel/kprobes-decode.c b/arch/arm/kernel/kprobes-decode.c index 348ff47acd06..a4dba1f7c87b 100644 --- a/arch/arm/kernel/kprobes-decode.c +++ b/arch/arm/kernel/kprobes-decode.c | |||
@@ -70,6 +70,12 @@ | |||
70 | 70 | ||
71 | #define is_r15(insn, bitpos) (((insn) & (0xf << bitpos)) == (0xf << bitpos)) | 71 | #define is_r15(insn, bitpos) (((insn) & (0xf << bitpos)) == (0xf << bitpos)) |
72 | 72 | ||
73 | /* | ||
74 | * Test if load/store instructions writeback the address register. | ||
75 | * if P (bit 24) == 0 or W (bit 21) == 1 | ||
76 | */ | ||
77 | #define is_writeback(insn) ((insn ^ 0x01000000) & 0x01200000) | ||
78 | |||
73 | #define PSR_fs (PSR_f|PSR_s) | 79 | #define PSR_fs (PSR_f|PSR_s) |
74 | 80 | ||
75 | #define KPROBE_RETURN_INSTRUCTION 0xe1a0f00e /* mov pc, lr */ | 81 | #define KPROBE_RETURN_INSTRUCTION 0xe1a0f00e /* mov pc, lr */ |
@@ -886,6 +892,9 @@ prep_emulate_ldr_str(kprobe_opcode_t insn, struct arch_specific_insn *asi) | |||
886 | int not_imm = (insn & (1 << 26)) ? (insn & (1 << 25)) | 892 | int not_imm = (insn & (1 << 26)) ? (insn & (1 << 25)) |
887 | : (~insn & (1 << 22)); | 893 | : (~insn & (1 << 22)); |
888 | 894 | ||
895 | if (is_writeback(insn) && is_r15(insn, 16)) | ||
896 | return INSN_REJECTED; /* Writeback to PC */ | ||
897 | |||
889 | insn &= 0xfff00fff; | 898 | insn &= 0xfff00fff; |
890 | insn |= 0x00001000; /* Rn = r0, Rd = r1 */ | 899 | insn |= 0x00001000; /* Rn = r0, Rd = r1 */ |
891 | if (not_imm) { | 900 | if (not_imm) { |
@@ -1167,6 +1176,11 @@ space_cccc_000x(kprobe_opcode_t insn, struct arch_specific_insn *asi) | |||
1167 | 1176 | ||
1168 | } else if ((insn & 0x0e1000d0) == 0x00000d0) { | 1177 | } else if ((insn & 0x0e1000d0) == 0x00000d0) { |
1169 | /* STRD/LDRD */ | 1178 | /* STRD/LDRD */ |
1179 | if ((insn & 0x0000e000) == 0x0000e000) | ||
1180 | return INSN_REJECTED; /* Rd is LR or PC */ | ||
1181 | if (is_writeback(insn) && is_r15(insn, 16)) | ||
1182 | return INSN_REJECTED; /* Writeback to PC */ | ||
1183 | |||
1170 | insn &= 0xfff00fff; | 1184 | insn &= 0xfff00fff; |
1171 | insn |= 0x00002000; /* Rn = r0, Rd = r2 */ | 1185 | insn |= 0x00002000; /* Rn = r0, Rd = r2 */ |
1172 | if (insn & (1 << 22)) { | 1186 | if (insn & (1 << 22)) { |
@@ -1180,6 +1194,9 @@ space_cccc_000x(kprobe_opcode_t insn, struct arch_specific_insn *asi) | |||
1180 | return INSN_GOOD; | 1194 | return INSN_GOOD; |
1181 | } | 1195 | } |
1182 | 1196 | ||
1197 | /* LDRH/STRH/LDRSB/LDRSH */ | ||
1198 | if (is_r15(insn, 12)) | ||
1199 | return INSN_REJECTED; /* Rd is PC */ | ||
1183 | return prep_emulate_ldr_str(insn, asi); | 1200 | return prep_emulate_ldr_str(insn, asi); |
1184 | } | 1201 | } |
1185 | 1202 | ||