mirror of
https://github.com/torvalds/linux.git
synced 2024-11-11 22:51:42 +00:00
x86/irq: Make run_on_irqstack_cond() typesafe
Sami reported that run_on_irqstack_cond() requires the caller to cast
functions to mismatching types, which trips indirect call Control-Flow
Integrity (CFI) in Clang.
Instead of disabling CFI on that function, provide proper helpers for
the three call variants. The actual ASM code stays the same as that is
out of reach.
[ bp: Fix __run_on_irqstack() prototype to match. ]
Fixes: 931b941459
("x86/entry: Provide helpers for executing on the irqstack")
Reported-by: Nathan Chancellor <natechancellor@gmail.com>
Reported-by: Sami Tolvanen <samitolvanen@google.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Borislav Petkov <bp@suse.de>
Tested-by: Sami Tolvanen <samitolvanen@google.com>
Cc: <stable@vger.kernel.org>
Link: https://github.com/ClangBuiltLinux/linux/issues/1052
Link: https://lkml.kernel.org/r/87pn6eb5tv.fsf@nanos.tec.linutronix.de
This commit is contained in:
parent
9847774063
commit
a7b3474cbb
@ -299,7 +299,7 @@ __visible noinstr void xen_pv_evtchn_do_upcall(struct pt_regs *regs)
|
|||||||
old_regs = set_irq_regs(regs);
|
old_regs = set_irq_regs(regs);
|
||||||
|
|
||||||
instrumentation_begin();
|
instrumentation_begin();
|
||||||
run_on_irqstack_cond(__xen_pv_evtchn_do_upcall, NULL, regs);
|
run_on_irqstack_cond(__xen_pv_evtchn_do_upcall, regs);
|
||||||
instrumentation_begin();
|
instrumentation_begin();
|
||||||
|
|
||||||
set_irq_regs(old_regs);
|
set_irq_regs(old_regs);
|
||||||
|
@ -682,6 +682,8 @@ SYM_CODE_END(.Lbad_gs)
|
|||||||
* rdx: Function argument (can be NULL if none)
|
* rdx: Function argument (can be NULL if none)
|
||||||
*/
|
*/
|
||||||
SYM_FUNC_START(asm_call_on_stack)
|
SYM_FUNC_START(asm_call_on_stack)
|
||||||
|
SYM_INNER_LABEL(asm_call_sysvec_on_stack, SYM_L_GLOBAL)
|
||||||
|
SYM_INNER_LABEL(asm_call_irq_on_stack, SYM_L_GLOBAL)
|
||||||
/*
|
/*
|
||||||
* Save the frame pointer unconditionally. This allows the ORC
|
* Save the frame pointer unconditionally. This allows the ORC
|
||||||
* unwinder to handle the stack switch.
|
* unwinder to handle the stack switch.
|
||||||
|
@ -242,7 +242,7 @@ __visible noinstr void func(struct pt_regs *regs) \
|
|||||||
instrumentation_begin(); \
|
instrumentation_begin(); \
|
||||||
irq_enter_rcu(); \
|
irq_enter_rcu(); \
|
||||||
kvm_set_cpu_l1tf_flush_l1d(); \
|
kvm_set_cpu_l1tf_flush_l1d(); \
|
||||||
run_on_irqstack_cond(__##func, regs, regs); \
|
run_sysvec_on_irqstack_cond(__##func, regs); \
|
||||||
irq_exit_rcu(); \
|
irq_exit_rcu(); \
|
||||||
instrumentation_end(); \
|
instrumentation_end(); \
|
||||||
irqentry_exit(regs, state); \
|
irqentry_exit(regs, state); \
|
||||||
|
@ -12,20 +12,50 @@ static __always_inline bool irqstack_active(void)
|
|||||||
return __this_cpu_read(irq_count) != -1;
|
return __this_cpu_read(irq_count) != -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void asm_call_on_stack(void *sp, void *func, void *arg);
|
void asm_call_on_stack(void *sp, void (*func)(void), void *arg);
|
||||||
|
void asm_call_sysvec_on_stack(void *sp, void (*func)(struct pt_regs *regs),
|
||||||
|
struct pt_regs *regs);
|
||||||
|
void asm_call_irq_on_stack(void *sp, void (*func)(struct irq_desc *desc),
|
||||||
|
struct irq_desc *desc);
|
||||||
|
|
||||||
static __always_inline void __run_on_irqstack(void *func, void *arg)
|
static __always_inline void __run_on_irqstack(void (*func)(void))
|
||||||
{
|
{
|
||||||
void *tos = __this_cpu_read(hardirq_stack_ptr);
|
void *tos = __this_cpu_read(hardirq_stack_ptr);
|
||||||
|
|
||||||
__this_cpu_add(irq_count, 1);
|
__this_cpu_add(irq_count, 1);
|
||||||
asm_call_on_stack(tos - 8, func, arg);
|
asm_call_on_stack(tos - 8, func, NULL);
|
||||||
|
__this_cpu_sub(irq_count, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static __always_inline void
|
||||||
|
__run_sysvec_on_irqstack(void (*func)(struct pt_regs *regs),
|
||||||
|
struct pt_regs *regs)
|
||||||
|
{
|
||||||
|
void *tos = __this_cpu_read(hardirq_stack_ptr);
|
||||||
|
|
||||||
|
__this_cpu_add(irq_count, 1);
|
||||||
|
asm_call_sysvec_on_stack(tos - 8, func, regs);
|
||||||
|
__this_cpu_sub(irq_count, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static __always_inline void
|
||||||
|
__run_irq_on_irqstack(void (*func)(struct irq_desc *desc),
|
||||||
|
struct irq_desc *desc)
|
||||||
|
{
|
||||||
|
void *tos = __this_cpu_read(hardirq_stack_ptr);
|
||||||
|
|
||||||
|
__this_cpu_add(irq_count, 1);
|
||||||
|
asm_call_irq_on_stack(tos - 8, func, desc);
|
||||||
__this_cpu_sub(irq_count, 1);
|
__this_cpu_sub(irq_count, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#else /* CONFIG_X86_64 */
|
#else /* CONFIG_X86_64 */
|
||||||
static inline bool irqstack_active(void) { return false; }
|
static inline bool irqstack_active(void) { return false; }
|
||||||
static inline void __run_on_irqstack(void *func, void *arg) { }
|
static inline void __run_on_irqstack(void (*func)(void)) { }
|
||||||
|
static inline void __run_sysvec_on_irqstack(void (*func)(struct pt_regs *regs),
|
||||||
|
struct pt_regs *regs) { }
|
||||||
|
static inline void __run_irq_on_irqstack(void (*func)(struct irq_desc *desc),
|
||||||
|
struct irq_desc *desc) { }
|
||||||
#endif /* !CONFIG_X86_64 */
|
#endif /* !CONFIG_X86_64 */
|
||||||
|
|
||||||
static __always_inline bool irq_needs_irq_stack(struct pt_regs *regs)
|
static __always_inline bool irq_needs_irq_stack(struct pt_regs *regs)
|
||||||
@ -37,17 +67,40 @@ static __always_inline bool irq_needs_irq_stack(struct pt_regs *regs)
|
|||||||
return !user_mode(regs) && !irqstack_active();
|
return !user_mode(regs) && !irqstack_active();
|
||||||
}
|
}
|
||||||
|
|
||||||
static __always_inline void run_on_irqstack_cond(void *func, void *arg,
|
|
||||||
|
static __always_inline void run_on_irqstack_cond(void (*func)(void),
|
||||||
struct pt_regs *regs)
|
struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
void (*__func)(void *arg) = func;
|
|
||||||
|
|
||||||
lockdep_assert_irqs_disabled();
|
lockdep_assert_irqs_disabled();
|
||||||
|
|
||||||
if (irq_needs_irq_stack(regs))
|
if (irq_needs_irq_stack(regs))
|
||||||
__run_on_irqstack(__func, arg);
|
__run_on_irqstack(func);
|
||||||
else
|
else
|
||||||
__func(arg);
|
func();
|
||||||
|
}
|
||||||
|
|
||||||
|
static __always_inline void
|
||||||
|
run_sysvec_on_irqstack_cond(void (*func)(struct pt_regs *regs),
|
||||||
|
struct pt_regs *regs)
|
||||||
|
{
|
||||||
|
lockdep_assert_irqs_disabled();
|
||||||
|
|
||||||
|
if (irq_needs_irq_stack(regs))
|
||||||
|
__run_sysvec_on_irqstack(func, regs);
|
||||||
|
else
|
||||||
|
func(regs);
|
||||||
|
}
|
||||||
|
|
||||||
|
static __always_inline void
|
||||||
|
run_irq_on_irqstack_cond(void (*func)(struct irq_desc *desc), struct irq_desc *desc,
|
||||||
|
struct pt_regs *regs)
|
||||||
|
{
|
||||||
|
lockdep_assert_irqs_disabled();
|
||||||
|
|
||||||
|
if (irq_needs_irq_stack(regs))
|
||||||
|
__run_irq_on_irqstack(func, desc);
|
||||||
|
else
|
||||||
|
func(desc);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -227,7 +227,7 @@ static __always_inline void handle_irq(struct irq_desc *desc,
|
|||||||
struct pt_regs *regs)
|
struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
if (IS_ENABLED(CONFIG_X86_64))
|
if (IS_ENABLED(CONFIG_X86_64))
|
||||||
run_on_irqstack_cond(desc->handle_irq, desc, regs);
|
run_irq_on_irqstack_cond(desc->handle_irq, desc, regs);
|
||||||
else
|
else
|
||||||
__handle_irq(desc, regs);
|
__handle_irq(desc, regs);
|
||||||
}
|
}
|
||||||
|
@ -74,5 +74,5 @@ int irq_init_percpu_irqstack(unsigned int cpu)
|
|||||||
|
|
||||||
void do_softirq_own_stack(void)
|
void do_softirq_own_stack(void)
|
||||||
{
|
{
|
||||||
run_on_irqstack_cond(__do_softirq, NULL, NULL);
|
run_on_irqstack_cond(__do_softirq, NULL);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user