Commit 6ec62961 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'core-objtool-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull objtool updates from Ingo Molnar:
 "This is a series from Peter Zijlstra that adds x86 build-time uaccess
  validation of SMAP to objtool, which will detect and warn about the
  following uaccess API usage bugs and weirdnesses:

   - call to %s() with UACCESS enabled
   - return with UACCESS enabled
   - return with UACCESS disabled from a UACCESS-safe function
   - recursive UACCESS enable
   - redundant UACCESS disable
   - UACCESS-safe disables UACCESS

  As it turns out not leaking uaccess permissions outside the intended
  uaccess functionality is hard when the interfaces are complex and when
  such bugs are mostly dormant.

  As a bonus we now also check the DF flag. We had at least one
  high-profile bug in that area in the early days of Linux, and the
  checking is fairly simple. The checks performed and warnings emitted
  are:

   - call to %s() with DF set
   - return with DF set
   - return with modified stack frame
   - recursive STD
   - redundant CLD

  It's all x86-only for now, but later on this can also be used for PAN
  on ARM and objtool is fairly cross-platform in principle.

  While all warnings emitted by this new checking facility that got
  reported to us were fixed, there might be GCC version dependent
  warnings that were not reported yet - which we'll address, should they
  trigger.

  The warnings are non-fatal build warnings"

* 'core-objtool-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (27 commits)
  mm/uaccess: Use 'unsigned long' to placate UBSAN warnings on older GCC versions
  x86/uaccess: Dont leak the AC flag into __put_user() argument evaluation
  sched/x86_64: Don't save flags on context switch
  objtool: Add Direction Flag validation
  objtool: Add UACCESS validation
  objtool: Fix sibling call detection
  objtool: Rewrite alt->skip_orig
  objtool: Add --backtrace support
  objtool: Rewrite add_ignores()
  objtool: Handle function aliases
  objtool: Set insn->func for alternatives
  x86/uaccess, kcov: Disable stack protector
  x86/uaccess, ftrace: Fix ftrace_likely_update() vs. SMAP
  x86/uaccess, ubsan: Fix UBSAN vs. SMAP
  x86/uaccess, kasan: Fix KASAN vs SMAP
  x86/smap: Ditch __stringify()
  x86/uaccess: Introduce user_access_{save,restore}()
  x86/uaccess, signal: Fix AC=1 bloat
  x86/uaccess: Always inline user_access_begin()
  x86/uaccess, xen: Suppress SMAP warnings
  ...
