Add PUSHF / POPF state.uaccess restore logic. This makes user_access_save() / user_access_restore() 'work' with objtool.
XXX: should be merged with the previous patch such that KASAN doesn't explode in between. Split for review. Signed-off-by: Peter Zijlstra (Intel) <pet...@infradead.org> --- tools/objtool/arch.h | 2 ++ tools/objtool/arch/x86/decode.c | 4 ++-- tools/objtool/check.c | 30 ++++++++++++++++++++++++++---- tools/objtool/check.h | 1 + 4 files changed, 31 insertions(+), 6 deletions(-) --- a/tools/objtool/arch.h +++ b/tools/objtool/arch.h @@ -43,6 +43,7 @@ enum op_dest_type { OP_DEST_REG_INDIRECT, OP_DEST_MEM, OP_DEST_PUSH, + OP_DEST_PUSHF, OP_DEST_LEAVE, }; @@ -57,6 +58,7 @@ enum op_src_type { OP_SRC_REG_INDIRECT, OP_SRC_CONST, OP_SRC_POP, + OP_SRC_POPF, OP_SRC_ADD, OP_SRC_AND, }; --- a/tools/objtool/arch/x86/decode.c +++ b/tools/objtool/arch/x86/decode.c @@ -357,13 +357,13 @@ int arch_decode_instruction(struct elf * /* pushf */ *type = INSN_STACK; op->src.type = OP_SRC_CONST; - op->dest.type = OP_DEST_PUSH; + op->dest.type = OP_DEST_PUSHF; break; case 0x9d: /* popf */ *type = INSN_STACK; - op->src.type = OP_SRC_POP; + op->src.type = OP_SRC_POPF; op->dest.type = OP_DEST_MEM; break; --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -1392,11 +1392,11 @@ static int update_insn_state_regs(struct return 0; /* push */ - if (op->dest.type == OP_DEST_PUSH) + if (op->dest.type == OP_DEST_PUSH || op->dest.type == OP_DEST_PUSHF) cfa->offset += 8; /* pop */ - if (op->src.type == OP_SRC_POP) + if (op->src.type == OP_SRC_POP || op->src.type == OP_SRC_POPF) cfa->offset -= 8; /* add immediate to sp */ @@ -1653,6 +1653,7 @@ static int update_insn_state(struct inst break; case OP_SRC_POP: + case OP_SRC_POPF: if (!state->drap && op->dest.type == OP_DEST_REG && op->dest.reg == cfa->base) { @@ -1717,6 +1718,7 @@ static int update_insn_state(struct inst break; case OP_DEST_PUSH: + case OP_DEST_PUSHF: state->stack_size += 8; if (cfa->base == CFI_SP) cfa->offset += 8; @@ -1807,7 +1809,7 @@ static int update_insn_state(struct inst break; case OP_DEST_MEM: - if (op->src.type != OP_SRC_POP) { + if (op->src.type != OP_SRC_POP && op->src.type != OP_SRC_POPF) { WARN_FUNC("unknown stack-related memory operation", insn->sec, insn->offset); return -1; @@ -2109,6 +2111,26 @@ static int validate_branch(struct objtoo if (update_insn_state(insn, &state)) return 1; + if (insn->stack_op.dest.type == OP_DEST_PUSHF) { + if (!state.uaccess_stack) { + state.uaccess_stack = 1; + } else if (state.uaccess_stack >> 31) { + WARN_FUNC("PUSHF stack exhausted", sec, insn->offset); + return 1; + } + state.uaccess_stack <<= 1; + state.uaccess_stack |= state.uaccess; + } + + if (insn->stack_op.src.type == OP_SRC_POPF) { + if (state.uaccess_stack) { + state.uaccess = state.uaccess_stack & 1; + state.uaccess_stack >>= 1; + if (state.uaccess_stack == 1) + state.uaccess_stack = 0; + } + } + break; case INSN_STAC: @@ -2126,7 +2148,7 @@ static int validate_branch(struct objtoo return 1; } - if (func_uaccess_safe(func)) { + if (func_uaccess_safe(func) && !state.uaccess_stack) { WARN_FUNC("UACCESS-safe disables UACCESS", sec, insn->offset); return 1; } --- a/tools/objtool/check.h +++ b/tools/objtool/check.h @@ -32,6 +32,7 @@ struct insn_state { unsigned char type; bool bp_scratch; bool drap, end, uaccess; + unsigned int uaccess_stack; int drap_reg, drap_offset; struct cfi_reg vals[CFI_NUM_REGS]; };