mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-02-17 04:16:58 -05:00
For X86_FEATURE_SMAP alternatives which replace NOP with STAC or CLAC,
uaccess validation skips the NOP branch to avoid following impossible
code paths, e.g. where a STAC would be patched but a CLAC wouldn't.
However, it's not safe to assume an X86_FEATURE_SMAP alternative is
patching STAC/CLAC. There can be other alternatives, like
static_cpu_has(), where both branches need to be validated.
Fix that by repurposing ANNOTATE_IGNORE_ALTERNATIVE for skipping either
original instructions or new ones. This is a more generic approach
which enables the removal of the feature checking hacks and the
insn->ignore bit.
Fixes the following warnings:
arch/x86/mm/fault.o: warning: objtool: do_user_addr_fault+0x8ec: __stack_chk_fail() missing __noreturn in .c/.h or NORETURN() in noreturns.h
arch/x86/mm/fault.o: warning: objtool: do_user_addr_fault+0x8f1: unreachable instruction
[ mingo: Fix up conflicts with recent x86 changes. ]
Fixes: ea24213d80 ("objtool: Add UACCESS validation")
Reported-by: kernel test robot <lkp@intel.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Link: https://lore.kernel.org/r/de0621ca242130156a55d5d74fed86994dfa4c9c.1742852846.git.jpoimboe@kernel.org
Closes: https://lore.kernel.org/oe-kbuild-all/202503181736.zkZUBv4N-lkp@intel.com/
129 lines
2.8 KiB
C
129 lines
2.8 KiB
C
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
/*
|
|
* Copyright (C) 2017 Josh Poimboeuf <jpoimboe@redhat.com>
|
|
*/
|
|
|
|
#ifndef _CHECK_H
|
|
#define _CHECK_H
|
|
|
|
#include <stdbool.h>
|
|
#include <objtool/cfi.h>
|
|
#include <objtool/arch.h>
|
|
|
|
struct insn_state {
|
|
struct cfi_state cfi;
|
|
unsigned int uaccess_stack;
|
|
bool uaccess;
|
|
bool df;
|
|
bool noinstr;
|
|
s8 instr;
|
|
};
|
|
|
|
struct alt_group {
|
|
/*
|
|
* Pointer from a replacement group to the original group. NULL if it
|
|
* *is* the original group.
|
|
*/
|
|
struct alt_group *orig_group;
|
|
|
|
/* First and last instructions in the group */
|
|
struct instruction *first_insn, *last_insn, *nop;
|
|
|
|
/*
|
|
* Byte-offset-addressed len-sized array of pointers to CFI structs.
|
|
* This is shared with the other alt_groups in the same alternative.
|
|
*/
|
|
struct cfi_state **cfi;
|
|
|
|
bool ignore;
|
|
};
|
|
|
|
#define INSN_CHUNK_BITS 8
|
|
#define INSN_CHUNK_SIZE (1 << INSN_CHUNK_BITS)
|
|
#define INSN_CHUNK_MAX (INSN_CHUNK_SIZE - 1)
|
|
|
|
struct instruction {
|
|
struct hlist_node hash;
|
|
struct list_head call_node;
|
|
struct section *sec;
|
|
unsigned long offset;
|
|
unsigned long immediate;
|
|
|
|
u8 len;
|
|
u8 prev_len;
|
|
u8 type;
|
|
s8 instr;
|
|
|
|
u32 idx : INSN_CHUNK_BITS,
|
|
dead_end : 1,
|
|
ignore_alts : 1,
|
|
hint : 1,
|
|
save : 1,
|
|
restore : 1,
|
|
retpoline_safe : 1,
|
|
noendbr : 1,
|
|
unret : 1,
|
|
visited : 4,
|
|
no_reloc : 1;
|
|
/* 10 bit hole */
|
|
|
|
struct alt_group *alt_group;
|
|
struct instruction *jump_dest;
|
|
struct instruction *first_jump_src;
|
|
union {
|
|
struct symbol *_call_dest;
|
|
struct {
|
|
struct reloc *_jump_table;
|
|
unsigned long _jump_table_size;
|
|
};
|
|
};
|
|
struct alternative *alts;
|
|
struct symbol *sym;
|
|
struct stack_op *stack_ops;
|
|
struct cfi_state *cfi;
|
|
};
|
|
|
|
static inline struct symbol *insn_func(struct instruction *insn)
|
|
{
|
|
struct symbol *sym = insn->sym;
|
|
|
|
if (sym && sym->type != STT_FUNC)
|
|
sym = NULL;
|
|
|
|
return sym;
|
|
}
|
|
|
|
#define VISITED_BRANCH 0x01
|
|
#define VISITED_BRANCH_UACCESS 0x02
|
|
#define VISITED_BRANCH_MASK 0x03
|
|
#define VISITED_UNRET 0x04
|
|
|
|
static inline bool is_static_jump(struct instruction *insn)
|
|
{
|
|
return insn->type == INSN_JUMP_CONDITIONAL ||
|
|
insn->type == INSN_JUMP_UNCONDITIONAL;
|
|
}
|
|
|
|
static inline bool is_dynamic_jump(struct instruction *insn)
|
|
{
|
|
return insn->type == INSN_JUMP_DYNAMIC ||
|
|
insn->type == INSN_JUMP_DYNAMIC_CONDITIONAL;
|
|
}
|
|
|
|
static inline bool is_jump(struct instruction *insn)
|
|
{
|
|
return is_static_jump(insn) || is_dynamic_jump(insn);
|
|
}
|
|
|
|
struct instruction *find_insn(struct objtool_file *file,
|
|
struct section *sec, unsigned long offset);
|
|
|
|
struct instruction *next_insn_same_sec(struct objtool_file *file, struct instruction *insn);
|
|
|
|
#define sec_for_each_insn(file, _sec, insn) \
|
|
for (insn = find_insn(file, _sec, 0); \
|
|
insn && insn->sec == _sec; \
|
|
insn = next_insn_same_sec(file, insn))
|
|
|
|
#endif /* _CHECK_H */
|