parents 171c2bcb 29da93fe
...@@ -650,6 +650,7 @@ ENTRY(__switch_to_asm) ...@@ -650,6 +650,7 @@ ENTRY(__switch_to_asm)
pushl %ebx pushl %ebx
pushl %edi pushl %edi
pushl %esi pushl %esi
pushfl
/* switch stack */ /* switch stack */
movl %esp, TASK_threadsp(%eax) movl %esp, TASK_threadsp(%eax)
...@@ -672,6 +673,7 @@ ENTRY(__switch_to_asm) ...@@ -672,6 +673,7 @@ ENTRY(__switch_to_asm)
#endif #endif
/* restore callee-saved registers */ /* restore callee-saved registers */
popfl
popl %esi popl %esi
popl %edi popl %edi
popl %ebx popl %ebx
......
...@@ -61,9 +61,8 @@ ...@@ -61,9 +61,8 @@
} while (0) } while (0)
#define RELOAD_SEG(seg) { \ #define RELOAD_SEG(seg) { \
unsigned int pre = GET_SEG(seg); \ unsigned int pre = (seg) | 3; \
unsigned int cur = get_user_seg(seg); \ unsigned int cur = get_user_seg(seg); \
pre |= 3; \
if (pre != cur) \ if (pre != cur) \
set_user_seg(seg, pre); \ set_user_seg(seg, pre); \
} }
...@@ -72,6 +71,7 @@ static int ia32_restore_sigcontext(struct pt_regs *regs, ...@@ -72,6 +71,7 @@ static int ia32_restore_sigcontext(struct pt_regs *regs,
struct sigcontext_32 __user *sc) struct sigcontext_32 __user *sc)
{ {
unsigned int tmpflags, err = 0; unsigned int tmpflags, err = 0;
u16 gs, fs, es, ds;
void __user *buf; void __user *buf;
u32 tmp; u32 tmp;
...@@ -79,16 +79,10 @@ static int ia32_restore_sigcontext(struct pt_regs *regs, ...@@ -79,16 +79,10 @@ static int ia32_restore_sigcontext(struct pt_regs *regs,
current->restart_block.fn = do_no_restart_syscall; current->restart_block.fn = do_no_restart_syscall;
get_user_try { get_user_try {
/* gs = GET_SEG(gs);
* Reload fs and gs if they have changed in the signal fs = GET_SEG(fs);
* handler. This does not handle long fs/gs base changes in ds = GET_SEG(ds);
* the handler, but does not clobber them at least in the es = GET_SEG(es);
* normal case.
*/
RELOAD_SEG(gs);
RELOAD_SEG(fs);
RELOAD_SEG(ds);
RELOAD_SEG(es);
COPY(di); COPY(si); COPY(bp); COPY(sp); COPY(bx); COPY(di); COPY(si); COPY(bp); COPY(sp); COPY(bx);
COPY(dx); COPY(cx); COPY(ip); COPY(ax); COPY(dx); COPY(cx); COPY(ip); COPY(ax);
...@@ -106,6 +100,17 @@ static int ia32_restore_sigcontext(struct pt_regs *regs, ...@@ -106,6 +100,17 @@ static int ia32_restore_sigcontext(struct pt_regs *regs,
buf = compat_ptr(tmp); buf = compat_ptr(tmp);
} get_user_catch(err); } get_user_catch(err);
/*
* Reload fs and gs if they have changed in the signal
* handler. This does not handle long fs/gs base changes in
* the handler, but does not clobber them at least in the
* normal case.
*/
RELOAD_SEG(gs);
RELOAD_SEG(fs);
RELOAD_SEG(ds);
RELOAD_SEG(es);
err |= fpu__restore_sig(buf, 1); err |= fpu__restore_sig(buf, 1);
force_iret(); force_iret();
......
...@@ -19,6 +19,17 @@ ...@@ -19,6 +19,17 @@
.endm .endm
#endif #endif
/*
* objtool annotation to ignore the alternatives and only consider the original
* instruction(s).
*/
.macro ANNOTATE_IGNORE_ALTERNATIVE
.Lannotate_\@:
.pushsection .discard.ignore_alts
.long .Lannotate_\@ - .
.popsection
.endm
/* /*
* Issue one struct alt_instr descriptor entry (need to put it into * Issue one struct alt_instr descriptor entry (need to put it into
* the section .altinstructions, see below). This entry contains * the section .altinstructions, see below). This entry contains
......
...@@ -45,6 +45,16 @@ ...@@ -45,6 +45,16 @@
#define LOCK_PREFIX "" #define LOCK_PREFIX ""
#endif #endif
/*
* objtool annotation to ignore the alternatives and only consider the original
* instruction(s).
*/
#define ANNOTATE_IGNORE_ALTERNATIVE \
"999:\n\t" \
".pushsection .discard.ignore_alts\n\t" \
".long 999b - .\n\t" \
".popsection\n\t"
struct alt_instr { struct alt_instr {
s32 instr_offset; /* original instruction */ s32 instr_offset; /* original instruction */
s32 repl_offset; /* offset to replacement instruction */ s32 repl_offset; /* offset to replacement instruction */
......
...@@ -148,30 +148,6 @@ ...@@ -148,30 +148,6 @@
_ASM_PTR (entry); \ _ASM_PTR (entry); \
.popsection .popsection
.macro ALIGN_DESTINATION
/* check for bad alignment of destination */
movl %edi,%ecx
andl $7,%ecx
jz 102f /* already aligned */
subl $8,%ecx
negl %ecx
subl %ecx,%edx
100: movb (%rsi),%al
101: movb %al,(%rdi)
incq %rsi
incq %rdi
decl %ecx
jnz 100b
102:
.section .fixup,"ax"
103: addl %ecx,%edx /* ecx is zerorest also */
jmp copy_user_handle_tail
.previous
_ASM_EXTABLE_UA(100b, 103b)
_ASM_EXTABLE_UA(101b, 103b)
.endm
#else #else
# define _EXPAND_EXTABLE_HANDLE(x) #x # define _EXPAND_EXTABLE_HANDLE(x) #x
# define _ASM_EXTABLE_HANDLE(from, to, handler) \ # define _ASM_EXTABLE_HANDLE(from, to, handler) \
......
...@@ -10,6 +10,15 @@ ...@@ -10,6 +10,15 @@
#include <asm/cpufeatures.h> #include <asm/cpufeatures.h>
#include <asm/msr-index.h> #include <asm/msr-index.h>
/*
* This should be used immediately before a retpoline alternative. It tells
* objtool where the retpolines are so that it can make sense of the control
* flow by just reading the original instruction(s) and ignoring the
* alternatives.
*/
#define ANNOTATE_NOSPEC_ALTERNATIVE \
ANNOTATE_IGNORE_ALTERNATIVE
/* /*
* Fill the CPU return stack buffer. * Fill the CPU return stack buffer.
* *
...@@ -56,19 +65,6 @@ ...@@ -56,19 +65,6 @@
#ifdef __ASSEMBLY__ #ifdef __ASSEMBLY__
/*
* This should be used immediately before a retpoline alternative. It tells
* objtool where the retpolines are so that it can make sense of the control
* flow by just reading the original instruction(s) and ignoring the
* alternatives.
*/
.macro ANNOTATE_NOSPEC_ALTERNATIVE
.Lannotate_\@:
.pushsection .discard.nospec
.long .Lannotate_\@ - .
.popsection
.endm
/* /*
* This should be used immediately before an indirect jump/call. It tells * This should be used immediately before an indirect jump/call. It tells
* objtool the subsequent indirect jump/call is vouched safe for retpoline * objtool the subsequent indirect jump/call is vouched safe for retpoline
...@@ -152,12 +148,6 @@ ...@@ -152,12 +148,6 @@
#else /* __ASSEMBLY__ */ #else /* __ASSEMBLY__ */
#define ANNOTATE_NOSPEC_ALTERNATIVE \
"999:\n\t" \
".pushsection .discard.nospec\n\t" \
".long 999b - .\n\t" \
".popsection\n\t"
#define ANNOTATE_RETPOLINE_SAFE \ #define ANNOTATE_RETPOLINE_SAFE \
"999:\n\t" \ "999:\n\t" \
".pushsection .discard.retpoline_safe\n\t" \ ".pushsection .discard.retpoline_safe\n\t" \
......
...@@ -13,13 +13,12 @@ ...@@ -13,13 +13,12 @@
#ifndef _ASM_X86_SMAP_H #ifndef _ASM_X86_SMAP_H
#define _ASM_X86_SMAP_H #define _ASM_X86_SMAP_H
#include <linux/stringify.h>
#include <asm/nops.h> #include <asm/nops.h>
#include <asm/cpufeatures.h> #include <asm/cpufeatures.h>
/* "Raw" instruction opcodes */ /* "Raw" instruction opcodes */
#define __ASM_CLAC .byte 0x0f,0x01,0xca #define __ASM_CLAC ".byte 0x0f,0x01,0xca"
#define __ASM_STAC .byte 0x0f,0x01,0xcb #define __ASM_STAC ".byte 0x0f,0x01,0xcb"
#ifdef __ASSEMBLY__ #ifdef __ASSEMBLY__
...@@ -28,10 +27,10 @@ ...@@ -28,10 +27,10 @@
#ifdef CONFIG_X86_SMAP #ifdef CONFIG_X86_SMAP
#define ASM_CLAC \ #define ASM_CLAC \
ALTERNATIVE "", __stringify(__ASM_CLAC), X86_FEATURE_SMAP ALTERNATIVE "", __ASM_CLAC, X86_FEATURE_SMAP
#define ASM_STAC \ #define ASM_STAC \
ALTERNATIVE "", __stringify(__ASM_STAC), X86_FEATURE_SMAP ALTERNATIVE "", __ASM_STAC, X86_FEATURE_SMAP
#else /* CONFIG_X86_SMAP */ #else /* CONFIG_X86_SMAP */
...@@ -49,26 +48,46 @@ ...@@ -49,26 +48,46 @@
static __always_inline void clac(void) static __always_inline void clac(void)
{ {
/* Note: a barrier is implicit in alternative() */ /* Note: a barrier is implicit in alternative() */
alternative("", __stringify(__ASM_CLAC), X86_FEATURE_SMAP); alternative("", __ASM_CLAC, X86_FEATURE_SMAP);
} }
static __always_inline void stac(void) static __always_inline void stac(void)
{ {
/* Note: a barrier is implicit in alternative() */ /* Note: a barrier is implicit in alternative() */
alternative("", __stringify(__ASM_STAC), X86_FEATURE_SMAP); alternative("", __ASM_STAC, X86_FEATURE_SMAP);
}
static __always_inline unsigned long smap_save(void)
{
unsigned long flags;
asm volatile (ALTERNATIVE("", "pushf; pop %0; " __ASM_CLAC,
X86_FEATURE_SMAP)
: "=rm" (flags) : : "memory", "cc");
return flags;
}
static __always_inline void smap_restore(unsigned long flags)
{
asm volatile (ALTERNATIVE("", "push %0; popf", X86_FEATURE_SMAP)
: : "g" (flags) : "memory", "cc");
} }
/* These macros can be used in asm() statements */ /* These macros can be used in asm() statements */
#define ASM_CLAC \ #define ASM_CLAC \
ALTERNATIVE("", __stringify(__ASM_CLAC), X86_FEATURE_SMAP) ALTERNATIVE("", __ASM_CLAC, X86_FEATURE_SMAP)
#define ASM_STAC \ #define ASM_STAC \
ALTERNATIVE("", __stringify(__ASM_STAC), X86_FEATURE_SMAP) ALTERNATIVE("", __ASM_STAC, X86_FEATURE_SMAP)
#else /* CONFIG_X86_SMAP */ #else /* CONFIG_X86_SMAP */
static inline void clac(void) { } static inline void clac(void) { }
static inline void stac(void) { } static inline void stac(void) { }
static inline unsigned long smap_save(void) { return 0; }
static inline void smap_restore(unsigned long flags) { }
#define ASM_CLAC #define ASM_CLAC
#define ASM_STAC #define ASM_STAC
......
...@@ -46,6 +46,7 @@ struct inactive_task_frame { ...@@ -46,6 +46,7 @@ struct inactive_task_frame {
unsigned long r13; unsigned long r13;
unsigned long r12; unsigned long r12;
#else #else
unsigned long flags;
unsigned long si; unsigned long si;
unsigned long di; unsigned long di;
#endif #endif
......
...@@ -427,10 +427,11 @@ do { \ ...@@ -427,10 +427,11 @@ do { \
({ \ ({ \
__label__ __pu_label; \ __label__ __pu_label; \
int __pu_err = -EFAULT; \ int __pu_err = -EFAULT; \
__typeof__(*(ptr)) __pu_val; \ __typeof__(*(ptr)) __pu_val = (x); \
__pu_val = x; \ __typeof__(ptr) __pu_ptr = (ptr); \
__typeof__(size) __pu_size = (size); \
__uaccess_begin(); \ __uaccess_begin(); \
__put_user_size(__pu_val, (ptr), (size), __pu_label); \ __put_user_size(__pu_val, __pu_ptr, __pu_size, __pu_label); \
__pu_err = 0; \ __pu_err = 0; \
__pu_label: \ __pu_label: \
__uaccess_end(); \ __uaccess_end(); \
...@@ -705,7 +706,7 @@ extern struct movsl_mask { ...@@ -705,7 +706,7 @@ extern struct movsl_mask {
* checking before using them, but you have to surround them with the * checking before using them, but you have to surround them with the
* user_access_begin/end() pair. * user_access_begin/end() pair.
*/ */
static __must_check inline bool user_access_begin(const void __user *ptr, size_t len) static __must_check __always_inline bool user_access_begin(const void __user *ptr, size_t len)
{ {
if (unlikely(!access_ok(ptr,len))) if (unlikely(!access_ok(ptr,len)))
return 0; return 0;
...@@ -715,6 +716,9 @@ static __must_check inline bool user_access_begin(const void __user *ptr, size_t ...@@ -715,6 +716,9 @@ static __must_check inline bool user_access_begin(const void __user *ptr, size_t
#define user_access_begin(a,b) user_access_begin(a,b) #define user_access_begin(a,b) user_access_begin(a,b)
#define user_access_end() __uaccess_end() #define user_access_end() __uaccess_end()
#define user_access_save() smap_save()
#define user_access_restore(x) smap_restore(x)
#define unsafe_put_user(x, ptr, label) \ #define unsafe_put_user(x, ptr, label) \
__put_user_size((__typeof__(*(ptr)))(x), (ptr), sizeof(*(ptr)), label) __put_user_size((__typeof__(*(ptr)))(x), (ptr), sizeof(*(ptr)), label)
......
...@@ -207,9 +207,6 @@ __copy_from_user_flushcache(void *dst, const void __user *src, unsigned size) ...@@ -207,9 +207,6 @@ __copy_from_user_flushcache(void *dst, const void __user *src, unsigned size)
return __copy_user_flushcache(dst, src, size); return __copy_user_flushcache(dst, src, size);
} }
unsigned long
copy_user_handle_tail(char *to, char *from, unsigned len);
unsigned long unsigned long
mcsafe_handle_tail(char *to, char *from, unsigned len); mcsafe_handle_tail(char *to, char *from, unsigned len);
......
...@@ -217,6 +217,22 @@ xen_single_call(unsigned int call, ...@@ -217,6 +217,22 @@ xen_single_call(unsigned int call,
return (long)__res; return (long)__res;
} }
static __always_inline void __xen_stac(void)
{
/*
* Suppress objtool seeing the STAC/CLAC and getting confused about it
* calling random code with AC=1.
*/
asm volatile(ANNOTATE_IGNORE_ALTERNATIVE
ASM_STAC ::: "memory", "flags");
}
static __always_inline void __xen_clac(void)
{
asm volatile(ANNOTATE_IGNORE_ALTERNATIVE
ASM_CLAC ::: "memory", "flags");
}
static inline long static inline long
privcmd_call(unsigned int call, privcmd_call(unsigned int call,
unsigned long a1, unsigned long a2, unsigned long a1, unsigned long a2,
...@@ -225,9 +241,9 @@ privcmd_call(unsigned int call, ...@@ -225,9 +241,9 @@ privcmd_call(unsigned int call,
{ {
long res; long res;
stac(); __xen_stac();
res = xen_single_call(call, a1, a2, a3, a4, a5); res = xen_single_call(call, a1, a2, a3, a4, a5);
clac(); __xen_clac();
return res; return res;
} }
...@@ -424,9 +440,9 @@ HYPERVISOR_dm_op( ...@@ -424,9 +440,9 @@ HYPERVISOR_dm_op(
domid_t dom, unsigned int nr_bufs, struct xen_dm_op_buf *bufs) domid_t dom, unsigned int nr_bufs, struct xen_dm_op_buf *bufs)
{ {
int ret; int ret;
stac(); __xen_stac();
ret = _hypercall3(int, dm_op, dom, nr_bufs, bufs); ret = _hypercall3(int, dm_op, dom, nr_bufs, bufs);
clac(); __xen_clac();
return ret; return ret;
} }
......
...@@ -127,6 +127,13 @@ int copy_thread_tls(unsigned long clone_flags, unsigned long sp, ...@@ -127,6 +127,13 @@ int copy_thread_tls(unsigned long clone_flags, unsigned long sp,
struct task_struct *tsk; struct task_struct *tsk;
int err; int err;
/*
* For a new task use the RESET flags value since there is no before.
* All the status flags are zero; DF and all the system flags must also
* be 0, specifically IF must be 0 because we context switch to the new
* task with interrupts disabled.
*/
frame->flags = X86_EFLAGS_FIXED;
frame->bp = 0; frame->bp = 0;
frame->ret_addr = (unsigned long) ret_from_fork; frame->ret_addr = (unsigned long) ret_from_fork;
p->thread.sp = (unsigned long) fork_frame; p->thread.sp = (unsigned long) fork_frame;
......
...@@ -392,6 +392,7 @@ int copy_thread_tls(unsigned long clone_flags, unsigned long sp, ...@@ -392,6 +392,7 @@ int copy_thread_tls(unsigned long clone_flags, unsigned long sp,
childregs = task_pt_regs(p); childregs = task_pt_regs(p);
fork_frame = container_of(childregs, struct fork_frame, regs); fork_frame = container_of(childregs, struct fork_frame, regs);
frame = &fork_frame->frame; frame = &fork_frame->frame;
frame->bp = 0; frame->bp = 0;
frame->ret_addr = (unsigned long) ret_from_fork; frame->ret_addr = (unsigned long) ret_from_fork;
p->thread.sp = (unsigned long) fork_frame; p->thread.sp = (unsigned long) fork_frame;
......
...@@ -132,16 +132,6 @@ static int restore_sigcontext(struct pt_regs *regs, ...@@ -132,16 +132,6 @@ static int restore_sigcontext(struct pt_regs *regs,
COPY_SEG_CPL3(cs); COPY_SEG_CPL3(cs);
COPY_SEG_CPL3(ss); COPY_SEG_CPL3(ss);
#ifdef CONFIG_X86_64
/*
* Fix up SS if needed for the benefit of old DOSEMU and
* CRIU.
*/
if (unlikely(!(uc_flags & UC_STRICT_RESTORE_SS) &&
user_64bit_mode(regs)))
force_valid_ss(regs);
#endif
get_user_ex(tmpflags, &sc->flags); get_user_ex(tmpflags, &sc->flags);
regs->flags = (regs->flags & ~FIX_EFLAGS) | (tmpflags & FIX_EFLAGS); regs->flags = (regs->flags & ~FIX_EFLAGS) | (tmpflags & FIX_EFLAGS);
regs->orig_ax = -1; /* disable syscall checks */ regs->orig_ax = -1; /* disable syscall checks */
...@@ -150,6 +140,15 @@ static int restore_sigcontext(struct pt_regs *regs, ...@@ -150,6 +140,15 @@ static int restore_sigcontext(struct pt_regs *regs,
buf = (void __user *)buf_val; buf = (void __user *)buf_val;
} get_user_catch(err); } get_user_catch(err);
#ifdef CONFIG_X86_64
/*
* Fix up SS if needed for the benefit of old DOSEMU and
* CRIU.
*/
if (unlikely(!(uc_flags & UC_STRICT_RESTORE_SS) && user_64bit_mode(regs)))
force_valid_ss(regs);
#endif
err |= fpu__restore_sig(buf, IS_ENABLED(CONFIG_X86_32)); err |= fpu__restore_sig(buf, IS_ENABLED(CONFIG_X86_32));
force_iret(); force_iret();
...@@ -461,6 +460,7 @@ static int __setup_rt_frame(int sig, struct ksignal *ksig, ...@@ -461,6 +460,7 @@ static int __setup_rt_frame(int sig, struct ksignal *ksig,
{ {
struct rt_sigframe __user *frame; struct rt_sigframe __user *frame;
void __user *fp = NULL; void __user *fp = NULL;
unsigned long uc_flags;
int err = 0; int err = 0;
frame = get_sigframe(&ksig->ka, regs, sizeof(struct rt_sigframe), &fp); frame = get_sigframe(&ksig->ka, regs, sizeof(struct rt_sigframe), &fp);
...@@ -473,9 +473,11 @@ static int __setup_rt_frame(int sig, struct ksignal *ksig, ...@@ -473,9 +473,11 @@ static int __setup_rt_frame(int sig, struct ksignal *ksig,
return -EFAULT; return -EFAULT;
} }
uc_flags = frame_uc_flags(regs);
put_user_try { put_user_try {
/* Create the ucontext. */ /* Create the ucontext. */
put_user_ex(frame_uc_flags(regs), &frame->uc.uc_flags); put_user_ex(uc_flags, &frame->uc.uc_flags);
put_user_ex(0, &frame->uc.uc_link); put_user_ex(0, &frame->uc.uc_link);
save_altstack_ex(&frame->uc.uc_stack, regs->sp); save_altstack_ex(&frame->uc.uc_stack, regs->sp);
...@@ -541,6 +543,7 @@ static int x32_setup_rt_frame(struct ksignal *ksig, ...@@ -541,6 +543,7 @@ static int x32_setup_rt_frame(struct ksignal *ksig,
{ {
#ifdef CONFIG_X86_X32_ABI #ifdef CONFIG_X86_X32_ABI
struct rt_sigframe_x32 __user *frame; struct rt_sigframe_x32 __user *frame;
unsigned long uc_flags;
void __user *restorer; void __user *restorer;
int err = 0; int err = 0;
void __user *fpstate = NULL; void __user *fpstate = NULL;
...@@ -555,9 +558,11 @@ static int x32_setup_rt_frame(struct ksignal *ksig, ...@@ -555,9 +558,11 @@ static int x32_setup_rt_frame(struct ksignal *ksig,
return -EFAULT; return -EFAULT;
} }
uc_flags = frame_uc_flags(regs);
put_user_try { put_user_try {
/* Create the ucontext. */ /* Create the ucontext. */
put_user_ex(frame_uc_flags(regs), &frame->uc.uc_flags); put_user_ex(uc_flags, &frame->uc.uc_flags);
put_user_ex(0, &frame->uc.uc_link); put_user_ex(0, &frame->uc.uc_link);
compat_save_altstack_ex(&frame->uc.uc_stack, regs->sp); compat_save_altstack_ex(&frame->uc.uc_stack, regs->sp);
put_user_ex(0, &frame->uc.uc__pad0); put_user_ex(0, &frame->uc.uc__pad0);
......
...@@ -16,6 +16,30 @@ ...@@ -16,6 +16,30 @@
#include <asm/smap.h> #include <asm/smap.h>
#include <asm/export.h> #include <asm/export.h>
.macro ALIGN_DESTINATION
/* check for bad alignment of destination */
movl %edi,%ecx
andl $7,%ecx
jz 102f /* already aligned */
subl $8,%ecx
negl %ecx
subl %ecx,%edx
100: movb (%rsi),%al
101: movb %al,(%rdi)
incq %rsi
incq %rdi
decl %ecx
jnz 100b
102:
.section .fixup,"ax"
103: addl %ecx,%edx /* ecx is zerorest also */
jmp copy_user_handle_tail
.previous
_ASM_EXTABLE_UA(100b, 103b)
_ASM_EXTABLE_UA(101b, 103b)
.endm
/* /*
* copy_user_generic_unrolled - memory copy with exception handling. * copy_user_generic_unrolled - memory copy with exception handling.
* This version is for CPUs like P4 that don't have efficient micro * This version is for CPUs like P4 that don't have efficient micro
...@@ -193,6 +217,30 @@ ENTRY(copy_user_enhanced_fast_string) ...@@ -193,6 +217,30 @@ ENTRY(copy_user_enhanced_fast_string)
ENDPROC(copy_user_enhanced_fast_string) ENDPROC(copy_user_enhanced_fast_string)
EXPORT_SYMBOL(copy_user_enhanced_fast_string) EXPORT_SYMBOL(copy_user_enhanced_fast_string)
/*
* Try to copy last bytes and clear the rest if needed.
* Since protection fault in copy_from/to_user is not a normal situation,
* it is not necessary to optimize tail handling.
*
* Input:
* rdi destination
* rsi source
* rdx count
*
* Output:
* eax uncopied bytes or 0 if successful.
*/
ALIGN;
copy_user_handle_tail:
movl %edx,%ecx
1: rep movsb
2: mov %ecx,%eax
ASM_CLAC
ret
_ASM_EXTABLE_UA(1b, 2b)
ENDPROC(copy_user_handle_tail)
/* /*
* copy_user_nocache - Uncached memory copy with exception handling * copy_user_nocache - Uncached memory copy with exception handling
* This will force destination out of cache for more performance. * This will force destination out of cache for more performance.
......
...@@ -257,6 +257,7 @@ ENTRY(__memcpy_mcsafe) ...@@ -257,6 +257,7 @@ ENTRY(__memcpy_mcsafe)
/* Copy successful. Return zero */ /* Copy successful. Return zero */
.L_done_memcpy_trap: .L_done_memcpy_trap:
xorl %eax, %eax xorl %eax, %eax
.L_done:
ret ret
ENDPROC(__memcpy_mcsafe) ENDPROC(__memcpy_mcsafe)
EXPORT_SYMBOL_GPL(__memcpy_mcsafe) EXPORT_SYMBOL_GPL(__memcpy_mcsafe)
...@@ -273,7 +274,7 @@ EXPORT_SYMBOL_GPL(__memcpy_mcsafe) ...@@ -273,7 +274,7 @@ EXPORT_SYMBOL_GPL(__memcpy_mcsafe)
addl %edx, %ecx addl %edx, %ecx
.E_trailing_bytes: .E_trailing_bytes:
mov %ecx, %eax mov %ecx, %eax
ret jmp .L_done
/* /*
* For write fault handling, given the destination is unaligned, * For write fault handling, given the destination is unaligned,
......
...@@ -54,26 +54,6 @@ unsigned long clear_user(void __user *to, unsigned long n) ...@@ -54,26 +54,6 @@ unsigned long clear_user(void __user *to, unsigned long n)
} }
EXPORT_SYMBOL(clear_user); EXPORT_SYMBOL(clear_user);
/*
* Try to copy last bytes and clear the rest if needed.
* Since protection fault in copy_from/to_user is not a normal situation,
* it is not necessary to optimize tail handling.
*/
__visible unsigned long
copy_user_handle_tail(char *to, char *from, unsigned len)
{
for (; len; --len, to++) {
char c;
if (__get_user_nocheck(c, from++, sizeof(char)))
break;
if (__put_user_nocheck(c, to, sizeof(char)))
break;
}
clac();
return len;
}
/* /*
* Similar to copy_user_handle_tail, probe for the write fault point, * Similar to copy_user_handle_tail, probe for the write fault point,
* but reuse __memcpy_mcsafe in case a new read error is encountered. * but reuse __memcpy_mcsafe in case a new read error is encountered.
......
...@@ -1667,6 +1667,7 @@ static int eb_copy_relocations(const struct i915_execbuffer *eb) ...@@ -1667,6 +1667,7 @@ static int eb_copy_relocations(const struct i915_execbuffer *eb)
len)) { len)) {
end_user: end_user:
user_access_end(); user_access_end();
end:
kvfree(relocs); kvfree(relocs);
err = -EFAULT; err = -EFAULT;
goto err; goto err;
...@@ -1686,7 +1687,7 @@ static int eb_copy_relocations(const struct i915_execbuffer *eb) ...@@ -1686,7 +1687,7 @@ static int eb_copy_relocations(const struct i915_execbuffer *eb)
* relocations were valid. * relocations were valid.
*/ */
if (!user_access_begin(urelocs, size)) if (!user_access_begin(urelocs, size))
goto end_user; goto end;
for (copied = 0; copied < nreloc; copied++) for (copied = 0; copied < nreloc; copied++)
unsafe_put_user(-1, unsafe_put_user(-1,
...@@ -2695,7 +2696,7 @@ i915_gem_execbuffer2_ioctl(struct drm_device *dev, void *data, ...@@ -2695,7 +2696,7 @@ i915_gem_execbuffer2_ioctl(struct drm_device *dev, void *data,
* when we did the "copy_from_user()" above. * when we did the "copy_from_user()" above.
*/ */
if (!user_access_begin(user_exec_list, count * sizeof(*user_exec_list))) if (!user_access_begin(user_exec_list, count * sizeof(*user_exec_list)))
goto end_user; goto end;
for (i = 0; i < args->buffer_count; i++) { for (i = 0; i < args->buffer_count; i++) {
if (!(exec2_list[i].offset & UPDATE)) if (!(exec2_list[i].offset & UPDATE))
...@@ -2709,6 +2710,7 @@ i915_gem_execbuffer2_ioctl(struct drm_device *dev, void *data, ...@@ -2709,6 +2710,7 @@ i915_gem_execbuffer2_ioctl(struct drm_device *dev, void *data,
} }
end_user: end_user:
user_access_end(); user_access_end();
end:;
} }
args->flags &= ~__I915_EXEC_UNKNOWN_FLAGS; args->flags &= ~__I915_EXEC_UNKNOWN_FLAGS;
......
...@@ -67,7 +67,7 @@ void ftrace_likely_update(struct ftrace_likely_data *f, int val, ...@@ -67,7 +67,7 @@ void ftrace_likely_update(struct ftrace_likely_data *f, int val,
.line = __LINE__, \ .line = __LINE__, \
}; \ }; \
______r = !!(cond); \ ______r = !!(cond); \
______f.miss_hit[______r]++; \ ______r ? ______f.miss_hit[1]++ : ______f.miss_hit[0]++;\
______r; \ ______r; \
})) }))
#endif /* CONFIG_PROFILE_ALL_BRANCHES */ #endif /* CONFIG_PROFILE_ALL_BRANCHES */
......
...@@ -268,6 +268,8 @@ extern long strncpy_from_unsafe(char *dst, const void *unsafe_addr, long count); ...@@ -268,6 +268,8 @@ extern long strncpy_from_unsafe(char *dst, const void *unsafe_addr, long count);
#define user_access_end() do { } while (0) #define user_access_end() do { } while (0)
#define unsafe_get_user(x, ptr, err) do { if (unlikely(__get_user(x, ptr))) goto err; } while (0) #define unsafe_get_user(x, ptr, err) do { if (unlikely(__get_user(x, ptr))) goto err; } while (0)
#define unsafe_put_user(x, ptr, err) do { if (unlikely(__put_user(x, ptr))) goto err; } while (0) #define unsafe_put_user(x, ptr, err) do { if (unlikely(__put_user(x, ptr))) goto err; } while (0)
static inline unsigned long user_access_save(void) { return 0UL; }
static inline void user_access_restore(unsigned long flags) { }
#endif #endif
#ifdef CONFIG_HARDENED_USERCOPY #ifdef CONFIG_HARDENED_USERCOPY
......
...@@ -30,6 +30,7 @@ KCOV_INSTRUMENT_extable.o := n ...@@ -30,6 +30,7 @@ KCOV_INSTRUMENT_extable.o := n
# Don't self-instrument. # Don't self-instrument.
KCOV_INSTRUMENT_kcov.o := n KCOV_INSTRUMENT_kcov.o := n
KASAN_SANITIZE_kcov.o := n KASAN_SANITIZE_kcov.o := n
CFLAGS_kcov.o := $(call cc-option, -fno-conserve-stack -fno-stack-protector)
# cond_syscall is currently not LTO compatible # cond_syscall is currently not LTO compatible
CFLAGS_sys_ni.o = $(DISABLE_LTO) CFLAGS_sys_ni.o = $(DISABLE_LTO)
......
...@@ -205,6 +205,8 @@ void trace_likely_condition(struct ftrace_likely_data *f, int val, int expect) ...@@ -205,6 +205,8 @@ void trace_likely_condition(struct ftrace_likely_data *f, int val, int expect)
void ftrace_likely_update(struct ftrace_likely_data *f, int val, void ftrace_likely_update(struct ftrace_likely_data *f, int val,
int expect, int is_constant) int expect, int is_constant)
{ {
unsigned long flags = user_access_save();
/* A constant is always correct */ /* A constant is always correct */
if (is_constant) { if (is_constant) {
f->constant++; f->constant++;
...@@ -223,6 +225,8 @@ void ftrace_likely_update(struct ftrace_likely_data *f, int val, ...@@ -223,6 +225,8 @@ void ftrace_likely_update(struct ftrace_likely_data *f, int val,
f->data.correct++; f->data.correct++;
else else
f->data.incorrect++; f->data.incorrect++;
user_access_restore(flags);
} }
EXPORT_SYMBOL(ftrace_likely_update); EXPORT_SYMBOL(ftrace_likely_update);
......
...@@ -279,6 +279,7 @@ obj-$(CONFIG_UCS2_STRING) += ucs2_string.o ...@@ -279,6 +279,7 @@ obj-$(CONFIG_UCS2_STRING) += ucs2_string.o
obj-$(CONFIG_UBSAN) += ubsan.o obj-$(CONFIG_UBSAN) += ubsan.o
UBSAN_SANITIZE_ubsan.o := n UBSAN_SANITIZE_ubsan.o := n
CFLAGS_ubsan.o := $(call cc-option, -fno-conserve-stack -fno-stack-protector)
obj-$(CONFIG_SBITMAP) += sbitmap.o obj-$(CONFIG_SBITMAP) += sbitmap.o
......
...@@ -23,10 +23,11 @@ ...@@ -23,10 +23,11 @@
* hit it), 'max' is the address space maximum (and we return * hit it), 'max' is the address space maximum (and we return
* -EFAULT if we hit it). * -EFAULT if we hit it).
*/ */
static inline long do_strncpy_from_user(char *dst, const char __user *src, long count, unsigned long max) static inline long do_strncpy_from_user(char *dst, const char __user *src,
unsigned long count, unsigned long max)
{ {
const struct word_at_a_time constants = WORD_AT_A_TIME_CONSTANTS; const struct word_at_a_time constants = WORD_AT_A_TIME_CONSTANTS;
long res = 0; unsigned long res = 0;
/* /*
* Truncate 'max' to the user-specified limit, so that * Truncate 'max' to the user-specified limit, so that
......
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
static inline long do_strnlen_user(const char __user *src, unsigned long count, unsigned long max) static inline long do_strnlen_user(const char __user *src, unsigned long count, unsigned long max)
{ {
const struct word_at_a_time constants = WORD_AT_A_TIME_CONSTANTS; const struct word_at_a_time constants = WORD_AT_A_TIME_CONSTANTS;
long align, res = 0; unsigned long align, res = 0;
unsigned long c; unsigned long c;
/* /*
...@@ -42,7 +42,7 @@ static inline long do_strnlen_user(const char __user *src, unsigned long count, ...@@ -42,7 +42,7 @@ static inline long do_strnlen_user(const char __user *src, unsigned long count,
* Do everything aligned. But that means that we * Do everything aligned. But that means that we
* need to also expand the maximum.. * need to also expand the maximum..
*/ */
align = (sizeof(long) - 1) & (unsigned long)src; align = (sizeof(unsigned long) - 1) & (unsigned long)src;
src -= align; src -= align;
max += align; max += align;
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/uaccess.h>
#include "ubsan.h" #include "ubsan.h"
...@@ -311,6 +312,7 @@ static void handle_object_size_mismatch(struct type_mismatch_data_common *data, ...@@ -311,6 +312,7 @@ static void handle_object_size_mismatch(struct type_mismatch_data_common *data,
static void ubsan_type_mismatch_common(struct type_mismatch_data_common *data, static void ubsan_type_mismatch_common(struct type_mismatch_data_common *data,
unsigned long ptr) unsigned long ptr)
{ {
unsigned long flags = user_access_save();
if (!ptr) if (!ptr)
handle_null_ptr_deref(data); handle_null_ptr_deref(data);
...@@ -318,6 +320,8 @@ static void ubsan_type_mismatch_common(struct type_mismatch_data_common *data, ...@@ -318,6 +320,8 @@ static void ubsan_type_mismatch_common(struct type_mismatch_data_common *data,
handle_misaligned_access(data, ptr); handle_misaligned_access(data, ptr);
else else
handle_object_size_mismatch(data, ptr); handle_object_size_mismatch(data, ptr);
user_access_restore(flags);
} }
void __ubsan_handle_type_mismatch(struct type_mismatch_data *data, void __ubsan_handle_type_mismatch(struct type_mismatch_data *data,
......
...@@ -2,11 +2,13 @@ ...@@ -2,11 +2,13 @@
KASAN_SANITIZE := n KASAN_SANITIZE := n
UBSAN_SANITIZE_common.o := n UBSAN_SANITIZE_common.o := n
UBSAN_SANITIZE_generic.o := n UBSAN_SANITIZE_generic.o := n
UBSAN_SANITIZE_generic_report.o := n
UBSAN_SANITIZE_tags.o := n UBSAN_SANITIZE_tags.o := n
KCOV_INSTRUMENT := n KCOV_INSTRUMENT := n
CFLAGS_REMOVE_common.o = -pg CFLAGS_REMOVE_common.o = -pg
CFLAGS_REMOVE_generic.o = -pg CFLAGS_REMOVE_generic.o = -pg
CFLAGS_REMOVE_generic_report.o = -pg
CFLAGS_REMOVE_tags.o = -pg CFLAGS_REMOVE_tags.o = -pg
# Function splitter causes unnecessary splits in __asan_load1/__asan_store1 # Function splitter causes unnecessary splits in __asan_load1/__asan_store1
...@@ -14,6 +16,7 @@ CFLAGS_REMOVE_tags.o = -pg ...@@ -14,6 +16,7 @@ CFLAGS_REMOVE_tags.o = -pg
CFLAGS_common.o := $(call cc-option, -fno-conserve-stack -fno-stack-protector) CFLAGS_common.o := $(call cc-option, -fno-conserve-stack -fno-stack-protector)
CFLAGS_generic.o := $(call cc-option, -fno-conserve-stack -fno-stack-protector) CFLAGS_generic.o := $(call cc-option, -fno-conserve-stack -fno-stack-protector)
CFLAGS_generic_report.o := $(call cc-option, -fno-conserve-stack -fno-stack-protector)
CFLAGS_tags.o := $(call cc-option, -fno-conserve-stack -fno-stack-protector) CFLAGS_tags.o := $(call cc-option, -fno-conserve-stack -fno-stack-protector)
obj-$(CONFIG_KASAN) := common.o init.o report.o obj-$(CONFIG_KASAN) := common.o init.o report.o
......
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
#include <linux/types.h> #include <linux/types.h>
#include <linux/vmalloc.h> #include <linux/vmalloc.h>
#include <linux/bug.h> #include <linux/bug.h>
#include <linux/uaccess.h>
#include "kasan.h" #include "kasan.h"
#include "../slab.h" #include "../slab.h"
...@@ -614,6 +615,15 @@ void kasan_free_shadow(const struct vm_struct *vm) ...@@ -614,6 +615,15 @@ void kasan_free_shadow(const struct vm_struct *vm)
vfree(kasan_mem_to_shadow(vm->addr)); vfree(kasan_mem_to_shadow(vm->addr));
} }
extern void __kasan_report(unsigned long addr, size_t size, bool is_write, unsigned long ip);
void kasan_report(unsigned long addr, size_t size, bool is_write, unsigned long ip)
{
unsigned long flags = user_access_save();
__kasan_report(addr, size, is_write, ip);
user_access_restore(flags);
}
#ifdef CONFIG_MEMORY_HOTPLUG #ifdef CONFIG_MEMORY_HOTPLUG
static bool shadow_mapped(unsigned long addr) static bool shadow_mapped(unsigned long addr)
{ {
......
...@@ -281,8 +281,7 @@ void kasan_report_invalid_free(void *object, unsigned long ip) ...@@ -281,8 +281,7 @@ void kasan_report_invalid_free(void *object, unsigned long ip)
end_report(&flags); end_report(&flags);
} }
void kasan_report(unsigned long addr, size_t size, void __kasan_report(unsigned long addr, size_t size, bool is_write, unsigned long ip)
bool is_write, unsigned long ip)
{ {
struct kasan_access_info info; struct kasan_access_info info;
void *tagged_addr; void *tagged_addr;
......
...@@ -222,6 +222,9 @@ endif ...@@ -222,6 +222,9 @@ endif
ifdef CONFIG_RETPOLINE ifdef CONFIG_RETPOLINE
objtool_args += --retpoline objtool_args += --retpoline
endif endif
ifdef CONFIG_X86_SMAP
objtool_args += --uaccess
endif
# 'OBJECT_FILES_NON_STANDARD := y': skip objtool checking for a directory # 'OBJECT_FILES_NON_STANDARD := y': skip objtool checking for a directory
# 'OBJECT_FILES_NON_STANDARD_foo.o := 'y': skip objtool checking for a file # 'OBJECT_FILES_NON_STANDARD_foo.o := 'y': skip objtool checking for a file
......
...@@ -33,7 +33,11 @@ ...@@ -33,7 +33,11 @@
#define INSN_STACK 8 #define INSN_STACK 8
#define INSN_BUG 9 #define INSN_BUG 9
#define INSN_NOP 10 #define INSN_NOP 10
#define INSN_OTHER 11 #define INSN_STAC 11
#define INSN_CLAC 12
#define INSN_STD 13
#define INSN_CLD 14
#define INSN_OTHER 15
#define INSN_LAST INSN_OTHER #define INSN_LAST INSN_OTHER
enum op_dest_type { enum op_dest_type {
...@@ -41,6 +45,7 @@ enum op_dest_type { ...@@ -41,6 +45,7 @@ enum op_dest_type {
OP_DEST_REG_INDIRECT, OP_DEST_REG_INDIRECT,
OP_DEST_MEM, OP_DEST_MEM,
OP_DEST_PUSH, OP_DEST_PUSH,
OP_DEST_PUSHF,
OP_DEST_LEAVE, OP_DEST_LEAVE,
}; };
...@@ -55,6 +60,7 @@ enum op_src_type { ...@@ -55,6 +60,7 @@ enum op_src_type {
OP_SRC_REG_INDIRECT, OP_SRC_REG_INDIRECT,
OP_SRC_CONST, OP_SRC_CONST,
OP_SRC_POP, OP_SRC_POP,
OP_SRC_POPF,
OP_SRC_ADD, OP_SRC_ADD,
OP_SRC_AND, OP_SRC_AND,
}; };
......
...@@ -357,19 +357,26 @@ int arch_decode_instruction(struct elf *elf, struct section *sec, ...@@ -357,19 +357,26 @@ int arch_decode_instruction(struct elf *elf, struct section *sec,
/* pushf */ /* pushf */
*type = INSN_STACK; *type = INSN_STACK;
op->src.type = OP_SRC_CONST; op->src.type = OP_SRC_CONST;
op->dest.type = OP_DEST_PUSH; op->dest.type = OP_DEST_PUSHF;
break; break;
case 0x9d: case 0x9d:
/* popf */ /* popf */
*type = INSN_STACK; *type = INSN_STACK;
op->src.type = OP_SRC_POP; op->src.type = OP_SRC_POPF;
op->dest.type = OP_DEST_MEM; op->dest.type = OP_DEST_MEM;
break; break;
case 0x0f: case 0x0f:
if (op2 >= 0x80 && op2 <= 0x8f) { if (op2 == 0x01) {
if (modrm == 0xca)
*type = INSN_CLAC;
else if (modrm == 0xcb)
*type = INSN_STAC;
} else if (op2 >= 0x80 && op2 <= 0x8f) {
*type = INSN_JUMP_CONDITIONAL; *type = INSN_JUMP_CONDITIONAL;
...@@ -444,6 +451,14 @@ int arch_decode_instruction(struct elf *elf, struct section *sec, ...@@ -444,6 +451,14 @@ int arch_decode_instruction(struct elf *elf, struct section *sec,
*type = INSN_CALL; *type = INSN_CALL;
break; break;
case 0xfc:
*type = INSN_CLD;
break;
case 0xfd:
*type = INSN_STD;
break;
case 0xff: case 0xff:
if (modrm_reg == 2 || modrm_reg == 3) if (modrm_reg == 2 || modrm_reg == 3)
......
...@@ -29,7 +29,7 @@ ...@@ -29,7 +29,7 @@
#include "builtin.h" #include "builtin.h"
#include "check.h" #include "check.h"
bool no_fp, no_unreachable, retpoline, module; bool no_fp, no_unreachable, retpoline, module, backtrace, uaccess;
static const char * const check_usage[] = { static const char * const check_usage[] = {
"objtool check [<options>] file.o", "objtool check [<options>] file.o",
...@@ -41,6 +41,8 @@ const struct option check_options[] = { ...@@ -41,6 +41,8 @@ const struct option check_options[] = {
OPT_BOOLEAN('u', "no-unreachable", &no_unreachable, "Skip 'unreachable instruction' warnings"), OPT_BOOLEAN('u', "no-unreachable", &no_unreachable, "Skip 'unreachable instruction' warnings"),
OPT_BOOLEAN('r', "retpoline", &retpoline, "Validate retpoline assumptions"), OPT_BOOLEAN('r', "retpoline", &retpoline, "Validate retpoline assumptions"),
OPT_BOOLEAN('m', "module", &module, "Indicates the object will be part of a kernel module"), OPT_BOOLEAN('m', "module", &module, "Indicates the object will be part of a kernel module"),
OPT_BOOLEAN('b', "backtrace", &backtrace, "unwind on error"),
OPT_BOOLEAN('a', "uaccess", &uaccess, "enable uaccess checking"),
OPT_END(), OPT_END(),
}; };
......
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
#include <subcmd/parse-options.h> #include <subcmd/parse-options.h>
extern const struct option check_options[]; extern const struct option check_options[];
extern bool no_fp, no_unreachable, retpoline, module; extern bool no_fp, no_unreachable, retpoline, module, backtrace, uaccess;
extern int cmd_check(int argc, const char **argv); extern int cmd_check(int argc, const char **argv);
extern int cmd_orc(int argc, const char **argv); extern int cmd_orc(int argc, const char **argv);
......
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
struct alternative { struct alternative {
struct list_head list; struct list_head list;
struct instruction *insn; struct instruction *insn;
bool skip_orig;
}; };
const char *objname; const char *objname;
...@@ -104,29 +105,6 @@ static struct instruction *next_insn_same_func(struct objtool_file *file, ...@@ -104,29 +105,6 @@ static struct instruction *next_insn_same_func(struct objtool_file *file,
for (insn = next_insn_same_sec(file, insn); insn; \ for (insn = next_insn_same_sec(file, insn); insn; \
insn = next_insn_same_sec(file, insn)) insn = next_insn_same_sec(file, insn))
/*
* Check if the function has been manually whitelisted with the
* STACK_FRAME_NON_STANDARD macro, or if it should be automatically whitelisted
* due to its use of a context switching instruction.
*/
static bool ignore_func(struct objtool_file *file, struct symbol *func)
{
struct rela *rela;
/* check for STACK_FRAME_NON_STANDARD */
if (file->whitelist && file->whitelist->rela)
list_for_each_entry(rela, &file->whitelist->rela->rela_list, list) {
if (rela->sym->type == STT_SECTION &&
rela->sym->sec == func->sec &&
rela->addend == func->offset)
return true;
if (rela->sym->type == STT_FUNC && rela->sym == func)
return true;
}
return false;
}
/* /*
* This checks to see if the given function is a "noreturn" function. * This checks to see if the given function is a "noreturn" function.
* *
...@@ -437,18 +415,107 @@ static void add_ignores(struct objtool_file *file) ...@@ -437,18 +415,107 @@ static void add_ignores(struct objtool_file *file)
struct instruction *insn; struct instruction *insn;
struct section *sec; struct section *sec;
struct symbol *func; struct symbol *func;
struct rela *rela;
for_each_sec(file, sec) { sec = find_section_by_name(file->elf, ".rela.discard.func_stack_frame_non_standard");
list_for_each_entry(func, &sec->symbol_list, list) { if (!sec)
if (func->type != STT_FUNC) return;
list_for_each_entry(rela, &sec->rela_list, list) {
switch (rela->sym->type) {
case STT_FUNC:
func = rela->sym;
break;
case STT_SECTION:
func = find_symbol_by_offset(rela->sym->sec, rela->addend);
if (!func || func->type != STT_FUNC)
continue; continue;
break;
if (!ignore_func(file, func)) default:
WARN("unexpected relocation symbol type in %s: %d", sec->name, rela->sym->type);
continue; continue;
}
func_for_each_insn_all(file, func, insn) func_for_each_insn_all(file, func, insn)
insn->ignore = true; insn->ignore = true;
} }
}
/*
* This is a whitelist of functions that is allowed to be called with AC set.
* The list is meant to be minimal and only contains compiler instrumentation
* ABI and a few functions used to implement *_{to,from}_user() functions.
*
* These functions must not directly change AC, but may PUSHF/POPF.
*/
static const char *uaccess_safe_builtin[] = {
/* KASAN */
"kasan_report",
"check_memory_region",
/* KASAN out-of-line */
"__asan_loadN_noabort",
"__asan_load1_noabort",
"__asan_load2_noabort",
"__asan_load4_noabort",
"__asan_load8_noabort",
"__asan_load16_noabort",
"__asan_storeN_noabort",
"__asan_store1_noabort",
"__asan_store2_noabort",
"__asan_store4_noabort",
"__asan_store8_noabort",
"__asan_store16_noabort",
/* KASAN in-line */
"__asan_report_load_n_noabort",
"__asan_report_load1_noabort",
"__asan_report_load2_noabort",
"__asan_report_load4_noabort",
"__asan_report_load8_noabort",
"__asan_report_load16_noabort",
"__asan_report_store_n_noabort",
"__asan_report_store1_noabort",
"__asan_report_store2_noabort",
"__asan_report_store4_noabort",
"__asan_report_store8_noabort",
"__asan_report_store16_noabort",
/* KCOV */
"write_comp_data",
"__sanitizer_cov_trace_pc",
"__sanitizer_cov_trace_const_cmp1",
"__sanitizer_cov_trace_const_cmp2",
"__sanitizer_cov_trace_const_cmp4",
"__sanitizer_cov_trace_const_cmp8",
"__sanitizer_cov_trace_cmp1",
"__sanitizer_cov_trace_cmp2",
"__sanitizer_cov_trace_cmp4",
"__sanitizer_cov_trace_cmp8",
/* UBSAN */
"ubsan_type_mismatch_common",
"__ubsan_handle_type_mismatch",
"__ubsan_handle_type_mismatch_v1",
/* misc */
"csum_partial_copy_generic",
"__memcpy_mcsafe",
"ftrace_likely_update", /* CONFIG_TRACE_BRANCH_PROFILING */
NULL
};
static void add_uaccess_safe(struct objtool_file *file)
{
struct symbol *func;
const char **name;
if (!uaccess)
return;
for (name = uaccess_safe_builtin; *name; name++) {
func = find_symbol_by_name(file->elf, *name);
if (!func)
continue;
func->alias->uaccess_safe = true;
} }
} }
...@@ -458,13 +525,13 @@ static void add_ignores(struct objtool_file *file) ...@@ -458,13 +525,13 @@ static void add_ignores(struct objtool_file *file)
* But it at least allows objtool to understand the control flow *around* the * But it at least allows objtool to understand the control flow *around* the
* retpoline. * retpoline.
*/ */
static int add_nospec_ignores(struct objtool_file *file) static int add_ignore_alternatives(struct objtool_file *file)
{ {
struct section *sec; struct section *sec;
struct rela *rela; struct rela *rela;
struct instruction *insn; struct instruction *insn;
sec = find_section_by_name(file->elf, ".rela.discard.nospec"); sec = find_section_by_name(file->elf, ".rela.discard.ignore_alts");
if (!sec) if (!sec)
return 0; return 0;
...@@ -476,7 +543,7 @@ static int add_nospec_ignores(struct objtool_file *file) ...@@ -476,7 +543,7 @@ static int add_nospec_ignores(struct objtool_file *file)
insn = find_insn(file, rela->sym->sec, rela->addend); insn = find_insn(file, rela->sym->sec, rela->addend);
if (!insn) { if (!insn) {
WARN("bad .discard.nospec entry"); WARN("bad .discard.ignore_alts entry");
return -1; return -1;
} }
...@@ -525,7 +592,8 @@ static int add_jump_destinations(struct objtool_file *file) ...@@ -525,7 +592,8 @@ static int add_jump_destinations(struct objtool_file *file)
continue; continue;
} else { } else {
/* sibling call */ /* sibling call */
insn->jump_dest = 0; insn->call_dest = rela->sym;
insn->jump_dest = NULL;
continue; continue;
} }
...@@ -546,26 +614,39 @@ static int add_jump_destinations(struct objtool_file *file) ...@@ -546,26 +614,39 @@ static int add_jump_destinations(struct objtool_file *file)
return -1; return -1;
} }
/*
* Cross-function jump.
*/
if (insn->func && insn->jump_dest->func &&
insn->func != insn->jump_dest->func) {
/* /*
* For GCC 8+, create parent/child links for any cold * For GCC 8+, create parent/child links for any cold
* subfunctions. This is _mostly_ redundant with a similar * subfunctions. This is _mostly_ redundant with a
* initialization in read_symbols(). * similar initialization in read_symbols().
* *
* If a function has aliases, we want the *first* such function * If a function has aliases, we want the *first* such
* in the symbol table to be the subfunction's parent. In that * function in the symbol table to be the subfunction's
* case we overwrite the initialization done in read_symbols(). * parent. In that case we overwrite the
* initialization done in read_symbols().
* *
* However this code can't completely replace the * However this code can't completely replace the
* read_symbols() code because this doesn't detect the case * read_symbols() code because this doesn't detect the
* where the parent function's only reference to a subfunction * case where the parent function's only reference to a
* is through a switch table. * subfunction is through a switch table.
*/ */
if (insn->func && insn->jump_dest->func && if (!strstr(insn->func->name, ".cold.") &&
insn->func != insn->jump_dest->func &&
!strstr(insn->func->name, ".cold.") &&
strstr(insn->jump_dest->func->name, ".cold.")) { strstr(insn->jump_dest->func->name, ".cold.")) {
insn->func->cfunc = insn->jump_dest->func; insn->func->cfunc = insn->jump_dest->func;
insn->jump_dest->func->pfunc = insn->func; insn->jump_dest->func->pfunc = insn->func;
} else if (insn->jump_dest->func->pfunc != insn->func->pfunc &&
insn->jump_dest->offset == insn->jump_dest->func->offset) {
/* sibling class */
insn->call_dest = insn->jump_dest->func;
insn->jump_dest = NULL;
}
} }
} }
...@@ -634,9 +715,6 @@ static int add_call_destinations(struct objtool_file *file) ...@@ -634,9 +715,6 @@ static int add_call_destinations(struct objtool_file *file)
* conditionally jumps to the _end_ of the entry. We have to modify these * conditionally jumps to the _end_ of the entry. We have to modify these
* jumps' destinations to point back to .text rather than the end of the * jumps' destinations to point back to .text rather than the end of the
* entry in .altinstr_replacement. * entry in .altinstr_replacement.
*
* 4. It has been requested that we don't validate the !POPCNT feature path
* which is a "very very small percentage of machines".
*/ */
static int handle_group_alt(struct objtool_file *file, static int handle_group_alt(struct objtool_file *file,
struct special_alt *special_alt, struct special_alt *special_alt,
...@@ -652,9 +730,6 @@ static int handle_group_alt(struct objtool_file *file, ...@@ -652,9 +730,6 @@ static int handle_group_alt(struct objtool_file *file,
if (insn->offset >= special_alt->orig_off + special_alt->orig_len) if (insn->offset >= special_alt->orig_off + special_alt->orig_len)
break; break;
if (special_alt->skip_orig)
insn->type = INSN_NOP;
insn->alt_group = true; insn->alt_group = true;
last_orig_insn = insn; last_orig_insn = insn;
} }
...@@ -696,6 +771,7 @@ static int handle_group_alt(struct objtool_file *file, ...@@ -696,6 +771,7 @@ static int handle_group_alt(struct objtool_file *file,
last_new_insn = insn; last_new_insn = insn;
insn->ignore = orig_insn->ignore_alts; insn->ignore = orig_insn->ignore_alts;
insn->func = orig_insn->func;
if (insn->type != INSN_JUMP_CONDITIONAL && if (insn->type != INSN_JUMP_CONDITIONAL &&
insn->type != INSN_JUMP_UNCONDITIONAL) insn->type != INSN_JUMP_UNCONDITIONAL)
...@@ -818,6 +894,8 @@ static int add_special_section_alts(struct objtool_file *file) ...@@ -818,6 +894,8 @@ static int add_special_section_alts(struct objtool_file *file)
} }
alt->insn = new_insn; alt->insn = new_insn;
alt->skip_orig = special_alt->skip_orig;
orig_insn->ignore_alts |= special_alt->skip_alt;
list_add_tail(&alt->list, &orig_insn->alts); list_add_tail(&alt->list, &orig_insn->alts);
list_del(&special_alt->list); list_del(&special_alt->list);
...@@ -1239,8 +1317,9 @@ static int decode_sections(struct objtool_file *file) ...@@ -1239,8 +1317,9 @@ static int decode_sections(struct objtool_file *file)
return ret; return ret;
add_ignores(file); add_ignores(file);
add_uaccess_safe(file);
ret = add_nospec_ignores(file); ret = add_ignore_alternatives(file);
if (ret) if (ret)
return ret; return ret;
...@@ -1320,11 +1399,11 @@ static int update_insn_state_regs(struct instruction *insn, struct insn_state *s ...@@ -1320,11 +1399,11 @@ static int update_insn_state_regs(struct instruction *insn, struct insn_state *s
return 0; return 0;
/* push */ /* push */
if (op->dest.type == OP_DEST_PUSH) if (op->dest.type == OP_DEST_PUSH || op->dest.type == OP_DEST_PUSHF)
cfa->offset += 8; cfa->offset += 8;
/* pop */ /* pop */
if (op->src.type == OP_SRC_POP) if (op->src.type == OP_SRC_POP || op->src.type == OP_SRC_POPF)
cfa->offset -= 8; cfa->offset -= 8;
/* add immediate to sp */ /* add immediate to sp */
...@@ -1581,6 +1660,7 @@ static int update_insn_state(struct instruction *insn, struct insn_state *state) ...@@ -1581,6 +1660,7 @@ static int update_insn_state(struct instruction *insn, struct insn_state *state)
break; break;
case OP_SRC_POP: case OP_SRC_POP:
case OP_SRC_POPF:
if (!state->drap && op->dest.type == OP_DEST_REG && if (!state->drap && op->dest.type == OP_DEST_REG &&
op->dest.reg == cfa->base) { op->dest.reg == cfa->base) {
...@@ -1645,6 +1725,7 @@ static int update_insn_state(struct instruction *insn, struct insn_state *state) ...@@ -1645,6 +1725,7 @@ static int update_insn_state(struct instruction *insn, struct insn_state *state)
break; break;
case OP_DEST_PUSH: case OP_DEST_PUSH:
case OP_DEST_PUSHF:
state->stack_size += 8; state->stack_size += 8;
if (cfa->base == CFI_SP) if (cfa->base == CFI_SP)
cfa->offset += 8; cfa->offset += 8;
...@@ -1735,7 +1816,7 @@ static int update_insn_state(struct instruction *insn, struct insn_state *state) ...@@ -1735,7 +1816,7 @@ static int update_insn_state(struct instruction *insn, struct insn_state *state)
break; break;
case OP_DEST_MEM: 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", WARN_FUNC("unknown stack-related memory operation",
insn->sec, insn->offset); insn->sec, insn->offset);
return -1; return -1;
...@@ -1799,6 +1880,50 @@ static bool insn_state_match(struct instruction *insn, struct insn_state *state) ...@@ -1799,6 +1880,50 @@ static bool insn_state_match(struct instruction *insn, struct insn_state *state)
return false; return false;
} }
static inline bool func_uaccess_safe(struct symbol *func)
{
if (func)
return func->alias->uaccess_safe;
return false;
}
static inline const char *insn_dest_name(struct instruction *insn)
{
if (insn->call_dest)
return insn->call_dest->name;
return "{dynamic}";
}
static int validate_call(struct instruction *insn, struct insn_state *state)
{
if (state->uaccess && !func_uaccess_safe(insn->call_dest)) {
WARN_FUNC("call to %s() with UACCESS enabled",
insn->sec, insn->offset, insn_dest_name(insn));
return 1;
}
if (state->df) {
WARN_FUNC("call to %s() with DF set",
insn->sec, insn->offset, insn_dest_name(insn));
return 1;
}
return 0;
}
static int validate_sibling_call(struct instruction *insn, struct insn_state *state)
{
if (has_modified_stack_frame(state)) {
WARN_FUNC("sibling call from callable instruction with modified stack frame",
insn->sec, insn->offset);
return 1;
}
return validate_call(insn, state);
}
/* /*
* Follow the branch starting at the given instruction, and recursively follow * Follow the branch starting at the given instruction, and recursively follow
* any other branches (jumps). Meanwhile, track the frame pointer state at * any other branches (jumps). Meanwhile, track the frame pointer state at
...@@ -1844,6 +1969,8 @@ static int validate_branch(struct objtool_file *file, struct instruction *first, ...@@ -1844,6 +1969,8 @@ static int validate_branch(struct objtool_file *file, struct instruction *first,
if (!insn->hint && !insn_state_match(insn, &state)) if (!insn->hint && !insn_state_match(insn, &state))
return 1; return 1;
/* If we were here with AC=0, but now have AC=1, go again */
if (insn->state.uaccess || !state.uaccess)
return 0; return 0;
} }
...@@ -1893,16 +2020,42 @@ static int validate_branch(struct objtool_file *file, struct instruction *first, ...@@ -1893,16 +2020,42 @@ static int validate_branch(struct objtool_file *file, struct instruction *first,
insn->visited = true; insn->visited = true;
if (!insn->ignore_alts) { if (!insn->ignore_alts) {
bool skip_orig = false;
list_for_each_entry(alt, &insn->alts, list) { list_for_each_entry(alt, &insn->alts, list) {
if (alt->skip_orig)
skip_orig = true;
ret = validate_branch(file, alt->insn, state); ret = validate_branch(file, alt->insn, state);
if (ret) if (ret) {
return 1; if (backtrace)
BT_FUNC("(alt)", insn);
return ret;
} }
} }
if (skip_orig)
return 0;
}
switch (insn->type) { switch (insn->type) {
case INSN_RETURN: case INSN_RETURN:
if (state.uaccess && !func_uaccess_safe(func)) {
WARN_FUNC("return with UACCESS enabled", sec, insn->offset);
return 1;
}
if (!state.uaccess && func_uaccess_safe(func)) {
WARN_FUNC("return with UACCESS disabled from a UACCESS-safe function", sec, insn->offset);
return 1;
}
if (state.df) {
WARN_FUNC("return with DF set", sec, insn->offset);
return 1;
}
if (func && has_modified_stack_frame(&state)) { if (func && has_modified_stack_frame(&state)) {
WARN_FUNC("return with modified stack frame", WARN_FUNC("return with modified stack frame",
sec, insn->offset); sec, insn->offset);
...@@ -1918,6 +2071,12 @@ static int validate_branch(struct objtool_file *file, struct instruction *first, ...@@ -1918,6 +2071,12 @@ static int validate_branch(struct objtool_file *file, struct instruction *first,
return 0; return 0;
case INSN_CALL: case INSN_CALL:
case INSN_CALL_DYNAMIC:
ret = validate_call(insn, &state);
if (ret)
return ret;
if (insn->type == INSN_CALL) {
if (is_fentry_call(insn)) if (is_fentry_call(insn))
break; break;
...@@ -1926,9 +2085,8 @@ static int validate_branch(struct objtool_file *file, struct instruction *first, ...@@ -1926,9 +2085,8 @@ static int validate_branch(struct objtool_file *file, struct instruction *first,
return 0; return 0;
if (ret == -1) if (ret == -1)
return 1; return 1;
}
/* fallthrough */
case INSN_CALL_DYNAMIC:
if (!no_fp && func && !has_valid_stack_frame(&state)) { if (!no_fp && func && !has_valid_stack_frame(&state)) {
WARN_FUNC("call without frame pointer save/setup", WARN_FUNC("call without frame pointer save/setup",
sec, insn->offset); sec, insn->offset);
...@@ -1938,18 +2096,21 @@ static int validate_branch(struct objtool_file *file, struct instruction *first, ...@@ -1938,18 +2096,21 @@ static int validate_branch(struct objtool_file *file, struct instruction *first,
case INSN_JUMP_CONDITIONAL: case INSN_JUMP_CONDITIONAL:
case INSN_JUMP_UNCONDITIONAL: case INSN_JUMP_UNCONDITIONAL:
if (insn->jump_dest && if (func && !insn->jump_dest) {
ret = validate_sibling_call(insn, &state);
if (ret)
return ret;
} else if (insn->jump_dest &&
(!func || !insn->jump_dest->func || (!func || !insn->jump_dest->func ||
insn->jump_dest->func->pfunc == func)) { insn->jump_dest->func->pfunc == func)) {
ret = validate_branch(file, insn->jump_dest, ret = validate_branch(file, insn->jump_dest,
state); state);
if (ret) if (ret) {
return 1; if (backtrace)
BT_FUNC("(branch)", insn);
} else if (func && has_modified_stack_frame(&state)) { return ret;
WARN_FUNC("sibling call from callable instruction with modified stack frame", }
sec, insn->offset);
return 1;
} }
if (insn->type == INSN_JUMP_UNCONDITIONAL) if (insn->type == INSN_JUMP_UNCONDITIONAL)
...@@ -1958,11 +2119,10 @@ static int validate_branch(struct objtool_file *file, struct instruction *first, ...@@ -1958,11 +2119,10 @@ static int validate_branch(struct objtool_file *file, struct instruction *first,
break; break;
case INSN_JUMP_DYNAMIC: case INSN_JUMP_DYNAMIC:
if (func && list_empty(&insn->alts) && if (func && list_empty(&insn->alts)) {
has_modified_stack_frame(&state)) { ret = validate_sibling_call(insn, &state);
WARN_FUNC("sibling call from callable instruction with modified stack frame", if (ret)
sec, insn->offset); return ret;
return 1;
} }
return 0; return 0;
...@@ -1979,6 +2139,63 @@ static int validate_branch(struct objtool_file *file, struct instruction *first, ...@@ -1979,6 +2139,63 @@ static int validate_branch(struct objtool_file *file, struct instruction *first,
if (update_insn_state(insn, &state)) if (update_insn_state(insn, &state))
return 1; 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:
if (state.uaccess) {
WARN_FUNC("recursive UACCESS enable", sec, insn->offset);
return 1;
}
state.uaccess = true;
break;
case INSN_CLAC:
if (!state.uaccess && insn->func) {
WARN_FUNC("redundant UACCESS disable", sec, insn->offset);
return 1;
}
if (func_uaccess_safe(func) && !state.uaccess_stack) {
WARN_FUNC("UACCESS-safe disables UACCESS", sec, insn->offset);
return 1;
}
state.uaccess = false;
break;
case INSN_STD:
if (state.df)
WARN_FUNC("recursive STD", sec, insn->offset);
state.df = true;
break;
case INSN_CLD:
if (!state.df && insn->func)
WARN_FUNC("redundant CLD", sec, insn->offset);
state.df = false;
break; break;
default: default:
...@@ -2015,6 +2232,8 @@ static int validate_unwind_hints(struct objtool_file *file) ...@@ -2015,6 +2232,8 @@ static int validate_unwind_hints(struct objtool_file *file)
for_each_insn(file, insn) { for_each_insn(file, insn) {
if (insn->hint && !insn->visited) { if (insn->hint && !insn->visited) {
ret = validate_branch(file, insn, state); ret = validate_branch(file, insn, state);
if (ret && backtrace)
BT_FUNC("<=== (hint)", insn);
warnings += ret; warnings += ret;
} }
} }
...@@ -2142,7 +2361,11 @@ static int validate_functions(struct objtool_file *file) ...@@ -2142,7 +2361,11 @@ static int validate_functions(struct objtool_file *file)
if (!insn || insn->ignore) if (!insn || insn->ignore)
continue; continue;
state.uaccess = func->alias->uaccess_safe;
ret = validate_branch(file, insn, state); ret = validate_branch(file, insn, state);
if (ret && backtrace)
BT_FUNC("<=== (func)", insn);
warnings += ret; warnings += ret;
} }
} }
...@@ -2199,7 +2422,6 @@ int check(const char *_objname, bool orc) ...@@ -2199,7 +2422,6 @@ int check(const char *_objname, bool orc)
INIT_LIST_HEAD(&file.insn_list); INIT_LIST_HEAD(&file.insn_list);
hash_init(file.insn_hash); hash_init(file.insn_hash);
file.whitelist = find_section_by_name(file.elf, ".discard.func_stack_frame_non_standard");
file.c_file = find_section_by_name(file.elf, ".comment"); file.c_file = find_section_by_name(file.elf, ".comment");
file.ignore_unreachables = no_unreachable; file.ignore_unreachables = no_unreachable;
file.hints = false; file.hints = false;
......
...@@ -31,7 +31,8 @@ struct insn_state { ...@@ -31,7 +31,8 @@ struct insn_state {
int stack_size; int stack_size;
unsigned char type; unsigned char type;
bool bp_scratch; bool bp_scratch;
bool drap, end; bool drap, end, uaccess, df;
unsigned int uaccess_stack;
int drap_reg, drap_offset; int drap_reg, drap_offset;
struct cfi_reg vals[CFI_NUM_REGS]; struct cfi_reg vals[CFI_NUM_REGS];
}; };
...@@ -60,7 +61,6 @@ struct objtool_file { ...@@ -60,7 +61,6 @@ struct objtool_file {
struct elf *elf; struct elf *elf;
struct list_head insn_list; struct list_head insn_list;
DECLARE_HASHTABLE(insn_hash, 16); DECLARE_HASHTABLE(insn_hash, 16);
struct section *whitelist;
bool ignore_unreachables, c_file, hints, rodata; bool ignore_unreachables, c_file, hints, rodata;
}; };
......
...@@ -219,7 +219,7 @@ static int read_sections(struct elf *elf) ...@@ -219,7 +219,7 @@ static int read_sections(struct elf *elf)
static int read_symbols(struct elf *elf) static int read_symbols(struct elf *elf)
{ {
struct section *symtab, *sec; struct section *symtab, *sec;
struct symbol *sym, *pfunc; struct symbol *sym, *pfunc, *alias;
struct list_head *entry, *tmp; struct list_head *entry, *tmp;
int symbols_nr, i; int symbols_nr, i;
char *coldstr; char *coldstr;
...@@ -239,6 +239,7 @@ static int read_symbols(struct elf *elf) ...@@ -239,6 +239,7 @@ static int read_symbols(struct elf *elf)
return -1; return -1;
} }
memset(sym, 0, sizeof(*sym)); memset(sym, 0, sizeof(*sym));
alias = sym;
sym->idx = i; sym->idx = i;
...@@ -288,11 +289,17 @@ static int read_symbols(struct elf *elf) ...@@ -288,11 +289,17 @@ static int read_symbols(struct elf *elf)
break; break;
} }
if (sym->offset == s->offset && sym->len >= s->len) { if (sym->offset == s->offset) {
if (sym->len == s->len && alias == sym)
alias = s;
if (sym->len >= s->len) {
entry = tmp; entry = tmp;
break; break;
} }
} }
}
sym->alias = alias;
list_add(&sym->list, entry); list_add(&sym->list, entry);
hash_add(sym->sec->symbol_hash, &sym->hash, sym->idx); hash_add(sym->sec->symbol_hash, &sym->hash, sym->idx);
} }
......
...@@ -61,7 +61,8 @@ struct symbol { ...@@ -61,7 +61,8 @@ struct symbol {
unsigned char bind, type; unsigned char bind, type;
unsigned long offset; unsigned long offset;
unsigned int len; unsigned int len;
struct symbol *pfunc, *cfunc; struct symbol *pfunc, *cfunc, *alias;
bool uaccess_safe;
}; };
struct rela { struct rela {
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "builtin.h"
#include "special.h" #include "special.h"
#include "warn.h" #include "warn.h"
...@@ -42,6 +43,7 @@ ...@@ -42,6 +43,7 @@
#define ALT_NEW_LEN_OFFSET 11 #define ALT_NEW_LEN_OFFSET 11
#define X86_FEATURE_POPCNT (4*32+23) #define X86_FEATURE_POPCNT (4*32+23)
#define X86_FEATURE_SMAP (9*32+20)
struct special_entry { struct special_entry {
const char *sec; const char *sec;
...@@ -110,6 +112,22 @@ static int get_alt_entry(struct elf *elf, struct special_entry *entry, ...@@ -110,6 +112,22 @@ static int get_alt_entry(struct elf *elf, struct special_entry *entry,
*/ */
if (feature == X86_FEATURE_POPCNT) if (feature == X86_FEATURE_POPCNT)
alt->skip_orig = true; alt->skip_orig = true;
/*
* If UACCESS validation is enabled; force that alternative;
* otherwise force it the other way.
*
* What we want to avoid is having both the original and the
* alternative code flow at the same time, in that case we can
* find paths that see the STAC but take the NOP instead of
* CLAC and the other way around.
*/
if (feature == X86_FEATURE_SMAP) {
if (uaccess)
alt->skip_orig = true;
else
alt->skip_alt = true;
}
} }
orig_rela = find_rela_by_dest(sec, offset + entry->orig); orig_rela = find_rela_by_dest(sec, offset + entry->orig);
......
...@@ -26,6 +26,7 @@ struct special_alt { ...@@ -26,6 +26,7 @@ struct special_alt {
bool group; bool group;
bool skip_orig; bool skip_orig;
bool skip_alt;
bool jump_or_nop; bool jump_or_nop;
struct section *orig_sec; struct section *orig_sec;
......
...@@ -64,6 +64,14 @@ static inline char *offstr(struct section *sec, unsigned long offset) ...@@ -64,6 +64,14 @@ static inline char *offstr(struct section *sec, unsigned long offset)
free(_str); \ free(_str); \
}) })
#define BT_FUNC(format, insn, ...) \
({ \
struct instruction *_insn = (insn); \
char *_str = offstr(_insn->sec, _insn->offset); \
WARN(" %s: " format, _str, ##__VA_ARGS__); \
free(_str); \
})
#define WARN_ELF(format, ...) \ #define WARN_ELF(format, ...) \
WARN(format ": %s", ##__VA_ARGS__, elf_errmsg(-1)) WARN(format ": %s", ##__VA_ARGS__, elf_errmsg(-1))
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment