Skip to content

Commit

Permalink
entry: kmsan: introduce kmsan_unpoison_entry_regs()
Browse files Browse the repository at this point in the history
struct pt_regs passed into IRQ entry code is set up by uninstrumented asm
functions, therefore KMSAN may not notice the registers are initialized.

kmsan_unpoison_entry_regs() unpoisons the contents of struct pt_regs,
preventing potential false positives.  Unlike kmsan_unpoison_memory(), it
can be called under kmsan_in_runtime(), which is often the case in IRQ
entry code.

Link: https://lkml.kernel.org/r/[email protected]
Signed-off-by: Alexander Potapenko <[email protected]>
Cc: Alexander Viro <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Andrey Konovalov <[email protected]>
Cc: Andrey Konovalov <[email protected]>
Cc: Andy Lutomirski <[email protected]>
Cc: Arnd Bergmann <[email protected]>
Cc: Borislav Petkov <[email protected]>
Cc: Christoph Hellwig <[email protected]>
Cc: Christoph Lameter <[email protected]>
Cc: David Rientjes <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: Eric Biggers <[email protected]>
Cc: Eric Biggers <[email protected]>
Cc: Eric Dumazet <[email protected]>
Cc: Greg Kroah-Hartman <[email protected]>
Cc: Herbert Xu <[email protected]>
Cc: Ilya Leoshkevich <[email protected]>
Cc: Ingo Molnar <[email protected]>
Cc: Jens Axboe <[email protected]>
Cc: Joonsoo Kim <[email protected]>
Cc: Kees Cook <[email protected]>
Cc: Marco Elver <[email protected]>
Cc: Mark Rutland <[email protected]>
Cc: Matthew Wilcox <[email protected]>
Cc: Michael S. Tsirkin <[email protected]>
Cc: Pekka Enberg <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Petr Mladek <[email protected]>
Cc: Stephen Rothwell <[email protected]>
Cc: Steven Rostedt <[email protected]>
Cc: Thomas Gleixner <[email protected]>
Cc: Vasily Gorbik <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Vlastimil Babka <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
  • Loading branch information
ramosian-glider authored and akpm00 committed Oct 3, 2022
1 parent 37ad4ee commit 6cae637
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 0 deletions.
15 changes: 15 additions & 0 deletions include/linux/kmsan.h
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,17 @@ void kmsan_handle_dma_sg(struct scatterlist *sg, int nents,
*/
void kmsan_handle_urb(const struct urb *urb, bool is_out);

/**
* kmsan_unpoison_entry_regs() - Handle pt_regs in low-level entry code.
* @regs: struct pt_regs pointer received from assembly code.
*
* KMSAN unpoisons the contents of the passed pt_regs, preventing potential
* false positive reports. Unlike kmsan_unpoison_memory(),
* kmsan_unpoison_entry_regs() can be called from the regions where
* kmsan_in_runtime() returns true, which is the case in early entry code.
*/
void kmsan_unpoison_entry_regs(const struct pt_regs *regs);

#else

static inline void kmsan_init_shadow(void)
Expand Down Expand Up @@ -310,6 +321,10 @@ static inline void kmsan_handle_urb(const struct urb *urb, bool is_out)
{
}

static inline void kmsan_unpoison_entry_regs(const struct pt_regs *regs)
{
}

#endif

#endif /* _LINUX_KMSAN_H */
5 changes: 5 additions & 0 deletions kernel/entry/common.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <linux/resume_user_mode.h>
#include <linux/highmem.h>
#include <linux/jump_label.h>
#include <linux/kmsan.h>
#include <linux/livepatch.h>
#include <linux/audit.h>
#include <linux/tick.h>
Expand All @@ -24,6 +25,7 @@ static __always_inline void __enter_from_user_mode(struct pt_regs *regs)
user_exit_irqoff();

instrumentation_begin();
kmsan_unpoison_entry_regs(regs);
trace_hardirqs_off_finish();
instrumentation_end();
}
Expand Down Expand Up @@ -352,6 +354,7 @@ noinstr irqentry_state_t irqentry_enter(struct pt_regs *regs)
lockdep_hardirqs_off(CALLER_ADDR0);
ct_irq_enter();
instrumentation_begin();
kmsan_unpoison_entry_regs(regs);
trace_hardirqs_off_finish();
instrumentation_end();

Expand All @@ -367,6 +370,7 @@ noinstr irqentry_state_t irqentry_enter(struct pt_regs *regs)
*/
lockdep_hardirqs_off(CALLER_ADDR0);
instrumentation_begin();
kmsan_unpoison_entry_regs(regs);
rcu_irq_enter_check_tick();
trace_hardirqs_off_finish();
instrumentation_end();
Expand Down Expand Up @@ -452,6 +456,7 @@ irqentry_state_t noinstr irqentry_nmi_enter(struct pt_regs *regs)
ct_nmi_enter();

instrumentation_begin();
kmsan_unpoison_entry_regs(regs);
trace_hardirqs_off_finish();
ftrace_nmi_enter();
instrumentation_end();
Expand Down
26 changes: 26 additions & 0 deletions mm/kmsan/hooks.c
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,32 @@ void kmsan_unpoison_memory(const void *address, size_t size)
}
EXPORT_SYMBOL(kmsan_unpoison_memory);

/*
* Version of kmsan_unpoison_memory() that can be called from within the KMSAN
* runtime.
*
* Non-instrumented IRQ entry functions receive struct pt_regs from assembly
* code. Those regs need to be unpoisoned, otherwise using them will result in
* false positives.
* Using kmsan_unpoison_memory() is not an option in entry code, because the
* return value of in_task() is inconsistent - as a result, certain calls to
* kmsan_unpoison_memory() are ignored. kmsan_unpoison_entry_regs() ensures that
* the registers are unpoisoned even if kmsan_in_runtime() is true in the early
* entry code.
*/
void kmsan_unpoison_entry_regs(const struct pt_regs *regs)
{
unsigned long ua_flags;

if (!kmsan_enabled)
return;

ua_flags = user_access_save();
kmsan_internal_unpoison_memory((void *)regs, sizeof(*regs),
KMSAN_POISON_NOCHECK);
user_access_restore(ua_flags);
}

void kmsan_check_memory(const void *addr, size_t size)
{
if (!kmsan_enabled)
Expand Down

0 comments on commit 6cae637

Please sign in to comment.