Re: [RFC][PATCH] objtool: STAC/CLAC validation

Peter Zijlstra
2019-02-25T13:21:17Z

On Mon, Feb 25, 2019 at 12:47:00AM -0800, hpa@zytor.com wrote:
> It doesn't have to understand the contents of the memop, but it seems
> that the presence of a modrm with mode ≠ 3 should be plenty. It needs
> to know that much in order to know the length of instructions anyway.
> For extra credit, ignore LEA or hinting instructions.

A little something like so then?

arch/x86/kernel/fpu/signal.o: warning: objtool: .altinstr_replacement+0x9c: UACCESS disable without MEMOPs: copy_fpstate_to_sigframe()

00000000023c 000200000002 R_X86_64_PC32 0000000000000000 .text + 604
000000000240 000700000002 R_X86_64_PC32 0000000000000000 .altinstr_replacement + 99
000000000249 000200000002 R_X86_64_PC32 0000000000000000 .text + 610
00000000024d 000700000002 R_X86_64_PC32 0000000000000000 .altinstr_replacement + 9c

.text

604: 90 nop
605: 90 nop
606: 90 nop
607: 83 ce 03 or $0x3,%esi
60a: 89 b3 00 02 00 00 mov %esi,0x200(%rbx)
610: 90 nop
611: 90 nop
612: 90 nop

.altinstr_replacement

99: 0f 01 cb stac
9c: 0f 01 ca clac

Which looks like the tool failed to recognise that MOV as a memop.

---
--- a/tools/objtool/arch.h
+++ b/tools/objtool/arch.h
@@ -37,7 +37,8 @@
#define INSN_CLAC 12
#define INSN_STD 13
#define INSN_CLD 14
-#define INSN_OTHER 15
+#define INSN_MEMOP 15
+#define INSN_OTHER 16
#define INSN_LAST INSN_OTHER

enum op_dest_type {
--- a/tools/objtool/arch/x86/decode.c
+++ b/tools/objtool/arch/x86/decode.c
@@ -123,6 +123,9 @@ int arch_decode_instruction(struct elf *
modrm_mod = X86_MODRM_MOD(modrm);
modrm_reg = X86_MODRM_REG(modrm);
modrm_rm = X86_MODRM_RM(modrm);
+
+ if (modrm_mod != 3)
+ *type = INSN_MEMOP;
}

if (insn.sib.nbytes)
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -2047,6 +2047,7 @@ static int validate_branch(struct objtoo
WARN_FUNC("recursive UACCESS enable", sec, insn->offset);

state.uaccess = true;
+ state.memop = false;
break;

case INSN_CLAC:
@@ -2058,6 +2059,9 @@ static int validate_branch(struct objtoo
break;
}

+ if (!state.memop && insn->func)
+ WARN_FUNC("UACCESS disable without MEMOPs: %s()", sec, insn->offset, insn->func->name);
+
state.uaccess = false;
break;

@@ -2075,6 +2079,10 @@ static int validate_branch(struct objtoo
state.df = false;
break;

+ case INSN_MEMOP:
+ state.memop = true;
+ break;
+
default:
break;
}
--- a/tools/objtool/check.h
+++ b/tools/objtool/check.h
@@ -31,7 +31,7 @@ struct insn_state {
int stack_size;
unsigned char type;
bool bp_scratch;
- bool drap, end, uaccess, uaccess_safe, df;
+ bool drap, end, uaccess, uaccess_safe, df, memop;
int drap_reg, drap_offset;
struct cfi_reg vals[CFI_NUM_REGS];
};