Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

objtool: Fix X86_FEATURE_SMAP alternative handling

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: ea24213d8088 ("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/

authored by

Josh Poimboeuf and committed by
Ingo Molnar
1154bbd3 c84301d7

+37 -89
+4 -2
arch/x86/include/asm/arch_hweight.h
··· 16 16 { 17 17 unsigned int res; 18 18 19 - asm_inline (ALTERNATIVE("call __sw_hweight32", 19 + asm_inline (ALTERNATIVE(ANNOTATE_IGNORE_ALTERNATIVE 20 + "call __sw_hweight32", 20 21 "popcntl %[val], %[cnt]", X86_FEATURE_POPCNT) 21 22 : [cnt] "=" REG_OUT (res), ASM_CALL_CONSTRAINT 22 23 : [val] REG_IN (w)); ··· 46 45 { 47 46 unsigned long res; 48 47 49 - asm_inline (ALTERNATIVE("call __sw_hweight64", 48 + asm_inline (ALTERNATIVE(ANNOTATE_IGNORE_ALTERNATIVE 49 + "call __sw_hweight64", 50 50 "popcntq %[val], %[cnt]", X86_FEATURE_POPCNT) 51 51 : [cnt] "=" REG_OUT (res), ASM_CALL_CONSTRAINT 52 52 : [val] REG_IN (w));
+15 -8
arch/x86/include/asm/smap.h
··· 16 16 #ifdef __ASSEMBLER__ 17 17 18 18 #define ASM_CLAC \ 19 - ALTERNATIVE "", "clac", X86_FEATURE_SMAP 19 + ALTERNATIVE __stringify(ANNOTATE_IGNORE_ALTERNATIVE), "clac", X86_FEATURE_SMAP 20 20 21 21 #define ASM_STAC \ 22 - ALTERNATIVE "", "stac", X86_FEATURE_SMAP 22 + ALTERNATIVE __stringify(ANNOTATE_IGNORE_ALTERNATIVE), "stac", X86_FEATURE_SMAP 23 23 24 24 #else /* __ASSEMBLER__ */ 25 25 26 26 static __always_inline void clac(void) 27 27 { 28 28 /* Note: a barrier is implicit in alternative() */ 29 - alternative("", "clac", X86_FEATURE_SMAP); 29 + alternative(ANNOTATE_IGNORE_ALTERNATIVE "", "clac", X86_FEATURE_SMAP); 30 30 } 31 31 32 32 static __always_inline void stac(void) 33 33 { 34 34 /* Note: a barrier is implicit in alternative() */ 35 - alternative("", "stac", X86_FEATURE_SMAP); 35 + alternative(ANNOTATE_IGNORE_ALTERNATIVE "", "stac", X86_FEATURE_SMAP); 36 36 } 37 37 38 38 static __always_inline unsigned long smap_save(void) ··· 40 40 unsigned long flags; 41 41 42 42 asm volatile ("# smap_save\n\t" 43 - ALTERNATIVE("", "pushf; pop %0; " "clac" "\n\t", 43 + ALTERNATIVE(ANNOTATE_IGNORE_ALTERNATIVE 44 + "", "pushf; pop %0; clac", 44 45 X86_FEATURE_SMAP) 45 46 : "=rm" (flags) : : "memory", "cc"); 46 47 ··· 51 50 static __always_inline void smap_restore(unsigned long flags) 52 51 { 53 52 asm volatile ("# smap_restore\n\t" 54 - ALTERNATIVE("", "push %0; popf\n\t", 53 + ALTERNATIVE(ANNOTATE_IGNORE_ALTERNATIVE 54 + "", "push %0; popf", 55 55 X86_FEATURE_SMAP) 56 56 : : "g" (flags) : "memory", "cc"); 57 57 } 58 58 59 59 /* These macros can be used in asm() statements */ 60 60 #define ASM_CLAC \ 61 - ALTERNATIVE("", "clac", X86_FEATURE_SMAP) 61 + ALTERNATIVE(ANNOTATE_IGNORE_ALTERNATIVE "", "clac", X86_FEATURE_SMAP) 62 62 #define ASM_STAC \ 63 - ALTERNATIVE("", "stac", X86_FEATURE_SMAP) 63 + ALTERNATIVE(ANNOTATE_IGNORE_ALTERNATIVE "", "stac", X86_FEATURE_SMAP) 64 + 65 + #define ASM_CLAC_UNSAFE \ 66 + ALTERNATIVE("", ANNOTATE_IGNORE_ALTERNATIVE "clac", X86_FEATURE_SMAP) 67 + #define ASM_STAC_UNSAFE \ 68 + ALTERNATIVE("", ANNOTATE_IGNORE_ALTERNATIVE "stac", X86_FEATURE_SMAP) 64 69 65 70 #endif /* __ASSEMBLER__ */ 66 71
+2 -4
arch/x86/include/asm/xen/hypercall.h
··· 231 231 * Suppress objtool seeing the STAC/CLAC and getting confused about it 232 232 * calling random code with AC=1. 233 233 */ 234 - asm volatile(ANNOTATE_IGNORE_ALTERNATIVE 235 - ASM_STAC ::: "memory", "flags"); 234 + asm volatile(ASM_STAC_UNSAFE ::: "memory", "flags"); 236 235 } 237 236 238 237 static __always_inline void __xen_clac(void) 239 238 { 240 - asm volatile(ANNOTATE_IGNORE_ALTERNATIVE 241 - ASM_CLAC ::: "memory", "flags"); 239 + asm volatile(ASM_CLAC_UNSAFE ::: "memory", "flags"); 242 240 } 243 241 244 242 static inline long
+1 -32
tools/objtool/arch/x86/special.c
··· 5 5 #include <objtool/builtin.h> 6 6 #include <objtool/warn.h> 7 7 8 - #define X86_FEATURE_POPCNT (4 * 32 + 23) 9 - #define X86_FEATURE_SMAP (9 * 32 + 20) 10 - 11 - void arch_handle_alternative(unsigned short feature, struct special_alt *alt) 8 + void arch_handle_alternative(struct special_alt *alt) 12 9 { 13 10 static struct special_alt *group, *prev; 14 11 ··· 29 32 } else group = alt; 30 33 31 34 prev = alt; 32 - 33 - switch (feature) { 34 - case X86_FEATURE_SMAP: 35 - /* 36 - * If UACCESS validation is enabled; force that alternative; 37 - * otherwise force it the other way. 38 - * 39 - * What we want to avoid is having both the original and the 40 - * alternative code flow at the same time, in that case we can 41 - * find paths that see the STAC but take the NOP instead of 42 - * CLAC and the other way around. 43 - */ 44 - if (opts.uaccess) 45 - alt->skip_orig = true; 46 - else 47 - alt->skip_alt = true; 48 - break; 49 - case X86_FEATURE_POPCNT: 50 - /* 51 - * It has been requested that we don't validate the !POPCNT 52 - * feature path which is a "very very small percentage of 53 - * machines". 54 - */ 55 - alt->skip_orig = true; 56 - break; 57 - default: 58 - break; 59 - } 60 35 } 61 36 62 37 bool arch_support_alt_relocation(struct special_alt *special_alt,
+10 -29
tools/objtool/check.c
··· 25 25 struct alternative { 26 26 struct alternative *next; 27 27 struct instruction *insn; 28 - bool skip_orig; 29 28 }; 30 29 31 30 static unsigned long nr_cfi, nr_cfi_reused, nr_cfi_cache; ··· 1695 1696 orig_alt_group->first_insn = orig_insn; 1696 1697 orig_alt_group->last_insn = last_orig_insn; 1697 1698 orig_alt_group->nop = NULL; 1699 + orig_alt_group->ignore = orig_insn->ignore_alts; 1698 1700 } else { 1699 1701 if (orig_alt_group->last_insn->offset + orig_alt_group->last_insn->len - 1700 1702 orig_alt_group->first_insn->offset != special_alt->orig_len) { ··· 1735 1735 nop->type = INSN_NOP; 1736 1736 nop->sym = orig_insn->sym; 1737 1737 nop->alt_group = new_alt_group; 1738 - nop->ignore = orig_insn->ignore_alts; 1739 1738 } 1740 1739 1741 1740 if (!special_alt->new_len) { ··· 1751 1752 1752 1753 last_new_insn = insn; 1753 1754 1754 - insn->ignore = orig_insn->ignore_alts; 1755 1755 insn->sym = orig_insn->sym; 1756 1756 insn->alt_group = new_alt_group; 1757 1757 ··· 1797 1799 new_alt_group->first_insn = *new_insn; 1798 1800 new_alt_group->last_insn = last_new_insn; 1799 1801 new_alt_group->nop = nop; 1802 + new_alt_group->ignore = (*new_insn)->ignore_alts; 1800 1803 new_alt_group->cfi = orig_alt_group->cfi; 1801 1804 return 0; 1802 1805 } ··· 1915 1916 } 1916 1917 1917 1918 alt->insn = new_insn; 1918 - alt->skip_orig = special_alt->skip_orig; 1919 - orig_insn->ignore_alts |= special_alt->skip_alt; 1920 1919 alt->next = orig_insn->alts; 1921 1920 orig_insn->alts = alt; 1922 1921 ··· 2292 2295 static int __annotate_early(struct objtool_file *file, int type, struct instruction *insn) 2293 2296 { 2294 2297 switch (type) { 2298 + 2299 + /* Must be before add_special_section_alts() */ 2295 2300 case ANNOTYPE_IGNORE_ALTS: 2296 2301 insn->ignore_alts = true; 2297 2302 break; ··· 3487 3488 return 1; 3488 3489 } 3489 3490 3490 - if (func && insn->ignore) { 3491 - WARN_INSN(insn, "BUG: why am I validating an ignored function?"); 3492 - return 1; 3493 - } 3494 - 3495 3491 visited = VISITED_BRANCH << state.uaccess; 3496 3492 if (insn->visited & VISITED_BRANCH_MASK) { 3497 3493 if (!insn->hint && !insn_cfi_match(insn, &state.cfi)) ··· 3558 3564 if (propagate_alt_cfi(file, insn)) 3559 3565 return 1; 3560 3566 3561 - if (!insn->ignore_alts && insn->alts) { 3562 - bool skip_orig = false; 3563 - 3567 + if (insn->alts) { 3564 3568 for (alt = insn->alts; alt; alt = alt->next) { 3565 - if (alt->skip_orig) 3566 - skip_orig = true; 3567 - 3568 3569 ret = validate_branch(file, func, alt->insn, state); 3569 3570 if (ret) { 3570 3571 BT_INSN(insn, "(alt)"); 3571 3572 return ret; 3572 3573 } 3573 3574 } 3574 - 3575 - if (skip_orig) 3576 - return 0; 3577 3575 } 3576 + 3577 + if (insn->alt_group && insn->alt_group->ignore) 3578 + return 0; 3578 3579 3579 3580 if (handle_insn_ops(insn, next_insn, &state)) 3580 3581 return 1; ··· 3757 3768 3758 3769 insn->visited |= VISITED_UNRET; 3759 3770 3760 - if (!insn->ignore_alts && insn->alts) { 3771 + if (insn->alts) { 3761 3772 struct alternative *alt; 3762 - bool skip_orig = false; 3763 - 3764 3773 for (alt = insn->alts; alt; alt = alt->next) { 3765 - if (alt->skip_orig) 3766 - skip_orig = true; 3767 - 3768 3774 ret = validate_unret(file, alt->insn); 3769 3775 if (ret) { 3770 3776 BT_INSN(insn, "(alt)"); 3771 3777 return ret; 3772 3778 } 3773 3779 } 3774 - 3775 - if (skip_orig) 3776 - return 0; 3777 3780 } 3778 3781 3779 3782 switch (insn->type) { ··· 3916 3935 struct instruction *prev_insn; 3917 3936 int i; 3918 3937 3919 - if (insn->ignore || insn->type == INSN_NOP || insn->type == INSN_TRAP || (func && func->ignore)) 3938 + if (insn->type == INSN_NOP || insn->type == INSN_TRAP || (func && func->ignore)) 3920 3939 return true; 3921 3940 3922 3941 /*
+2 -1
tools/objtool/include/objtool/check.h
··· 34 34 * This is shared with the other alt_groups in the same alternative. 35 35 */ 36 36 struct cfi_state **cfi; 37 + 38 + bool ignore; 37 39 }; 38 40 39 41 #define INSN_CHUNK_BITS 8 ··· 56 54 57 55 u32 idx : INSN_CHUNK_BITS, 58 56 dead_end : 1, 59 - ignore : 1, 60 57 ignore_alts : 1, 61 58 hint : 1, 62 59 save : 1,
+1 -3
tools/objtool/include/objtool/special.h
··· 16 16 struct list_head list; 17 17 18 18 bool group; 19 - bool skip_orig; 20 - bool skip_alt; 21 19 bool jump_or_nop; 22 20 u8 key_addend; 23 21 ··· 30 32 31 33 int special_get_alts(struct elf *elf, struct list_head *alts); 32 34 33 - void arch_handle_alternative(unsigned short feature, struct special_alt *alt); 35 + void arch_handle_alternative(struct special_alt *alt); 34 36 35 37 bool arch_support_alt_relocation(struct special_alt *special_alt, 36 38 struct instruction *insn,
+2 -10
tools/objtool/special.c
··· 54 54 {}, 55 55 }; 56 56 57 - void __weak arch_handle_alternative(unsigned short feature, struct special_alt *alt) 57 + void __weak arch_handle_alternative(struct special_alt *alt) 58 58 { 59 59 } 60 60 ··· 92 92 93 93 reloc_to_sec_off(orig_reloc, &alt->orig_sec, &alt->orig_off); 94 94 95 - if (entry->feature) { 96 - unsigned short feature; 97 - 98 - feature = bswap_if_needed(elf, 99 - *(unsigned short *)(sec->data->d_buf + 100 - offset + 101 - entry->feature)); 102 - arch_handle_alternative(feature, alt); 103 - } 95 + arch_handle_alternative(alt); 104 96 105 97 if (!entry->group || alt->new_len) { 106 98 new_reloc = find_reloc_by_dest(elf, sec, offset + entry->new);