| From aa6369a2a2af3643f9a44217a98078ce4c0b71a7 Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Thu, 25 Mar 2021 19:08:31 +0900 |
| Subject: x86/kprobes: Fix to check non boostable prefixes correctly |
| |
| From: Masami Hiramatsu <mhiramat@kernel.org> |
| |
| [ Upstream commit 6dd3b8c9f58816a1354be39559f630cd1bd12159 ] |
| |
| There are 2 bugs in the can_boost() function because of using |
| x86 insn decoder. Since the insn->opcode never has a prefix byte, |
| it can not find CS override prefix in it. And the insn->attr is |
| the attribute of the opcode, thus inat_is_address_size_prefix( |
| insn->attr) always returns false. |
| |
| Fix those by checking each prefix bytes with for_each_insn_prefix |
| loop and getting the correct attribute for each prefix byte. |
| Also, this removes unlikely, because this is a slow path. |
| |
| Fixes: a8d11cd0714f ("kprobes/x86: Consolidate insn decoder users for copying code") |
| Signed-off-by: Masami Hiramatsu <mhiramat@kernel.org> |
| Signed-off-by: Ingo Molnar <mingo@kernel.org> |
| Link: https://lore.kernel.org/r/161666691162.1120877.2808435205294352583.stgit@devnote2 |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| arch/x86/kernel/kprobes/core.c | 17 ++++++++++++----- |
| 1 file changed, 12 insertions(+), 5 deletions(-) |
| |
| diff --git a/arch/x86/kernel/kprobes/core.c b/arch/x86/kernel/kprobes/core.c |
| index a65e9e97857f..4e81d86a1470 100644 |
| --- a/arch/x86/kernel/kprobes/core.c |
| +++ b/arch/x86/kernel/kprobes/core.c |
| @@ -159,6 +159,8 @@ NOKPROBE_SYMBOL(skip_prefixes); |
| int can_boost(struct insn *insn, void *addr) |
| { |
| kprobe_opcode_t opcode; |
| + insn_byte_t prefix; |
| + int i; |
| |
| if (search_exception_tables((unsigned long)addr)) |
| return 0; /* Page fault may occur on this address. */ |
| @@ -171,9 +173,14 @@ int can_boost(struct insn *insn, void *addr) |
| if (insn->opcode.nbytes != 1) |
| return 0; |
| |
| - /* Can't boost Address-size override prefix */ |
| - if (unlikely(inat_is_address_size_prefix(insn->attr))) |
| - return 0; |
| + for_each_insn_prefix(insn, i, prefix) { |
| + insn_attr_t attr; |
| + |
| + attr = inat_get_opcode_attribute(prefix); |
| + /* Can't boost Address-size override prefix and CS override prefix */ |
| + if (prefix == 0x2e || inat_is_address_size_prefix(attr)) |
| + return 0; |
| + } |
| |
| opcode = insn->opcode.bytes[0]; |
| |
| @@ -198,8 +205,8 @@ int can_boost(struct insn *insn, void *addr) |
| /* clear and set flags are boostable */ |
| return (opcode == 0xf5 || (0xf7 < opcode && opcode < 0xfe)); |
| default: |
| - /* CS override prefix and call are not boostable */ |
| - return (opcode != 0x2e && opcode != 0x9a); |
| + /* call is not boostable */ |
| + return opcode != 0x9a; |
| } |
| } |
| |
| -- |
| 2.30.2 |
| |