Add KernelMemorySanitizer infrastructure
Subsystems (47)
[email protected]
maintainercover Add KernelMemorySanitizer infrastructure
KernelMemorySanitizer (KMSAN) is a detector of errors related to uses of uninitialized memory. It relies on compile-time Clang instrumentation (similar to MSan: https://clang.llvm.org/docs/MemorySanitizer.html)
and tracks the state of every bit of kernel memory, being able to report an error if uninitialized value is used in a condition, dereferenced or copied to userspace, USB or network.
KMSAN has reported more than 200 bugs in the past two years, most of them with the help of syzkaller (http://syzkaller.appspot.com).
The proposed patchset contains KMSAN runtime implementation together with small changes to other subsystems needed to make KMSAN work. The latter changes fall into several categories:
- nice-to-have features that are independent from KMSAN but simplify its implementation (stackdepot changes, CONFIG_GENERIC_CSUM etc.);
- Kconfig changes that prohibit options incompatible with KMSAN;
- calls to KMSAN runtime functions that help KMSAN do the bookkeeping (e.g. tell it to allocate, copy or delete the metadata);
- calls to KMSAN runtime functions that tell KMSAN to check memory escaping the kernel for uninitialized values. These are required to increase the number of true positive error reports;
- calls to runtime functions that tell KMSAN to ignore certain memory ranges to avoid false negative reports. Most certainly there can be better ways to deal with every such report.
This patchset allows one to boot and run a defconfig+KMSAN kernel on a QEMU without known major false positives. It however doesn't guarantee there are no false positives in drivers of certain devices or less tested subsystems, although KMSAN is actively tested on syzbot with quite a rich config.
One may find it handy to review these patches in Gerrit:
https://linux-review.googlesource.com/c/linux/kernel/git/torvalds/linux/+/1081
I've ensured the Change-Id: tags stay away from commit descriptions.
Note: checkpatch.pl complains a lot about the use of BUG_ON in KMSAN source. I don't have a strong opinion on this, but KMSAN is a debugging tool, so any runtime invariant violation in it renders the tool useless. Therefore it doesn't make much sense to not terminate after a bug in KMSAN.
Alexander Potapenko (26):
stackdepot: check depot_index before accessing the stack slab
stackdepot: prevent Clang from optimizing away stackdepot_memcmp()
kasan: stackdepot: move filter_irq_stacks() to stackdepot.c
stackdepot: reserve 5 extra bits in depot_stack_handle_t
printk_safe: externalize printk_context
kasan: compiler.h: rename __no_kasan_or_inline into
__no_memory_tool_or_inline
kmsan: add ReST documentation
kmsan: gfp: introduce __GFP_NO_KMSAN_SHADOW
kmsan: introduce __no_sanitize_memory and __SANITIZE_MEMORY__
kmsan: reduce vmalloc space
kmsan: add KMSAN runtime
kmsan: x86: sync metadata pages on page fault
kmsan: add tests for KMSAN
kmsan: make READ_ONCE_TASK_STACK() return initialized values
kmsan: Kconfig changes to disable options incompatible with KMSAN
kmsan: Changing existing files to enable KMSAN builds
kmsan: disable KMSAN instrumentation for certain kernel parts
kmsan: mm: call KMSAN hooks from SLUB code
kmsan: call KMSAN hooks where needed
kmsan: disable instrumentation of certain functions
kmsan: unpoison |tlb| in arch_tlb_gather_mmu()
kmsan: use __msan_memcpy() where possible.
kmsan: unpoisoning buffers from devices etc.
kmsan: hooks for copy_to_user() and friends
kmsan: disable strscpy() optimization under KMSAN
net: kasan: kmsan: support CONFIG_GENERIC_CSUM on x86, enable it for
KASAN/KMSAN
To: Alexander Potapenko <[email protected]>
Cc: Alexander Viro <[email protected]>
Cc: Andrew Morton <[email protected]>
Cc: Andrey Ryabinin <[email protected]>
Cc: Andy Lutomirski <[email protected]>
Cc: Ard Biesheuvel <[email protected]>
Cc: Arnd Bergmann <[email protected]>
Cc: Christoph Hellwig <[email protected]>
Cc: Dmitry Torokhov <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: Eric Dumazet <[email protected]>
Cc: Eric Van Hensbergen <[email protected]>
Cc: Greg Kroah-Hartman <[email protected]>
Cc: Harry Wentland <[email protected]>
Cc: Herbert Xu <[email protected]>
Cc: Ingo Molnar <[email protected]>
Cc: Jens Axboe <[email protected]>
Cc: Martin K. Petersen <[email protected]>
Cc: Martin Schwidefsky <[email protected]>
Cc: "Michael S. Tsirkin" <[email protected]>
Cc: Michal Simek <[email protected]>
Cc: Petr Mladek <[email protected]>
Cc: Sergey Senozhatsky <[email protected]>
Cc: Steven Rostedt <[email protected]>
Cc: Takashi Iwai <[email protected]>
Cc: "Theodore Ts'o" <[email protected]>
Cc: Thomas Gleixner <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Wolfram Sang <[email protected]>
Cc: Vasily Gorbik <[email protected]>
Cc: Ilya Leoshkevich <[email protected]>
Cc: Mark Rutland <[email protected]>
Cc: [email protected]
Documentation/dev-tools/index.rst | 1 + Documentation/dev-tools/kmsan.rst | 418 +++++++++++++++++ Makefile | 3 +- arch/Kconfig | 2 +- arch/s390/include/asm/processor.h | 4 +- arch/x86/Kconfig | 7 + arch/x86/Kconfig.debug | 3 + arch/x86/boot/Makefile | 2 + arch/x86/boot/compressed/Makefile | 2 + arch/x86/boot/compressed/misc.h | 1 + arch/x86/entry/common.c | 1 + arch/x86/entry/entry_64.S | 18 + arch/x86/entry/vdso/Makefile | 3 + arch/x86/include/asm/checksum.h | 10 +- arch/x86/include/asm/irq_regs.h | 1 + arch/x86/include/asm/kmsan.h | 129 ++++++ arch/x86/include/asm/page_64.h | 13 + arch/x86/include/asm/page_64_types.h | 12 +- arch/x86/include/asm/pgtable_64_types.h | 15 + arch/x86/include/asm/string_64.h | 9 +- arch/x86/include/asm/syscall_wrapper.h | 1 + arch/x86/include/asm/uaccess.h | 12 + arch/x86/include/asm/unwind.h | 9 +- arch/x86/kernel/Makefile | 4 + arch/x86/kernel/apic/apic.c | 2 + arch/x86/kernel/cpu/Makefile | 1 + arch/x86/kernel/dumpstack_64.c | 1 + arch/x86/kernel/process_64.c | 5 + arch/x86/kernel/traps.c | 12 +- arch/x86/kernel/uprobes.c | 7 +- arch/x86/lib/Makefile | 2 + arch/x86/mm/Makefile | 2 + arch/x86/mm/fault.c | 20 + arch/x86/mm/ioremap.c | 3 + arch/x86/realmode/rm/Makefile | 2 + block/bio.c | 20 + block/blk.h | 7 + block/partition-generic.c | 9 +- crypto/Kconfig | 26 ++ drivers/char/random.c | 2 + drivers/firmware/efi/libstub/Makefile | 1 + drivers/gpu/drm/amd/display/Kconfig | 2 +- drivers/i2c/i2c-core-base.c | 2 + drivers/input/serio/libps2.c | 6 +- drivers/scsi/scsi_lib.c | 4 + drivers/usb/core/message.c | 6 +- drivers/usb/core/urb.c | 2 + drivers/virtio/virtio_ring.c | 14 + fs/buffer.c | 7 +- include/asm-generic/cacheflush.h | 7 +- include/asm-generic/uaccess.h | 12 +- include/linux/compiler-clang.h | 8 + include/linux/compiler-gcc.h | 5 + include/linux/compiler.h | 21 +- include/linux/dma-mapping.h | 2 + include/linux/gfp.h | 4 +- include/linux/highmem.h | 4 + include/linux/kmsan-checks.h | 123 +++++ include/linux/kmsan.h | 143 ++++++ include/linux/mm_types.h | 9 + include/linux/sched.h | 5 + include/linux/skbuff.h | 5 +- include/linux/stackdepot.h | 10 + include/linux/string.h | 2 + include/linux/uaccess.h | 32 +- init/main.c | 3 + kernel/Makefile | 1 + kernel/exit.c | 2 + kernel/fork.c | 2 + kernel/kthread.c | 2 + kernel/printk/printk.c | 8 +- kernel/printk/printk_safe.c | 2 +- kernel/profile.c | 1 + kernel/sched/core.c | 11 + kernel/softirq.c | 5 + lib/Kconfig.debug | 5 + lib/Kconfig.kmsan | 22 + lib/Makefile | 2 + lib/ioremap.c | 5 + lib/iov_iter.c | 6 + lib/stackdepot.c | 72 ++- lib/string.c | 5 +- lib/test_kmsan.c | 221 +++++++++ lib/usercopy.c | 6 +- mm/Makefile | 1 + mm/compaction.c | 9 + mm/filemap.c | 3 + mm/gup.c | 3 + mm/kasan/common.c | 23 - mm/kmsan/Makefile | 4 + mm/kmsan/kmsan.c | 570 ++++++++++++++++++++++++ mm/kmsan/kmsan.h | 147 ++++++ mm/kmsan/kmsan_entry.c | 130 ++++++ mm/kmsan/kmsan_hooks.c | 416 +++++++++++++++++ mm/kmsan/kmsan_init.c | 88 ++++ mm/kmsan/kmsan_instr.c | 259 +++++++++++ mm/kmsan/kmsan_report.c | 135 ++++++ mm/kmsan/kmsan_shadow.c | 543 ++++++++++++++++++++++ mm/kmsan/kmsan_shadow.h | 30 ++ mm/memory.c | 2 + mm/mmu_gather.c | 10 + mm/page_alloc.c | 16 + mm/readahead.c | 6 + mm/slub.c | 37 +- mm/vmalloc.c | 23 +- net/9p/protocol.c | 2 + net/sched/sch_generic.c | 2 + scripts/Makefile.kmsan | 12 + scripts/Makefile.lib | 6 + sound/core/oss/pcm_oss.c | 7 + 110 files changed, 4022 insertions(+), 80 deletions(-) create mode 100644 Documentation/dev-tools/kmsan.rst create mode 100644 arch/x86/include/asm/kmsan.h create mode 100644 include/linux/kmsan-checks.h create mode 100644 include/linux/kmsan.h create mode 100644 lib/Kconfig.kmsan create mode 100644 lib/test_kmsan.c create mode 100644 mm/kmsan/Makefile create mode 100644 mm/kmsan/kmsan.c create mode 100644 mm/kmsan/kmsan.h create mode 100644 mm/kmsan/kmsan_entry.c create mode 100644 mm/kmsan/kmsan_hooks.c create mode 100644 mm/kmsan/kmsan_init.c create mode 100644 mm/kmsan/kmsan_instr.c create mode 100644 mm/kmsan/kmsan_report.c create mode 100644 mm/kmsan/kmsan_shadow.c create mode 100644 mm/kmsan/kmsan_shadow.h create mode 100644 scripts/Makefile.kmsan
Signature · 1 line
2.23.0.866.gb869b98d4c-goog
[email protected]
maintainerRFC PATCH 1/26 stackdepot: check depot_index before accessing the stack slab
Avoid crashes on corrupted stack ids.
To: Alexander Potapenko <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]
---
Change-Id: I0a0b38ed5057090696a2c6ff0be7cfcc24ae6738
--- lib/stackdepot.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-)
lib/stackdepot.c +15 -2
@@ -198,9 +198,22 @@ unsigned int stack_depot_fetch(depot_stack_handle_t handle, unsigned long **entries) {union handle_parts parts = { .handle = handle }; -void *slab = stack_slabs[parts.slabindex]; +void *slab;size_t offset = parts.offset <<STACK_ALLOC_ALIGN ; -struct stack_record *stack = slab + offset; +struct stack_record *stack; + +if (parts.slabindex > depot_index) { +WARN (1 ,"slab index %d out of bounds (%d) for stack id %08x\n" , + parts.slabindex, depot_index, handle); +__memset (trace,0 ,sizeof (*trace)); +return ; + } + slab = stack_slabs[parts.slabindex]; + stack = slab + offset; +if (!stack) { + entries =NULL ; +return 0 ; + } *entries = stack->entries;return stack->size;
Signature · 1 line
2.23.0.866.gb869b98d4c-goog
[email protected]
maintainerRFC PATCH 2/26 stackdepot: prevent Clang from optimizing away stackdepot_memcmp()
In reply to [email protected]Clang may replace stackdepot_memcmp() with a call to instrumented bcmp(), which is exactly what we wanted to avoid creating stackdepot_memcmp(). Add a compiler barrier() to prevent optimizations.
To: Alexander Potapenko <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]
---
Change-Id: I4495b617b15c0ab003a61c1f0d54d0026fa8b144
--- lib/stackdepot.c | 5 +++++ 1 file changed, 5 insertions(+)
lib/stackdepot.c +5 -0
@@ -163,6 +163,11 @@ int stackdepot_memcmp(const unsigned long *u1, const unsigned long *u2, unsigned int n) {for ( ; n-- ; u1++, u2++) { +/* +* Prevent Clang from replacing this function with a bcmp() +* call. +*/ +barrier ();if (*u1 != *u2)return 1 ; }
Signature · 1 line
2.23.0.866.gb869b98d4c-goog
[email protected]
maintainerRFC PATCH 3/26 kasan: stackdepot: move filter_irq_stacks() to stackdepot.c
In reply to [email protected]filter_irq_stacks() can be used by other tools (e.g. KMSAN), so it needs to be moved to a common location.lib/stackdepot.c seems a good place, as filter_irq_stacks() is usually applied to the output of stack_trace_save().
To: Alexander Potapenko <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: Andrey Ryabinin <[email protected]>
Cc: [email protected]
---
Change-Id: I65acecf64930a3658e8c2aa7423801082ded8602
--- include/linux/stackdepot.h | 2 ++ lib/stackdepot.c | 23 +++++++++++++++++++++++ mm/kasan/common.c | 23 ----------------------- 3 files changed, 25 insertions(+), 23 deletions(-)
include/linux/stackdepot.h +2 -0
@@ -19,4 +19,6 @@ depot_stack_handle_t stack_depot_save(unsigned long *entries,unsigned int stack_depot_fetch (depot_stack_handle_t handle,unsigned long **entries); +unsigned int filter_irq_stacks (unsigned long *entries,unsigned int nr_entries); +#endif
lib/stackdepot.c +23 -0
@@ -20,6 +20,7 @@*/ #include <linux/gfp.h> +#include <linux/interrupt.h> #include <linux/jhash.h> #include <linux/kernel.h> #include <linux/mm.h>
@@ -319,3 +320,25 @@ depot_stack_handle_t stack_depot_save(unsigned long *entries,return retval; }EXPORT_SYMBOL_GPL (stack_depot_save); + +static inline int in_irqentry_text (unsigned long ptr) +{ +return (ptr >= (unsigned long )&__irqentry_text_start && + ptr < (unsigned long )&__irqentry_text_end) || + (ptr >= (unsigned long )&__softirqentry_text_start && + ptr < (unsigned long )&__softirqentry_text_end); +} + +unsigned int filter_irq_stacks (unsigned long *entries, +unsigned int nr_entries) +{ +unsigned int i; + +for (i =0 ; i < nr_entries; i++) { +if (in_irqentry_text (entries[i])) { +/* Include the irqentry function into the stack. */ +return i +1 ; + } + } +return nr_entries; +}
mm/kasan/common.c +0 -23
@@ -15,7 +15,6 @@*/ #include <linux/export.h> -#include <linux/interrupt.h> #include <linux/init.h> #include <linux/kasan.h> #include <linux/kernel.h>
@@ -39,28 +38,6 @@#include "kasan.h" #include "../slab.h" -static inline int in_irqentry_text (unsigned long ptr) -{ -return (ptr >= (unsigned long )&__irqentry_text_start && - ptr < (unsigned long )&__irqentry_text_end) || - (ptr >= (unsigned long )&__softirqentry_text_start && - ptr < (unsigned long )&__softirqentry_text_end); -} - -static inline unsigned int filter_irq_stacks (unsigned long *entries, -unsigned int nr_entries) -{ -unsigned int i; - -for (i =0 ; i < nr_entries; i++) { -if (in_irqentry_text (entries[i])) { -/* Include the irqentry function into the stack. */ -return i +1 ; - } - } -return nr_entries; -} -static inline depot_stack_handle_t save_stack (gfp_t flags) {unsigned long entries[KASAN_STACK_DEPTH ];
Signature · 1 line
2.23.0.866.gb869b98d4c-goog
[email protected]
maintainerRFC PATCH 4/26 stackdepot: reserve 5 extra bits in depot_stack_handle_t
In reply to [email protected]Some users (currently only KMSAN) may want to use spare bits in depot_stack_handle_t. Let them do so and provide get_dsh_extra_bits() and set_dsh_extra_bits() to access those bits.
To: Alexander Potapenko <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]
---
Change-Id: I23580dbde85908eeda0bdd8f83a8c3882ab3e012
--- include/linux/stackdepot.h | 8 ++++++++ lib/stackdepot.c | 24 +++++++++++++++++++++++- 2 files changed, 31 insertions(+), 1 deletion(-)
include/linux/stackdepot.h +8 -0
@@ -12,6 +12,11 @@#define _LINUX_STACKDEPOT_Htypedef u32 depot_stack_handle_t ; +/* +* Number of bits in the handle that stack depot doesn't use. Users may store +* information in them. +*/ +#define STACK_DEPOT_EXTRA_BITS 5depot_stack_handle_t stack_depot_save (unsigned long *entries,unsigned int nr_entries,gfp_t gfp_flags);
@@ -20,5 +25,8 @@ unsigned int stack_depot_fetch(depot_stack_handle_t handle, unsigned long **entries);unsigned int filter_irq_stacks (unsigned long *entries,unsigned int nr_entries); +depot_stack_handle_t set_dsh_extra_bits (depot_stack_handle_t handle, +unsigned int bits); +unsigned int get_dsh_extra_bits (depot_stack_handle_t handle);#endif
lib/stackdepot.c +23 -1
@@ -40,8 +40,10 @@#define STACK_ALLOC_ALIGN 4#define STACK_ALLOC_OFFSET_BITS (STACK_ALLOC_ORDER + PAGE_SHIFT - \ STACK_ALLOC_ALIGN) +#define STACK_ALLOC_INDEX_BITS (DEPOT_STACK_BITS - \ - STACK_ALLOC_NULL_PROTECTION_BITS - STACK_ALLOC_OFFSET_BITS) + STACK_ALLOC_NULL_PROTECTION_BITS - \ + STACK_ALLOC_OFFSET_BITS - STACK_DEPOT_EXTRA_BITS)#define STACK_ALLOC_SLABS_CAP 8192#define STACK_ALLOC_MAX_SLABS \ (((1LL << (STACK_ALLOC_INDEX_BITS)) < STACK_ALLOC_SLABS_CAP) ? \
@@ -54,6 +56,7 @@ union handle_parts { u32 slabindex : STACK_ALLOC_INDEX_BITS; u32 offset : STACK_ALLOC_OFFSET_BITS; u32 valid : STACK_ALLOC_NULL_PROTECTION_BITS; + u32 extra : STACK_DEPOT_EXTRA_BITS; }; };
@@ -72,6 +75,24 @@ static int next_slab_inited;static size_t depot_offset;static DEFINE_SPINLOCK (depot_lock); +depot_stack_handle_t set_dsh_extra_bits (depot_stack_handle_t handle, +u32 bits) +{ +union handle_parts parts = { .handle = handle }; + + parts.extra = bits & ((1U <<STACK_DEPOT_EXTRA_BITS ) -1 ); +return parts.handle; +} +EXPORT_SYMBOL_GPL (set_dsh_extra_bits); + +u32 get_dsh_extra_bits (depot_stack_handle_t handle) +{ +union handle_parts parts = { .handle = handle }; + +return parts.extra; +} +EXPORT_SYMBOL_GPL (get_dsh_extra_bits); +static bool init_stack_slab (void **prealloc) {if (!*prealloc)
@@ -132,6 +153,7 @@ static struct stack_record *depot_alloc_stack(unsigned long *entries, int size, stack->handle.slabindex = depot_index; stack->handle.offset = depot_offset >>STACK_ALLOC_ALIGN ; stack->handle.valid =1 ; + stack->handle.extra =0 ;memcpy (stack->entries, entries, size *sizeof (unsigned long )); depot_offset += required_size;
Signature · 1 line
2.23.0.866.gb869b98d4c-goog
[email protected]
maintainerRFC PATCH 5/26 printk_safe: externalize printk_context
In reply to [email protected]Petr Mladek suggested we use
this_cpu_read(printk_context) & PRINTK_SAFE_CONTEXT_MASK
instead of checking the spinlock status in kmsan_pr_err()
This appears to be less intrusive, although we'll need to declare printk_context in some printk header.
To: Alexander Potapenko <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Petr Mladek <[email protected]>
Cc: Sergey Senozhatsky <[email protected]>
Cc: Steven Rostedt <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]
---
Change-Id: Idb5e7c7cedf6ab6ba635a64423d58d702ad3eca3
--- kernel/printk/printk_safe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/printk/printk_safe.c +1 -1
@@ -42,7 +42,7 @@ struct printk_safe_seq_buf { }; static DEFINE_PER_CPU(struct printk_safe_seq_buf, safe_print_seq); -static DEFINE_PER_CPU(int, printk_context); +DEFINE_PER_CPU (int , printk_context);#ifdef CONFIG_PRINTK_NMI static DEFINE_PER_CPU(struct printk_safe_seq_buf, nmi_print_seq);
Signature · 1 line
2.23.0.866.gb869b98d4c-goog
Petr Mladek
maintainerRe: [PATCH RFC v1 05/26] printk_safe: externalize printk_context
Quoted 3 lines · On Fri 2019-10-18 11:42:43, [email protected] wrote:
Petr Mladek suggested we use
this_cpu_read(printk_context)&PRINTK_SAFE_CONTEXT_MASK
instead of checking the spinlock status inkmsan_pr_err()
I would like to understand why the check is needed.
My guess is that it prevents a infinite recursion. Is this the case? It might be possible to debug this using trace_printk().
It is important. If it is the recursion then there are two possibilities how to prevent it. Either prevent calling the recursive printk(). Or prevent calling the memory checker recursive. I am not sure what makes more sense.
Is printk() the only piece of code where you need to avoid recursion? I wonder how it is prevented in the other cases.
Quoted 2 lines
This appears to be less intrusive, although we'll need to declare
printk_contextin some printk header.
It is easier than the original approach. But the main motivation is that it is more reliable. The spinlock is a global lock. But it seems that it is enough to check the state of the local CPU.
Finally, could you please CC me to the patch(es) that are using this variable? I would actually prefer to be in CC of entire patchsets so that I see the full context.
Best Regards,
Petr
Alexander Potapenko
maintainerRe: [PATCH RFC v1 05/26] printk_safe: externalize printk_context
Quoted 11 lines · Petr Mladek
Quoted 3 lines · On Fri 2019-10-18 11:42:43, [email protected] wrote:
Petr Mladek suggested we use
this_cpu_read(printk_context)&PRINTK_SAFE_CONTEXT_MASK
instead of checking the spinlock status inkmsan_pr_err()I would like to understand why the check is needed.
My guess is that it prevents a infinite recursion. Is this the case? It might be possible to debug this using
trace_printk().
This indeed used to prevent infinite recursion, however looks like the check isn't needed anymore.
When I remove it, KMSAN doesn't hang, probably because printk is doing a better job at deadlock prevention.
What I'm seeing now is that e.g. in the following case:
ptr = kmalloc(sizeof(int), GFP_KERNEL);
if (ptr)
pr_info("true\n");
else
pr_info("false\n");KMSAN detects errors within pr_info(), namely in vprintk_store(). If I understand correctly, printing from that point causes printk to use the per-cpu buffer, which is flushed once we're done with the original printing:
[ 58.984971][ T8390] BUG: KMSAN: uninit-value in kmsan_handle_vprintk+0xa0/0xf0 ... [ 59.061976][ C0] BUG: KMSAN: uninit-value in vsnprintf+0x3276/0x32b0 ... [ 59.062457][ C0] BUG: KMSAN: uninit-value in format_decode+0x17f/0x1900 ... [ 59.062961][ C0] BUG: KMSAN: uninit-value in format_decode+0x17f/0x1900 ... [ 59.063338][ C0] Lost 6207 message(s)!
So it turns out we'd better disable reporting under logbuf_lock, otherwise these lost messages will confuse the developers. Because the first pr_info() isn't called by KMSAN itself, the tool still needs a way to know it's running inside printk.
Quoted 23 lines
It is important. If it is the recursion then there are two possibilities how to prevent it. Either prevent calling the recursive
printk(). Or prevent calling the memory checker recursive. I am not sure what makes more sense.Is
printk()the only piece of code where you need to avoid recursion? I wonder how it is prevented in the other cases.Quoted 2 lines
This appears to be less intrusive, although we'll need to declare
printk_contextin some printk header.It is easier than the original approach. But the main motivation is that it is more reliable. The spinlock is a global lock. But it seems that it is enough to check the state of the local CPU.
Finally, could you please CC me to the patch(es) that are using this variable? I would actually prefer to be in CC of entire patchsets so that I see the full context.
Best Regards,
Petr
Signature · 10 lines
Alexander Potapenko Software Engineer Google Germany GmbH Erika-Mann-Straße, 33 80636 München Geschäftsführer: Paul Manicle, Halimah DeLaine Prado Registergericht und -nummer: Hamburg, HRB 86891 Sitz der Gesellschaft: Hamburg
Alexander Potapenko
maintainerRe: [PATCH RFC v1 05/26] printk_safe: externalize printk_context
Quoted 41 lines · Alexander Potapenko
Quoted 11 lines · Petr Mladek
Quoted 3 lines · On Fri 2019-10-18 11:42:43, [email protected] wrote:
Petr Mladek suggested we use
this_cpu_read(printk_context)&PRINTK_SAFE_CONTEXT_MASK
instead of checking the spinlock status inkmsan_pr_err()I would like to understand why the check is needed.
My guess is that it prevents a infinite recursion. Is this the case? It might be possible to debug this using
trace_printk().This indeed used to prevent infinite recursion, however looks like the check isn't needed anymore.
When I remove it, KMSAN doesn't hang, probably because printk is doing a better job at deadlock prevention.What I'm seeing now is that e.g. in the following case:
ptr =kmalloc(sizeof(int), GFP_KERNEL);
if (ptr)pr_info("true\n"); else pr_info("false\n");KMSAN detects errors within
pr_info(), namely invprintk_store(). If I understand correctly, printing from that point causes printk to use the per-cpu buffer, which is flushed once we're done with the original printing:[ 58.984971][ T8390] BUG: KMSAN: uninit-value in kmsan_handle_vprintk+0xa0/0xf0 ... [ 59.061976][ C0] BUG: KMSAN: uninit-value in vsnprintf+0x3276/0x32b0 ... [ 59.062457][ C0] BUG: KMSAN: uninit-value in format_decode+0x17f/0x1900 ... [ 59.062961][ C0] BUG: KMSAN: uninit-value in format_decode+0x17f/0x1900 ... [ 59.063338][ C0] Lost 6207 message(s)!
Quoted 25 lines
So it turns out we'd better disable reporting under
logbuf_lock, otherwise these lost messages will confuse the developers. Because the firstpr_info()isn't called by KMSAN itself, the tool still needs a way to know it's running inside printk.Quoted 20 lines
It is important. If it is the recursion then there are two possibilities how to prevent it. Either prevent calling the recursive
printk(). Or prevent calling the memory checker recursive. I am not sure what makes more sense.Is
printk()the only piece of code where you need to avoid recursion? I wonder how it is prevented in the other cases.Quoted 2 lines
This appears to be less intrusive, although we'll need to declare
printk_contextin some printk header.It is easier than the original approach. But the main motivation is that it is more reliable. The spinlock is a global lock. But it seems that it is enough to check the state of the local CPU.
Finally, could you please CC me to the patch(es) that are using this variable? I would actually prefer to be in CC of entire patchsets so that I see the full context.
I am very sorry about this. I'm going to re-send the patches soon and CC everyone on every patch.
What I thought to be reducing the email burden on every reviewer turned out to be really inconvenient.
Quoted 16 lines
Quoted 2 lines
Best Regards,
Petr--
Alexander Potapenko
Software EngineerGoogle Germany GmbH
Erika-Mann-Straße, 33
80636 MünchenGeschäftsführer: Paul Manicle, Halimah DeLaine Prado Registergericht und -nummer: Hamburg, HRB 86891 Sitz der Gesellschaft: Hamburg
Signature · 10 lines
Alexander Potapenko Software Engineer Google Germany GmbH Erika-Mann-Straße, 33 80636 München Geschäftsführer: Paul Manicle, Halimah DeLaine Prado Registergericht und -nummer: Hamburg, HRB 86891 Sitz der Gesellschaft: Hamburg
Petr Mladek
maintainerRe: [PATCH RFC v1 05/26] printk_safe: externalize printk_context
In reply to Alexander PotapenkoQuoted 9 lines · Alexander Potapenko
Quoted 8 lines · Petr Mladek
Quoted 3 lines · On Fri 2019-10-18 11:42:43, [email protected] wrote:
Petr Mladek suggested we use
this_cpu_read(printk_context)&PRINTK_SAFE_CONTEXT_MASK
instead of checking the spinlock status inkmsan_pr_err()I would like to understand why the check is needed.
Quoted 7 lines
Quoted 3 lines
My guess is that it prevents a infinite recursion. Is this the case? It might be possible to debug this using
trace_printk().This indeed used to prevent infinite recursion, however looks like the check isn't needed anymore.
When I remove it, KMSAN doesn't hang, probably because printk is doing a better job at deadlock prevention.
Great.
Quoted 12 lines
What I'm seeing now is that e.g. in the following case:
ptr =kmalloc(sizeof(int), GFP_KERNEL);
if (ptr)pr_info("true\n"); else pr_info("false\n");KMSAN detects errors within
pr_info(), namely invprintk_store(). If I understand correctly, printing from that point causes printk to use the per-cpu buffer, which is flushed once we're done with the original printing:
Quoted 8 lines
[ 58.984971][ T8390] BUG: KMSAN: uninit-value in kmsan_handle_vprintk+0xa0/0xf0 ... [ 59.061976][ C0] BUG: KMSAN: uninit-value in vsnprintf+0x3276/0x32b0 ... [ 59.062457][ C0] BUG: KMSAN: uninit-value in format_decode+0x17f/0x1900 ... [ 59.062961][ C0] BUG: KMSAN: uninit-value in format_decode+0x17f/0x1900
Please, do you have an explanation where the uninitialized values come from? Is it a false positive? Or is there really a bug how the printk() parameters are passed?
Quoted 2 lines
...
[ 59.063338][ C0] Lost 6207 message(s)!
This message comes from report_message_lost(). It means that many recursive messages did not fit into the limited per-CPU buffers.
Quoted 4 lines
So it turns out we'd better disable reporting under
logbuf_lock, otherwise these lost messages will confuse the developers. Because the firstpr_info()isn't called by KMSAN itself, the tool still needs a way to know it's running inside printk.
OK. Well, I still would like to understand why the recursive messages are printed at all. Is it a bug in the printk() code or a false positive?
There should be no recursive messages if we fix the bug or make some annotations to prevent the false positives.
That said, I am not completely against disabling the recursive messages. But it would be pity to hide some real bugs.
Best Regards,
Petr
Alexander Potapenko
maintainerRe: [PATCH RFC v1 05/26] printk_safe: externalize printk_context
Quoted 42 lines · Petr Mladek
Quoted 9 lines · Alexander Potapenko
Quoted 8 lines · Petr Mladek
Quoted 3 lines · On Fri 2019-10-18 11:42:43, [email protected] wrote:
Petr Mladek suggested we use
this_cpu_read(printk_context)&PRINTK_SAFE_CONTEXT_MASK
instead of checking the spinlock status inkmsan_pr_err()I would like to understand why the check is needed.
Quoted 7 lines
Quoted 3 lines
My guess is that it prevents a infinite recursion. Is this the case? It might be possible to debug this using
trace_printk().This indeed used to prevent infinite recursion, however looks like the check isn't needed anymore.
When I remove it, KMSAN doesn't hang, probably because printk is doing a better job at deadlock prevention.Great.
Quoted 12 lines
What I'm seeing now is that e.g. in the following case:
ptr =kmalloc(sizeof(int), GFP_KERNEL);
if (ptr)pr_info("true\n"); else pr_info("false\n");KMSAN detects errors within
pr_info(), namely invprintk_store(). If I understand correctly, printing from that point causes printk to use the per-cpu buffer, which is flushed once we're done with the original printing:Quoted 8 lines
[ 58.984971][ T8390] BUG: KMSAN: uninit-value in kmsan_handle_vprintk+0xa0/0xf0 ... [ 59.061976][ C0] BUG: KMSAN: uninit-value in vsnprintf+0x3276/0x32b0 ... [ 59.062457][ C0] BUG: KMSAN: uninit-value in format_decode+0x17f/0x1900 ... [ 59.062961][ C0] BUG: KMSAN: uninit-value in format_decode+0x17f/0x1900
Quoted 3 lines
Please, do you have an explanation where the uninitialized values come from? Is it a false positive? Or is there really a bug how the
printk()parameters are passed?
I believe these are true bugs.
The problem is, when we pass an uninitialized argument into printk(), KMSAN will report an error every time this uninitialized argument is used.
E.g. for an uninitialized format string there'll be at least strlen(fmt) reports in format_decode(), followed by several reports in vsnprintf().
Although these reports seem to be real, printing only the first of them should be more than enough.
In the future we'll actually want KMSAN to check every printk() argument (which will require parsing the format string to figure out the arguments' lengths), but disable reports within printk.
Quoted 14 lines
Quoted 2 lines
...
[ 59.063338][ C0] Lost 6207 message(s)!This message comes from
report_message_lost(). It means that many recursive messages did not fit into the limited per-CPU buffers.Quoted 4 lines
So it turns out we'd better disable reporting under
logbuf_lock, otherwise these lost messages will confuse the developers. Because the firstpr_info()isn't called by KMSAN itself, the tool still needs a way to know it's running inside printk.OK. Well, I still would like to understand why the recursive messages are printed at all. Is it a bug in the
printk()code or a false positive?
Quoted 8 lines
There should be no recursive messages if we fix the bug or make some annotations to prevent the false positives.
That said, I am not completely against disabling the recursive messages. But it would be pity to hide some real bugs.
Best Regards,
Petr
--
Alexander Potapenko
Software Engineer
Google Germany GmbH
Erika-Mann-Straße, 33
80636 München
Geschäftsführer: Paul Manicle, Halimah DeLaine Prado Registergericht und -nummer: Hamburg, HRB 86891 Sitz der Gesellschaft: Hamburg
Petr Mladek
maintainerRe: [PATCH RFC v1 05/26] printk_safe: externalize printk_context
Quoted 23 lines · Alexander Potapenko
Quoted 22 lines · Petr Mladek
Quoted 20 lines · Alexander Potapenko
What I'm seeing now is that e.g. in the following case:
ptr =kmalloc(sizeof(int), GFP_KERNEL);
if (ptr)pr_info("true\n"); else pr_info("false\n");KMSAN detects errors within
pr_info(), namely invprintk_store(). If I understand correctly, printing from that point causes printk to use the per-cpu buffer, which is flushed once we're done with the original printing:[ 58.984971][ T8390] BUG: KMSAN: uninit-value in kmsan_handle_vprintk+0xa0/0xf0 ... [ 59.061976][ C0] BUG: KMSAN: uninit-value in vsnprintf+0x3276/0x32b0 ... [ 59.062457][ C0] BUG: KMSAN: uninit-value in format_decode+0x17f/0x1900 ... [ 59.062961][ C0] BUG: KMSAN: uninit-value in format_decode+0x17f/0x1900
Quoted 7 lines
Quoted 3 lines
Please, do you have an explanation where the uninitialized values come from? Is it a false positive? Or is there really a bug how the
printk()parameters are passed?I believe these are true bugs.
The problem is, when we pass an uninitialized argument intoprintk(), KMSAN will report an error every time this uninitialized argument is used.
I see, thanks for explanation.
Quoted 5 lines
E.g. for an uninitialized format string there'll be at least strlen(fmt) reports in
format_decode(), followed by several reports invsnprintf().
Although these reports seem to be real, printing only the first of them should be more than enough.
Isn't this a generic problem? I mean that uninitialized values can be passed and used also in many other locations.
printk() is special because this problem causes infinite loop. But it would make sense to report also any other problematic value only once.
Quoted 3 lines
In the future we'll actually want KMSAN to check every
printk()argument (which will require parsing the format string to figure out the arguments' lengths), but disable reports within printk.
What is the motivation for this, please?
It looks to me that you want to do very paranoid checks of variables passed to printk()? Do you want to prevent printk() crashes? Or do you want to make sure that printk() produces correct values?
From my POV, printk() is debugging tool. It is used to show values that people are interested in. On one hand, it might make sense to warn people that a particular value was not initalized. On the other hand, printk() is not important for the kernel behavior. It just reads values and does not affect any behavior.
I would like to understand how many printk-related code is worth the effort.
Best Regards,
Petr
Alexander Potapenko
maintainerRe: [PATCH RFC v1 05/26] printk_safe: externalize printk_context
Quoted 35 lines · Petr Mladek
Quoted 23 lines · Alexander Potapenko
Quoted 22 lines · Petr Mladek
Quoted 20 lines · Alexander Potapenko
What I'm seeing now is that e.g. in the following case:
ptr =kmalloc(sizeof(int), GFP_KERNEL);
if (ptr)pr_info("true\n"); else pr_info("false\n");KMSAN detects errors within
pr_info(), namely invprintk_store(). If I understand correctly, printing from that point causes printk to use the per-cpu buffer, which is flushed once we're done with the original printing:[ 58.984971][ T8390] BUG: KMSAN: uninit-value in kmsan_handle_vprintk+0xa0/0xf0 ... [ 59.061976][ C0] BUG: KMSAN: uninit-value in vsnprintf+0x3276/0x32b0 ... [ 59.062457][ C0] BUG: KMSAN: uninit-value in format_decode+0x17f/0x1900 ... [ 59.062961][ C0] BUG: KMSAN: uninit-value in format_decode+0x17f/0x1900Quoted 7 lines
Quoted 3 lines
Please, do you have an explanation where the uninitialized values come from? Is it a false positive? Or is there really a bug how the
printk()parameters are passed?I believe these are true bugs.
The problem is, when we pass an uninitialized argument intoprintk(), KMSAN will report an error every time this uninitialized argument is used.I see, thanks for explanation.
Quoted 8 lines
Quoted 5 lines
E.g. for an uninitialized format string there'll be at least strlen(fmt) reports in
format_decode(), followed by several reports invsnprintf().
Although these reports seem to be real, printing only the first of them should be more than enough.Isn't this a generic problem? I mean that uninitialized values can be passed and used also in many other locations.
More or less, yes.
Quoted 2 lines
printk()is special because this problem causes infinite loop. But it would make sense to report also any other problematic value only once.
Yes, printk is a bit different, because finding errors in it causes other printk() calls which now don't cause infinite loops, but may produce out-of-order error messages.
Quoted 10 lines
Quoted 4 lines
In the future we'll actually want KMSAN to check every
printk()argument (which will require parsing the format string to figure out the arguments' lengths), but disable reports within printk.What is the motivation for this, please?
It looks to me that you want to do very paranoid checks of variables passed to
printk()? Do you want to preventprintk()crashes? Or do you want to make sure thatprintk()produces correct values?
Simply passing an uninitialized value to printk() may result in cryptic error messages from printk guts, which may be harder to understand without knowing how printk works.
The idea is to check the arguments right before printk, so that we can print a really simple diagnostic message.
Quoted 8 lines
From my POV,
printk()is debugging tool. It is used to show values that people are interested in. On one hand, it might make sense to warn people that a particular value was not initalized. On the other hand,printk()is not important for the kernel behavior. It just reads values and does not affect any behavior.I would like to understand how many printk-related code is worth the effort.
I think overall you're right.
The feature I'm talking about isn't a critical part of KMSAN functionality, so in order to keep the code simple I'd better drop it, as long as we're able to report bugs when uninitialized memory is passed to printk.
The only change to printk that I'll probably still have to make is to initialize the result of vscnprintf() here:
https://github.com/google/kmsan/blob/master/kernel/printk/printk.c#L1921
Passing uninitialized data to vscnprintf() causes its result to also be uninitialized, which causes an avalanche of new reports in vprintk_store()
I'll send the updated patch (together with other KMSAN patches) your way today.
Quoted 2 lines
Best Regards,
Petr
Signature · 10 lines
Alexander Potapenko Software Engineer Google Germany GmbH Erika-Mann-Straße, 33 80636 München Geschäftsführer: Paul Manicle, Halimah DeLaine Prado Registergericht und -nummer: Hamburg, HRB 86891 Sitz der Gesellschaft: Hamburg
[email protected]
maintainerRFC PATCH 6/26 kasan: compiler.h: rename __no_kasan_or_inline into __no_memory_tool_or_inline
In reply to [email protected]As we use it for KMSAN as well now, let's change the name to be more generic.
To: Alexander Potapenko <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Martin Schwidefsky <[email protected]>
Cc: Andrey Ryabinin <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]
---
Note that __no_kasan_or_inline is currently used only on S390, and KMSAN patches don't introduce uses of __no_memory_tool_or_inline, so this patch can be landed later if needed.
Change-Id: I842d88b841bf1c75ab659b0016a808c6dcf7cdae
--- arch/s390/include/asm/processor.h | 4 ++-- include/linux/compiler.h | 12 +++++++----- 2 files changed, 9 insertions(+), 7 deletions(-)
arch/s390/include/asm/processor.h +2 -2
@@ -214,7 +214,7 @@ static inline unsigned long current_stack_pointer(void)return sp; } -static __no_kasan_or_inline unsigned short stap (void ) +static __no_memory_tool_or_inline unsigned short stap (void ) {unsigned short cpu_address;
@@ -253,7 +253,7 @@ static inline void __load_psw(psw_t psw)* Set PSW mask to specified value, while leaving the * PSW addr pointing to the next instruction. */ -static __no_kasan_or_inline void __load_psw_mask(unsigned long mask) +static __no_memory_tool_or_inline void __load_psw_mask(unsigned long mask) {unsigned long addr;psw_t psw;
include/linux/compiler.h +7 -5
@@ -199,19 +199,21 @@ void __read_once_size(const volatile void *p, void *res, int size) __READ_ONCE_SIZE; } -#ifdef CONFIG_KASAN +#if defined(CONFIG_KASAN )/* * We can't declare function 'inline' because __no_sanitize_address confilcts * with inlining. Attempt to inline it may cause a build failure. * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=67368 * '__maybe_unused' allows us to avoid defined-but-not-used warnings. */ -# define __no_kasan_or_inline __no_sanitize_address notrace __maybe_unused +# define __no_memory_tool_or_inline __no_sanitize_address notrace __maybe_unused +#elif defined(CONFIG_KMSAN ) +# define __no_memory_tool_or_inline __no_sanitize_memory notrace __maybe_unused#else -# define __no_kasan_or_inline __always_inline +# define __no_memory_tool_or_inline __always_inline#endif -static __no_kasan_or_inline +static __no_memory_tool_or_inline void __read_once_size_nocheck(const volatile void *p, void *res, int size) { __READ_ONCE_SIZE;
@@ -274,7 +276,7 @@ static __always_inline void __write_once_size(volatile void *p, void *res, int s*/ #define READ_ONCE_NOCHECK (x) __READ_ONCE(x, 0) -static __no_kasan_or_inline +static __no_memory_tool_or_inline unsigned long read_word_at_a_time (const void *addr) {kasan_check_read (addr,1 );
Signature · 1 line
2.23.0.866.gb869b98d4c-goog
[email protected]
maintainerRFC PATCH 7/26 kmsan: add ReST documentation
In reply to [email protected]Add Documentation/dev-tools/kmsan.rst and reference it in the dev-tools index.
To: Alexander Potapenko <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]
---
Change-Id: Iac6345065e6804ef811f1124fdf779c67ff1530e
--- Documentation/dev-tools/index.rst | 1 + Documentation/dev-tools/kmsan.rst | 418 ++++++++++++++++++++++++++++++ 2 files changed, 419 insertions(+) create mode 100644 Documentation/dev-tools/kmsan.rst
Documentation/dev-tools/index.rst +1 -0
@@ -19,6 +19,7 @@ whole; patches welcome! kcov gcov kasan + kmsan ubsan kmemleak gdb-kernel-debugging
Documentation/dev-tools/kmsan.rst new file +418 -0
418 lines not on this page · raw message
Signature · 1 line
2.23.0.866.gb869b98d4c-goog
[email protected]
maintainerRFC PATCH 8/26 kmsan: gfp: introduce __GFP_NO_KMSAN_SHADOW
In reply to [email protected]This flag is to be used by KMSAN runtime to mark that newly created memory pages don't need KMSAN metadata backing them.
To: Alexander Potapenko <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Andrew Morton <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]
---
We can't decide what to do here:
- do we need to conditionally define ___GFP_NO_KMSAN_SHADOW depending on CONFIG_KMSAN like LOCKDEP does?
- if KMSAN is defined, and LOCKDEP is not, do we want to "compactify" the GFP bits?
Change-Id: If5d0352fd5711ad103328e2c185eb885e826423a
--- include/linux/gfp.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
include/linux/gfp.h +3 -1
@@ -44,6 +44,7 @@ struct vm_area_struct;#else #define ___GFP_NOLOCKDEP 0#endif +#define ___GFP_NO_KMSAN_SHADOW 0x1000000u/* If the above are modified, __GFP_BITS_SHIFT may need updating */ /*
@@ -212,12 +213,13 @@ struct vm_area_struct;#define __GFP_NOWARN ((__force gfp_t)___GFP_NOWARN)#define __GFP_COMP ((__force gfp_t)___GFP_COMP)#define __GFP_ZERO ((__force gfp_t)___GFP_ZERO) +#define __GFP_NO_KMSAN_SHADOW ((__force gfp_t)___GFP_NO_KMSAN_SHADOW)/* Disable lockdep for GFP context tracking */ #define __GFP_NOLOCKDEP ((__force gfp_t)___GFP_NOLOCKDEP)/* Room for N __GFP_FOO bits */ -#define __GFP_BITS_SHIFT (23 + IS_ENABLED(CONFIG_LOCKDEP)) +#define __GFP_BITS_SHIFT (25)#define __GFP_BITS_MASK ((__force gfp_t)((1 << __GFP_BITS_SHIFT) - 1))/**
Signature · 1 line
2.23.0.866.gb869b98d4c-goog
[email protected]
maintainerRFC PATCH 9/26 kmsan: introduce __no_sanitize_memory and __SANITIZE_MEMORY__
In reply to [email protected]__no_sanitize_memory is a function attribute that makes KMSAN ignore the uninitialized values coming from the function's inputs, and initialize the function's outputs.
Functions marked with this attribute can't be inlined into functions not marked with it, and vice versa.
__SANITIZE_MEMORY__ is a macro that's defined iff the file is instrumented with KMSAN. This is not the same as CONFIG_KMSAN, which is defined for every file.
To: Alexander Potapenko <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]
---
Change-Id: I1f1672652c8392f15f7ca8ac26cd4e71f9cc1e4b
--- include/linux/compiler-clang.h | 8 ++++++++ include/linux/compiler-gcc.h | 5 +++++ 2 files changed, 13 insertions(+)
include/linux/compiler-clang.h +8 -0
@@ -24,6 +24,14 @@#define __no_sanitize_address#endif +/* KMSAN is a Clang-only tool, thus putting the defines here */ +#if __has_feature (memory_sanitizer) +# define __SANITIZE_MEMORY__ +# define __no_sanitize_memory __attribute__((no_sanitize("kernel-memory"))) +#else +# define __no_sanitize_memory +#endif +/* * Not all versions of clang implement the the type-generic versions * of the builtin overflow checkers. Fortunately, clang implements
include/linux/compiler-gcc.h +5 -0
@@ -145,6 +145,11 @@#define __no_sanitize_address#endif +/* +* GCC doesn't support KMSAN. +*/ +#define __no_sanitize_memory +#if GCC_VERSION >=50100 #define COMPILER_HAS_GENERIC_BUILTIN_OVERFLOW 1#endif
Signature · 1 line
2.23.0.866.gb869b98d4c-goog
[email protected]
maintainerRFC PATCH 10/26 kmsan: reduce vmalloc space
In reply to [email protected]KMSAN is going to use 3/4 of existing vmalloc space to hold the metadata, therefore we lower VMALLOC_END to make sure vmalloc() doesn't allocate past the first 1/4.
To: Alexander Potapenko <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Andrew Morton <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]
---
Change-Id: Iaa5e8e0fc2aa66c956f937f5a1de6e5ef40d57cc
--- arch/x86/include/asm/pgtable_64_types.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+)
arch/x86/include/asm/pgtable_64_types.h +15 -0
@@ -139,7 +139,22 @@ extern unsigned int ptrs_per_p4d;# define VMEMMAP_START __VMEMMAP_BASE_L4#endif /* CONFIG_DYNAMIC_MEMORY_LAYOUT */ +#ifndef CONFIG_KMSAN #define VMALLOC_END (VMALLOC_START + (VMALLOC_SIZE_TB << 40) - 1) +#else +/* +* In KMSAN builds vmalloc area is four times smaller, and the remaining 3/4 +* are used to keep the metadata for virtual pages. +*/ +#define VMALLOC_QUARTER_SIZE ((VMALLOC_SIZE_TB << 40) >> 2) +#define VMALLOC_END (VMALLOC_START + VMALLOC_QUARTER_SIZE - 1) +#define VMALLOC_SHADOW_OFFSET VMALLOC_QUARTER_SIZE +#define VMALLOC_ORIGIN_OFFSET (VMALLOC_QUARTER_SIZE * 2) +#define VMALLOC_META_END (VMALLOC_END + VMALLOC_ORIGIN_OFFSET) +#define MODULES_SHADOW_START (VMALLOC_META_END + 1) +#define MODULES_ORIGIN_START (MODULES_SHADOW_START + MODULES_LEN) +#define MODULES_ORIGIN_END (MODULES_ORIGIN_START + MODULES_LEN) +#endif #define MODULES_VADDR (__START_KERNEL_map + KERNEL_IMAGE_SIZE)/* The module sections ends with the start of the fixmap */
Signature · 1 line
2.23.0.866.gb869b98d4c-goog
[email protected]
maintainerRFC PATCH 11/26 kmsan: add KMSAN runtime
In reply to [email protected]This patch adds the KernelMemorySanitizer runtime and associated files:
- arch/x86/include/asm/kmsan.h: assembly definitions for hooking
interrupt handlers;
- include/linux/kmsan-checks.h: user API to enable/disable KMSAN, poison/unpoison memory etc.
- include/linux/kmsan.h: declarations of KMSAN memory hooks to be referenced outside KMSAN runtime
- lib/Kconfig.kmsan: declarations for CONFIG_KMSAN and CONFIG_TEST_KMSAN
- mm/kmsan/Makefile: boilerplate Makefile
- mm/kmsan/kmsan.h: internal KMSAN declarations
- mm/kmsan/kmsan.c: core functions that operate with shadow and origin memory and perform checks, utility functions
- mm/kmsan/kmsan_entry.c: KMSAN hooks for entry_64.S
- mm/kmsan/kmsan_hooks.c: KMSAN hooks for kernel subsystems
- mm/kmsan/kmsan_init.c: KMSAN initialization routines
- mm/kmsan/kmsan_instr.c: functions called by KMSAN instrumentation
- scripts/Makefile.kmsan: CFLAGS_KMSAN
To: Alexander Potapenko <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]
---
Change-Id: I4b3a7aba6d5804afac4f5f7274cadf8675b6e119
--- arch/x86/Kconfig | 1 + arch/x86/include/asm/kmsan.h | 129 ++++++++ include/linux/kmsan-checks.h | 123 ++++++++ include/linux/kmsan.h | 143 +++++++++ lib/Kconfig.debug | 2 + lib/Kconfig.kmsan | 22 ++ mm/kmsan/Makefile | 4 + mm/kmsan/kmsan.c | 570 +++++++++++++++++++++++++++++++++++ mm/kmsan/kmsan.h | 147 +++++++++ mm/kmsan/kmsan_entry.c | 130 ++++++++ mm/kmsan/kmsan_hooks.c | 416 +++++++++++++++++++++++++ mm/kmsan/kmsan_init.c | 88 ++++++ mm/kmsan/kmsan_instr.c | 259 ++++++++++++++++ mm/kmsan/kmsan_report.c | 135 +++++++++ mm/kmsan/kmsan_shadow.c | 543 +++++++++++++++++++++++++++++++++ mm/kmsan/kmsan_shadow.h | 30 ++ scripts/Makefile.kmsan | 12 + 17 files changed, 2754 insertions(+) create mode 100644 arch/x86/include/asm/kmsan.h create mode 100644 include/linux/kmsan-checks.h create mode 100644 include/linux/kmsan.h create mode 100644 lib/Kconfig.kmsan create mode 100644 mm/kmsan/Makefile create mode 100644 mm/kmsan/kmsan.c create mode 100644 mm/kmsan/kmsan.h create mode 100644 mm/kmsan/kmsan_entry.c create mode 100644 mm/kmsan/kmsan_hooks.c create mode 100644 mm/kmsan/kmsan_init.c create mode 100644 mm/kmsan/kmsan_instr.c create mode 100644 mm/kmsan/kmsan_report.c create mode 100644 mm/kmsan/kmsan_shadow.c create mode 100644 mm/kmsan/kmsan_shadow.h create mode 100644 scripts/Makefile.kmsan
arch/x86/Kconfig +1 -0
@@ -135,6 +135,7 @@ config X86 select HAVE_ARCH_JUMP_LABEL select HAVE_ARCH_JUMP_LABEL_RELATIVE select HAVE_ARCH_KASAN if X86_64 + select HAVE_ARCH_KMSAN if X86_64 select HAVE_ARCH_KGDB select HAVE_ARCH_MMAP_RND_BITS if MMU select HAVE_ARCH_MMAP_RND_COMPAT_BITS if MMU && COMPAT
arch/x86/include/asm/kmsan.h new file +129 -0
129 lines not on this page · raw message
include/linux/kmsan-checks.h new file +123 -0
123 lines not on this page · raw message
include/linux/kmsan.h new file +143 -0
143 lines not on this page · raw message
lib/Kconfig.debug +2 -0
@@ -756,6 +756,8 @@ config DEBUG_STACKOVERFLOW source "lib/Kconfig.kasan" +source "lib/Kconfig.kmsan" + endmenu # "Memory Debugging" config ARCH_HAS_KCOV
lib/Kconfig.kmsan new file +22 -0
@@ -0,0 +1,22 @@ +config HAVE_ARCH_KMSAN + bool + +if HAVE_ARCH_KMSAN + +config KMSAN + bool "KMSAN: detector of uninitialized memory use" + depends on SLUB && !KASAN + select STACKDEPOT + help + KMSAN is a dynamic detector of uses of uninitialized memory in the + kernel. It is based on compiler instrumentation provided by Clang + and thus requires Clang 10.0.0+ to build. + +config TEST_KMSAN + tristate "Module for testing KMSAN for bug detection" + depends on m && KMSAN + help + Test module that can trigger various uses of uninitialized memory + detectable by KMSAN. + +endif
mm/kmsan/Makefile new file +4 -0
@@ -0,0 +1,4 @@ +obj-y := kmsan.o kmsan_instr.o kmsan_init.o kmsan_entry.o kmsan_hooks.o kmsan_report.o kmsan_shadow.o + +KMSAN_SANITIZE := n +KCOV_INSTRUMENT := n
mm/kmsan/kmsan.c new file +570 -0
570 lines not on this page · raw message
mm/kmsan/kmsan.h new file +147 -0
147 lines not on this page · raw message
mm/kmsan/kmsan_entry.c new file +130 -0
130 lines not on this page · raw message
mm/kmsan/kmsan_hooks.c new file +416 -0
416 lines not on this page · raw message
mm/kmsan/kmsan_init.c new file +88 -0
88 lines not on this page · raw message
mm/kmsan/kmsan_instr.c new file +259 -0
259 lines not on this page · raw message
mm/kmsan/kmsan_report.c new file +135 -0
135 lines not on this page · raw message
mm/kmsan/kmsan_shadow.c new file +543 -0
543 lines not on this page · raw message
mm/kmsan/kmsan_shadow.h new file +30 -0
@@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* +* KMSAN shadow API. +* +* This should be agnostic to shadow implementation details. +* +* Copyright (C) 2017-2019 Google LLC +* Author: Alexander Potapenko <[email protected]> +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +* +*/ + +#ifndef __MM_KMSAN_KMSAN_SHADOW_H +#define __MM_KMSAN_KMSAN_SHADOW_H + +#include <asm/cpu_entry_area.h> /* for CPU_ENTRY_AREA_MAP_SIZE */ + +struct shadow_origin_ptr { +void *s, *o; +}; + +struct shadow_origin_ptr kmsan_get_shadow_origin_ptr (void *addr,u64 size, +bool store); +void *kmsan_get_metadata (void *addr,size_t size,bool is_origin); +void __init kmsan_init_alloc_meta_for_range (void *start,void *end); + +#endif /* __MM_KMSAN_KMSAN_SHADOW_H */
scripts/Makefile.kmsan new file +12 -0
@@ -0,0 +1,12 @@ +ifdef CONFIG_KMSAN + +CFLAGS_KMSAN := -fsanitize=kernel-memory + +ifeq ($(call cc-option, $(CFLAGS_KMSAN) -Werror),) + ifneq ($(CONFIG_COMPILE_TEST),y) + $(warning Cannot use CONFIG_KMSAN: \ + -fsanitize=kernel-memory is not supported by compiler) + endif +endif + +endif
Signature · 1 line
2.23.0.866.gb869b98d4c-goog
[email protected]
maintainerRFC PATCH 12/26 kmsan: x86: sync metadata pages on page fault
In reply to [email protected]KMSAN assumes shadow and origin pages for every allocated page are accessible. For pages in vmalloc region those metadata pages reside in [VMALLOC_END, VMALLOC_META_END), therefore we must sync a bigger memory region.
To: Alexander Potapenko <[email protected]>
Cc: Ingo Molnar <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]
---
Change-Id: I0d54855489870ef1180b37fe2120b601da464bf7
--- arch/x86/mm/fault.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+)
arch/x86/mm/fault.c +20 -0
@@ -320,7 +320,17 @@ static void dump_pagetable(unsigned long address)void vmalloc_sync_all (void ) { +#ifdef CONFIG_KMSAN +/* +* For KMSAN, make sure metadata pages for vmalloc area and modules are +* also synced. +*/ +sync_global_pgds (VMALLOC_START &PGDIR_MASK ,VMALLOC_META_END ); +sync_global_pgds (MODULES_SHADOW_START &PGDIR_MASK , +MODULES_ORIGIN_END ); +#else sync_global_pgds (VMALLOC_START &PGDIR_MASK ,VMALLOC_END ); +#endif }/*
@@ -337,7 +347,17 @@ static noinline int vmalloc_fault(unsigned long address)pte_t *pte;/* Make sure we are in vmalloc area: */ +#ifdef CONFIG_KMSAN +/* +* For KMSAN, make sure metadata pages for vmalloc area and modules are +* also synced. +*/ +if (!(address >=VMALLOC_START && address <VMALLOC_META_END ) && + !(address >=MODULES_SHADOW_START && + address <MODULES_ORIGIN_END )) +#else if (!(address >= VMALLOC_START && address < VMALLOC_END)) +#endif return -1 ;/*
Signature · 1 line
2.23.0.866.gb869b98d4c-goog
[email protected]
maintainerRFC PATCH 13/26 kmsan: add tests for KMSAN
In reply to [email protected]The initial commit adds several tests that trigger KMSAN warnings in simple cases.
To use, build the kernel with CONFIG_TEST_KMSAN and do insmod test_kmsan.ko
To: Alexander Potapenko <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]
---
Change-Id: I287e86ae83a82b770f2baa46e5bbdce1dfa65195
--- lib/Makefile | 1 + lib/test_kmsan.c | 221 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 222 insertions(+) create mode 100644 lib/test_kmsan.c
lib/Makefile +1 -0
@@ -65,6 +65,7 @@ CFLAGS_test_kasan.o += $(call cc-disable-warning, vla) obj-$(CONFIG_TEST_UBSAN) += test_ubsan.o CFLAGS_test_ubsan.o += $(call cc-disable-warning, vla) UBSAN_SANITIZE_test_ubsan.o := y +obj-$(CONFIG_TEST_KMSAN) += test_kmsan.o obj-$(CONFIG_TEST_KSTRTOX) += test-kstrtox.o obj-$(CONFIG_TEST_LIST_SORT) += test_list_sort.o obj-$(CONFIG_TEST_LKM) += test_module.o
lib/test_kmsan.c new file +221 -0
221 lines not on this page · raw message
Signature · 1 line
2.23.0.866.gb869b98d4c-goog
[email protected]
maintainerRFC PATCH 14/26 kmsan: make READ_ONCE_TASK_STACK() return initialized values
In reply to [email protected]To avoid false positives, assume that reading from the task stack always produces initialized values.
To: Alexander Potapenko <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]
---
Change-Id: Ie73e5a41fdc8195699928e65f5cbe0d3d3c9e2fa
--- arch/x86/include/asm/unwind.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-)
arch/x86/include/asm/unwind.h +5 -4
@@ -100,9 +100,10 @@ void unwind_module_init(struct module *mod, void *orc_ip, size_t orc_ip_size,#endif /* -* This disables KASAN checking when reading a value from another task's stack, -* since the other task could be running on another CPU and could have poisoned -* the stack in the meantime. +* This disables KASAN/KMSAN checking when reading a value from another task's +* stack, since the other task could be running on another CPU and could have +* poisoned the stack in the meantime. Frame pointers are uninitialized by +* default, so for KMSAN we mark the return value initialized unconditionally. */ #define READ_ONCE_TASK_STACK (task, x) \ ({ \
@@ -111,7 +112,7 @@ void unwind_module_init(struct module *mod, void *orc_ip, size_t orc_ip_size, val =READ_ONCE (x); \ else \ val =READ_ONCE_NOCHECK (x); \ - val; \ +KMSAN_INIT_VALUE (val); \ })static inline bool task_on_another_cpu (struct task_struct *task)
Signature · 1 line
2.23.0.866.gb869b98d4c-goog
[email protected]
maintainerRFC PATCH 15/26 kmsan: Kconfig changes to disable options incompatible with KMSAN
In reply to [email protected]Certain config options are currently incompatible with KMSAN, disable them.
To: Alexander Potapenko <[email protected]>
Cc: Herbert Xu <[email protected]>
Cc: Harry Wentland <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]
---
Change-Id: I4521f5c764cfaeba2fae79c954925c302c5e3bc0
--- arch/Kconfig | 2 +- arch/x86/Kconfig | 1 + arch/x86/Kconfig.debug | 3 +++ crypto/Kconfig | 26 ++++++++++++++++++++++++++ drivers/gpu/drm/amd/display/Kconfig | 2 +- lib/Kconfig.debug | 3 +++ 6 files changed, 35 insertions(+), 2 deletions(-)
arch/Kconfig +1 -1
@@ -843,7 +843,7 @@ config HAVE_ARCH_VMAP_STACK config VMAP_STACK default y bool "Use a virtually-mapped stack" - depends on HAVE_ARCH_VMAP_STACK && !KASAN + depends on HAVE_ARCH_VMAP_STACK && !KASAN && !KMSAN ---help--- Enable this if you want the use virtually-mapped kernel stacks with guard pages. This causes kernel stack overflows to be
arch/x86/Kconfig +1 -0
@@ -2143,6 +2143,7 @@ config RELOCATABLE config RANDOMIZE_BASE bool "Randomize the address of the kernel image (KASLR)" depends on RELOCATABLE + depends on !KMSAN default y ---help--- In support of Kernel Address Space Layout Randomization (KASLR),
arch/x86/Kconfig.debug +3 -0
@@ -292,6 +292,9 @@ choice config UNWINDER_ORC bool "ORC unwinder" depends on X86_64 + # KMSAN doesn't support UNWINDER_ORC yet, + # see https://github.com/google/kmsan/issues/48. + depends on !KMSAN select STACK_VALIDATION ---help--- This option enables the ORC (Oops Rewind Capability) unwinder for
crypto/Kconfig +26 -0
@@ -309,11 +309,13 @@ config CRYPTO_AEGIS128 config CRYPTO_AEGIS128_SIMD bool "Support SIMD acceleration for AEGIS-128" depends on CRYPTO_AEGIS128 && ((ARM || ARM64) && KERNEL_MODE_NEON) + depends on !KMSAN default y config CRYPTO_AEGIS128_AESNI_SSE2 tristate "AEGIS-128 AEAD algorithm (x86_64 AESNI+SSE2 implementation)" depends on X86 && 64BIT + depends on !KMSAN select CRYPTO_AEAD select CRYPTO_SIMD help
@@ -571,6 +573,7 @@ config CRYPTO_CRC32C config CRYPTO_CRC32C_INTEL tristate "CRC32c INTEL hardware acceleration" depends on X86 + depends on !KMSAN select CRYPTO_HASH help In Intel processor with SSE4.2 supported, the processor will
@@ -611,6 +614,7 @@ config CRYPTO_CRC32 config CRYPTO_CRC32_PCLMUL tristate "CRC32 PCLMULQDQ hardware acceleration" depends on X86 + depends on !KMSAN select CRYPTO_HASH select CRC32 help
@@ -649,6 +653,7 @@ config CRYPTO_CRCT10DIF config CRYPTO_CRCT10DIF_PCLMUL tristate "CRCT10DIF PCLMULQDQ hardware acceleration" depends on X86 && 64BIT && CRC_T10DIF + depends on !KMSAN select CRYPTO_HASH help For x86_64 processors with SSE4.2 and PCLMULQDQ supported,
@@ -695,6 +700,7 @@ config CRYPTO_POLY1305 config CRYPTO_POLY1305_X86_64 tristate "Poly1305 authenticator algorithm (x86_64/SSE2/AVX2)" depends on X86 && 64BIT + depends on !KMSAN select CRYPTO_POLY1305 help Poly1305 authenticator algorithm, RFC7539.
@@ -814,6 +820,7 @@ config CRYPTO_SHA1 config CRYPTO_SHA1_SSSE3 tristate "SHA1 digest algorithm (SSSE3/AVX/AVX2/SHA-NI)" depends on X86 && 64BIT + depends on !KMSAN select CRYPTO_SHA1 select CRYPTO_HASH help
@@ -825,6 +832,7 @@ config CRYPTO_SHA1_SSSE3 config CRYPTO_SHA256_SSSE3 tristate "SHA256 digest algorithm (SSSE3/AVX/AVX2/SHA-NI)" depends on X86 && 64BIT + depends on !KMSAN select CRYPTO_SHA256 select CRYPTO_HASH help
@@ -837,6 +845,7 @@ config CRYPTO_SHA256_SSSE3 config CRYPTO_SHA512_SSSE3 tristate "SHA512 digest algorithm (SSSE3/AVX/AVX2)" depends on X86 && 64BIT + depends on !KMSAN select CRYPTO_SHA512 select CRYPTO_HASH help
@@ -1011,6 +1020,7 @@ config CRYPTO_WP512 config CRYPTO_GHASH_CLMUL_NI_INTEL tristate "GHASH hash function (CLMUL-NI accelerated)" depends on X86 && 64BIT + depends on !KMSAN select CRYPTO_CRYPTD help This is the x86_64 CLMUL-NI accelerated implementation of
@@ -1064,6 +1074,7 @@ config CRYPTO_AES_TI config CRYPTO_AES_NI_INTEL tristate "AES cipher algorithms (AES-NI)" depends on X86 + depends on !KMSAN select CRYPTO_AEAD select CRYPTO_LIB_AES select CRYPTO_ALGAPI
@@ -1190,6 +1201,7 @@ config CRYPTO_BLOWFISH_COMMON config CRYPTO_BLOWFISH_X86_64 tristate "Blowfish cipher algorithm (x86_64)" depends on X86 && 64BIT + depends on !KMSAN select CRYPTO_BLKCIPHER select CRYPTO_BLOWFISH_COMMON help
@@ -1221,6 +1233,7 @@ config CRYPTO_CAMELLIA_X86_64 tristate "Camellia cipher algorithm (x86_64)" depends on X86 && 64BIT depends on CRYPTO + depends on !KMSAN select CRYPTO_BLKCIPHER select CRYPTO_GLUE_HELPER_X86 help
@@ -1238,6 +1251,7 @@ config CRYPTO_CAMELLIA_AESNI_AVX_X86_64 tristate "Camellia cipher algorithm (x86_64/AES-NI/AVX)" depends on X86 && 64BIT depends on CRYPTO + depends on !KMSAN select CRYPTO_BLKCIPHER select CRYPTO_CAMELLIA_X86_64 select CRYPTO_GLUE_HELPER_X86
@@ -1258,6 +1272,7 @@ config CRYPTO_CAMELLIA_AESNI_AVX2_X86_64 tristate "Camellia cipher algorithm (x86_64/AES-NI/AVX2)" depends on X86 && 64BIT depends on CRYPTO + depends on !KMSAN select CRYPTO_CAMELLIA_AESNI_AVX_X86_64 help Camellia cipher algorithm module (x86_64/AES-NI/AVX2).
@@ -1303,6 +1318,7 @@ config CRYPTO_CAST5 config CRYPTO_CAST5_AVX_X86_64 tristate "CAST5 (CAST-128) cipher algorithm (x86_64/AVX)" depends on X86 && 64BIT + depends on !KMSAN select CRYPTO_BLKCIPHER select CRYPTO_CAST5 select CRYPTO_CAST_COMMON
@@ -1325,6 +1341,7 @@ config CRYPTO_CAST6 config CRYPTO_CAST6_AVX_X86_64 tristate "CAST6 (CAST-256) cipher algorithm (x86_64/AVX)" depends on X86 && 64BIT + depends on !KMSAN select CRYPTO_BLKCIPHER select CRYPTO_CAST6 select CRYPTO_CAST_COMMON
@@ -1360,6 +1377,7 @@ config CRYPTO_DES_SPARC64 config CRYPTO_DES3_EDE_X86_64 tristate "Triple DES EDE cipher algorithm (x86-64)" depends on X86 && 64BIT + depends on !KMSAN select CRYPTO_BLKCIPHER select CRYPTO_LIB_DES help
@@ -1426,6 +1444,7 @@ config CRYPTO_CHACHA20 config CRYPTO_CHACHA20_X86_64 tristate "ChaCha stream cipher algorithms (x86_64/SSSE3/AVX2/AVX-512VL)" depends on X86 && 64BIT + depends on !KMSAN select CRYPTO_BLKCIPHER select CRYPTO_CHACHA20 help
@@ -1462,6 +1481,7 @@ config CRYPTO_SERPENT config CRYPTO_SERPENT_SSE2_X86_64 tristate "Serpent cipher algorithm (x86_64/SSE2)" depends on X86 && 64BIT + depends on !KMSAN select CRYPTO_BLKCIPHER select CRYPTO_GLUE_HELPER_X86 select CRYPTO_SERPENT
@@ -1481,6 +1501,7 @@ config CRYPTO_SERPENT_SSE2_X86_64 config CRYPTO_SERPENT_SSE2_586 tristate "Serpent cipher algorithm (i586/SSE2)" depends on X86 && !64BIT + depends on !KMSAN select CRYPTO_BLKCIPHER select CRYPTO_GLUE_HELPER_X86 select CRYPTO_SERPENT
@@ -1500,6 +1521,7 @@ config CRYPTO_SERPENT_SSE2_586 config CRYPTO_SERPENT_AVX_X86_64 tristate "Serpent cipher algorithm (x86_64/AVX)" depends on X86 && 64BIT + depends on !KMSAN select CRYPTO_BLKCIPHER select CRYPTO_GLUE_HELPER_X86 select CRYPTO_SERPENT
@@ -1520,6 +1542,7 @@ config CRYPTO_SERPENT_AVX_X86_64 config CRYPTO_SERPENT_AVX2_X86_64 tristate "Serpent cipher algorithm (x86_64/AVX2)" depends on X86 && 64BIT + depends on !KMSAN select CRYPTO_SERPENT_AVX_X86_64 help Serpent cipher algorithm, by Anderson, Biham & Knudsen.
@@ -1615,6 +1638,7 @@ config CRYPTO_TWOFISH_586 config CRYPTO_TWOFISH_X86_64 tristate "Twofish cipher algorithm (x86_64)" depends on (X86 || UML_X86) && 64BIT + depends on !KMSAN select CRYPTO_ALGAPI select CRYPTO_TWOFISH_COMMON help
@@ -1631,6 +1655,7 @@ config CRYPTO_TWOFISH_X86_64 config CRYPTO_TWOFISH_X86_64_3WAY tristate "Twofish cipher algorithm (x86_64, 3-way parallel)" depends on X86 && 64BIT + depends on !KMSAN select CRYPTO_BLKCIPHER select CRYPTO_TWOFISH_COMMON select CRYPTO_TWOFISH_X86_64
@@ -1652,6 +1677,7 @@ config CRYPTO_TWOFISH_X86_64_3WAY config CRYPTO_TWOFISH_AVX_X86_64 tristate "Twofish cipher algorithm (x86_64/AVX)" depends on X86 && 64BIT + depends on !KMSAN select CRYPTO_BLKCIPHER select CRYPTO_GLUE_HELPER_X86 select CRYPTO_SIMD
drivers/gpu/drm/amd/display/Kconfig +1 -1
@@ -6,7 +6,7 @@ config DRM_AMD_DC bool "AMD DC - Enable new display engine" default y select SND_HDA_COMPONENT if SND_HDA_CORE - select DRM_AMD_DC_DCN1_0 if X86 && !(KCOV_INSTRUMENT_ALL && KCOV_ENABLE_COMPARISONS) + select DRM_AMD_DC_DCN1_0 if X86 && !(KCOV_INSTRUMENT_ALL && KCOV_ENABLE_COMPARISONS) && !KMSAN help Choose this option if you want to use the new display engine support for AMDGPU. This adds required support for Vega and
lib/Kconfig.debug +3 -0
@@ -1068,6 +1068,9 @@ menu "Lock Debugging (spinlocks, mutexes, etc...)" config LOCK_DEBUGGING_SUPPORT bool depends on TRACE_IRQFLAGS_SUPPORT && STACKTRACE_SUPPORT && LOCKDEP_SUPPORT + # KMSAN is incompatible with lockdep, + # see https://github.com/google/kmsan/issues/57. + depends on !KMSAN default y config PROVE_LOCKING
Signature · 1 line
2.23.0.866.gb869b98d4c-goog
Re: [PATCH RFC v1 15/26] kmsan: Kconfig changes to disable options incompatible with KMSAN
Quoted 10 lines · On 2019-10-18 5:42 a.m., [email protected] wrote:
Certain config options are currently incompatible with KMSAN, disable them.
Signed-off-by Alexander Potapenko <[email protected]>To: Alexander Potapenko <[email protected]>
Cc: Herbert Xu <[email protected]>
Cc: Harry Wentland <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]
Harry
Quoted 290 lines
---
Change-Id: I4521f5c764cfaeba2fae79c954925c302c5e3bc0
--- arch/Kconfig | 2 +- arch/x86/Kconfig | 1 + arch/x86/Kconfig.debug | 3 +++ crypto/Kconfig | 26 ++++++++++++++++++++++++++ drivers/gpu/drm/amd/display/Kconfig | 2 +- lib/Kconfig.debug | 3 +++ 6 files changed, 35 insertions(+), 2 deletions(-)arch/Kconfig +1
-1@@ -843,7 +843,7 @@ config HAVE_ARCH_VMAP_STACK config VMAP_STACK default y bool "Use a virtually-mapped stack" - depends on HAVE_ARCH_VMAP_STACK && !KASAN + depends on HAVE_ARCH_VMAP_STACK && !KASAN && !KMSAN ---help--- Enable this if you want the use virtually-mapped kernel stacks with guard pages. This causes kernel stack overflows to bearch/x86/Kconfig +1
-0@@ -2143,6 +2143,7 @@ config RELOCATABLE config RANDOMIZE_BASE bool "Randomize the address of the kernel image (KASLR)" depends on RELOCATABLE + depends on !KMSAN default y ---help--- In support of Kernel Address Space Layout Randomization (KASLR),arch/x86/Kconfig.debug +3
-0@@ -292,6 +292,9 @@ choice config UNWINDER_ORC bool "ORC unwinder" depends on X86_64 + # KMSAN doesn't support UNWINDER_ORC yet, + # see https://github.com/google/kmsan/issues/48. + depends on !KMSAN select STACK_VALIDATION ---help--- This option enables the ORC (Oops Rewind Capability) unwinder forcrypto/Kconfig +26
-0@@ -309,11 +309,13 @@ config CRYPTO_AEGIS128 config CRYPTO_AEGIS128_SIMD bool "Support SIMD acceleration for AEGIS-128" depends on CRYPTO_AEGIS128 && ((ARM || ARM64) && KERNEL_MODE_NEON) + depends on !KMSAN default y config CRYPTO_AEGIS128_AESNI_SSE2 tristate "AEGIS-128 AEAD algorithm (x86_64 AESNI+SSE2 implementation)" depends on X86 && 64BIT + depends on !KMSAN select CRYPTO_AEAD select CRYPTO_SIMD help@@ -571,6 +573,7 @@ config CRYPTO_CRC32C config CRYPTO_CRC32C_INTEL tristate "CRC32c INTEL hardware acceleration" depends on X86 + depends on !KMSAN select CRYPTO_HASH help In Intel processor with SSE4.2 supported, the processor will@@ -611,6 +614,7 @@ config CRYPTO_CRC32 config CRYPTO_CRC32_PCLMUL tristate "CRC32 PCLMULQDQ hardware acceleration" depends on X86 + depends on !KMSAN select CRYPTO_HASH select CRC32 help@@ -649,6 +653,7 @@ config CRYPTO_CRCT10DIF config CRYPTO_CRCT10DIF_PCLMUL tristate "CRCT10DIF PCLMULQDQ hardware acceleration" depends on X86 && 64BIT && CRC_T10DIF + depends on !KMSAN select CRYPTO_HASH help For x86_64 processors with SSE4.2 and PCLMULQDQ supported,@@ -695,6 +700,7 @@ config CRYPTO_POLY1305 config CRYPTO_POLY1305_X86_64 tristate "Poly1305 authenticator algorithm (x86_64/SSE2/AVX2)" depends on X86 && 64BIT + depends on !KMSAN select CRYPTO_POLY1305 help Poly1305 authenticator algorithm, RFC7539.@@ -814,6 +820,7 @@ config CRYPTO_SHA1 config CRYPTO_SHA1_SSSE3 tristate "SHA1 digest algorithm (SSSE3/AVX/AVX2/SHA-NI)" depends on X86 && 64BIT + depends on !KMSAN select CRYPTO_SHA1 select CRYPTO_HASH help@@ -825,6 +832,7 @@ config CRYPTO_SHA1_SSSE3 config CRYPTO_SHA256_SSSE3 tristate "SHA256 digest algorithm (SSSE3/AVX/AVX2/SHA-NI)" depends on X86 && 64BIT + depends on !KMSAN select CRYPTO_SHA256 select CRYPTO_HASH help@@ -837,6 +845,7 @@ config CRYPTO_SHA256_SSSE3 config CRYPTO_SHA512_SSSE3 tristate "SHA512 digest algorithm (SSSE3/AVX/AVX2)" depends on X86 && 64BIT + depends on !KMSAN select CRYPTO_SHA512 select CRYPTO_HASH help@@ -1011,6 +1020,7 @@ config CRYPTO_WP512 config CRYPTO_GHASH_CLMUL_NI_INTEL tristate "GHASH hash function (CLMUL-NI accelerated)" depends on X86 && 64BIT + depends on !KMSAN select CRYPTO_CRYPTD help This is the x86_64 CLMUL-NI accelerated implementation of@@ -1064,6 +1074,7 @@ config CRYPTO_AES_TI config CRYPTO_AES_NI_INTEL tristate "AES cipher algorithms (AES-NI)" depends on X86 + depends on !KMSAN select CRYPTO_AEAD select CRYPTO_LIB_AES select CRYPTO_ALGAPI@@ -1190,6 +1201,7 @@ config CRYPTO_BLOWFISH_COMMON config CRYPTO_BLOWFISH_X86_64 tristate "Blowfish cipher algorithm (x86_64)" depends on X86 && 64BIT + depends on !KMSAN select CRYPTO_BLKCIPHER select CRYPTO_BLOWFISH_COMMON help@@ -1221,6 +1233,7 @@ config CRYPTO_CAMELLIA_X86_64 tristate "Camellia cipher algorithm (x86_64)" depends on X86 && 64BIT depends on CRYPTO + depends on !KMSAN select CRYPTO_BLKCIPHER select CRYPTO_GLUE_HELPER_X86 help@@ -1238,6 +1251,7 @@ config CRYPTO_CAMELLIA_AESNI_AVX_X86_64 tristate "Camellia cipher algorithm (x86_64/AES-NI/AVX)" depends on X86 && 64BIT depends on CRYPTO + depends on !KMSAN select CRYPTO_BLKCIPHER select CRYPTO_CAMELLIA_X86_64 select CRYPTO_GLUE_HELPER_X86@@ -1258,6 +1272,7 @@ config CRYPTO_CAMELLIA_AESNI_AVX2_X86_64 tristate "Camellia cipher algorithm (x86_64/AES-NI/AVX2)" depends on X86 && 64BIT depends on CRYPTO + depends on !KMSAN select CRYPTO_CAMELLIA_AESNI_AVX_X86_64 help Camellia cipher algorithm module (x86_64/AES-NI/AVX2).@@ -1303,6 +1318,7 @@ config CRYPTO_CAST5 config CRYPTO_CAST5_AVX_X86_64 tristate "CAST5 (CAST-128) cipher algorithm (x86_64/AVX)" depends on X86 && 64BIT + depends on !KMSAN select CRYPTO_BLKCIPHER select CRYPTO_CAST5 select CRYPTO_CAST_COMMON@@ -1325,6 +1341,7 @@ config CRYPTO_CAST6 config CRYPTO_CAST6_AVX_X86_64 tristate "CAST6 (CAST-256) cipher algorithm (x86_64/AVX)" depends on X86 && 64BIT + depends on !KMSAN select CRYPTO_BLKCIPHER select CRYPTO_CAST6 select CRYPTO_CAST_COMMON@@ -1360,6 +1377,7 @@ config CRYPTO_DES_SPARC64 config CRYPTO_DES3_EDE_X86_64 tristate "Triple DES EDE cipher algorithm (x86-64)" depends on X86 && 64BIT + depends on !KMSAN select CRYPTO_BLKCIPHER select CRYPTO_LIB_DES help@@ -1426,6 +1444,7 @@ config CRYPTO_CHACHA20 config CRYPTO_CHACHA20_X86_64 tristate "ChaCha stream cipher algorithms (x86_64/SSSE3/AVX2/AVX-512VL)" depends on X86 && 64BIT + depends on !KMSAN select CRYPTO_BLKCIPHER select CRYPTO_CHACHA20 help@@ -1462,6 +1481,7 @@ config CRYPTO_SERPENT config CRYPTO_SERPENT_SSE2_X86_64 tristate "Serpent cipher algorithm (x86_64/SSE2)" depends on X86 && 64BIT + depends on !KMSAN select CRYPTO_BLKCIPHER select CRYPTO_GLUE_HELPER_X86 select CRYPTO_SERPENT@@ -1481,6 +1501,7 @@ config CRYPTO_SERPENT_SSE2_X86_64 config CRYPTO_SERPENT_SSE2_586 tristate "Serpent cipher algorithm (i586/SSE2)" depends on X86 && !64BIT + depends on !KMSAN select CRYPTO_BLKCIPHER select CRYPTO_GLUE_HELPER_X86 select CRYPTO_SERPENT@@ -1500,6 +1521,7 @@ config CRYPTO_SERPENT_SSE2_586 config CRYPTO_SERPENT_AVX_X86_64 tristate "Serpent cipher algorithm (x86_64/AVX)" depends on X86 && 64BIT + depends on !KMSAN select CRYPTO_BLKCIPHER select CRYPTO_GLUE_HELPER_X86 select CRYPTO_SERPENT@@ -1520,6 +1542,7 @@ config CRYPTO_SERPENT_AVX_X86_64 config CRYPTO_SERPENT_AVX2_X86_64 tristate "Serpent cipher algorithm (x86_64/AVX2)" depends on X86 && 64BIT + depends on !KMSAN select CRYPTO_SERPENT_AVX_X86_64 help Serpent cipher algorithm, by Anderson, Biham & Knudsen.@@ -1615,6 +1638,7 @@ config CRYPTO_TWOFISH_586 config CRYPTO_TWOFISH_X86_64 tristate "Twofish cipher algorithm (x86_64)" depends on (X86 || UML_X86) && 64BIT + depends on !KMSAN select CRYPTO_ALGAPI select CRYPTO_TWOFISH_COMMON help@@ -1631,6 +1655,7 @@ config CRYPTO_TWOFISH_X86_64 config CRYPTO_TWOFISH_X86_64_3WAY tristate "Twofish cipher algorithm (x86_64, 3-way parallel)" depends on X86 && 64BIT + depends on !KMSAN select CRYPTO_BLKCIPHER select CRYPTO_TWOFISH_COMMON select CRYPTO_TWOFISH_X86_64@@ -1652,6 +1677,7 @@ config CRYPTO_TWOFISH_X86_64_3WAY config CRYPTO_TWOFISH_AVX_X86_64 tristate "Twofish cipher algorithm (x86_64/AVX)" depends on X86 && 64BIT + depends on !KMSAN select CRYPTO_BLKCIPHER select CRYPTO_GLUE_HELPER_X86 select CRYPTO_SIMDdrivers/gpu/drm/amd/display/Kconfig +1
-1@@ -6,7 +6,7 @@ config DRM_AMD_DC bool "AMD DC - Enable new display engine" default y select SND_HDA_COMPONENT if SND_HDA_CORE - select DRM_AMD_DC_DCN1_0 if X86 && !(KCOV_INSTRUMENT_ALL && KCOV_ENABLE_COMPARISONS) + select DRM_AMD_DC_DCN1_0 if X86 && !(KCOV_INSTRUMENT_ALL && KCOV_ENABLE_COMPARISONS) && !KMSAN help Choose this option if you want to use the new display engine support for AMDGPU. This adds required support for Vega andlib/Kconfig.debug +3
-0@@ -1068,6 +1068,9 @@ menu "Lock Debugging (spinlocks, mutexes, etc...)" config LOCK_DEBUGGING_SUPPORT bool depends on TRACE_IRQFLAGS_SUPPORT && STACKTRACE_SUPPORT && LOCKDEP_SUPPORT + # KMSAN is incompatible with lockdep, + # see https://github.com/google/kmsan/issues/57. + depends on !KMSAN default y config PROVE_LOCKING
[email protected]
maintainerRFC PATCH 16/26 kmsan: Changing existing files to enable KMSAN builds
In reply to [email protected]Do the following to make KMSAN usable:
- add KMSAN declarations to struct page and struct task_struct;
- add Makefile.kmsan to top-level Makefile;
- call KMSAN initialization from init/main.c
- add asm hooks to arch/x86/entry/entry_64.S;
- increase task stack size under KMSAN;
- disable page merging in block/blk.h;
- disable CONFIG_KMSAN in arch/x86/boot/compressed/misc.h to use default string functions instead of KMSAN ones.
To: Alexander Potapenko <[email protected]>
Cc: Jens Axboe <[email protected]>
Cc: Andy Lutomirski <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]
---
Change-Id: I37e0b7f2d2f2b0aeac5753ff9d6b411485fc374e
--- Makefile | 3 ++- arch/x86/boot/compressed/misc.h | 1 + arch/x86/entry/entry_64.S | 18 ++++++++++++++++++ arch/x86/include/asm/page_64_types.h | 12 +++++++----- block/blk.h | 7 +++++++ include/linux/mm_types.h | 9 +++++++++ include/linux/sched.h | 5 +++++ init/main.c | 3 +++ lib/stackdepot.c | 7 ++++--- mm/Makefile | 1 + scripts/Makefile.lib | 6 ++++++ 11 files changed, 63 insertions(+), 9 deletions(-)
Makefile +2 -1
@@ -478,7 +478,7 @@ export KBUILD_HOSTCXXFLAGS KBUILD_HOSTLDFLAGS KBUILD_HOSTLDLIBS LDFLAGS_MODULE export KBUILD_CPPFLAGS NOSTDINC_FLAGS LINUXINCLUDE OBJCOPYFLAGS KBUILD_LDFLAGS export KBUILD_CFLAGS CFLAGS_KERNEL CFLAGS_MODULE -export CFLAGS_KASAN CFLAGS_KASAN_NOSANITIZE CFLAGS_UBSAN +export CFLAGS_KASAN CFLAGS_KASAN_NOSANITIZE CFLAGS_UBSAN CFLAGS_KMSAN export KBUILD_AFLAGS AFLAGS_KERNEL AFLAGS_MODULE export KBUILD_AFLAGS_MODULE KBUILD_CFLAGS_MODULE KBUILD_LDFLAGS_MODULE export KBUILD_AFLAGS_KERNEL KBUILD_CFLAGS_KERNEL
@@ -898,6 +898,7 @@ KBUILD_CFLAGS += $(call cc-option,-fcf-protection=none) endif include scripts/Makefile.kasan +include scripts/Makefile.kmsan include scripts/Makefile.extrawarn include scripts/Makefile.ubsan
arch/x86/boot/compressed/misc.h +1 -0
@@ -12,6 +12,7 @@#undef CONFIG_PARAVIRT_XXL#undef CONFIG_PARAVIRT_SPINLOCKS#undef CONFIG_KASAN +#undef CONFIG_KMSAN/* cpu_feature_enabled() cannot be used this early */ #define USE_EARLY_PGTABLE_L5
arch/x86/entry/entry_64.S +18 -0
@@ -35,6 +35,7 @@ #include <asm/asm.h> #include <asm/smap.h> #include <asm/pgtable_types.h> +#include <asm/kmsan.h> #include <asm/export.h> #include <asm/frame.h> #include <asm/nospec-branch.h>
@@ -168,12 +169,14 @@ GLOBAL(entry_SYSCALL_64_after_hwframe) PUSH_AND_CLEAR_REGS rax=$-ENOSYS TRACE_IRQS_OFF + KMSAN_SYSCALL_ENTER /* IRQs are off. */ movq %rax, %rdi movq %rsp, %rsi call do_syscall_64 /* returns with IRQs disabled */ + KMSAN_SYSCALL_EXIT TRACE_IRQS_IRETQ /* we're about to change IF */ /*
@@ -575,6 +578,7 @@ ENTRY(interrupt_entry) 1: ENTER_IRQ_STACK old_rsp=%rdi save_ret=1 + KMSAN_INTERRUPT_ENTER /* We entered an interrupt context - irqs are off: */ TRACE_IRQS_OFF
@@ -604,12 +608,14 @@ common_interrupt: addq $-0x80, (%rsp) /* Adjust vector to [-256, -1] range */ call interrupt_entry UNWIND_HINT_REGS indirect=1 + KMSAN_UNPOISON_PT_REGS call do_IRQ /* rdi points to pt_regs */ /* 0(%rsp): old RSP */ ret_from_intr: DISABLE_INTERRUPTS(CLBR_ANY) TRACE_IRQS_OFF + KMSAN_INTERRUPT_EXIT LEAVE_IRQ_STACK testb $3, CS(%rsp)
@@ -802,6 +808,7 @@ ENTRY(\sym) .Lcommon_\sym: call interrupt_entry UNWIND_HINT_REGS indirect=1 + KMSAN_UNPOISON_PT_REGS call \do_sym /* rdi points to pt_regs */ jmp ret_from_intr END(\sym)
@@ -909,15 +916,18 @@ apicinterrupt IRQ_WORK_VECTOR irq_work_interrupt smp_irq_work_interrupt .if \shift_ist != -1 subq $\ist_offset, CPU_TSS_IST(\shift_ist) + KMSAN_IST_ENTER(\shift_ist) .endif .if \read_cr2 movq %r12, %rdx /* Move CR2 into 3rd argument */ .endif + KMSAN_UNPOISON_PT_REGS call \do_sym .if \shift_ist != -1 + KMSAN_IST_EXIT(\shift_ist) addq $\ist_offset, CPU_TSS_IST(\shift_ist) .endif
@@ -1079,7 +1089,9 @@ ENTRY(do_softirq_own_stack) pushq %rbp mov %rsp, %rbp ENTER_IRQ_STACK regs=0 old_rsp=%r11 + KMSAN_SOFTIRQ_ENTER call __do_softirq + KMSAN_SOFTIRQ_EXIT LEAVE_IRQ_STACK regs=0 leaveq ret
@@ -1466,9 +1478,12 @@ ENTRY(nmi) * done with the NMI stack. */ + KMSAN_NMI_ENTER movq %rsp, %rdi movq $-1, %rsi + KMSAN_UNPOISON_PT_REGS call do_nmi + KMSAN_NMI_EXIT /* * Return back to user mode. We must *not* do the normal exit
@@ -1678,10 +1693,13 @@ end_repeat_nmi: call paranoid_entry UNWIND_HINT_REGS + KMSAN_NMI_ENTER /* paranoidentry do_nmi, 0; without TRACE_IRQS_OFF */ movq %rsp, %rdi movq $-1, %rsi + KMSAN_UNPOISON_PT_REGS call do_nmi + KMSAN_NMI_EXIT /* Always restore stashed CR3 value (see paranoid_entry) */ RESTORE_CR3 scratch_reg=%r15 save_reg=%r14
arch/x86/include/asm/page_64_types.h +7 -5
@@ -7,18 +7,20 @@#endif #ifdef CONFIG_KASAN -#define KASAN_STACK_ORDER 1 +#define EXTRA_STACK_ORDER 1 +#elif defined(CONFIG_KMSAN ) +#define EXTRA_STACK_ORDER 2#else -#define KASAN_STACK_ORDER 0 +#define EXTRA_STACK_ORDER 0#endif -#define THREAD_SIZE_ORDER (2 + KASAN_STACK_ORDER) +#define THREAD_SIZE_ORDER (2 + EXTRA_STACK_ORDER)#define THREAD_SIZE (PAGE_SIZE << THREAD_SIZE_ORDER) -#define EXCEPTION_STACK_ORDER (0 + KASAN_STACK_ORDER) +#define EXCEPTION_STACK_ORDER (0 + EXTRA_STACK_ORDER)#define EXCEPTION_STKSZ (PAGE_SIZE << EXCEPTION_STACK_ORDER) -#define IRQ_STACK_ORDER (2 + KASAN_STACK_ORDER) +#define IRQ_STACK_ORDER (2 + EXTRA_STACK_ORDER)#define IRQ_STACK_SIZE (PAGE_SIZE << IRQ_STACK_ORDER)/*
block/blk.h +7 -0
@@ -78,6 +78,13 @@ static inline bool biovec_phys_mergeable(struct request_queue *q,phys_addr_t addr1 =page_to_phys (vec1->bv_page) + vec1->bv_offset;phys_addr_t addr2 =page_to_phys (vec2->bv_page) + vec2->bv_offset; +#ifdef CONFIG_KMSAN +/* +* Merging consequent physical pages may not work correctly under KMSAN +* if their metadata pages aren't consequent. Just disable merging. +*/ +return false; +#endif if (addr1 + vec1->bv_len != addr2)return false;if (xen_domain () && !xen_biovec_phys_mergeable (vec1, vec2->bv_page))
include/linux/mm_types.h +9 -0
@@ -216,6 +216,15 @@ struct page {not kmapped, ie. highmem) */ #endif /* WANT_PAGE_VIRTUAL */ +#ifdef CONFIG_KMSAN +/* +* Bits in struct page are scarce, so the LSB in *shadow is used to +* indicate whether the page should be ignored by KMSAN or not. +*/ +struct page *shadow; +struct page *origin; +#endif +#ifdef LAST_CPUPID_NOT_IN_PAGE_FLAGS int _last_cpupid;#endif
include/linux/sched.h +5 -0
@@ -15,6 +15,7 @@#include <linux/sem.h> #include <linux/shm.h> #include <linux/kcov.h> +#include <linux/kmsan.h> #include <linux/mutex.h> #include <linux/plist.h> #include <linux/hrtimer.h>
@@ -1172,6 +1173,10 @@ struct task_struct {unsigned int kasan_depth;#endif +#ifdef CONFIG_KMSAN +struct kmsan_task_state kmsan; +#endif +#ifdef CONFIG_FUNCTION_GRAPH_TRACER /* Index of current stored address in ret_stack: */ int curr_ret_stack;
init/main.c +3 -0
@@ -32,6 +32,7 @@#include <linux/nmi.h> #include <linux/percpu.h> #include <linux/kmod.h> +#include <linux/kmsan.h> #include <linux/vmalloc.h> #include <linux/kernel_stat.h> #include <linux/start_kernel.h>
@@ -554,6 +555,7 @@ static void __init mm_init(void)*/ page_ext_init_flatmem ();report_meminit (); +kmsan_initialize_shadow ();mem_init ();kmem_cache_init ();kmemleak_init ();
@@ -625,6 +627,7 @@ asmlinkage __visible void __init start_kernel(void)sort_main_extable ();trap_init ();mm_init (); +kmsan_initialize ();ftrace_init ();
lib/stackdepot.c +4 -3
@@ -233,8 +233,8 @@ unsigned int stack_depot_fetch(depot_stack_handle_t handle,if (parts.slabindex > depot_index) {WARN (1 ,"slab index %d out of bounds (%d) for stack id %08x\n" , parts.slabindex, depot_index, handle); -__memset (trace,0 ,sizeof (*trace)); -return ; + *entries =NULL ; +return 0 ; } slab = stack_slabs[parts.slabindex]; stack = slab + offset;
@@ -299,7 +299,8 @@ depot_stack_handle_t stack_depot_save(unsigned long *entries,* contexts and I/O. */ alloc_flags &= ~GFP_ZONEMASK ; - alloc_flags &= (GFP_ATOMIC |GFP_KERNEL ); + alloc_flags &= (GFP_ATOMIC |GFP_KERNEL | + __GFP_NO_KMSAN_SHADOW); alloc_flags |= __GFP_NOWARN; page =alloc_pages (alloc_flags,STACK_ALLOC_ORDER );if (page)
mm/Makefile +1 -0
@@ -71,6 +71,7 @@ obj-$(CONFIG_PAGE_POISONING) += page_poison.o obj-$(CONFIG_SLAB) += slab.o obj-$(CONFIG_SLUB) += slub.o obj-$(CONFIG_KASAN) += kasan/ +obj-$(CONFIG_KMSAN) += kmsan/ obj-$(CONFIG_FAILSLAB) += failslab.o obj-$(CONFIG_MEMORY_HOTPLUG) += memory_hotplug.o obj-$(CONFIG_MEMTEST) += memtest.o
scripts/Makefile.lib +6 -0
@@ -140,6 +140,12 @@ _c_flags += $(if $(patsubst n%,, \ $(CFLAGS_KASAN), $(CFLAGS_KASAN_NOSANITIZE)) endif +ifeq ($(CONFIG_KMSAN),y) +_c_flags += $(if $(patsubst n%,, \ + $(KMSAN_SANITIZE_$(basetarget).o)$(KMSAN_SANITIZE)y), \ + $(CFLAGS_KMSAN)) +endif + ifeq ($(CONFIG_UBSAN),y) _c_flags += $(if $(patsubst n%,, \ $(UBSAN_SANITIZE_$(basetarget).o)$(UBSAN_SANITIZE)$(CONFIG_UBSAN_SANITIZE_ALL)), \
Signature · 1 line
2.23.0.866.gb869b98d4c-goog
[email protected]
maintainerRFC PATCH 17/26 kmsan: disable KMSAN instrumentation for certain kernel parts
In reply to [email protected]Instrumenting some files with KMSAN will result in kernel being unable to link, boot or crashing at runtime for various reasons (e.g. infinite recursion caused by instrumentation hooks calling instrumented code again).
Disable KMSAN in the following places:
- arch/x86/boot and arch/x86/realmode/rm, as KMSAN doesn't work for i386;
- arch/x86/entry/vdso, which isn't linked with KMSAN runtime;
- three files in arch/x86/kernel - boot problems;
- arch/x86/mm/cpu_entry_area.c - recursion;
- EFI stub - build failures;
- kcov, stackdepot - recursion.
To: Alexander Potapenko <[email protected]>
Cc: Ard Biesheuvel <[email protected]>
Cc: Thomas Gleixner <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]
---
Change-Id: I90961eabf2dcb9ae992aed259088953bad5e4d6d
--- arch/x86/boot/Makefile | 2 ++ arch/x86/boot/compressed/Makefile | 2 ++ arch/x86/entry/vdso/Makefile | 3 +++ arch/x86/kernel/Makefile | 4 ++++ arch/x86/kernel/cpu/Makefile | 1 + arch/x86/mm/Makefile | 2 ++ arch/x86/realmode/rm/Makefile | 2 ++ drivers/firmware/efi/libstub/Makefile | 1 + kernel/Makefile | 1 + lib/Makefile | 1 + 10 files changed, 19 insertions(+)
arch/x86/boot/Makefile +2 -0
@@ -10,6 +10,8 @@ # KASAN_SANITIZE := n +# KMSAN doesn't work for i386 +KMSAN_SANITIZE := n OBJECT_FILES_NON_STANDARD := y # Kernel does not boot with kcov instrumentation here.
arch/x86/boot/compressed/Makefile +2 -0
@@ -18,6 +18,8 @@ # compressed vmlinux.bin.all + u32 size of vmlinux.bin.all KASAN_SANITIZE := n +# KMSAN doesn't work for i386 +KMSAN_SANITIZE := n OBJECT_FILES_NON_STANDARD := y # Prevents link failures: __sanitizer_cov_trace_pc() is not linked in.
arch/x86/entry/vdso/Makefile +3 -0
@@ -11,6 +11,9 @@ include $(srctree)/lib/vdso/Makefile KBUILD_CFLAGS += $(DISABLE_LTO) KASAN_SANITIZE := n +# Undefined references to KMSAN hooks. +KMSAN_SANITIZE_vclock_gettime.o := n +KMSAN_SANITIZE_vgetcpu.o := n UBSAN_SANITIZE := n OBJECT_FILES_NON_STANDARD := y
arch/x86/kernel/Makefile +4 -0
@@ -28,6 +28,10 @@ KASAN_SANITIZE_dumpstack_$(BITS).o := n KASAN_SANITIZE_stacktrace.o := n KASAN_SANITIZE_paravirt.o := n +# Work around reboot loop. +KMSAN_SANITIZE_head$(BITS).o := n +KMSAN_SANITIZE_nmi.o := n + OBJECT_FILES_NON_STANDARD_relocate_kernel_$(BITS).o := y OBJECT_FILES_NON_STANDARD_test_nx.o := y OBJECT_FILES_NON_STANDARD_paravirt_patch.o := y
arch/x86/kernel/cpu/Makefile +1 -0
@@ -12,6 +12,7 @@ endif # If these files are instrumented, boot hangs during the first second. KCOV_INSTRUMENT_common.o := n KCOV_INSTRUMENT_perf_event.o := n +KMSAN_SANITIZE_common.o := n # Make sure load_percpu_segment has no stackprotector nostackp := $(call cc-option, -fno-stack-protector)
arch/x86/mm/Makefile +2 -0
@@ -7,6 +7,8 @@ KCOV_INSTRUMENT_mem_encrypt_identity.o := n KASAN_SANITIZE_mem_encrypt.o := n KASAN_SANITIZE_mem_encrypt_identity.o := n +KMSAN_SANITIZE_cpu_entry_area.o := n + ifdef CONFIG_FUNCTION_TRACER CFLAGS_REMOVE_mem_encrypt.o = -pg CFLAGS_REMOVE_mem_encrypt_identity.o = -pg
arch/x86/realmode/rm/Makefile +2 -0
@@ -7,6 +7,8 @@ # # KASAN_SANITIZE := n +# KMSAN doesn't work for i386 +KMSAN_SANITIZE := n OBJECT_FILES_NON_STANDARD := y # Prevents link failures: __sanitizer_cov_trace_pc() is not linked in.
drivers/firmware/efi/libstub/Makefile +1 -0
@@ -32,6 +32,7 @@ KBUILD_CFLAGS := $(cflags-y) -DDISABLE_BRANCH_PROFILING \ GCOV_PROFILE := n KASAN_SANITIZE := n +KMSAN_SANITIZE := n UBSAN_SANITIZE := n OBJECT_FILES_NON_STANDARD := y
kernel/Makefile +1 -0
@@ -30,6 +30,7 @@ KCOV_INSTRUMENT_extable.o := n # Don't self-instrument. KCOV_INSTRUMENT_kcov.o := n KASAN_SANITIZE_kcov.o := n +KMSAN_SANITIZE_kcov.o := n CFLAGS_kcov.o := $(call cc-option, -fno-conserve-stack -fno-stack-protector) # cond_syscall is currently not LTO compatible
lib/Makefile +1 -0
@@ -218,6 +218,7 @@ obj-$(CONFIG_IRQ_POLL) += irq_poll.o obj-$(CONFIG_STACKDEPOT) += stackdepot.o KASAN_SANITIZE_stackdepot.o := n +KMSAN_SANITIZE_stackdepot.o := n KCOV_INSTRUMENT_stackdepot.o := n libfdt_files = fdt.o fdt_ro.o fdt_wip.o fdt_rw.o fdt_sw.o fdt_strerror.o \
Signature · 1 line
2.23.0.866.gb869b98d4c-goog
[email protected]
maintainerRFC PATCH 18/26 kmsan: mm: call KMSAN hooks from SLUB code
In reply to [email protected]In order to report uninitialized memory coming from heap allocations KMSAN has to poison them unless they're created with __GFP_ZERO.
It's handy that we need KMSAN hooks in the places where init_on_alloc/init_on_free initialization is performed.
To: Alexander Potapenko <[email protected]>
Cc: Andrew Morton <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]
---
Change-Id: I51103b7981d3aabed747d0c85cbdc85568665871
--- mm/slub.c | 37 +++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-)
mm/slub.c +31 -6
@@ -21,6 +21,8 @@#include <linux/proc_fs.h> #include <linux/seq_file.h> #include <linux/kasan.h> +#include <linux/kmsan.h> +#include <linux/kmsan-checks.h> /* KMSAN_INIT_VALUE */ #include <linux/cpu.h> #include <linux/cpuset.h> #include <linux/mempolicy.h>
@@ -285,17 +287,27 @@ static void prefetch_freepointer(const struct kmem_cache *s, void *object)prefetch (object + s->offset); } +/* +* When running under KMSAN, get_freepointer_safe() may return an uninitialized +* pointer value in the case the current thread loses the race for the next +* memory chunk in the freelist. In that case this_cpu_cmpxchg_double() in +* slab_alloc_node() will fail, so the uninitialized value won't be used, but +* KMSAN will still check all arguments of cmpxchg because of imperfect +* handling of inline assembly. +* To work around this problem, use KMSAN_INIT_VALUE() to force initialize the +* return value of get_freepointer_safe(). +*/ static inline void *get_freepointer_safe (struct kmem_cache *s,void *object) {unsigned long freepointer_addr;void *p;if (!debug_pagealloc_enabled ()) -return get_freepointer (s, object); +return KMSAN_INIT_VALUE (get_freepointer (s, object)); freepointer_addr = (unsigned long )object + s->offset;probe_kernel_read (&p, (void **)freepointer_addr,sizeof (p)); -return freelist_ptr (s, p, freepointer_addr); +return KMSAN_INIT_VALUE (freelist_ptr (s, p, freepointer_addr)); }static inline void set_freepointer (struct kmem_cache *s,void *object,void *fp)
@@ -1390,6 +1402,7 @@ static inline void *kmalloc_large_node_hook(void *ptr, size_t size, gfp_t flags) ptr =kasan_kmalloc_large (ptr, size, flags);/* As ptr might get tagged, call kmemleak hook after KASAN. */ kmemleak_alloc (ptr, size,1 , flags); +kmsan_kmalloc_large (ptr, size, flags);return ptr; }
@@ -1397,6 +1410,7 @@ static __always_inline void kfree_hook(void *x) {kmemleak_free (x);kasan_kfree_large (x, _RET_IP_); +kmsan_kfree_large (x); } static __always_inline bool slab_free_hook(struct kmem_cache *s, void *x)
@@ -1453,6 +1467,12 @@ static inline bool slab_free_freelist_hook(struct kmem_cache *s, }while (object != old_tail); } +do { + object = next; + next =get_freepointer (s, object); +kmsan_slab_free (s, object); + }while (object != old_tail); +/* * Compiler cannot detect this function can be removed if slab_free_hook() * evaluates to nothing. Thus, catch all relevant config debug options here.
@@ -2769,6 +2789,7 @@ static __always_inline void *slab_alloc_node(struct kmem_cache *s,if (unlikely (slab_want_init_on_alloc (gfpflags, s)) && object)memset (object,0 , s->object_size); +kmsan_slab_alloc (s, object, gfpflags);slab_post_alloc_hook (s, gfpflags,1 , &object);return object;
@@ -2797,6 +2818,7 @@ void *kmem_cache_alloc_trace(struct kmem_cache *s, gfp_t gfpflags, size_t size)void *ret =slab_alloc (s, gfpflags, _RET_IP_);trace_kmalloc (_RET_IP_, ret, size, s->size, gfpflags); ret =kasan_kmalloc (s, ret, size, gfpflags); +return ret; }EXPORT_SYMBOL (kmem_cache_alloc_trace);
@@ -2809,7 +2831,6 @@ void *kmem_cache_alloc_node(struct kmem_cache *s, gfp_t gfpflags, int node)trace_kmem_cache_alloc_node (_RET_IP_, ret, s->object_size, s->size, gfpflags, node); -return ret; }EXPORT_SYMBOL (kmem_cache_alloc_node);
@@ -2825,6 +2846,7 @@ void *kmem_cache_alloc_node_trace(struct kmem_cache *s, size, s->size, gfpflags, node); ret =kasan_kmalloc (s, ret, size, gfpflags); +return ret; }EXPORT_SYMBOL (kmem_cache_alloc_node_trace);
@@ -3150,7 +3172,7 @@ int kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t size, void **p) {struct kmem_cache_cpu *c; -int i; +int i, j;/* memcg and kmem_cache debug support */ s =slab_pre_alloc_hook (s, flags);
@@ -3188,11 +3210,11 @@ int kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t size,/* Clear memory outside IRQ disabled fastpath loop */ if (unlikely (slab_want_init_on_alloc (flags, s))) { -int j; -for (j =0 ; j < i; j++)memset (p[j],0 , s->object_size); } +for (j =0 ; j < i; j++) +kmsan_slab_alloc (s, p[j], flags);/* memcg and kmem_cache debug support */ slab_post_alloc_hook (s, flags, size, p);
@@ -3793,6 +3815,7 @@ static int __init setup_slub_min_objects(char *str)__setup ("slub_min_objects=" , setup_slub_min_objects); +__no_sanitize_memory void *__kmalloc(size_t size, gfp_t flags) {struct kmem_cache *s;
@@ -5698,6 +5721,7 @@ static char *create_unique_id(struct kmem_cache *s) p +=sprintf (p,"%07u" , s->size);BUG_ON (p > name +ID_STR_LENGTH -1 ); +kmsan_unpoison_shadow (name, p - name);return name; }
@@ -5847,6 +5871,7 @@ static int sysfs_slab_alias(struct kmem_cache *s, const char *name) al->name = name; al->next = alias_list; alias_list = al; +kmsan_unpoison_shadow (al,sizeof (struct saved_alias ));return 0 ; }
Signature · 1 line
2.23.0.866.gb869b98d4c-goog
Re: [PATCH RFC v1 18/26] kmsan: mm: call KMSAN hooks from SLUB code
Quoted 5 lines · On Fri, 2019-10-18 at 11:42 +0200, [email protected] wrote:
In order to report uninitialized memory coming from heap allocations KMSAN has to poison them unless they're created with
__GFP_ZERO.It's handy that we need KMSAN hooks in the places where init_on_alloc/init_on_free initialization is performed.
Well, there is SLUB debug which has red zoning and poisoning checks. What's value of this patch?
Quoted 165 lines
Signed-off-by Alexander Potapenko <[email protected]>To: Alexander Potapenko <[email protected]>
Cc: Andrew Morton <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]
---Change-Id: I51103b7981d3aabed747d0c85cbdc85568665871
--- mm/slub.c | 37 +++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-)mm/slub.c +31
-6@@ -21,6 +21,8 @@#include <linux/proc_fs.h> #include <linux/seq_file.h> #include <linux/kasan.h> +#include <linux/kmsan.h> +#include <linux/kmsan-checks.h> /* KMSAN_INIT_VALUE */ #include <linux/cpu.h> #include <linux/cpuset.h> #include <linux/mempolicy.h> @@ -285,17 +287,27 @@ static void prefetch_freepointer(const struct kmem_cache *s, void *object)prefetch (object + s->offset); } +/* +* When running under KMSAN, get_freepointer_safe() may return an uninitialized +* pointer value in the case the current thread loses the race for the next +* memory chunk in the freelist. In that case this_cpu_cmpxchg_double() in +* slab_alloc_node() will fail, so the uninitialized value won't be used, but +* KMSAN will still check all arguments of cmpxchg because of imperfect +* handling of inline assembly. +* To work around this problem, use KMSAN_INIT_VALUE() to force initialize the +* return value of get_freepointer_safe(). +*/ static inline void *get_freepointer_safe (struct kmem_cache *s,void *object) {unsigned long freepointer_addr;void *p;if (!debug_pagealloc_enabled ()) -return get_freepointer (s, object); +return KMSAN_INIT_VALUE (get_freepointer (s, object)); freepointer_addr = (unsigned long )object + s->offset;probe_kernel_read (&p, (void **)freepointer_addr,sizeof (p)); -return freelist_ptr (s, p, freepointer_addr); +return KMSAN_INIT_VALUE (freelist_ptr (s, p, freepointer_addr)); }static inline void set_freepointer (struct kmem_cache *s,void *object,void *fp)@@ -1390,6 +1402,7 @@ static inline void *kmalloc_large_node_hook(void *ptr, size_t size, gfp_t flags) ptr =kasan_kmalloc_large (ptr, size, flags);/* As ptr might get tagged, call kmemleak hook after KASAN. */ kmemleak_alloc (ptr, size,1 , flags); +kmsan_kmalloc_large (ptr, size, flags);return ptr; }@@ -1397,6 +1410,7 @@ static __always_inline void kfree_hook(void *x) {kmemleak_free (x);kasan_kfree_large (x, _RET_IP_); +kmsan_kfree_large (x); } static __always_inline bool slab_free_hook(struct kmem_cache *s, void *x)@@ -1453,6 +1467,12 @@ static inline bool slab_free_freelist_hook(struct kmem_cache *s, }while (object != old_tail); } +do { + object = next; + next =get_freepointer (s, object); +kmsan_slab_free (s, object); + }while (object != old_tail); +/* * Compiler cannot detect this function can be removed if slab_free_hook() * evaluates to nothing. Thus, catch all relevant config debug options here. @@ -2769,6 +2789,7 @@ static __always_inline void *slab_alloc_node(struct kmem_cache *s,if (unlikely (slab_want_init_on_alloc (gfpflags, s)) && object)memset (object,0 , s->object_size); +kmsan_slab_alloc (s, object, gfpflags);slab_post_alloc_hook (s, gfpflags,1 , &object);return object;@@ -2797,6 +2818,7 @@ void *kmem_cache_alloc_trace(struct kmem_cache *s, gfp_t gfpflags, size_t size)void *ret =slab_alloc (s, gfpflags, _RET_IP_);trace_kmalloc (_RET_IP_, ret, size, s->size, gfpflags); ret =kasan_kmalloc (s, ret, size, gfpflags); +return ret; }EXPORT_SYMBOL (kmem_cache_alloc_trace);@@ -2809,7 +2831,6 @@ void *kmem_cache_alloc_node(struct kmem_cache *s, gfp_t gfpflags, int node)trace_kmem_cache_alloc_node (_RET_IP_, ret, s->object_size, s->size, gfpflags, node); -return ret; }EXPORT_SYMBOL (kmem_cache_alloc_node);@@ -2825,6 +2846,7 @@ void *kmem_cache_alloc_node_trace(struct kmem_cache *s, size, s->size, gfpflags, node); ret =kasan_kmalloc (s, ret, size, gfpflags); +return ret; }EXPORT_SYMBOL (kmem_cache_alloc_node_trace);@@ -3150,7 +3172,7 @@ int kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t size, void **p) {struct kmem_cache_cpu *c; -int i; +int i, j;/* memcg and kmem_cache debug support */ s =slab_pre_alloc_hook (s, flags);@@ -3188,11 +3210,11 @@ int kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t size,/* Clear memory outside IRQ disabled fastpath loop */ if (unlikely (slab_want_init_on_alloc (flags, s))) { -int j; -for (j =0 ; j < i; j++)memset (p[j],0 , s->object_size); } +for (j =0 ; j < i; j++) +kmsan_slab_alloc (s, p[j], flags);/* memcg and kmem_cache debug support */ slab_post_alloc_hook (s, flags, size, p);@@ -3793,6 +3815,7 @@ static int __init setup_slub_min_objects(char *str)__setup ("slub_min_objects=" , setup_slub_min_objects); +__no_sanitize_memory void *__kmalloc(size_t size, gfp_t flags) {struct kmem_cache *s;@@ -5698,6 +5721,7 @@ static char *create_unique_id(struct kmem_cache *s) p +=sprintf (p,"%07u" , s->size);BUG_ON (p > name +ID_STR_LENGTH -1 ); +kmsan_unpoison_shadow (name, p - name);return name; }@@ -5847,6 +5871,7 @@ static int sysfs_slab_alias(struct kmem_cache *s, const char *name) al->name = name; al->next = alias_list; alias_list = al; +kmsan_unpoison_shadow (al,sizeof (struct saved_alias ));return 0 ; }
Alexander Potapenko
maintainerRe: [PATCH RFC v1 18/26] kmsan: mm: call KMSAN hooks from SLUB code
Quoted 10 lines · Qian Cai
Quoted 5 lines · On Fri, 2019-10-18 at 11:42 +0200, [email protected] wrote:
In order to report uninitialized memory coming from heap allocations KMSAN has to poison them unless they're created with
__GFP_ZERO.It's handy that we need KMSAN hooks in the places where init_on_alloc/init_on_free initialization is performed.
Well, there is SLUB debug which has red zoning and poisoning checks. What's value of this patch?
Sorry, are you talking about the whole patchset or just this patch? Note that SLUB debug is unable to detect uninitialized values with bit-to-bit precision, neither have I heard of anyone using it for detecting uses of uninitialized memory in the kernel at all. The purpose of SLUB debug is to detect corruptions of freed memory.
Quoted 165 lines
Quoted 165 lines
Signed-off-by Alexander Potapenko <[email protected]>To: Alexander Potapenko <[email protected]>
Cc: Andrew Morton <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]
---Change-Id: I51103b7981d3aabed747d0c85cbdc85568665871
--- mm/slub.c | 37 +++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-)mm/slub.c +31
-6@@ -21,6 +21,8 @@#include <linux/proc_fs.h> #include <linux/seq_file.h> #include <linux/kasan.h> +#include <linux/kmsan.h> +#include <linux/kmsan-checks.h> /* KMSAN_INIT_VALUE */ #include <linux/cpu.h> #include <linux/cpuset.h> #include <linux/mempolicy.h> @@ -285,17 +287,27 @@ static void prefetch_freepointer(const struct kmem_cache *s, void *object)prefetch (object + s->offset); } +/* +* When running under KMSAN, get_freepointer_safe() may return an uninitialized +* pointer value in the case the current thread loses the race for the next +* memory chunk in the freelist. In that case this_cpu_cmpxchg_double() in +* slab_alloc_node() will fail, so the uninitialized value won't be used, but +* KMSAN will still check all arguments of cmpxchg because of imperfect +* handling of inline assembly. +* To work around this problem, use KMSAN_INIT_VALUE() to force initialize the +* return value of get_freepointer_safe(). +*/ static inline void *get_freepointer_safe (struct kmem_cache *s,void *object) {unsigned long freepointer_addr;void *p;if (!debug_pagealloc_enabled ()) -return get_freepointer (s, object); +return KMSAN_INIT_VALUE (get_freepointer (s, object)); freepointer_addr = (unsigned long )object + s->offset;probe_kernel_read (&p, (void **)freepointer_addr,sizeof (p)); -return freelist_ptr (s, p, freepointer_addr); +return KMSAN_INIT_VALUE (freelist_ptr (s, p, freepointer_addr)); }static inline void set_freepointer (struct kmem_cache *s,void *object,void *fp)@@ -1390,6 +1402,7 @@ static inline void *kmalloc_large_node_hook(void *ptr, size_t size, gfp_t flags) ptr =kasan_kmalloc_large (ptr, size, flags);/* As ptr might get tagged, call kmemleak hook after KASAN. */ kmemleak_alloc (ptr, size,1 , flags); +kmsan_kmalloc_large (ptr, size, flags);return ptr; }@@ -1397,6 +1410,7 @@ static __always_inline void kfree_hook(void *x) {kmemleak_free (x);kasan_kfree_large (x, _RET_IP_); +kmsan_kfree_large (x); } static __always_inline bool slab_free_hook(struct kmem_cache *s, void *x)@@ -1453,6 +1467,12 @@ static inline bool slab_free_freelist_hook(struct kmem_cache *s, }while (object != old_tail); } +do { + object = next; + next =get_freepointer (s, object); +kmsan_slab_free (s, object); + }while (object != old_tail); +/* * Compiler cannot detect this function can be removed if slab_free_hook() * evaluates to nothing. Thus, catch all relevant config debug options here. @@ -2769,6 +2789,7 @@ static __always_inline void *slab_alloc_node(struct kmem_cache *s,if (unlikely (slab_want_init_on_alloc (gfpflags, s)) && object)memset (object,0 , s->object_size); +kmsan_slab_alloc (s, object, gfpflags);slab_post_alloc_hook (s, gfpflags,1 , &object);return object;@@ -2797,6 +2818,7 @@ void *kmem_cache_alloc_trace(struct kmem_cache *s, gfp_t gfpflags, size_t size)void *ret =slab_alloc (s, gfpflags, _RET_IP_);trace_kmalloc (_RET_IP_, ret, size, s->size, gfpflags); ret =kasan_kmalloc (s, ret, size, gfpflags); +return ret; }EXPORT_SYMBOL (kmem_cache_alloc_trace);@@ -2809,7 +2831,6 @@ void *kmem_cache_alloc_node(struct kmem_cache *s, gfp_t gfpflags, int node)trace_kmem_cache_alloc_node (_RET_IP_, ret, s->object_size, s->size, gfpflags, node); -return ret; }EXPORT_SYMBOL (kmem_cache_alloc_node);@@ -2825,6 +2846,7 @@ void *kmem_cache_alloc_node_trace(struct kmem_cache *s, size, s->size, gfpflags, node); ret =kasan_kmalloc (s, ret, size, gfpflags); +return ret; }EXPORT_SYMBOL (kmem_cache_alloc_node_trace);@@ -3150,7 +3172,7 @@ int kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t size, void **p) {struct kmem_cache_cpu *c; -int i; +int i, j;/* memcg and kmem_cache debug support */ s =slab_pre_alloc_hook (s, flags);@@ -3188,11 +3210,11 @@ int kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t size,/* Clear memory outside IRQ disabled fastpath loop */ if (unlikely (slab_want_init_on_alloc (flags, s))) { -int j; -for (j =0 ; j < i; j++)memset (p[j],0 , s->object_size); } +for (j =0 ; j < i; j++) +kmsan_slab_alloc (s, p[j], flags);/* memcg and kmem_cache debug support */ slab_post_alloc_hook (s, flags, size, p);@@ -3793,6 +3815,7 @@ static int __init setup_slub_min_objects(char *str)__setup ("slub_min_objects=" , setup_slub_min_objects); +__no_sanitize_memory void *__kmalloc(size_t size, gfp_t flags) {struct kmem_cache *s;@@ -5698,6 +5721,7 @@ static char *create_unique_id(struct kmem_cache *s) p +=sprintf (p,"%07u" , s->size);BUG_ON (p > name +ID_STR_LENGTH -1 ); +kmsan_unpoison_shadow (name, p - name);return name; }@@ -5847,6 +5871,7 @@ static int sysfs_slab_alias(struct kmem_cache *s, const char *name) al->name = name; al->next = alias_list; alias_list = al; +kmsan_unpoison_shadow (al,sizeof (struct saved_alias ));return 0 ; }
Signature · 10 lines
Alexander Potapenko Software Engineer Google Germany GmbH Erika-Mann-Straße, 33 80636 München Geschäftsführer: Paul Manicle, Halimah DeLaine Prado Registergericht und -nummer: Hamburg, HRB 86891 Sitz der Gesellschaft: Hamburg
Re: [PATCH RFC v1 18/26] kmsan: mm: call KMSAN hooks from SLUB code
Quoted 6 lines · Alexander Potapenko
Quoted 5 lines · Qian Cai
Quoted 3 lines · On Fri, 2019-10-18 at 11:42 +0200, [email protected] wrote:
In order to report uninitialized memory coming from heap allocations KMSAN has to poison them unless they're created with
__GFP_ZERO.
Quoted 7 lines
Quoted 5 lines
Quoted 2 lines
It's handy that we need KMSAN hooks in the places where init_on_alloc/init_on_free initialization is performed.
Well, there is SLUB debug which has red zoning and poisoning checks. What's value of this patch?
Sorry, are you talking about the whole patchset or just this patch?
Just this patch.
Quoted 4 lines
Note that SLUB debug is unable to detect uninitialized values with bit-to-bit precision, neither have I heard of anyone using it for detecting uses of uninitialized memory in the kernel at all. The purpose of SLUB debug is to detect corruptions of freed memory.
The point is if developers have SLUB debug enabled, all the free objects will be poisoned, so what's the point of checking uninitialized memory there?
Quoted 168 lines
Quoted 165 lines
Quoted 165 lines
Signed-off-by Alexander Potapenko <[email protected]>To: Alexander Potapenko <[email protected]>
Cc: Andrew Morton <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]
---Change-Id: I51103b7981d3aabed747d0c85cbdc85568665871
--- mm/slub.c | 37 +++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-)mm/slub.c +31
-6@@ -21,6 +21,8 @@#include <linux/proc_fs.h> #include <linux/seq_file.h> #include <linux/kasan.h> +#include <linux/kmsan.h> +#include <linux/kmsan-checks.h> /* KMSAN_INIT_VALUE */ #include <linux/cpu.h> #include <linux/cpuset.h> #include <linux/mempolicy.h> @@ -285,17 +287,27 @@ static void prefetch_freepointer(const struct kmem_cache *s, void *object)prefetch (object + s->offset); } +/* +* When running under KMSAN, get_freepointer_safe() may return an uninitialized +* pointer value in the case the current thread loses the race for the next +* memory chunk in the freelist. In that case this_cpu_cmpxchg_double() in +* slab_alloc_node() will fail, so the uninitialized value won't be used, but +* KMSAN will still check all arguments of cmpxchg because of imperfect +* handling of inline assembly. +* To work around this problem, use KMSAN_INIT_VALUE() to force initialize the +* return value of get_freepointer_safe(). +*/ static inline void *get_freepointer_safe (struct kmem_cache *s,void *object) {unsigned long freepointer_addr;void *p;if (!debug_pagealloc_enabled ()) -return get_freepointer (s, object); +return KMSAN_INIT_VALUE (get_freepointer (s, object)); freepointer_addr = (unsigned long )object + s->offset;probe_kernel_read (&p, (void **)freepointer_addr,sizeof (p)); -return freelist_ptr (s, p, freepointer_addr); +return KMSAN_INIT_VALUE (freelist_ptr (s, p, freepointer_addr)); }static inline void set_freepointer (struct kmem_cache *s,void *object,void *fp)@@ -1390,6 +1402,7 @@ static inline void *kmalloc_large_node_hook(void *ptr, size_t size, gfp_t flags) ptr =kasan_kmalloc_large (ptr, size, flags);/* As ptr might get tagged, call kmemleak hook after KASAN. */ kmemleak_alloc (ptr, size,1 , flags); +kmsan_kmalloc_large (ptr, size, flags);return ptr; }@@ -1397,6 +1410,7 @@ static __always_inline void kfree_hook(void *x) {kmemleak_free (x);kasan_kfree_large (x, _RET_IP_); +kmsan_kfree_large (x); } static __always_inline bool slab_free_hook(struct kmem_cache *s, void *x)@@ -1453,6 +1467,12 @@ static inline bool slab_free_freelist_hook(struct kmem_cache *s, }while (object != old_tail); } +do { + object = next; + next =get_freepointer (s, object); +kmsan_slab_free (s, object); + }while (object != old_tail); +/* * Compiler cannot detect this function can be removed if slab_free_hook() * evaluates to nothing. Thus, catch all relevant config debug options here. @@ -2769,6 +2789,7 @@ static __always_inline void *slab_alloc_node(struct kmem_cache *s,if (unlikely (slab_want_init_on_alloc (gfpflags, s)) && object)memset (object,0 , s->object_size); +kmsan_slab_alloc (s, object, gfpflags);slab_post_alloc_hook (s, gfpflags,1 , &object);return object;@@ -2797,6 +2818,7 @@ void *kmem_cache_alloc_trace(struct kmem_cache *s, gfp_t gfpflags, size_t size)void *ret =slab_alloc (s, gfpflags, _RET_IP_);trace_kmalloc (_RET_IP_, ret, size, s->size, gfpflags); ret =kasan_kmalloc (s, ret, size, gfpflags); +return ret; }EXPORT_SYMBOL (kmem_cache_alloc_trace);@@ -2809,7 +2831,6 @@ void *kmem_cache_alloc_node(struct kmem_cache *s, gfp_t gfpflags, int node)trace_kmem_cache_alloc_node (_RET_IP_, ret, s->object_size, s->size, gfpflags, node); -return ret; }EXPORT_SYMBOL (kmem_cache_alloc_node);@@ -2825,6 +2846,7 @@ void *kmem_cache_alloc_node_trace(struct kmem_cache *s, size, s->size, gfpflags, node); ret =kasan_kmalloc (s, ret, size, gfpflags); +return ret; }EXPORT_SYMBOL (kmem_cache_alloc_node_trace);@@ -3150,7 +3172,7 @@ int kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t size, void **p) {struct kmem_cache_cpu *c; -int i; +int i, j;/* memcg and kmem_cache debug support */ s =slab_pre_alloc_hook (s, flags);@@ -3188,11 +3210,11 @@ int kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t size,/* Clear memory outside IRQ disabled fastpath loop */ if (unlikely (slab_want_init_on_alloc (flags, s))) { -int j; -for (j =0 ; j < i; j++)memset (p[j],0 , s->object_size); } +for (j =0 ; j < i; j++) +kmsan_slab_alloc (s, p[j], flags);/* memcg and kmem_cache debug support */ slab_post_alloc_hook (s, flags, size, p);@@ -3793,6 +3815,7 @@ static int __init setup_slub_min_objects(char *str)__setup ("slub_min_objects=" , setup_slub_min_objects); +__no_sanitize_memory void *__kmalloc(size_t size, gfp_t flags) {struct kmem_cache *s;@@ -5698,6 +5721,7 @@ static char *create_unique_id(struct kmem_cache *s) p +=sprintf (p,"%07u" , s->size);BUG_ON (p > name +ID_STR_LENGTH -1 ); +kmsan_unpoison_shadow (name, p - name);return name; }@@ -5847,6 +5871,7 @@ static int sysfs_slab_alias(struct kmem_cache *s, const char *name) al->name = name; al->next = alias_list; alias_list = al; +kmsan_unpoison_shadow (al,sizeof (struct saved_alias ));return 0 ; }
Alexander Potapenko
maintainerRe: [PATCH RFC v1 18/26] kmsan: mm: call KMSAN hooks from SLUB code
Quoted 18 lines · Qian Cai
Quoted 6 lines · Alexander Potapenko
Quoted 5 lines · Qian Cai
Quoted 3 lines · On Fri, 2019-10-18 at 11:42 +0200, [email protected] wrote:
In order to report uninitialized memory coming from heap allocations KMSAN has to poison them unless they're created with
__GFP_ZERO.Quoted 7 lines
Quoted 5 lines
Quoted 2 lines
It's handy that we need KMSAN hooks in the places where init_on_alloc/init_on_free initialization is performed.
Well, there is SLUB debug which has red zoning and poisoning checks. What's value of this patch?
Sorry, are you talking about the whole patchset or just this patch?
Just this patch.
Quoted 7 lines
Quoted 4 lines
Note that SLUB debug is unable to detect uninitialized values with bit-to-bit precision, neither have I heard of anyone using it for detecting uses of uninitialized memory in the kernel at all. The purpose of SLUB debug is to detect corruptions of freed memory.
The point is if developers have SLUB debug enabled, all the free objects will be poisoned, so what's the point of checking uninitialized memory there?
You are right, SLUB debug has to be handled separately. If I'm understanding correctly, right now KMSAN poisons freed memory before SLUB debug wipes it, therefore the memory will count as initialized. On the other hand, newly allocated memory is still marked as uninitialized, so a lot of bugs still remain detectable. TBH, I haven't tested KMSAN with SLUB debug good enough. Note that it's anyway a separate build that requires Clang, so right now it doesn't make much sense to combine KMSAN and SLUB debug together.
Quoted 168 lines
Quoted 168 lines
Quoted 165 lines
Quoted 165 lines
Signed-off-by Alexander Potapenko <[email protected]>To: Alexander Potapenko <[email protected]>
Cc: Andrew Morton <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]
---Change-Id: I51103b7981d3aabed747d0c85cbdc85568665871
--- mm/slub.c | 37 +++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-)mm/slub.c +31
-6@@ -21,6 +21,8 @@#include <linux/proc_fs.h> #include <linux/seq_file.h> #include <linux/kasan.h> +#include <linux/kmsan.h> +#include <linux/kmsan-checks.h> /* KMSAN_INIT_VALUE */ #include <linux/cpu.h> #include <linux/cpuset.h> #include <linux/mempolicy.h> @@ -285,17 +287,27 @@ static void prefetch_freepointer(const struct kmem_cache *s, void *object)prefetch (object + s->offset); } +/* +* When running under KMSAN, get_freepointer_safe() may return an uninitialized +* pointer value in the case the current thread loses the race for the next +* memory chunk in the freelist. In that case this_cpu_cmpxchg_double() in +* slab_alloc_node() will fail, so the uninitialized value won't be used, but +* KMSAN will still check all arguments of cmpxchg because of imperfect +* handling of inline assembly. +* To work around this problem, use KMSAN_INIT_VALUE() to force initialize the +* return value of get_freepointer_safe(). +*/ static inline void *get_freepointer_safe (struct kmem_cache *s,void *object) {unsigned long freepointer_addr;void *p;if (!debug_pagealloc_enabled ()) -return get_freepointer (s, object); +return KMSAN_INIT_VALUE (get_freepointer (s, object)); freepointer_addr = (unsigned long )object + s->offset;probe_kernel_read (&p, (void **)freepointer_addr,sizeof (p)); -return freelist_ptr (s, p, freepointer_addr); +return KMSAN_INIT_VALUE (freelist_ptr (s, p, freepointer_addr)); }static inline void set_freepointer (struct kmem_cache *s,void *object,void *fp)@@ -1390,6 +1402,7 @@ static inline void *kmalloc_large_node_hook(void *ptr, size_t size, gfp_t flags) ptr =kasan_kmalloc_large (ptr, size, flags);/* As ptr might get tagged, call kmemleak hook after KASAN. */ kmemleak_alloc (ptr, size,1 , flags); +kmsan_kmalloc_large (ptr, size, flags);return ptr; }@@ -1397,6 +1410,7 @@ static __always_inline void kfree_hook(void *x) {kmemleak_free (x);kasan_kfree_large (x, _RET_IP_); +kmsan_kfree_large (x); } static __always_inline bool slab_free_hook(struct kmem_cache *s, void *x)@@ -1453,6 +1467,12 @@ static inline bool slab_free_freelist_hook(struct kmem_cache *s, }while (object != old_tail); } +do { + object = next; + next =get_freepointer (s, object); +kmsan_slab_free (s, object); + }while (object != old_tail); +/* * Compiler cannot detect this function can be removed if slab_free_hook() * evaluates to nothing. Thus, catch all relevant config debug options here. @@ -2769,6 +2789,7 @@ static __always_inline void *slab_alloc_node(struct kmem_cache *s,if (unlikely (slab_want_init_on_alloc (gfpflags, s)) && object)memset (object,0 , s->object_size); +kmsan_slab_alloc (s, object, gfpflags);slab_post_alloc_hook (s, gfpflags,1 , &object);return object;@@ -2797,6 +2818,7 @@ void *kmem_cache_alloc_trace(struct kmem_cache *s, gfp_t gfpflags, size_t size)void *ret =slab_alloc (s, gfpflags, _RET_IP_);trace_kmalloc (_RET_IP_, ret, size, s->size, gfpflags); ret =kasan_kmalloc (s, ret, size, gfpflags); +return ret; }EXPORT_SYMBOL (kmem_cache_alloc_trace);@@ -2809,7 +2831,6 @@ void *kmem_cache_alloc_node(struct kmem_cache *s, gfp_t gfpflags, int node)trace_kmem_cache_alloc_node (_RET_IP_, ret, s->object_size, s->size, gfpflags, node); -return ret; }EXPORT_SYMBOL (kmem_cache_alloc_node);@@ -2825,6 +2846,7 @@ void *kmem_cache_alloc_node_trace(struct kmem_cache *s, size, s->size, gfpflags, node); ret =kasan_kmalloc (s, ret, size, gfpflags); +return ret; }EXPORT_SYMBOL (kmem_cache_alloc_node_trace);@@ -3150,7 +3172,7 @@ int kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t size, void **p) {struct kmem_cache_cpu *c; -int i; +int i, j;/* memcg and kmem_cache debug support */ s =slab_pre_alloc_hook (s, flags);@@ -3188,11 +3210,11 @@ int kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t size,/* Clear memory outside IRQ disabled fastpath loop */ if (unlikely (slab_want_init_on_alloc (flags, s))) { -int j; -for (j =0 ; j < i; j++)memset (p[j],0 , s->object_size); } +for (j =0 ; j < i; j++) +kmsan_slab_alloc (s, p[j], flags);/* memcg and kmem_cache debug support */ slab_post_alloc_hook (s, flags, size, p);@@ -3793,6 +3815,7 @@ static int __init setup_slub_min_objects(char *str)__setup ("slub_min_objects=" , setup_slub_min_objects); +__no_sanitize_memory void *__kmalloc(size_t size, gfp_t flags) {struct kmem_cache *s;@@ -5698,6 +5721,7 @@ static char *create_unique_id(struct kmem_cache *s) p +=sprintf (p,"%07u" , s->size);BUG_ON (p > name +ID_STR_LENGTH -1 ); +kmsan_unpoison_shadow (name, p - name);return name; }@@ -5847,6 +5871,7 @@ static int sysfs_slab_alias(struct kmem_cache *s, const char *name) al->name = name; al->next = alias_list; alias_list = al; +kmsan_unpoison_shadow (al,sizeof (struct saved_alias ));return 0 ; }
Signature · 10 lines
Alexander Potapenko Software Engineer Google Germany GmbH Erika-Mann-Straße, 33 80636 München Geschäftsführer: Paul Manicle, Halimah DeLaine Prado Registergericht und -nummer: Hamburg, HRB 86891 Sitz der Gesellschaft: Hamburg
Re: [PATCH RFC v1 18/26] kmsan: mm: call KMSAN hooks from SLUB code
Quoted 24 lines · Alexander Potapenko
Quoted 23 lines · Qian Cai
Quoted 6 lines · Alexander Potapenko
Quoted 5 lines · Qian Cai
Quoted 3 lines · On Fri, 2019-10-18 at 11:42 +0200, [email protected] wrote:
In order to report uninitialized memory coming from heap allocations KMSAN has to poison them unless they're created with
__GFP_ZERO.Quoted 7 lines
Quoted 5 lines
Quoted 2 lines
It's handy that we need KMSAN hooks in the places where init_on_alloc/init_on_free initialization is performed.
Well, there is SLUB debug which has red zoning and poisoning checks. What's value of this patch?
Sorry, are you talking about the whole patchset or just this patch?
Just this patch.
Quoted 4 lines
Note that SLUB debug is unable to detect uninitialized values with bit-to-bit precision, neither have I heard of anyone using it for detecting uses of uninitialized memory in the kernel at all. The purpose of SLUB debug is to detect corruptions of freed memory.
Quoted 8 lines
Quoted 2 lines
The point is if developers have SLUB debug enabled, all the free objects will be poisoned, so what's the point of checking uninitialized memory there?
You are right, SLUB debug has to be handled separately. If I'm understanding correctly, right now KMSAN poisons freed memory before SLUB debug wipes it, therefore the memory will count as initialized. On the other hand, newly allocated memory is still marked as uninitialized, so a lot of bugs still remain detectable.
Yes, but newly allocated slub objects will be poisoned as well.
Quoted 3 lines
TBH, I haven't tested KMSAN with SLUB debug good enough. Note that it's anyway a separate build that requires Clang, so right now it doesn't make much sense to combine KMSAN and SLUB debug together.
Can't you just build a debug kernel with SLUB debug enabled but dropping this patch? If there is an uninitialized memory here leading to data corruption, SLUB debug should be detected as well as this patch. If not, it needs to understand why.
Quoted 171 lines
Quoted 168 lines
Quoted 168 lines
Quoted 165 lines
Quoted 165 lines
Signed-off-by Alexander Potapenko <[email protected]>To: Alexander Potapenko <[email protected]>
Cc: Andrew Morton <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]
---Change-Id: I51103b7981d3aabed747d0c85cbdc85568665871
--- mm/slub.c | 37 +++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-)mm/slub.c +31
-6@@ -21,6 +21,8 @@#include <linux/proc_fs.h> #include <linux/seq_file.h> #include <linux/kasan.h> +#include <linux/kmsan.h> +#include <linux/kmsan-checks.h> /* KMSAN_INIT_VALUE */ #include <linux/cpu.h> #include <linux/cpuset.h> #include <linux/mempolicy.h> @@ -285,17 +287,27 @@ static void prefetch_freepointer(const struct kmem_cache *s, void *object)prefetch (object + s->offset); } +/* +* When running under KMSAN, get_freepointer_safe() may return an uninitialized +* pointer value in the case the current thread loses the race for the next +* memory chunk in the freelist. In that case this_cpu_cmpxchg_double() in +* slab_alloc_node() will fail, so the uninitialized value won't be used, but +* KMSAN will still check all arguments of cmpxchg because of imperfect +* handling of inline assembly. +* To work around this problem, use KMSAN_INIT_VALUE() to force initialize the +* return value of get_freepointer_safe(). +*/ static inline void *get_freepointer_safe (struct kmem_cache *s,void *object) {unsigned long freepointer_addr;void *p;if (!debug_pagealloc_enabled ()) -return get_freepointer (s, object); +return KMSAN_INIT_VALUE (get_freepointer (s, object)); freepointer_addr = (unsigned long )object + s->offset;probe_kernel_read (&p, (void **)freepointer_addr,sizeof (p)); -return freelist_ptr (s, p, freepointer_addr); +return KMSAN_INIT_VALUE (freelist_ptr (s, p, freepointer_addr)); }static inline void set_freepointer (struct kmem_cache *s,void *object,void *fp)@@ -1390,6 +1402,7 @@ static inline void *kmalloc_large_node_hook(void *ptr, size_t size, gfp_t flags) ptr =kasan_kmalloc_large (ptr, size, flags);/* As ptr might get tagged, call kmemleak hook after KASAN. */ kmemleak_alloc (ptr, size,1 , flags); +kmsan_kmalloc_large (ptr, size, flags);return ptr; }@@ -1397,6 +1410,7 @@ static __always_inline void kfree_hook(void *x) {kmemleak_free (x);kasan_kfree_large (x, _RET_IP_); +kmsan_kfree_large (x); } static __always_inline bool slab_free_hook(struct kmem_cache *s, void *x)@@ -1453,6 +1467,12 @@ static inline bool slab_free_freelist_hook(struct kmem_cache *s, }while (object != old_tail); } +do { + object = next; + next =get_freepointer (s, object); +kmsan_slab_free (s, object); + }while (object != old_tail); +/* * Compiler cannot detect this function can be removed if slab_free_hook() * evaluates to nothing. Thus, catch all relevant config debug options here. @@ -2769,6 +2789,7 @@ static __always_inline void *slab_alloc_node(struct kmem_cache *s,if (unlikely (slab_want_init_on_alloc (gfpflags, s)) && object)memset (object,0 , s->object_size); +kmsan_slab_alloc (s, object, gfpflags);slab_post_alloc_hook (s, gfpflags,1 , &object);return object;@@ -2797,6 +2818,7 @@ void *kmem_cache_alloc_trace(struct kmem_cache *s, gfp_t gfpflags, size_t size)void *ret =slab_alloc (s, gfpflags, _RET_IP_);trace_kmalloc (_RET_IP_, ret, size, s->size, gfpflags); ret =kasan_kmalloc (s, ret, size, gfpflags); +return ret; }EXPORT_SYMBOL (kmem_cache_alloc_trace);@@ -2809,7 +2831,6 @@ void *kmem_cache_alloc_node(struct kmem_cache *s, gfp_t gfpflags, int node)trace_kmem_cache_alloc_node (_RET_IP_, ret, s->object_size, s->size, gfpflags, node); -return ret; }EXPORT_SYMBOL (kmem_cache_alloc_node);@@ -2825,6 +2846,7 @@ void *kmem_cache_alloc_node_trace(struct kmem_cache *s, size, s->size, gfpflags, node); ret =kasan_kmalloc (s, ret, size, gfpflags); +return ret; }EXPORT_SYMBOL (kmem_cache_alloc_node_trace);@@ -3150,7 +3172,7 @@ int kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t size, void **p) {struct kmem_cache_cpu *c; -int i; +int i, j;/* memcg and kmem_cache debug support */ s =slab_pre_alloc_hook (s, flags);@@ -3188,11 +3210,11 @@ int kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t size,/* Clear memory outside IRQ disabled fastpath loop */ if (unlikely (slab_want_init_on_alloc (flags, s))) { -int j; -for (j =0 ; j < i; j++)memset (p[j],0 , s->object_size); } +for (j =0 ; j < i; j++) +kmsan_slab_alloc (s, p[j], flags);/* memcg and kmem_cache debug support */ slab_post_alloc_hook (s, flags, size, p);@@ -3793,6 +3815,7 @@ static int __init setup_slub_min_objects(char *str)__setup ("slub_min_objects=" , setup_slub_min_objects); +__no_sanitize_memory void *__kmalloc(size_t size, gfp_t flags) {struct kmem_cache *s;@@ -5698,6 +5721,7 @@ static char *create_unique_id(struct kmem_cache *s) p +=sprintf (p,"%07u" , s->size);BUG_ON (p > name +ID_STR_LENGTH -1 ); +kmsan_unpoison_shadow (name, p - name);return name; }@@ -5847,6 +5871,7 @@ static int sysfs_slab_alias(struct kmem_cache *s, const char *name) al->name = name; al->next = alias_list; alias_list = al; +kmsan_unpoison_shadow (al,sizeof (struct saved_alias ));return 0 ; }
Alexander Potapenko
maintainerRe: [PATCH RFC v1 18/26] kmsan: mm: call KMSAN hooks from SLUB code
Quoted 29 lines · Qian Cai
Quoted 27 lines · Alexander Potapenko
Quoted 25 lines · Qian Cai
Quoted 6 lines · Alexander Potapenko
Quoted 5 lines · Qian Cai
Quoted 3 lines · On Fri, 2019-10-18 at 11:42 +0200, [email protected] wrote:
In order to report uninitialized memory coming from heap allocations KMSAN has to poison them unless they're created with
__GFP_ZERO.Quoted 7 lines
Quoted 5 lines
Quoted 2 lines
It's handy that we need KMSAN hooks in the places where init_on_alloc/init_on_free initialization is performed.
Well, there is SLUB debug which has red zoning and poisoning checks. What's value of this patch?
Sorry, are you talking about the whole patchset or just this patch?
Just this patch.
Quoted 4 lines
Note that SLUB debug is unable to detect uninitialized values with bit-to-bit precision, neither have I heard of anyone using it for detecting uses of uninitialized memory in the kernel at all. The purpose of SLUB debug is to detect corruptions of freed memory.
The point is if developers have SLUB debug enabled, all the free objects will be poisoned, so what's the point of checking uninitialized memory there?
Quoted 7 lines
Quoted 5 lines
You are right, SLUB debug has to be handled separately. If I'm understanding correctly, right now KMSAN poisons freed memory before SLUB debug wipes it, therefore the memory will count as initialized. On the other hand, newly allocated memory is still marked as uninitialized, so a lot of bugs still remain detectable.
Yes, but newly allocated slub objects will be poisoned as well.
As far as I can tell, KMSAN hook marking newly allocated objects as uninitialized is called after slub poisoning.
Therefore these allocations will be treated by KMSAN as uninitialized.
Quoted 8 lines
Quoted 3 lines
TBH, I haven't tested KMSAN with SLUB debug good enough. Note that it's anyway a separate build that requires Clang, so right now it doesn't make much sense to combine KMSAN and SLUB debug together.
Can't you just build a debug kernel with SLUB debug enabled but dropping this patch? If there is an uninitialized memory here leading to data corruption, SLUB debug should be detected as well as this patch. If not, it needs to understand why.
Sorry, there might be some misunderstanding here.
KMSAN keeps the state of heap objects separately by keeping exactly the same amount of initialized/uninitialized bits for every allocation.
A call to kmsan_slab_alloc()/kmsan_slab_free() will mark an allocation as uninitialized for KMSAN. Not doing so will result in false reports. A call to memset(object, POISON_FREE, object_size) performed by SLAB debug will actually mark this allocation as initialized from KMSAN point of view, because we're memsetting a range with initialized data. Note that use of uninitialized data doesn't necessarily lead to immediate data corruption, so there might be nothing to detect for SLUB debug.
Quoted 172 lines
Quoted 172 lines
Quoted 169 lines
Quoted 169 lines
Quoted 166 lines
Quoted 166 lines
Signed-off-by Alexander Potapenko <[email protected]>To: Alexander Potapenko <[email protected]>
Cc: Andrew Morton <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]
---Change-Id: I51103b7981d3aabed747d0c85cbdc85568665871
--- mm/slub.c | 37 +++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-)mm/slub.c +31
-6@@ -21,6 +21,8 @@#include <linux/proc_fs.h> #include <linux/seq_file.h> #include <linux/kasan.h> +#include <linux/kmsan.h> +#include <linux/kmsan-checks.h> /* KMSAN_INIT_VALUE */ #include <linux/cpu.h> #include <linux/cpuset.h> #include <linux/mempolicy.h> @@ -285,17 +287,27 @@ static void prefetch_freepointer(const struct kmem_cache *s, void *object)prefetch (object + s->offset); } +/* +* When running under KMSAN, get_freepointer_safe() may return an uninitialized +* pointer value in the case the current thread loses the race for the next +* memory chunk in the freelist. In that case this_cpu_cmpxchg_double() in +* slab_alloc_node() will fail, so the uninitialized value won't be used, but +* KMSAN will still check all arguments of cmpxchg because of imperfect +* handling of inline assembly. +* To work around this problem, use KMSAN_INIT_VALUE() to force initialize the +* return value of get_freepointer_safe(). +*/ static inline void *get_freepointer_safe (struct kmem_cache *s,void *object) {unsigned long freepointer_addr;void *p;if (!debug_pagealloc_enabled ()) -return get_freepointer (s, object); +return KMSAN_INIT_VALUE (get_freepointer (s, object)); freepointer_addr = (unsigned long )object + s->offset;probe_kernel_read (&p, (void **)freepointer_addr,sizeof (p)); -return freelist_ptr (s, p, freepointer_addr); +return KMSAN_INIT_VALUE (freelist_ptr (s, p, freepointer_addr)); }static inline void set_freepointer (struct kmem_cache *s,void *object,void *fp)@@ -1390,6 +1402,7 @@ static inline void *kmalloc_large_node_hook(void *ptr, size_t size, gfp_t flags) ptr =kasan_kmalloc_large (ptr, size, flags);/* As ptr might get tagged, call kmemleak hook after KASAN. */ kmemleak_alloc (ptr, size,1 , flags); +kmsan_kmalloc_large (ptr, size, flags);return ptr; }@@ -1397,6 +1410,7 @@ static __always_inline void kfree_hook(void *x) {kmemleak_free (x);kasan_kfree_large (x, _RET_IP_); +kmsan_kfree_large (x); } static __always_inline bool slab_free_hook(struct kmem_cache *s, void *x)@@ -1453,6 +1467,12 @@ static inline bool slab_free_freelist_hook(struct kmem_cache *s, }while (object != old_tail); } +do { + object = next; + next =get_freepointer (s, object); +kmsan_slab_free (s, object); + }while (object != old_tail); +/* * Compiler cannot detect this function can be removed if slab_free_hook() * evaluates to nothing. Thus, catch all relevant config debug options here. @@ -2769,6 +2789,7 @@ static __always_inline void *slab_alloc_node(struct kmem_cache *s,if (unlikely (slab_want_init_on_alloc (gfpflags, s)) && object)memset (object,0 , s->object_size); +kmsan_slab_alloc (s, object, gfpflags);slab_post_alloc_hook (s, gfpflags,1 , &object);return object;@@ -2797,6 +2818,7 @@ void *kmem_cache_alloc_trace(struct kmem_cache *s, gfp_t gfpflags, size_t size)void *ret =slab_alloc (s, gfpflags, _RET_IP_);trace_kmalloc (_RET_IP_, ret, size, s->size, gfpflags); ret =kasan_kmalloc (s, ret, size, gfpflags); +return ret; }EXPORT_SYMBOL (kmem_cache_alloc_trace);@@ -2809,7 +2831,6 @@ void *kmem_cache_alloc_node(struct kmem_cache *s, gfp_t gfpflags, int node)trace_kmem_cache_alloc_node (_RET_IP_, ret, s->object_size, s->size, gfpflags, node); -return ret; }EXPORT_SYMBOL (kmem_cache_alloc_node);@@ -2825,6 +2846,7 @@ void *kmem_cache_alloc_node_trace(struct kmem_cache *s, size, s->size, gfpflags, node); ret =kasan_kmalloc (s, ret, size, gfpflags); +return ret; }EXPORT_SYMBOL (kmem_cache_alloc_node_trace);@@ -3150,7 +3172,7 @@ int kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t size, void **p) {struct kmem_cache_cpu *c; -int i; +int i, j;/* memcg and kmem_cache debug support */ s =slab_pre_alloc_hook (s, flags);@@ -3188,11 +3210,11 @@ int kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t size,/* Clear memory outside IRQ disabled fastpath loop */ if (unlikely (slab_want_init_on_alloc (flags, s))) { -int j; -for (j =0 ; j < i; j++)memset (p[j],0 , s->object_size); } +for (j =0 ; j < i; j++) +kmsan_slab_alloc (s, p[j], flags);/* memcg and kmem_cache debug support */ slab_post_alloc_hook (s, flags, size, p);@@ -3793,6 +3815,7 @@ static int __init setup_slub_min_objects(char *str)__setup ("slub_min_objects=" , setup_slub_min_objects); +__no_sanitize_memory void *__kmalloc(size_t size, gfp_t flags) {struct kmem_cache *s;@@ -5698,6 +5721,7 @@ static char *create_unique_id(struct kmem_cache *s) p +=sprintf (p,"%07u" , s->size);BUG_ON (p > name +ID_STR_LENGTH -1 ); +kmsan_unpoison_shadow (name, p - name);return name; }@@ -5847,6 +5871,7 @@ static int sysfs_slab_alias(struct kmem_cache *s, const char *name) al->name = name; al->next = alias_list; alias_list = al; +kmsan_unpoison_shadow (al,sizeof (struct saved_alias ));return 0 ; }
Signature · 10 lines
Alexander Potapenko Software Engineer Google Germany GmbH Erika-Mann-Straße, 33 80636 München Geschäftsführer: Paul Manicle, Halimah DeLaine Prado Registergericht und -nummer: Hamburg, HRB 86891 Sitz der Gesellschaft: Hamburg
Re: [PATCH RFC v1 18/26] kmsan: mm: call KMSAN hooks from SLUB code
Quoted 50 lines · Alexander Potapenko
Quoted 29 lines · Qian Cai
Quoted 27 lines · Alexander Potapenko
Quoted 25 lines · Qian Cai
Quoted 6 lines · Alexander Potapenko
Quoted 5 lines · Qian Cai
Quoted 3 lines · On Fri, 2019-10-18 at 11:42 +0200, [email protected] wrote:
In order to report uninitialized memory coming from heap allocations KMSAN has to poison them unless they're created with
__GFP_ZERO.Quoted 7 lines
Quoted 5 lines
Quoted 2 lines
It's handy that we need KMSAN hooks in the places where init_on_alloc/init_on_free initialization is performed.
Well, there is SLUB debug which has red zoning and poisoning checks. What's value of this patch?
Sorry, are you talking about the whole patchset or just this patch?
Just this patch.
Quoted 4 lines
Note that SLUB debug is unable to detect uninitialized values with bit-to-bit precision, neither have I heard of anyone using it for detecting uses of uninitialized memory in the kernel at all. The purpose of SLUB debug is to detect corruptions of freed memory.
The point is if developers have SLUB debug enabled, all the free objects will be poisoned, so what's the point of checking uninitialized memory there?
Quoted 7 lines
Quoted 5 lines
You are right, SLUB debug has to be handled separately. If I'm understanding correctly, right now KMSAN poisons freed memory before SLUB debug wipes it, therefore the memory will count as initialized. On the other hand, newly allocated memory is still marked as uninitialized, so a lot of bugs still remain detectable.
Yes, but newly allocated slub objects will be poisoned as well.
As far as I can tell, KMSAN hook marking newly allocated objects as uninitialized is called after slub poisoning.
Therefore these allocations will be treated by KMSAN as uninitialized.Quoted 8 lines
Quoted 3 lines
TBH, I haven't tested KMSAN with SLUB debug good enough. Note that it's anyway a separate build that requires Clang, so right now it doesn't make much sense to combine KMSAN and SLUB debug together.
Can't you just build a debug kernel with SLUB debug enabled but dropping this patch? If there is an uninitialized memory here leading to data corruption, SLUB debug should be detected as well as this patch. If not, it needs to understand why.
Quoted 12 lines
Sorry, there might be some misunderstanding here.
KMSAN keeps the state of heap objects separately by keeping exactly the same amount of initialized/uninitialized bits for every allocation.
A call to kmsan_slab_alloc()/kmsan_slab_free() will mark an allocation as uninitialized for KMSAN. Not doing so will result in false reports. A call tomemset(object, POISON_FREE, object_size)performed by SLAB debug will actually mark this allocation as initialized from KMSAN point of view, because we're memsetting a range with initialized data. Note that use of uninitialized data doesn't necessarily lead to immediate data corruption, so there might be nothing to detect for SLUB debug.
Well, SLUB debug would mark any access of uninitialized memory as data corruption with the help of poisoning and red zoning.
It is true that KMSAN might be doing a bit more than SLUB debug here, but the question is if it is worth the maintenance burden? Do you have any existing bugs to show that KMSAN would find by this patch but SLUB debug can't?
Quoted 175 lines
Quoted 172 lines
Quoted 172 lines
Quoted 169 lines
Quoted 169 lines
Quoted 166 lines
Quoted 166 lines
Signed-off-by Alexander Potapenko <[email protected]>To: Alexander Potapenko <[email protected]>
Cc: Andrew Morton <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]
---Change-Id: I51103b7981d3aabed747d0c85cbdc85568665871
--- mm/slub.c | 37 +++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-)mm/slub.c +31
-6@@ -21,6 +21,8 @@#include <linux/proc_fs.h> #include <linux/seq_file.h> #include <linux/kasan.h> +#include <linux/kmsan.h> +#include <linux/kmsan-checks.h> /* KMSAN_INIT_VALUE */ #include <linux/cpu.h> #include <linux/cpuset.h> #include <linux/mempolicy.h> @@ -285,17 +287,27 @@ static void prefetch_freepointer(const struct kmem_cache *s, void *object)prefetch (object + s->offset); } +/* +* When running under KMSAN, get_freepointer_safe() may return an uninitialized +* pointer value in the case the current thread loses the race for the next +* memory chunk in the freelist. In that case this_cpu_cmpxchg_double() in +* slab_alloc_node() will fail, so the uninitialized value won't be used, but +* KMSAN will still check all arguments of cmpxchg because of imperfect +* handling of inline assembly. +* To work around this problem, use KMSAN_INIT_VALUE() to force initialize the +* return value of get_freepointer_safe(). +*/ static inline void *get_freepointer_safe (struct kmem_cache *s,void *object) {unsigned long freepointer_addr;void *p;if (!debug_pagealloc_enabled ()) -return get_freepointer (s, object); +return KMSAN_INIT_VALUE (get_freepointer (s, object)); freepointer_addr = (unsigned long )object + s->offset;probe_kernel_read (&p, (void **)freepointer_addr,sizeof (p)); -return freelist_ptr (s, p, freepointer_addr); +return KMSAN_INIT_VALUE (freelist_ptr (s, p, freepointer_addr)); }static inline void set_freepointer (struct kmem_cache *s,void *object,void *fp)@@ -1390,6 +1402,7 @@ static inline void *kmalloc_large_node_hook(void *ptr, size_t size, gfp_t flags) ptr =kasan_kmalloc_large (ptr, size, flags);/* As ptr might get tagged, call kmemleak hook after KASAN. */ kmemleak_alloc (ptr, size,1 , flags); +kmsan_kmalloc_large (ptr, size, flags);return ptr; }@@ -1397,6 +1410,7 @@ static __always_inline void kfree_hook(void *x) {kmemleak_free (x);kasan_kfree_large (x, _RET_IP_); +kmsan_kfree_large (x); } static __always_inline bool slab_free_hook(struct kmem_cache *s, void *x)@@ -1453,6 +1467,12 @@ static inline bool slab_free_freelist_hook(struct kmem_cache *s, }while (object != old_tail); } +do { + object = next; + next =get_freepointer (s, object); +kmsan_slab_free (s, object); + }while (object != old_tail); +/* * Compiler cannot detect this function can be removed if slab_free_hook() * evaluates to nothing. Thus, catch all relevant config debug options here. @@ -2769,6 +2789,7 @@ static __always_inline void *slab_alloc_node(struct kmem_cache *s,if (unlikely (slab_want_init_on_alloc (gfpflags, s)) && object)memset (object,0 , s->object_size); +kmsan_slab_alloc (s, object, gfpflags);slab_post_alloc_hook (s, gfpflags,1 , &object);return object;@@ -2797,6 +2818,7 @@ void *kmem_cache_alloc_trace(struct kmem_cache *s, gfp_t gfpflags, size_t size)void *ret =slab_alloc (s, gfpflags, _RET_IP_);trace_kmalloc (_RET_IP_, ret, size, s->size, gfpflags); ret =kasan_kmalloc (s, ret, size, gfpflags); +return ret; }EXPORT_SYMBOL (kmem_cache_alloc_trace);@@ -2809,7 +2831,6 @@ void *kmem_cache_alloc_node(struct kmem_cache *s, gfp_t gfpflags, int node)trace_kmem_cache_alloc_node (_RET_IP_, ret, s->object_size, s->size, gfpflags, node); -return ret; }EXPORT_SYMBOL (kmem_cache_alloc_node);@@ -2825,6 +2846,7 @@ void *kmem_cache_alloc_node_trace(struct kmem_cache *s, size, s->size, gfpflags, node); ret =kasan_kmalloc (s, ret, size, gfpflags); +return ret; }EXPORT_SYMBOL (kmem_cache_alloc_node_trace);@@ -3150,7 +3172,7 @@ int kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t size, void **p) {struct kmem_cache_cpu *c; -int i; +int i, j;/* memcg and kmem_cache debug support */ s =slab_pre_alloc_hook (s, flags);@@ -3188,11 +3210,11 @@ int kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t size,/* Clear memory outside IRQ disabled fastpath loop */ if (unlikely (slab_want_init_on_alloc (flags, s))) { -int j; -for (j =0 ; j < i; j++)memset (p[j],0 , s->object_size); } +for (j =0 ; j < i; j++) +kmsan_slab_alloc (s, p[j], flags);/* memcg and kmem_cache debug support */ slab_post_alloc_hook (s, flags, size, p);@@ -3793,6 +3815,7 @@ static int __init setup_slub_min_objects(char *str)__setup ("slub_min_objects=" , setup_slub_min_objects); +__no_sanitize_memory void *__kmalloc(size_t size, gfp_t flags) {struct kmem_cache *s;@@ -5698,6 +5721,7 @@ static char *create_unique_id(struct kmem_cache *s) p +=sprintf (p,"%07u" , s->size);BUG_ON (p > name +ID_STR_LENGTH -1 ); +kmsan_unpoison_shadow (name, p - name);return name; }@@ -5847,6 +5871,7 @@ static int sysfs_slab_alias(struct kmem_cache *s, const char *name) al->name = name; al->next = alias_list; alias_list = al; +kmsan_unpoison_shadow (al,sizeof (struct saved_alias ));return 0 ; }
Alexander Potapenko
maintainerRe: [PATCH RFC v1 18/26] kmsan: mm: call KMSAN hooks from SLUB code
Quoted 65 lines · Qian Cai
Quoted 50 lines · Alexander Potapenko
Quoted 29 lines · Qian Cai
Quoted 27 lines · Alexander Potapenko
Quoted 25 lines · Qian Cai
Quoted 6 lines · Alexander Potapenko
Quoted 5 lines · Qian Cai
Quoted 3 lines · On Fri, 2019-10-18 at 11:42 +0200, [email protected] wrote:
In order to report uninitialized memory coming from heap allocations KMSAN has to poison them unless they're created with
__GFP_ZERO.Quoted 7 lines
Quoted 5 lines
Quoted 2 lines
It's handy that we need KMSAN hooks in the places where init_on_alloc/init_on_free initialization is performed.
Well, there is SLUB debug which has red zoning and poisoning checks. What's value of this patch?
Sorry, are you talking about the whole patchset or just this patch?
Just this patch.
Quoted 4 lines
Note that SLUB debug is unable to detect uninitialized values with bit-to-bit precision, neither have I heard of anyone using it for detecting uses of uninitialized memory in the kernel at all. The purpose of SLUB debug is to detect corruptions of freed memory.
The point is if developers have SLUB debug enabled, all the free objects will be poisoned, so what's the point of checking uninitialized memory there?
Quoted 7 lines
Quoted 5 lines
You are right, SLUB debug has to be handled separately. If I'm understanding correctly, right now KMSAN poisons freed memory before SLUB debug wipes it, therefore the memory will count as initialized. On the other hand, newly allocated memory is still marked as uninitialized, so a lot of bugs still remain detectable.
Yes, but newly allocated slub objects will be poisoned as well.
As far as I can tell, KMSAN hook marking newly allocated objects as uninitialized is called after slub poisoning.
Therefore these allocations will be treated by KMSAN as uninitialized.Quoted 8 lines
Quoted 3 lines
TBH, I haven't tested KMSAN with SLUB debug good enough. Note that it's anyway a separate build that requires Clang, so right now it doesn't make much sense to combine KMSAN and SLUB debug together.
Can't you just build a debug kernel with SLUB debug enabled but dropping this patch? If there is an uninitialized memory here leading to data corruption, SLUB debug should be detected as well as this patch. If not, it needs to understand why.
Quoted 12 lines
Sorry, there might be some misunderstanding here.
KMSAN keeps the state of heap objects separately by keeping exactly the same amount of initialized/uninitialized bits for every allocation.
A call to kmsan_slab_alloc()/kmsan_slab_free() will mark an allocation as uninitialized for KMSAN. Not doing so will result in false reports. A call tomemset(object, POISON_FREE, object_size)performed by SLAB debug will actually mark this allocation as initialized from KMSAN point of view, because we're memsetting a range with initialized data. Note that use of uninitialized data doesn't necessarily lead to immediate data corruption, so there might be nothing to detect for SLUB debug.
Quoted 6 lines
Well, SLUB debug would mark any access of uninitialized memory as data corruption with the help of poisoning and red zoning.
It is true that KMSAN might be doing a bit more than SLUB debug here, but the question is if it is worth the maintenance burden? Do you have any existing bugs to show that KMSAN would find by this patch but SLUB debug can't?
Consider the following case:
int *a = kmalloc(sizeof(int), GFP_KERNEL);
if (*a) {
// do something, e.g. become root
}IIUC SLUB poisoning will result in the branch being taken, but as long as there are no invalid accesses the kernel won't crash and no errors will be reported.
KMSAN however will report an error, because the contents of the buffer pointed to by |a| are poisoned by kmsan_slab_alloc().
The compiler instrumentation will retrieve the shadow value for *a, compare it to zero and report a use of uninitialized memory.
Another example would be copying the contents of newly allocated buffer to the userspace.
Quoted 175 lines
Quoted 175 lines
Quoted 172 lines
Quoted 172 lines
Quoted 169 lines
Quoted 169 lines
Quoted 166 lines
Quoted 166 lines
Signed-off-by Alexander Potapenko <[email protected]>To: Alexander Potapenko <[email protected]>
Cc: Andrew Morton <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]
---Change-Id: I51103b7981d3aabed747d0c85cbdc85568665871
--- mm/slub.c | 37 +++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-)mm/slub.c +31
-6@@ -21,6 +21,8 @@#include <linux/proc_fs.h> #include <linux/seq_file.h> #include <linux/kasan.h> +#include <linux/kmsan.h> +#include <linux/kmsan-checks.h> /* KMSAN_INIT_VALUE */ #include <linux/cpu.h> #include <linux/cpuset.h> #include <linux/mempolicy.h> @@ -285,17 +287,27 @@ static void prefetch_freepointer(const struct kmem_cache *s, void *object)prefetch (object + s->offset); } +/* +* When running under KMSAN, get_freepointer_safe() may return an uninitialized +* pointer value in the case the current thread loses the race for the next +* memory chunk in the freelist. In that case this_cpu_cmpxchg_double() in +* slab_alloc_node() will fail, so the uninitialized value won't be used, but +* KMSAN will still check all arguments of cmpxchg because of imperfect +* handling of inline assembly. +* To work around this problem, use KMSAN_INIT_VALUE() to force initialize the +* return value of get_freepointer_safe(). +*/ static inline void *get_freepointer_safe (struct kmem_cache *s,void *object) {unsigned long freepointer_addr;void *p;if (!debug_pagealloc_enabled ()) -return get_freepointer (s, object); +return KMSAN_INIT_VALUE (get_freepointer (s, object)); freepointer_addr = (unsigned long )object + s->offset;probe_kernel_read (&p, (void **)freepointer_addr,sizeof (p)); -return freelist_ptr (s, p, freepointer_addr); +return KMSAN_INIT_VALUE (freelist_ptr (s, p, freepointer_addr)); }static inline void set_freepointer (struct kmem_cache *s,void *object,void *fp)@@ -1390,6 +1402,7 @@ static inline void *kmalloc_large_node_hook(void *ptr, size_t size, gfp_t flags) ptr =kasan_kmalloc_large (ptr, size, flags);/* As ptr might get tagged, call kmemleak hook after KASAN. */ kmemleak_alloc (ptr, size,1 , flags); +kmsan_kmalloc_large (ptr, size, flags);return ptr; }@@ -1397,6 +1410,7 @@ static __always_inline void kfree_hook(void *x) {kmemleak_free (x);kasan_kfree_large (x, _RET_IP_); +kmsan_kfree_large (x); } static __always_inline bool slab_free_hook(struct kmem_cache *s, void *x)@@ -1453,6 +1467,12 @@ static inline bool slab_free_freelist_hook(struct kmem_cache *s, }while (object != old_tail); } +do { + object = next; + next =get_freepointer (s, object); +kmsan_slab_free (s, object); + }while (object != old_tail); +/* * Compiler cannot detect this function can be removed if slab_free_hook() * evaluates to nothing. Thus, catch all relevant config debug options here. @@ -2769,6 +2789,7 @@ static __always_inline void *slab_alloc_node(struct kmem_cache *s,if (unlikely (slab_want_init_on_alloc (gfpflags, s)) && object)memset (object,0 , s->object_size); +kmsan_slab_alloc (s, object, gfpflags);slab_post_alloc_hook (s, gfpflags,1 , &object);return object;@@ -2797,6 +2818,7 @@ void *kmem_cache_alloc_trace(struct kmem_cache *s, gfp_t gfpflags, size_t size)void *ret =slab_alloc (s, gfpflags, _RET_IP_);trace_kmalloc (_RET_IP_, ret, size, s->size, gfpflags); ret =kasan_kmalloc (s, ret, size, gfpflags); +return ret; }EXPORT_SYMBOL (kmem_cache_alloc_trace);@@ -2809,7 +2831,6 @@ void *kmem_cache_alloc_node(struct kmem_cache *s, gfp_t gfpflags, int node)trace_kmem_cache_alloc_node (_RET_IP_, ret, s->object_size, s->size, gfpflags, node); -return ret; }EXPORT_SYMBOL (kmem_cache_alloc_node);@@ -2825,6 +2846,7 @@ void *kmem_cache_alloc_node_trace(struct kmem_cache *s, size, s->size, gfpflags, node); ret =kasan_kmalloc (s, ret, size, gfpflags); +return ret; }EXPORT_SYMBOL (kmem_cache_alloc_node_trace);@@ -3150,7 +3172,7 @@ int kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t size, void **p) {struct kmem_cache_cpu *c; -int i; +int i, j;/* memcg and kmem_cache debug support */ s =slab_pre_alloc_hook (s, flags);@@ -3188,11 +3210,11 @@ int kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t size,/* Clear memory outside IRQ disabled fastpath loop */ if (unlikely (slab_want_init_on_alloc (flags, s))) { -int j; -for (j =0 ; j < i; j++)memset (p[j],0 , s->object_size); } +for (j =0 ; j < i; j++) +kmsan_slab_alloc (s, p[j], flags);/* memcg and kmem_cache debug support */ slab_post_alloc_hook (s, flags, size, p);@@ -3793,6 +3815,7 @@ static int __init setup_slub_min_objects(char *str)__setup ("slub_min_objects=" , setup_slub_min_objects); +__no_sanitize_memory void *__kmalloc(size_t size, gfp_t flags) {struct kmem_cache *s;@@ -5698,6 +5721,7 @@ static char *create_unique_id(struct kmem_cache *s) p +=sprintf (p,"%07u" , s->size);BUG_ON (p > name +ID_STR_LENGTH -1 ); +kmsan_unpoison_shadow (name, p - name);return name; }@@ -5847,6 +5871,7 @@ static int sysfs_slab_alias(struct kmem_cache *s, const char *name) al->name = name; al->next = alias_list; alias_list = al; +kmsan_unpoison_shadow (al,sizeof (struct saved_alias ));return 0 ; }
Signature · 10 lines
Alexander Potapenko Software Engineer Google Germany GmbH Erika-Mann-Straße, 33 80636 München Geschäftsführer: Paul Manicle, Halimah DeLaine Prado Registergericht und -nummer: Hamburg, HRB 86891 Sitz der Gesellschaft: Hamburg
Re: [PATCH RFC v1 18/26] kmsan: mm: call KMSAN hooks from SLUB code
Quoted 88 lines · Alexander Potapenko
Quoted 65 lines · Qian Cai
Quoted 50 lines · Alexander Potapenko
Quoted 29 lines · Qian Cai
Quoted 27 lines · Alexander Potapenko
Quoted 25 lines · Qian Cai
Quoted 6 lines · Alexander Potapenko
Quoted 5 lines · Qian Cai
Quoted 3 lines · On Fri, 2019-10-18 at 11:42 +0200, [email protected] wrote:
In order to report uninitialized memory coming from heap allocations KMSAN has to poison them unless they're created with
__GFP_ZERO.Quoted 7 lines
Quoted 5 lines
Quoted 2 lines
It's handy that we need KMSAN hooks in the places where init_on_alloc/init_on_free initialization is performed.
Well, there is SLUB debug which has red zoning and poisoning checks. What's value of this patch?
Sorry, are you talking about the whole patchset or just this patch?
Just this patch.
Quoted 4 lines
Note that SLUB debug is unable to detect uninitialized values with bit-to-bit precision, neither have I heard of anyone using it for detecting uses of uninitialized memory in the kernel at all. The purpose of SLUB debug is to detect corruptions of freed memory.
The point is if developers have SLUB debug enabled, all the free objects will be poisoned, so what's the point of checking uninitialized memory there?
Quoted 7 lines
Quoted 5 lines
You are right, SLUB debug has to be handled separately. If I'm understanding correctly, right now KMSAN poisons freed memory before SLUB debug wipes it, therefore the memory will count as initialized. On the other hand, newly allocated memory is still marked as uninitialized, so a lot of bugs still remain detectable.
Yes, but newly allocated slub objects will be poisoned as well.
As far as I can tell, KMSAN hook marking newly allocated objects as uninitialized is called after slub poisoning.
Therefore these allocations will be treated by KMSAN as uninitialized.Quoted 8 lines
Quoted 3 lines
TBH, I haven't tested KMSAN with SLUB debug good enough. Note that it's anyway a separate build that requires Clang, so right now it doesn't make much sense to combine KMSAN and SLUB debug together.
Can't you just build a debug kernel with SLUB debug enabled but dropping this patch? If there is an uninitialized memory here leading to data corruption, SLUB debug should be detected as well as this patch. If not, it needs to understand why.
Quoted 12 lines
Sorry, there might be some misunderstanding here.
KMSAN keeps the state of heap objects separately by keeping exactly the same amount of initialized/uninitialized bits for every allocation.
A call to kmsan_slab_alloc()/kmsan_slab_free() will mark an allocation as uninitialized for KMSAN. Not doing so will result in false reports. A call tomemset(object, POISON_FREE, object_size)performed by SLAB debug will actually mark this allocation as initialized from KMSAN point of view, because we're memsetting a range with initialized data. Note that use of uninitialized data doesn't necessarily lead to immediate data corruption, so there might be nothing to detect for SLUB debug.Quoted 6 lines
Well, SLUB debug would mark any access of uninitialized memory as data corruption with the help of poisoning and red zoning.
It is true that KMSAN might be doing a bit more than SLUB debug here, but the question is if it is worth the maintenance burden? Do you have any existing bugs to show that KMSAN would find by this patch but SLUB debug can't?
Consider the following case:
int *a = kmalloc(sizeof(int), GFP_KERNEL); if (*a) { // do something, e.g. become root }IIUC SLUB poisoning will result in the branch being taken, but as long as there are no invalid accesses the kernel won't crash and no errors will be reported.
KMSAN however will report an error, because the contents of the buffer pointed to by |a| are poisoned bykmsan_slab_alloc().
The compiler instrumentation will retrieve the shadow value for *a, compare it to zero and report a use of uninitialized memory.
Quoted 2 lines
Another example would be copying the contents of newly allocated buffer to the userspace.
Make sense. BTW, it might worth testing this patchset with this MM debug config based on linux-next to edge out some corner cases if you could,
https://raw.githubusercontent.com/cailca/linux-mm/master/x86.config
Quoted 179 lines
Quoted 176 lines
Quoted 176 lines
Quoted 173 lines
Quoted 173 lines
Quoted 170 lines
Quoted 170 lines
Quoted 167 lines
Quoted 167 lines
Signed-off-by Alexander Potapenko <[email protected]>To: Alexander Potapenko <[email protected]>
Cc: Andrew Morton <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]
---Change-Id: I51103b7981d3aabed747d0c85cbdc85568665871
--- mm/slub.c | 37 +++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-)mm/slub.c +31
-6@@ -21,6 +21,8 @@#include <linux/proc_fs.h> #include <linux/seq_file.h> #include <linux/kasan.h> +#include <linux/kmsan.h> +#include <linux/kmsan-checks.h> /* KMSAN_INIT_VALUE */ #include <linux/cpu.h> #include <linux/cpuset.h> #include <linux/mempolicy.h> @@ -285,17 +287,27 @@ static void prefetch_freepointer(const struct kmem_cache *s, void *object)prefetch (object + s->offset); } +/* +* When running under KMSAN, get_freepointer_safe() may return an uninitialized +* pointer value in the case the current thread loses the race for the next +* memory chunk in the freelist. In that case this_cpu_cmpxchg_double() in +* slab_alloc_node() will fail, so the uninitialized value won't be used, but +* KMSAN will still check all arguments of cmpxchg because of imperfect +* handling of inline assembly. +* To work around this problem, use KMSAN_INIT_VALUE() to force initialize the +* return value of get_freepointer_safe(). +*/ static inline void *get_freepointer_safe (struct kmem_cache *s,void *object) {unsigned long freepointer_addr;void *p;if (!debug_pagealloc_enabled ()) -return get_freepointer (s, object); +return KMSAN_INIT_VALUE (get_freepointer (s, object)); freepointer_addr = (unsigned long )object + s->offset;probe_kernel_read (&p, (void **)freepointer_addr,sizeof (p)); -return freelist_ptr (s, p, freepointer_addr); +return KMSAN_INIT_VALUE (freelist_ptr (s, p, freepointer_addr)); }static inline void set_freepointer (struct kmem_cache *s,void *object,void *fp)@@ -1390,6 +1402,7 @@ static inline void *kmalloc_large_node_hook(void *ptr, size_t size, gfp_t flags) ptr =kasan_kmalloc_large (ptr, size, flags);/* As ptr might get tagged, call kmemleak hook after KASAN. */ kmemleak_alloc (ptr, size,1 , flags); +kmsan_kmalloc_large (ptr, size, flags);return ptr; }@@ -1397,6 +1410,7 @@ static __always_inline void kfree_hook(void *x) {kmemleak_free (x);kasan_kfree_large (x, _RET_IP_); +kmsan_kfree_large (x); } static __always_inline bool slab_free_hook(struct kmem_cache *s, void *x)@@ -1453,6 +1467,12 @@ static inline bool slab_free_freelist_hook(struct kmem_cache *s, }while (object != old_tail); } +do { + object = next; + next =get_freepointer (s, object); +kmsan_slab_free (s, object); + }while (object != old_tail); +/* * Compiler cannot detect this function can be removed if slab_free_hook() * evaluates to nothing. Thus, catch all relevant config debug options here. @@ -2769,6 +2789,7 @@ static __always_inline void *slab_alloc_node(struct kmem_cache *s,if (unlikely (slab_want_init_on_alloc (gfpflags, s)) && object)memset (object,0 , s->object_size); +kmsan_slab_alloc (s, object, gfpflags);slab_post_alloc_hook (s, gfpflags,1 , &object);return object;@@ -2797,6 +2818,7 @@ void *kmem_cache_alloc_trace(struct kmem_cache *s, gfp_t gfpflags, size_t size)void *ret =slab_alloc (s, gfpflags, _RET_IP_);trace_kmalloc (_RET_IP_, ret, size, s->size, gfpflags); ret =kasan_kmalloc (s, ret, size, gfpflags); +return ret; }EXPORT_SYMBOL (kmem_cache_alloc_trace);@@ -2809,7 +2831,6 @@ void *kmem_cache_alloc_node(struct kmem_cache *s, gfp_t gfpflags, int node)trace_kmem_cache_alloc_node (_RET_IP_, ret, s->object_size, s->size, gfpflags, node); -return ret; }EXPORT_SYMBOL (kmem_cache_alloc_node);@@ -2825,6 +2846,7 @@ void *kmem_cache_alloc_node_trace(struct kmem_cache *s, size, s->size, gfpflags, node); ret =kasan_kmalloc (s, ret, size, gfpflags); +return ret; }EXPORT_SYMBOL (kmem_cache_alloc_node_trace);@@ -3150,7 +3172,7 @@ int kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t size, void **p) {struct kmem_cache_cpu *c; -int i; +int i, j;/* memcg and kmem_cache debug support */ s =slab_pre_alloc_hook (s, flags);@@ -3188,11 +3210,11 @@ int kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t size,/* Clear memory outside IRQ disabled fastpath loop */ if (unlikely (slab_want_init_on_alloc (flags, s))) { -int j; -for (j =0 ; j < i; j++)memset (p[j],0 , s->object_size); } +for (j =0 ; j < i; j++) +kmsan_slab_alloc (s, p[j], flags);/* memcg and kmem_cache debug support */ slab_post_alloc_hook (s, flags, size, p);@@ -3793,6 +3815,7 @@ static int __init setup_slub_min_objects(char *str)__setup ("slub_min_objects=" , setup_slub_min_objects); +__no_sanitize_memory void *__kmalloc(size_t size, gfp_t flags) {struct kmem_cache *s;@@ -5698,6 +5721,7 @@ static char *create_unique_id(struct kmem_cache *s) p +=sprintf (p,"%07u" , s->size);BUG_ON (p > name +ID_STR_LENGTH -1 ); +kmsan_unpoison_shadow (name, p - name);return name; }@@ -5847,6 +5871,7 @@ static int sysfs_slab_alias(struct kmem_cache *s, const char *name) al->name = name; al->next = alias_list; alias_list = al; +kmsan_unpoison_shadow (al,sizeof (struct saved_alias ));return 0 ; }
[email protected]
maintainerRFC PATCH 19/26 kmsan: call KMSAN hooks where needed
In reply to [email protected]Insert KMSAN hooks that check for potential memory errors and/or make necessary bookkeeping changes:
- allocate/split/deallocate metadata pages in alloc_pages()/split_page()/free_page();
- clear page shadow and origins in clear_page(), copy_user_highpage();
- copy page metadata in copy_highpage(), wp_page_copy();
- handle vmap()/vunmap()/iounmap();
- handle task creation and deletion;
- handle vprintk();
- call softirq entry/exit hooks in kernel/softirq.c;
- check/initialize memory sent to/read from USB, I2C, and network
To: Alexander Potapenko <[email protected]>
Cc: Andrew Morton <[email protected]>
Cc: Greg Kroah-Hartman <[email protected]>
Cc: Eric Dumazet <[email protected]>
Cc: Wolfram Sang <[email protected]>
Cc: Petr Mladek <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]
---
Change-Id: I1250a928d9263bf71fdaa067a070bdee686ef47b
--- arch/x86/include/asm/page_64.h | 13 +++++++++++++ arch/x86/mm/ioremap.c | 3 +++ drivers/i2c/i2c-core-base.c | 2 ++ drivers/usb/core/urb.c | 2 ++ include/linux/highmem.h | 4 ++++ kernel/exit.c | 2 ++ kernel/fork.c | 2 ++ kernel/kthread.c | 2 ++ kernel/printk/printk.c | 8 +++++++- kernel/softirq.c | 5 +++++ lib/ioremap.c | 5 +++++ mm/compaction.c | 9 +++++++++ mm/gup.c | 3 +++ mm/memory.c | 2 ++ mm/page_alloc.c | 16 ++++++++++++++++ mm/vmalloc.c | 23 +++++++++++++++++++++-- net/sched/sch_generic.c | 2 ++ 17 files changed, 100 insertions(+), 3 deletions(-)
arch/x86/include/asm/page_64.h +13 -0
@@ -44,14 +44,27 @@ void clear_page_orig(void *page);void clear_page_rep (void *page);void clear_page_erms (void *page); +/* This is an assembly header, avoid including too much of kmsan.h */ +#ifdef CONFIG_KMSAN +void kmsan_clear_page (void *page_addr); +#endif +__no_sanitize_memory static inline void clear_page(void *page) { +#ifdef CONFIG_KMSAN +/* alternative_call_2() changes |page|. */ +void *page_copy = page; +#endif alternative_call_2 (clear_page_orig, clear_page_rep,X86_FEATURE_REP_GOOD , clear_page_erms,X86_FEATURE_ERMS ,"=D" (page), "0" (page) : "cc", "memory", "rax", "rcx"); +#ifdef CONFIG_KMSAN +/* Clear KMSAN shadow for the pages that have it. */ +kmsan_clear_page (page_copy); +#endif }void copy_page (void *to,void *from);
arch/x86/mm/ioremap.c +3 -0
@@ -7,6 +7,7 @@* (C) Copyright 1995 1996 Linus Torvalds */ +#include <linux/kmsan.h> #include <linux/memblock.h> #include <linux/init.h> #include <linux/io.h>
@@ -451,6 +452,8 @@ void iounmap(volatile void __iomem *addr)return ; } +kmsan_iounmap_page_range ((unsigned long )addr, + (unsigned long )addr +get_vm_area_size (p));free_memtype (p->phys_addr, p->phys_addr +get_vm_area_size (p));/* Finally remove it */
drivers/i2c/i2c-core-base.c +2 -0
@@ -27,6 +27,7 @@#include <linux/irqflags.h> #include <linux/jump_label.h> #include <linux/kernel.h> +#include <linux/kmsan.h> #include <linux/module.h> #include <linux/mutex.h> #include <linux/of_device.h>
@@ -1975,6 +1976,7 @@ int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)trace_i2c_write (adap, &msgs[i], i); } +kmsan_handle_i2c_transfer (msgs, num);/* Retry automatically on arbitration loss */ orig_jiffies = jiffies;for (ret =0 , try =0 ; try <= adap->retries; try++) {
drivers/usb/core/urb.c +2 -0
@@ -8,6 +8,7 @@#include <linux/bitops.h> #include <linux/slab.h> #include <linux/log2.h> +#include <linux/kmsan-checks.h> #include <linux/usb.h> #include <linux/wait.h> #include <linux/usb/hcd.h>
@@ -401,6 +402,7 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)URB_SETUP_MAP_SINGLE |URB_SETUP_MAP_LOCAL | URB_DMA_SG_COMBINED); urb->transfer_flags |= (is_out ?URB_DIR_OUT :URB_DIR_IN ); +kmsan_handle_urb (urb, is_out);if (xfertype !=USB_ENDPOINT_XFER_CONTROL && dev->state <USB_STATE_CONFIGURED )
include/linux/highmem.h +4 -0
@@ -5,6 +5,7 @@#include <linux/fs.h> #include <linux/kernel.h> #include <linux/bug.h> +#include <linux/kmsan.h> #include <linux/mm.h> #include <linux/uaccess.h> #include <linux/hardirq.h>
@@ -255,6 +256,8 @@ static inline void copy_user_highpage(struct page *to, struct page *from, vfrom =kmap_atomic (from); vto =kmap_atomic (to);copy_user_page (vto, vfrom, vaddr, to); +/* User pages don't have shadow, just clear the destination. */ +kmsan_clear_page (page_address (to));kunmap_atomic (vto);kunmap_atomic (vfrom); }
@@ -270,6 +273,7 @@ static inline void copy_highpage(struct page *to, struct page *from) vfrom =kmap_atomic (from); vto =kmap_atomic (to);copy_page (vto, vfrom); +kmsan_copy_page_meta (to, from);kunmap_atomic (vto);kunmap_atomic (vfrom); }
kernel/exit.c +2 -0
@@ -60,6 +60,7 @@#include <linux/writeback.h> #include <linux/shm.h> #include <linux/kcov.h> +#include <linux/kmsan.h> #include <linux/random.h> #include <linux/rcuwait.h> #include <linux/compat.h>
@@ -719,6 +720,7 @@ void __noreturn do_exit(long code)profile_task_exit (tsk);kcov_task_exit (tsk); +kmsan_task_exit (tsk);WARN_ON (blk_needs_flush_plug (tsk));
kernel/fork.c +2 -0
@@ -37,6 +37,7 @@#include <linux/fdtable.h> #include <linux/iocontext.h> #include <linux/key.h> +#include <linux/kmsan.h> #include <linux/binfmts.h> #include <linux/mman.h> #include <linux/mmu_notifier.h>
@@ -931,6 +932,7 @@ static struct task_struct *dup_task_struct(struct task_struct *orig, int node)account_kernel_stack (tsk,1 );kcov_task_init (tsk); +kmsan_task_create (tsk);#ifdef CONFIG_FAULT_INJECTION tsk->fail_nth =0 ;
kernel/kthread.c +2 -0
@@ -17,6 +17,7 @@#include <linux/unistd.h> #include <linux/file.h> #include <linux/export.h> +#include <linux/kmsan.h> #include <linux/mutex.h> #include <linux/slab.h> #include <linux/freezer.h>
@@ -350,6 +351,7 @@ struct task_struct *__kthread_create_on_node(int (*threadfn)(void *data),set_cpus_allowed_ptr (task, cpu_all_mask); }kfree (create); +kmsan_task_create (task);return task; }
kernel/printk/printk.c +7 -1
@@ -1914,7 +1914,12 @@ int vprintk_store(int facility, int level,* The printf needs to come first; we need the syslog * prefix which might be passed-in as a parameter. */ - text_len =vscnprintf (text,sizeof (textbuf), fmt, args); +/* +* We've checked the printk arguments in vprintk_emit() already. +* Initialize |text_len| to prevent the errors from spreading. +*/ + text_len =KMSAN_INIT_VALUE (vscnprintf (text,sizeof (textbuf), fmt, + args));/* mark and strip a trailing newline */ if (text_len && text[text_len-1 ] =='\n' ) {
@@ -1972,6 +1977,7 @@ asmlinkage int vprintk_emit(int facility, int level,boot_delay_msec (level);printk_delay (); +kmsan_handle_vprintk (&fmt, args);/* This stops the holder of console_sem just where we want him */ logbuf_lock_irqsave (flags); curr_log_seq = log_next_seq;
kernel/softirq.c +5 -0
@@ -11,6 +11,7 @@#include <linux/export.h> #include <linux/kernel_stat.h> +#include <linux/kmsan.h> #include <linux/interrupt.h> #include <linux/init.h> #include <linux/mm.h>
@@ -370,7 +371,9 @@ static inline void invoke_softirq(void)* it is the irq stack, because it should be near empty * at this stage. */ +kmsan_softirq_enter ();__do_softirq (); +kmsan_softirq_exit ();#else /* * Otherwise, irq_exit() is called on the task stack that can
@@ -600,7 +603,9 @@ static void run_ksoftirqd(unsigned int cpu)* We can safely run softirq on inline stack, as we are not deep * in the task stack here. */ +kmsan_softirq_enter ();__do_softirq (); +kmsan_softirq_exit ();local_irq_enable ();cond_resched ();return ;
lib/ioremap.c +5 -0
@@ -6,6 +6,7 @@* * (C) Copyright 1995 1996 Linus Torvalds */ +#include <linux/kmsan.h> #include <linux/vmalloc.h> #include <linux/mm.h> #include <linux/sched.h>
@@ -214,6 +215,8 @@ int ioremap_page_range(unsigned long addr,unsigned long start;unsigned long next;int err; +unsigned long old_addr = addr; +phys_addr_t old_phys_addr = phys_addr;might_sleep ();BUG_ON (addr >= end);
@@ -228,6 +231,8 @@ int ioremap_page_range(unsigned long addr, }while (pgd++, phys_addr += (next - addr), addr = next, addr != end);flush_cache_vmap (start, end); +if (!err) +kmsan_ioremap_page_range (old_addr, end, old_phys_addr, prot);return err; }
mm/compaction.c +9 -0
@@ -84,6 +84,15 @@ static void split_map_pages(struct list_head *list)for (i =0 ; i < nr_pages; i++) {list_add (&page->lru, &tmp_list); +#ifdef CONFIG_KMSAN +/* +* TODO(glider): we may lose the metadata when copying +* something to these pages. Need to allocate shadow +* and origin pages here instead. +*/ + page->shadow =NULL ; + page->origin =NULL ; +#endif page++; } }
mm/gup.c +3 -0
@@ -4,6 +4,7 @@#include <linux/err.h> #include <linux/spinlock.h> +#include <linux/kmsan.h> #include <linux/mm.h> #include <linux/memremap.h> #include <linux/pagemap.h>
@@ -2347,6 +2348,7 @@ int __get_user_pages_fast(unsigned long start, int nr_pages, int write, gup_fast_permitted(start, end)) {local_irq_save (flags);gup_pgd_range (start, end, write ?FOLL_WRITE :0 , pages, &nr); +kmsan_gup_pgd_range (pages, nr);local_irq_restore (flags); }
@@ -2416,6 +2418,7 @@ int get_user_pages_fast(unsigned long start, int nr_pages, gup_fast_permitted(start, end)) {local_irq_disable ();gup_pgd_range (addr, end, gup_flags, pages, &nr); +kmsan_gup_pgd_range (pages, nr);local_irq_enable (); ret = nr; }
mm/memory.c +2 -0
@@ -51,6 +51,7 @@#include <linux/highmem.h> #include <linux/pagemap.h> #include <linux/memremap.h> +#include <linux/kmsan.h> #include <linux/ksm.h> #include <linux/rmap.h> #include <linux/export.h>
@@ -2328,6 +2329,7 @@ static vm_fault_t wp_page_copy(struct vm_fault *vmf)if (!new_page) goto oom;cow_user_page (new_page, old_page, vmf->address, vma); +kmsan_copy_page_meta (new_page, old_page); }if (mem_cgroup_try_charge_delay (new_page, mm,GFP_KERNEL , &memcg, false))
mm/page_alloc.c +16 -0
@@ -26,6 +26,8 @@#include <linux/compiler.h> #include <linux/kernel.h> #include <linux/kasan.h> +#include <linux/kmsan.h> +#include <linux/kmsan-checks.h> #include <linux/module.h> #include <linux/suspend.h> #include <linux/pagevec.h>
@@ -1133,6 +1135,7 @@ static __always_inline bool free_pages_prepare(struct page *page,VM_BUG_ON_PAGE (PageTail (page), page);trace_mm_page_free (page, order); +kmsan_free_page (page, order);/* * Check tail pages before head page information is cleared to
@@ -3121,6 +3124,7 @@ void split_page(struct page *page, unsigned int order)VM_BUG_ON_PAGE (PageCompound (page), page);VM_BUG_ON_PAGE (!page_count (page), page); +kmsan_split_page (page, order);for (i =1 ; i < (1 << order); i++)set_page_refcounted (page + i);split_page_owner (page, order);
@@ -3253,6 +3257,13 @@ static struct page *rmqueue_pcplist(struct zone *preferred_zone,/* * Allocate a page from the given zone. Use pcplists for order-0 allocations. */ +/* +* TODO(glider): rmqueue() may call __msan_poison_alloca() through a call to +* set_pfnblock_flags_mask(). If __msan_poison_alloca() attempts to allocate +* pages for the stack depot, it may call rmqueue() again, which will result +* in a deadlock. +*/ +__no_sanitize_memory static inline struct page *rmqueue(struct zone *preferred_zone, struct zone *zone, unsigned int order,
@@ -4779,6 +4790,11 @@ __alloc_pages_nodemask(gfp_t gfp_mask, unsigned int order, int preferred_nid,trace_mm_page_alloc (page, order, alloc_mask, ac.migratetype); +if (page) +if (kmsan_alloc_page (page, order, gfp_mask)) { +__free_pages (page, order); + page =NULL ; + }return page; }EXPORT_SYMBOL (__alloc_pages_nodemask);
mm/vmalloc.c +21 -2
@@ -29,6 +29,7 @@#include <linux/rcupdate.h> #include <linux/pfn.h> #include <linux/kmemleak.h> +#include <linux/kmsan.h> #include <linux/atomic.h> #include <linux/compiler.h> #include <linux/llist.h>
@@ -119,7 +120,8 @@ static void vunmap_p4d_range(pgd_t *pgd, unsigned long addr, unsigned long end) }while (p4d++, addr = next, addr != end); } -static void vunmap_page_range (unsigned long addr,unsigned long end) +/* Exported for KMSAN, visible in mm/kmsan/kmsan.h only. */ +void __vunmap_page_range (unsigned long addr,unsigned long end) {pgd_t *pgd;unsigned long next;
@@ -133,6 +135,12 @@ static void vunmap_page_range(unsigned long addr, unsigned long end)vunmap_p4d_range (pgd, addr, next); }while (pgd++, addr = next, addr != end); } +EXPORT_SYMBOL (__vunmap_page_range); +static void vunmap_page_range (unsigned long addr,unsigned long end) +{ +kmsan_vunmap_page_range (addr, end); +__vunmap_page_range (addr, end); +}static int vmap_pte_range (pmd_t *pmd,unsigned long addr,unsigned long end,pgprot_t prot,struct page **pages,int *nr)
@@ -216,8 +224,11 @@ static int vmap_p4d_range(pgd_t *pgd, unsigned long addr,* will have pfns corresponding to the "pages" array. * * Ie. pte at addr+N*PAGE_SIZE shall point to pfn corresponding to pages[N] +* +* This function is exported for use in KMSAN, but is only declared in KMSAN +* headers. */ -static int vmap_page_range_noflush (unsigned long start,unsigned long end, +int __vmap_page_range_noflush (unsigned long start,unsigned long end,pgprot_t prot,struct page **pages) {pgd_t *pgd;
@@ -237,6 +248,14 @@ static int vmap_page_range_noflush(unsigned long start, unsigned long end,return nr; } +EXPORT_SYMBOL (__vmap_page_range_noflush); + +static int vmap_page_range_noflush (unsigned long start,unsigned long end, +pgprot_t prot,struct page **pages) +{ +kmsan_vmap_page_range_noflush (start, end, prot, pages); +return __vmap_page_range_noflush (start, end, prot, pages); +}static int vmap_page_range (unsigned long start,unsigned long end,pgprot_t prot,struct page **pages)
net/sched/sch_generic.c +2 -0
@@ -11,6 +11,7 @@#include <linux/module.h> #include <linux/types.h> #include <linux/kernel.h> +#include <linux/kmsan-checks.h> #include <linux/sched.h> #include <linux/string.h> #include <linux/errno.h>
@@ -659,6 +660,7 @@ static struct sk_buff *pfifo_fast_dequeue(struct Qdisc *qdisc) }else { qdisc->empty = true; } +kmsan_check_skb (skb);return skb; }
Signature · 1 line
2.23.0.866.gb869b98d4c-goog
Re: [PATCH RFC v1 19/26] kmsan: call KMSAN hooks where needed
Quoted 4 lines · On Fri, 2019-10-18 at 11:42 +0200, [email protected] wrote:
Insert KMSAN hooks that check for potential memory errors and/or make necessary bookkeeping changes:
- allocate/split/deallocate metadata pages in alloc_pages()/split_page()/free_page();
This also seems unnecessary where there are options like page_poison and debug_pagealloc should be able detect uninitialized memory access in the page allocator as well. Even KASAN has some of the functionality.
Quoted 517 lines
- clear page shadow and origins in
clear_page(),copy_user_highpage();
- copy page metadata incopy_highpage(),wp_page_copy();
- handle vmap()/vunmap()/iounmap();
- handle task creation and deletion;
- handlevprintk();
- call softirq entry/exit hooks inkernel/softirq.c;
- check/initialize memory sent to/read from USB, I2C, and networkSigned-off-by Alexander Potapenko <[email protected]>To: Alexander Potapenko <[email protected]>
Cc: Andrew Morton <[email protected]>
Cc: Greg Kroah-Hartman <[email protected]>
Cc: Eric Dumazet <[email protected]>
Cc: Wolfram Sang <[email protected]>
Cc: Petr Mladek <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]
---Change-Id: I1250a928d9263bf71fdaa067a070bdee686ef47b
--- arch/x86/include/asm/page_64.h | 13 +++++++++++++ arch/x86/mm/ioremap.c | 3 +++ drivers/i2c/i2c-core-base.c | 2 ++ drivers/usb/core/urb.c | 2 ++ include/linux/highmem.h | 4 ++++ kernel/exit.c | 2 ++ kernel/fork.c | 2 ++ kernel/kthread.c | 2 ++ kernel/printk/printk.c | 8 +++++++- kernel/softirq.c | 5 +++++ lib/ioremap.c | 5 +++++ mm/compaction.c | 9 +++++++++ mm/gup.c | 3 +++ mm/memory.c | 2 ++ mm/page_alloc.c | 16 ++++++++++++++++ mm/vmalloc.c | 23 +++++++++++++++++++++-- net/sched/sch_generic.c | 2 ++ 17 files changed, 100 insertions(+), 3 deletions(-)arch/x86/include/asm/page_64.h +13
-0@@ -44,14 +44,27 @@ void clear_page_orig(void *page);void clear_page_rep (void *page);void clear_page_erms (void *page); +/* This is an assembly header, avoid including too much of kmsan.h */ +#ifdef CONFIG_KMSAN +void kmsan_clear_page (void *page_addr); +#endif +__no_sanitize_memory static inline void clear_page(void *page) { +#ifdef CONFIG_KMSAN +/* alternative_call_2() changes |page|. */ +void *page_copy = page; +#endif alternative_call_2 (clear_page_orig, clear_page_rep,X86_FEATURE_REP_GOOD , clear_page_erms,X86_FEATURE_ERMS ,"=D" (page), "0" (page) : "cc", "memory", "rax", "rcx"); +#ifdef CONFIG_KMSAN +/* Clear KMSAN shadow for the pages that have it. */ +kmsan_clear_page (page_copy); +#endif }void copy_page (void *to,void *from);arch/x86/mm/ioremap.c +3
-0@@ -7,6 +7,7 @@* (C) Copyright 1995 1996 Linus Torvalds */ +#include <linux/kmsan.h> #include <linux/memblock.h> #include <linux/init.h> #include <linux/io.h> @@ -451,6 +452,8 @@ void iounmap(volatile void __iomem *addr)return ; } +kmsan_iounmap_page_range ((unsigned long )addr, + (unsigned long )addr +get_vm_area_size (p));free_memtype (p->phys_addr, p->phys_addr +get_vm_area_size (p));/* Finally remove it */ drivers/i2c/i2c-core-base.c +2
-0@@ -27,6 +27,7 @@#include <linux/irqflags.h> #include <linux/jump_label.h> #include <linux/kernel.h> +#include <linux/kmsan.h> #include <linux/module.h> #include <linux/mutex.h> #include <linux/of_device.h> @@ -1975,6 +1976,7 @@ int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)trace_i2c_write (adap, &msgs[i], i); } +kmsan_handle_i2c_transfer (msgs, num);/* Retry automatically on arbitration loss */ orig_jiffies = jiffies;for (ret =0 , try =0 ; try <= adap->retries; try++) {drivers/usb/core/urb.c +2
-0@@ -8,6 +8,7 @@#include <linux/bitops.h> #include <linux/slab.h> #include <linux/log2.h> +#include <linux/kmsan-checks.h> #include <linux/usb.h> #include <linux/wait.h> #include <linux/usb/hcd.h> @@ -401,6 +402,7 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)URB_SETUP_MAP_SINGLE |URB_SETUP_MAP_LOCAL | URB_DMA_SG_COMBINED); urb->transfer_flags |= (is_out ?URB_DIR_OUT :URB_DIR_IN ); +kmsan_handle_urb (urb, is_out);if (xfertype !=USB_ENDPOINT_XFER_CONTROL && dev->state <USB_STATE_CONFIGURED )include/linux/highmem.h +4
-0@@ -5,6 +5,7 @@#include <linux/fs.h> #include <linux/kernel.h> #include <linux/bug.h> +#include <linux/kmsan.h> #include <linux/mm.h> #include <linux/uaccess.h> #include <linux/hardirq.h> @@ -255,6 +256,8 @@ static inline void copy_user_highpage(struct page *to, struct page *from, vfrom =kmap_atomic (from); vto =kmap_atomic (to);copy_user_page (vto, vfrom, vaddr, to); +/* User pages don't have shadow, just clear the destination. */ +kmsan_clear_page (page_address (to));kunmap_atomic (vto);kunmap_atomic (vfrom); }@@ -270,6 +273,7 @@ static inline void copy_highpage(struct page *to, struct page *from) vfrom =kmap_atomic (from); vto =kmap_atomic (to);copy_page (vto, vfrom); +kmsan_copy_page_meta (to, from);kunmap_atomic (vto);kunmap_atomic (vfrom); }kernel/exit.c +2
-0@@ -60,6 +60,7 @@#include <linux/writeback.h> #include <linux/shm.h> #include <linux/kcov.h> +#include <linux/kmsan.h> #include <linux/random.h> #include <linux/rcuwait.h> #include <linux/compat.h> @@ -719,6 +720,7 @@ void __noreturn do_exit(long code)profile_task_exit (tsk);kcov_task_exit (tsk); +kmsan_task_exit (tsk);WARN_ON (blk_needs_flush_plug (tsk));kernel/fork.c +2
-0@@ -37,6 +37,7 @@#include <linux/fdtable.h> #include <linux/iocontext.h> #include <linux/key.h> +#include <linux/kmsan.h> #include <linux/binfmts.h> #include <linux/mman.h> #include <linux/mmu_notifier.h> @@ -931,6 +932,7 @@ static struct task_struct *dup_task_struct(struct task_struct *orig, int node)account_kernel_stack (tsk,1 );kcov_task_init (tsk); +kmsan_task_create (tsk);#ifdef CONFIG_FAULT_INJECTION tsk->fail_nth =0 ;kernel/kthread.c +2
-0@@ -17,6 +17,7 @@#include <linux/unistd.h> #include <linux/file.h> #include <linux/export.h> +#include <linux/kmsan.h> #include <linux/mutex.h> #include <linux/slab.h> #include <linux/freezer.h> @@ -350,6 +351,7 @@ struct task_struct *__kthread_create_on_node(int (*threadfn)(void *data),set_cpus_allowed_ptr (task, cpu_all_mask); }kfree (create); +kmsan_task_create (task);return task; }kernel/printk/printk.c +7
-1@@ -1914,7 +1914,12 @@ int vprintk_store(int facility, int level,* The printf needs to come first; we need the syslog * prefix which might be passed-in as a parameter. */ - text_len =vscnprintf (text,sizeof (textbuf), fmt, args); +/* +* We've checked the printk arguments in vprintk_emit() already. +* Initialize |text_len| to prevent the errors from spreading. +*/ + text_len =KMSAN_INIT_VALUE (vscnprintf (text,sizeof (textbuf), fmt, + args));/* mark and strip a trailing newline */ if (text_len && text[text_len-1 ] =='\n' ) {@@ -1972,6 +1977,7 @@ asmlinkage int vprintk_emit(int facility, int level,boot_delay_msec (level);printk_delay (); +kmsan_handle_vprintk (&fmt, args);/* This stops the holder of console_sem just where we want him */ logbuf_lock_irqsave (flags); curr_log_seq = log_next_seq;kernel/softirq.c +5
-0@@ -11,6 +11,7 @@#include <linux/export.h> #include <linux/kernel_stat.h> +#include <linux/kmsan.h> #include <linux/interrupt.h> #include <linux/init.h> #include <linux/mm.h> @@ -370,7 +371,9 @@ static inline void invoke_softirq(void)* it is the irq stack, because it should be near empty * at this stage. */ +kmsan_softirq_enter ();__do_softirq (); +kmsan_softirq_exit ();#else /* * Otherwise, irq_exit() is called on the task stack that can @@ -600,7 +603,9 @@ static void run_ksoftirqd(unsigned int cpu)* We can safely run softirq on inline stack, as we are not deep * in the task stack here. */ +kmsan_softirq_enter ();__do_softirq (); +kmsan_softirq_exit ();local_irq_enable ();cond_resched ();return ;lib/ioremap.c +5
-0@@ -6,6 +6,7 @@* * (C) Copyright 1995 1996 Linus Torvalds */ +#include <linux/kmsan.h> #include <linux/vmalloc.h> #include <linux/mm.h> #include <linux/sched.h> @@ -214,6 +215,8 @@ int ioremap_page_range(unsigned long addr,unsigned long start;unsigned long next;int err; +unsigned long old_addr = addr; +phys_addr_t old_phys_addr = phys_addr;might_sleep ();BUG_ON (addr >= end);@@ -228,6 +231,8 @@ int ioremap_page_range(unsigned long addr, }while (pgd++, phys_addr += (next - addr), addr = next, addr != end);flush_cache_vmap (start, end); +if (!err) +kmsan_ioremap_page_range (old_addr, end, old_phys_addr, prot);return err; }mm/compaction.c +9
-0@@ -84,6 +84,15 @@ static void split_map_pages(struct list_head *list)for (i =0 ; i < nr_pages; i++) {list_add (&page->lru, &tmp_list); +#ifdef CONFIG_KMSAN +/* +* TODO(glider): we may lose the metadata when copying +* something to these pages. Need to allocate shadow +* and origin pages here instead. +*/ + page->shadow =NULL ; + page->origin =NULL ; +#endif page++; } }mm/gup.c +3
-0@@ -4,6 +4,7 @@#include <linux/err.h> #include <linux/spinlock.h> +#include <linux/kmsan.h> #include <linux/mm.h> #include <linux/memremap.h> #include <linux/pagemap.h> @@ -2347,6 +2348,7 @@ int __get_user_pages_fast(unsigned long start, int nr_pages, int write, gup_fast_permitted(start, end)) {local_irq_save (flags);gup_pgd_range (start, end, write ?FOLL_WRITE :0 , pages, &nr); +kmsan_gup_pgd_range (pages, nr);local_irq_restore (flags); }@@ -2416,6 +2418,7 @@ int get_user_pages_fast(unsigned long start, int nr_pages, gup_fast_permitted(start, end)) {local_irq_disable ();gup_pgd_range (addr, end, gup_flags, pages, &nr); +kmsan_gup_pgd_range (pages, nr);local_irq_enable (); ret = nr; }mm/memory.c +2
-0@@ -51,6 +51,7 @@#include <linux/highmem.h> #include <linux/pagemap.h> #include <linux/memremap.h> +#include <linux/kmsan.h> #include <linux/ksm.h> #include <linux/rmap.h> #include <linux/export.h> @@ -2328,6 +2329,7 @@ static vm_fault_t wp_page_copy(struct vm_fault *vmf)if (!new_page) goto oom;cow_user_page (new_page, old_page, vmf->address, vma); +kmsan_copy_page_meta (new_page, old_page); }if (mem_cgroup_try_charge_delay (new_page, mm,GFP_KERNEL , &memcg, false))mm/page_alloc.c +16
-0@@ -26,6 +26,8 @@#include <linux/compiler.h> #include <linux/kernel.h> #include <linux/kasan.h> +#include <linux/kmsan.h> +#include <linux/kmsan-checks.h> #include <linux/module.h> #include <linux/suspend.h> #include <linux/pagevec.h> @@ -1133,6 +1135,7 @@ static __always_inline bool free_pages_prepare(struct page *page,VM_BUG_ON_PAGE (PageTail (page), page);trace_mm_page_free (page, order); +kmsan_free_page (page, order);/* * Check tail pages before head page information is cleared to @@ -3121,6 +3124,7 @@ void split_page(struct page *page, unsigned int order)VM_BUG_ON_PAGE (PageCompound (page), page);VM_BUG_ON_PAGE (!page_count (page), page); +kmsan_split_page (page, order);for (i =1 ; i < (1 << order); i++)set_page_refcounted (page + i);split_page_owner (page, order);@@ -3253,6 +3257,13 @@ static struct page *rmqueue_pcplist(struct zone *preferred_zone,/* * Allocate a page from the given zone. Use pcplists for order-0 allocations. */ +/* +* TODO(glider): rmqueue() may call __msan_poison_alloca() through a call to +* set_pfnblock_flags_mask(). If __msan_poison_alloca() attempts to allocate +* pages for the stack depot, it may call rmqueue() again, which will result +* in a deadlock. +*/ +__no_sanitize_memory static inline struct page *rmqueue(struct zone *preferred_zone, struct zone *zone, unsigned int order,@@ -4779,6 +4790,11 @@ __alloc_pages_nodemask(gfp_t gfp_mask, unsigned int order, int preferred_nid,trace_mm_page_alloc (page, order, alloc_mask, ac.migratetype); +if (page) +if (kmsan_alloc_page (page, order, gfp_mask)) { +__free_pages (page, order); + page =NULL ; + }return page; }EXPORT_SYMBOL (__alloc_pages_nodemask);mm/vmalloc.c +21
-2@@ -29,6 +29,7 @@#include <linux/rcupdate.h> #include <linux/pfn.h> #include <linux/kmemleak.h> +#include <linux/kmsan.h> #include <linux/atomic.h> #include <linux/compiler.h> #include <linux/llist.h> @@ -119,7 +120,8 @@ static void vunmap_p4d_range(pgd_t *pgd, unsigned long addr, unsigned long end) }while (p4d++, addr = next, addr != end); } -static void vunmap_page_range (unsigned long addr,unsigned long end) +/* Exported for KMSAN, visible in mm/kmsan/kmsan.h only. */ +void __vunmap_page_range (unsigned long addr,unsigned long end) {pgd_t *pgd;unsigned long next;@@ -133,6 +135,12 @@ static void vunmap_page_range(unsigned long addr, unsigned long end)vunmap_p4d_range (pgd, addr, next); }while (pgd++, addr = next, addr != end); } +EXPORT_SYMBOL (__vunmap_page_range); +static void vunmap_page_range (unsigned long addr,unsigned long end) +{ +kmsan_vunmap_page_range (addr, end); +__vunmap_page_range (addr, end); +}static int vmap_pte_range (pmd_t *pmd,unsigned long addr,unsigned long end,pgprot_t prot,struct page **pages,int *nr)@@ -216,8 +224,11 @@ static int vmap_p4d_range(pgd_t *pgd, unsigned long addr,* will have pfns corresponding to the "pages" array. * * Ie. pte at addr+N*PAGE_SIZE shall point to pfn corresponding to pages[N] +* +* This function is exported for use in KMSAN, but is only declared in KMSAN +* headers. */ -static int vmap_page_range_noflush (unsigned long start,unsigned long end, +int __vmap_page_range_noflush (unsigned long start,unsigned long end,pgprot_t prot,struct page **pages) {pgd_t *pgd;@@ -237,6 +248,14 @@ static int vmap_page_range_noflush(unsigned long start, unsigned long end,return nr; } +EXPORT_SYMBOL (__vmap_page_range_noflush); + +static int vmap_page_range_noflush (unsigned long start,unsigned long end, +pgprot_t prot,struct page **pages) +{ +kmsan_vmap_page_range_noflush (start, end, prot, pages); +return __vmap_page_range_noflush (start, end, prot, pages); +}static int vmap_page_range (unsigned long start,unsigned long end,pgprot_t prot,struct page **pages)net/sched/sch_generic.c +2
-0@@ -11,6 +11,7 @@#include <linux/module.h> #include <linux/types.h> #include <linux/kernel.h> +#include <linux/kmsan-checks.h> #include <linux/sched.h> #include <linux/string.h> #include <linux/errno.h> @@ -659,6 +660,7 @@ static struct sk_buff *pfifo_fast_dequeue(struct Qdisc *qdisc) }else { qdisc->empty = true; } +kmsan_check_skb (skb);return skb; }
Alexander Potapenko
maintainerRe: [PATCH RFC v1 19/26] kmsan: call KMSAN hooks where needed
Quoted 10 lines · Qian Cai
Quoted 4 lines · On Fri, 2019-10-18 at 11:42 +0200, [email protected] wrote:
Insert KMSAN hooks that check for potential memory errors and/or make necessary bookkeeping changes:
- allocate/split/deallocate metadata pages in alloc_pages()/split_page()/free_page();This also seems unnecessary where there are options like
page_poisonanddebug_pageallocshould be able detect uninitialized memory access in the page allocator as well. Even KASAN has some of the functionality.
I still believe there's some misunderstanding between us because I didn't CC you on KMSAN runtime or the documentation.
I'll do this and will be happy to answer questions if you still have them. Without these hooks, KMSAN will be virtually unusable, because it won't know about heap allocations.
When a buffer is allocated on heap, KMSAN updates the metadata for that region of memory, writing 0xFF (meaning uninitialized) to every shadow byte and a stack ID to every 4 region bytes.
Note that shadow and origin bytes are stored separately and are generally incompatible with what debug pagealloc/page poison do.
Quoted 517 lines
Quoted 517 lines
- clear page shadow and origins in
clear_page(),copy_user_highpage();
- copy page metadata incopy_highpage(),wp_page_copy();
- handle vmap()/vunmap()/iounmap();
- handle task creation and deletion;
- handlevprintk();
- call softirq entry/exit hooks inkernel/softirq.c;
- check/initialize memory sent to/read from USB, I2C, and networkSigned-off-by Alexander Potapenko <[email protected]>To: Alexander Potapenko <[email protected]>
Cc: Andrew Morton <[email protected]>
Cc: Greg Kroah-Hartman <[email protected]>
Cc: Eric Dumazet <[email protected]>
Cc: Wolfram Sang <[email protected]>
Cc: Petr Mladek <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]
---Change-Id: I1250a928d9263bf71fdaa067a070bdee686ef47b
--- arch/x86/include/asm/page_64.h | 13 +++++++++++++ arch/x86/mm/ioremap.c | 3 +++ drivers/i2c/i2c-core-base.c | 2 ++ drivers/usb/core/urb.c | 2 ++ include/linux/highmem.h | 4 ++++ kernel/exit.c | 2 ++ kernel/fork.c | 2 ++ kernel/kthread.c | 2 ++ kernel/printk/printk.c | 8 +++++++- kernel/softirq.c | 5 +++++ lib/ioremap.c | 5 +++++ mm/compaction.c | 9 +++++++++ mm/gup.c | 3 +++ mm/memory.c | 2 ++ mm/page_alloc.c | 16 ++++++++++++++++ mm/vmalloc.c | 23 +++++++++++++++++++++-- net/sched/sch_generic.c | 2 ++ 17 files changed, 100 insertions(+), 3 deletions(-)arch/x86/include/asm/page_64.h +13
-0@@ -44,14 +44,27 @@ void clear_page_orig(void *page);void clear_page_rep (void *page);void clear_page_erms (void *page); +/* This is an assembly header, avoid including too much of kmsan.h */ +#ifdef CONFIG_KMSAN +void kmsan_clear_page (void *page_addr); +#endif +__no_sanitize_memory static inline void clear_page(void *page) { +#ifdef CONFIG_KMSAN +/* alternative_call_2() changes |page|. */ +void *page_copy = page; +#endif alternative_call_2 (clear_page_orig, clear_page_rep,X86_FEATURE_REP_GOOD , clear_page_erms,X86_FEATURE_ERMS ,"=D" (page), "0" (page) : "cc", "memory", "rax", "rcx"); +#ifdef CONFIG_KMSAN +/* Clear KMSAN shadow for the pages that have it. */ +kmsan_clear_page (page_copy); +#endif }void copy_page (void *to,void *from);arch/x86/mm/ioremap.c +3
-0@@ -7,6 +7,7 @@* (C) Copyright 1995 1996 Linus Torvalds */ +#include <linux/kmsan.h> #include <linux/memblock.h> #include <linux/init.h> #include <linux/io.h> @@ -451,6 +452,8 @@ void iounmap(volatile void __iomem *addr)return ; } +kmsan_iounmap_page_range ((unsigned long )addr, + (unsigned long )addr +get_vm_area_size (p));free_memtype (p->phys_addr, p->phys_addr +get_vm_area_size (p));/* Finally remove it */ drivers/i2c/i2c-core-base.c +2
-0@@ -27,6 +27,7 @@#include <linux/irqflags.h> #include <linux/jump_label.h> #include <linux/kernel.h> +#include <linux/kmsan.h> #include <linux/module.h> #include <linux/mutex.h> #include <linux/of_device.h> @@ -1975,6 +1976,7 @@ int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)trace_i2c_write (adap, &msgs[i], i); } +kmsan_handle_i2c_transfer (msgs, num);/* Retry automatically on arbitration loss */ orig_jiffies = jiffies;for (ret =0 , try =0 ; try <= adap->retries; try++) {drivers/usb/core/urb.c +2
-0@@ -8,6 +8,7 @@#include <linux/bitops.h> #include <linux/slab.h> #include <linux/log2.h> +#include <linux/kmsan-checks.h> #include <linux/usb.h> #include <linux/wait.h> #include <linux/usb/hcd.h> @@ -401,6 +402,7 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)URB_SETUP_MAP_SINGLE |URB_SETUP_MAP_LOCAL | URB_DMA_SG_COMBINED); urb->transfer_flags |= (is_out ?URB_DIR_OUT :URB_DIR_IN ); +kmsan_handle_urb (urb, is_out);if (xfertype !=USB_ENDPOINT_XFER_CONTROL && dev->state <USB_STATE_CONFIGURED )include/linux/highmem.h +4
-0@@ -5,6 +5,7 @@#include <linux/fs.h> #include <linux/kernel.h> #include <linux/bug.h> +#include <linux/kmsan.h> #include <linux/mm.h> #include <linux/uaccess.h> #include <linux/hardirq.h> @@ -255,6 +256,8 @@ static inline void copy_user_highpage(struct page *to, struct page *from, vfrom =kmap_atomic (from); vto =kmap_atomic (to);copy_user_page (vto, vfrom, vaddr, to); +/* User pages don't have shadow, just clear the destination. */ +kmsan_clear_page (page_address (to));kunmap_atomic (vto);kunmap_atomic (vfrom); }@@ -270,6 +273,7 @@ static inline void copy_highpage(struct page *to, struct page *from) vfrom =kmap_atomic (from); vto =kmap_atomic (to);copy_page (vto, vfrom); +kmsan_copy_page_meta (to, from);kunmap_atomic (vto);kunmap_atomic (vfrom); }kernel/exit.c +2
-0@@ -60,6 +60,7 @@#include <linux/writeback.h> #include <linux/shm.h> #include <linux/kcov.h> +#include <linux/kmsan.h> #include <linux/random.h> #include <linux/rcuwait.h> #include <linux/compat.h> @@ -719,6 +720,7 @@ void __noreturn do_exit(long code)profile_task_exit (tsk);kcov_task_exit (tsk); +kmsan_task_exit (tsk);WARN_ON (blk_needs_flush_plug (tsk));kernel/fork.c +2
-0@@ -37,6 +37,7 @@#include <linux/fdtable.h> #include <linux/iocontext.h> #include <linux/key.h> +#include <linux/kmsan.h> #include <linux/binfmts.h> #include <linux/mman.h> #include <linux/mmu_notifier.h> @@ -931,6 +932,7 @@ static struct task_struct *dup_task_struct(struct task_struct *orig, int node)account_kernel_stack (tsk,1 );kcov_task_init (tsk); +kmsan_task_create (tsk);#ifdef CONFIG_FAULT_INJECTION tsk->fail_nth =0 ;kernel/kthread.c +2
-0@@ -17,6 +17,7 @@#include <linux/unistd.h> #include <linux/file.h> #include <linux/export.h> +#include <linux/kmsan.h> #include <linux/mutex.h> #include <linux/slab.h> #include <linux/freezer.h> @@ -350,6 +351,7 @@ struct task_struct *__kthread_create_on_node(int (*threadfn)(void *data),set_cpus_allowed_ptr (task, cpu_all_mask); }kfree (create); +kmsan_task_create (task);return task; }kernel/printk/printk.c +7
-1@@ -1914,7 +1914,12 @@ int vprintk_store(int facility, int level,* The printf needs to come first; we need the syslog * prefix which might be passed-in as a parameter. */ - text_len =vscnprintf (text,sizeof (textbuf), fmt, args); +/* +* We've checked the printk arguments in vprintk_emit() already. +* Initialize |text_len| to prevent the errors from spreading. +*/ + text_len =KMSAN_INIT_VALUE (vscnprintf (text,sizeof (textbuf), fmt, + args));/* mark and strip a trailing newline */ if (text_len && text[text_len-1 ] =='\n' ) {@@ -1972,6 +1977,7 @@ asmlinkage int vprintk_emit(int facility, int level,boot_delay_msec (level);printk_delay (); +kmsan_handle_vprintk (&fmt, args);/* This stops the holder of console_sem just where we want him */ logbuf_lock_irqsave (flags); curr_log_seq = log_next_seq;kernel/softirq.c +5
-0@@ -11,6 +11,7 @@#include <linux/export.h> #include <linux/kernel_stat.h> +#include <linux/kmsan.h> #include <linux/interrupt.h> #include <linux/init.h> #include <linux/mm.h> @@ -370,7 +371,9 @@ static inline void invoke_softirq(void)* it is the irq stack, because it should be near empty * at this stage. */ +kmsan_softirq_enter ();__do_softirq (); +kmsan_softirq_exit ();#else /* * Otherwise, irq_exit() is called on the task stack that can @@ -600,7 +603,9 @@ static void run_ksoftirqd(unsigned int cpu)* We can safely run softirq on inline stack, as we are not deep * in the task stack here. */ +kmsan_softirq_enter ();__do_softirq (); +kmsan_softirq_exit ();local_irq_enable ();cond_resched ();return ;lib/ioremap.c +5
-0@@ -6,6 +6,7 @@* * (C) Copyright 1995 1996 Linus Torvalds */ +#include <linux/kmsan.h> #include <linux/vmalloc.h> #include <linux/mm.h> #include <linux/sched.h> @@ -214,6 +215,8 @@ int ioremap_page_range(unsigned long addr,unsigned long start;unsigned long next;int err; +unsigned long old_addr = addr; +phys_addr_t old_phys_addr = phys_addr;might_sleep ();BUG_ON (addr >= end);@@ -228,6 +231,8 @@ int ioremap_page_range(unsigned long addr, }while (pgd++, phys_addr += (next - addr), addr = next, addr != end);flush_cache_vmap (start, end); +if (!err) +kmsan_ioremap_page_range (old_addr, end, old_phys_addr, prot);return err; }mm/compaction.c +9
-0@@ -84,6 +84,15 @@ static void split_map_pages(struct list_head *list)for (i =0 ; i < nr_pages; i++) {list_add (&page->lru, &tmp_list); +#ifdef CONFIG_KMSAN +/* +* TODO(glider): we may lose the metadata when copying +* something to these pages. Need to allocate shadow +* and origin pages here instead. +*/ + page->shadow =NULL ; + page->origin =NULL ; +#endif page++; } }mm/gup.c +3
-0@@ -4,6 +4,7 @@#include <linux/err.h> #include <linux/spinlock.h> +#include <linux/kmsan.h> #include <linux/mm.h> #include <linux/memremap.h> #include <linux/pagemap.h> @@ -2347,6 +2348,7 @@ int __get_user_pages_fast(unsigned long start, int nr_pages, int write, gup_fast_permitted(start, end)) {local_irq_save (flags);gup_pgd_range (start, end, write ?FOLL_WRITE :0 , pages, &nr); +kmsan_gup_pgd_range (pages, nr);local_irq_restore (flags); }@@ -2416,6 +2418,7 @@ int get_user_pages_fast(unsigned long start, int nr_pages, gup_fast_permitted(start, end)) {local_irq_disable ();gup_pgd_range (addr, end, gup_flags, pages, &nr); +kmsan_gup_pgd_range (pages, nr);local_irq_enable (); ret = nr; }mm/memory.c +2
-0@@ -51,6 +51,7 @@#include <linux/highmem.h> #include <linux/pagemap.h> #include <linux/memremap.h> +#include <linux/kmsan.h> #include <linux/ksm.h> #include <linux/rmap.h> #include <linux/export.h> @@ -2328,6 +2329,7 @@ static vm_fault_t wp_page_copy(struct vm_fault *vmf)if (!new_page) goto oom;cow_user_page (new_page, old_page, vmf->address, vma); +kmsan_copy_page_meta (new_page, old_page); }if (mem_cgroup_try_charge_delay (new_page, mm,GFP_KERNEL , &memcg, false))mm/page_alloc.c +16
-0@@ -26,6 +26,8 @@#include <linux/compiler.h> #include <linux/kernel.h> #include <linux/kasan.h> +#include <linux/kmsan.h> +#include <linux/kmsan-checks.h> #include <linux/module.h> #include <linux/suspend.h> #include <linux/pagevec.h> @@ -1133,6 +1135,7 @@ static __always_inline bool free_pages_prepare(struct page *page,VM_BUG_ON_PAGE (PageTail (page), page);trace_mm_page_free (page, order); +kmsan_free_page (page, order);/* * Check tail pages before head page information is cleared to @@ -3121,6 +3124,7 @@ void split_page(struct page *page, unsigned int order)VM_BUG_ON_PAGE (PageCompound (page), page);VM_BUG_ON_PAGE (!page_count (page), page); +kmsan_split_page (page, order);for (i =1 ; i < (1 << order); i++)set_page_refcounted (page + i);split_page_owner (page, order);@@ -3253,6 +3257,13 @@ static struct page *rmqueue_pcplist(struct zone *preferred_zone,/* * Allocate a page from the given zone. Use pcplists for order-0 allocations. */ +/* +* TODO(glider): rmqueue() may call __msan_poison_alloca() through a call to +* set_pfnblock_flags_mask(). If __msan_poison_alloca() attempts to allocate +* pages for the stack depot, it may call rmqueue() again, which will result +* in a deadlock. +*/ +__no_sanitize_memory static inline struct page *rmqueue(struct zone *preferred_zone, struct zone *zone, unsigned int order,@@ -4779,6 +4790,11 @@ __alloc_pages_nodemask(gfp_t gfp_mask, unsigned int order, int preferred_nid,trace_mm_page_alloc (page, order, alloc_mask, ac.migratetype); +if (page) +if (kmsan_alloc_page (page, order, gfp_mask)) { +__free_pages (page, order); + page =NULL ; + }return page; }EXPORT_SYMBOL (__alloc_pages_nodemask);mm/vmalloc.c +21
-2@@ -29,6 +29,7 @@#include <linux/rcupdate.h> #include <linux/pfn.h> #include <linux/kmemleak.h> +#include <linux/kmsan.h> #include <linux/atomic.h> #include <linux/compiler.h> #include <linux/llist.h> @@ -119,7 +120,8 @@ static void vunmap_p4d_range(pgd_t *pgd, unsigned long addr, unsigned long end) }while (p4d++, addr = next, addr != end); } -static void vunmap_page_range (unsigned long addr,unsigned long end) +/* Exported for KMSAN, visible in mm/kmsan/kmsan.h only. */ +void __vunmap_page_range (unsigned long addr,unsigned long end) {pgd_t *pgd;unsigned long next;@@ -133,6 +135,12 @@ static void vunmap_page_range(unsigned long addr, unsigned long end)vunmap_p4d_range (pgd, addr, next); }while (pgd++, addr = next, addr != end); } +EXPORT_SYMBOL (__vunmap_page_range); +static void vunmap_page_range (unsigned long addr,unsigned long end) +{ +kmsan_vunmap_page_range (addr, end); +__vunmap_page_range (addr, end); +}static int vmap_pte_range (pmd_t *pmd,unsigned long addr,unsigned long end,pgprot_t prot,struct page **pages,int *nr)@@ -216,8 +224,11 @@ static int vmap_p4d_range(pgd_t *pgd, unsigned long addr,* will have pfns corresponding to the "pages" array. * * Ie. pte at addr+N*PAGE_SIZE shall point to pfn corresponding to pages[N] +* +* This function is exported for use in KMSAN, but is only declared in KMSAN +* headers. */ -static int vmap_page_range_noflush (unsigned long start,unsigned long end, +int __vmap_page_range_noflush (unsigned long start,unsigned long end,pgprot_t prot,struct page **pages) {pgd_t *pgd;@@ -237,6 +248,14 @@ static int vmap_page_range_noflush(unsigned long start, unsigned long end,return nr; } +EXPORT_SYMBOL (__vmap_page_range_noflush); + +static int vmap_page_range_noflush (unsigned long start,unsigned long end, +pgprot_t prot,struct page **pages) +{ +kmsan_vmap_page_range_noflush (start, end, prot, pages); +return __vmap_page_range_noflush (start, end, prot, pages); +}static int vmap_page_range (unsigned long start,unsigned long end,pgprot_t prot,struct page **pages)net/sched/sch_generic.c +2
-0@@ -11,6 +11,7 @@#include <linux/module.h> #include <linux/types.h> #include <linux/kernel.h> +#include <linux/kmsan-checks.h> #include <linux/sched.h> #include <linux/string.h> #include <linux/errno.h> @@ -659,6 +660,7 @@ static struct sk_buff *pfifo_fast_dequeue(struct Qdisc *qdisc) }else { qdisc->empty = true; } +kmsan_check_skb (skb);return skb; }
Signature · 10 lines
Alexander Potapenko Software Engineer Google Germany GmbH Erika-Mann-Straße, 33 80636 München Geschäftsführer: Paul Manicle, Halimah DeLaine Prado Registergericht und -nummer: Hamburg, HRB 86891 Sitz der Gesellschaft: Hamburg
Re: [PATCH RFC v1 19/26] kmsan: call KMSAN hooks where needed
Quoted 18 lines · Alexander Potapenko
Quoted 10 lines · Qian Cai
Quoted 4 lines · On Fri, 2019-10-18 at 11:42 +0200, [email protected] wrote:
Insert KMSAN hooks that check for potential memory errors and/or make necessary bookkeeping changes:
- allocate/split/deallocate metadata pages in alloc_pages()/split_page()/free_page();This also seems unnecessary where there are options like
page_poisonanddebug_pageallocshould be able detect uninitialized memory access in the page allocator as well. Even KASAN has some of the functionality.I still believe there's some misunderstanding between us because I didn't CC you on KMSAN runtime or the documentation.
I'll do this and will be happy to answer questions if you still have them. Without these hooks, KMSAN will be virtually unusable, because it won't know about heap allocations.
Quoted 5 lines
When a buffer is allocated on heap, KMSAN updates the metadata for that region of memory, writing
0xFF(meaning uninitialized) to every shadow byte and a stack ID to every 4 region bytes.
Note that shadow and origin bytes are stored separately and are generally incompatible with what debug pagealloc/page poison do.
That makes more sense to me now. I was a bit worry about some of those options could stub one's toe with KMSAN, but I had shared with you the MM debug config in another email before, so you could try it out, so I don't need to spam you later once this hit the linux-next.
Petr Mladek
maintainerRe: [PATCH RFC v1 19/26] kmsan: call KMSAN hooks where needed
In reply to [email protected]Quoted 9 lines · On Fri 2019-10-18 11:42:57, [email protected] wrote:
Insert KMSAN hooks that check for potential memory errors and/or make necessary bookkeeping changes:
- allocate/split/deallocate metadata pages in alloc_pages()/split_page()/free_page();
- clear page shadow and origins inclear_page(),copy_user_highpage();
- copy page metadata incopy_highpage(),wp_page_copy();
- handle vmap()/vunmap()/iounmap();
- handle task creation and deletion;
- handlevprintk();
I looked only at the printk part.
Quoted 4 lines
- call softirq entry/exit hooks in
kernel/softirq.c;
- check/initialize memory sent to/read from USB, I2C, and networkSigned-off-by Alexander Potapenko <[email protected]>
Quoted 9 lines
To: Alexander Potapenko <[email protected]>
Cc: Andrew Morton <[email protected]>
Cc: Greg Kroah-Hartman <[email protected]>
Cc: Eric Dumazet <[email protected]>
Cc: Wolfram Sang <[email protected]>
Cc: Petr Mladek <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]
Could you please add into CC also the other printk co-maitainers?
+ Sergey Senozhatsky <[email protected]>
+ Steven Rostedt <[email protected]>
Quoted 5 lines
kernel/printk/printk.c +0
-0
kernel/printk/printk.c int vprintk_store(int facility, int level, · line 1922
Quoted 10 lines
kernel/printk/printk.c +6
-1@@ -1914,7 +1914,12 @@ int vprintk_store(int facility, int level,* The printf needs to come first; we need the syslog * prefix which might be passed-in as a parameter. */ - text_len =vscnprintf (text,sizeof (textbuf), fmt, args); +/* +* We've checked the printk arguments in vprintk_emit() already. +* Initialize |text_len| to prevent the errors from spreading. +*/ + text_len =KMSAN_INIT_VALUE (vscnprintf (text,sizeof (textbuf), fmt, + args));
I am a bit confused by the comment.
What is the exact meaning of KMSAN_INIT_VALUE(), please? Does it prevent checking fmt again?
Does make the text_len variable special? In which way?
kernel/printk/printk.c asmlinkage int vprintk_emit(int facility, int level, · line 1980
Quoted 7 lines
kernel/printk/printk.c +1
-0/* mark and strip a trailing newline */ if (text_len && text[text_len-1 ] =='\n' ) {@@ -1972,6 +1977,7 @@ asmlinkage int vprintk_emit(int facility, int level,boot_delay_msec (level);printk_delay (); +kmsan_handle_vprintk (&fmt, args);
What does this function, please?
Could I find more details in another patch?
kernel/printk/printk.c asmlinkage int vprintk_emit(int facility, int level, · line 1983
Quoted 3 lines
kernel/printk/printk.c +0
-0/* This stops the holder of console_sem just where we want him */ logbuf_lock_irqsave (flags); curr_log_seq = log_next_seq;
Best Regards,
Petr
Alexander Potapenko
maintainerRe: [PATCH RFC v1 19/26] kmsan: call KMSAN hooks where needed
Quoted 28 lines · Petr Mladek
Quoted 9 lines · On Fri 2019-10-18 11:42:57, [email protected] wrote:
Insert KMSAN hooks that check for potential memory errors and/or make necessary bookkeeping changes:
- allocate/split/deallocate metadata pages in alloc_pages()/split_page()/free_page();
- clear page shadow and origins inclear_page(),copy_user_highpage();
- copy page metadata incopy_highpage(),wp_page_copy();
- handle vmap()/vunmap()/iounmap();
- handle task creation and deletion;
- handlevprintk();I looked only at the printk part.
Quoted 4 lines
- call softirq entry/exit hooks in
kernel/softirq.c;
- check/initialize memory sent to/read from USB, I2C, and networkSigned-off-by Alexander Potapenko <[email protected]>Quoted 9 lines
To: Alexander Potapenko <[email protected]>
Cc: Andrew Morton <[email protected]>
Cc: Greg Kroah-Hartman <[email protected]>
Cc: Eric Dumazet <[email protected]>
Cc: Wolfram Sang <[email protected]>
Cc: Petr Mladek <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]
Quoted 1 line
Could you please add into CC also the other printk co-maitainers?
Will do in the next version of this patch.
Quoted 13 lines
+ Sergey Senozhatsky <[email protected]>
+ Steven Rostedt <[email protected]>Quoted 9 lines
kernel/printk/printk.c +0
-1@@ -1914,7 +1914,12 @@ int vprintk_store(int facility, int level,* The printf needs to come first; we need the syslog * prefix which might be passed-in as a parameter. */ - text_len =vscnprintf (text,sizeof (textbuf), fmt, args);
Quoted 8 lines
kernel/printk/printk.c int vprintk_store(int facility, int level, · line 1922
Quoted 6 lines
kernel/printk/printk.c +6
-0+/* +* We've checked the printk arguments in vprintk_emit() already. +* Initialize |text_len| to prevent the errors from spreading. +*/ + text_len =KMSAN_INIT_VALUE (vscnprintf (text,sizeof (textbuf), fmt, + args));I am a bit confused by the comment.
Fixed in v2.
Quoted 1 line
What is the exact meaning of
KMSAN_INIT_VALUE(), please?
Hope it'll be clear from the "kmsan: add KMSAN runtime" patch. In fact KMSAN_INIT_VALUE is a wrapper that turns an uninitialized value into an initialized one:
https://github.com/google/kmsan/blob/3207d604ad68f7e5defdbbd6e63cb97750f380c1/include/linux/kmsan-checks.h#L58
It passes the value into an identity function that always returns unpoisoned values.
As a result, from now on text_len is considered initialized, regardless of what vscnprintf() returned.
Without KMSAN_INIT_VALUE the tool will report a huge number of errors when text_len is used in vprintk_store() and log_output().
Quoted 21 lines
Does it prevent checking fmt again?
Does make thetext_lenvariable special? In which way?kernel/printk/printk.c asmlinkage int vprintk_emit(int facility, int level, · line 1980
Quoted 7 lines
kernel/printk/printk.c +1
-0/* mark and strip a trailing newline */ if (text_len && text[text_len-1 ] =='\n' ) {@@ -1972,6 +1977,7 @@ asmlinkage int vprintk_emit(int facility, int level,boot_delay_msec (level);printk_delay (); +kmsan_handle_vprintk (&fmt, args);What does this function, please?
Could I find more details in another patch?kernel/printk/printk.c asmlinkage int vprintk_emit(int facility, int level, · line 1983
Quoted 3 lines
kernel/printk/printk.c +0
-0/* This stops the holder of console_sem just where we want him */ logbuf_lock_irqsave (flags); curr_log_seq = log_next_seq;Best Regards,
Petr
Signature · 10 lines
Alexander Potapenko Software Engineer Google Germany GmbH Erika-Mann-Straße, 33 80636 München Geschäftsführer: Paul Manicle, Halimah DeLaine Prado Registergericht und -nummer: Hamburg, HRB 86891 Sitz der Gesellschaft: Hamburg
[email protected]
maintainerRFC PATCH 20/26 kmsan: disable instrumentation of certain functions
In reply to [email protected]Some functions are called from handwritten assembly, and therefore don't have their arguments' metadata fully set up by the instrumentation code. Mark them with __no_sanitize_memory to avoid false positives from spreading further.
Certain functions perform task switching, so that the value of |current| is different as they proceed. Because KMSAN state pointer is only read once at the beginning of the function, touching it after |current| has changed may be dangerous.
To: Alexander Potapenko <[email protected]>
Cc: Thomas Gleixner <[email protected]>
Cc: Andrew Morton <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]
---
Change-Id: I684d23dac5a22eb0a4cea71993cb934302b17cea
--- arch/x86/entry/common.c | 1 + arch/x86/include/asm/irq_regs.h | 1 + arch/x86/include/asm/syscall_wrapper.h | 1 + arch/x86/kernel/apic/apic.c | 2 ++ arch/x86/kernel/dumpstack_64.c | 1 + arch/x86/kernel/process_64.c | 5 +++++ arch/x86/kernel/traps.c | 12 ++++++++++-- arch/x86/kernel/uprobes.c | 7 ++++++- kernel/profile.c | 1 + kernel/sched/core.c | 11 +++++++++++ 10 files changed, 39 insertions(+), 3 deletions(-)
arch/x86/entry/common.c +1 -0
@@ -275,6 +275,7 @@ __visible inline void syscall_return_slowpath(struct pt_regs *regs) }#ifdef CONFIG_X86_64 +__no_sanitize_memory __visible void do_syscall_64(unsigned long nr, struct pt_regs *regs) {struct thread_info *ti;
arch/x86/include/asm/irq_regs.h +1 -0
@@ -14,6 +14,7 @@ DECLARE_PER_CPU(struct pt_regs *, irq_regs); +__no_sanitize_memory static inline struct pt_regs *get_irq_regs(void) {return __this_cpu_read (irq_regs);
arch/x86/include/asm/syscall_wrapper.h +1 -0
@@ -159,6 +159,7 @@ ALLOW_ERROR_INJECTION(__x64_sys##name, ERRNO); \ static long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__)); \ static inline long __do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__));\ + __no_sanitize_memory \ asmlinkage long __x64_sys##name(const struct pt_regs *regs) \ { \ return __se_sys##name(SC_X86_64_REGS_TO_ARGS(x,__VA_ARGS__));\
arch/x86/kernel/apic/apic.c +2 -0
@@ -1118,6 +1118,8 @@ static void local_apic_timer_interrupt(void)* [ if a single-CPU system runs an SMP kernel then we call the local * interrupt as well. Thus we cannot inline the local irq ... ] */ +/* TODO(glider): |regs| is uninitialized, so is |*regs|. */ +__no_sanitize_memory __visible void __irq_entry smp_apic_timer_interrupt(struct pt_regs *regs) {struct pt_regs *old_regs =set_irq_regs (regs);
arch/x86/kernel/dumpstack_64.c +1 -0
@@ -143,6 +143,7 @@ static bool in_irq_stack(unsigned long *stack, struct stack_info *info)return true; } +__no_sanitize_memory int get_stack_info(unsigned long *stack, struct task_struct *task,struct stack_info *info,unsigned long *visit_mask) {
arch/x86/kernel/process_64.c +5 -0
@@ -500,6 +500,11 @@ void compat_start_thread(struct pt_regs *regs, u32 new_ip, u32 new_sp)* Kprobes not supported here. Set the probe on schedule instead. * Function graph tracer not supported too. */ +/* +* TODO(glider): __switch_to() does weird things with tasks, don't report +* anything here (also avoid touching the KMSAN state). +*/ +__no_sanitize_memory __visible __notrace_funcgraph struct task_struct *__switch_to (struct task_struct *prev_p,struct task_struct *next_p) {
arch/x86/kernel/traps.c +10 -2
@@ -618,7 +618,10 @@ NOKPROBE_SYMBOL(do_int3);* Help handler running on a per-cpu (IST or entry trampoline) stack * to switch to the normal thread stack if the interrupted code was in * user mode. The actual stack switch is done in entry_64.S +* +* This function switches the registers - don't instrument it with KMSAN! */ +__no_sanitize_memory asmlinkage __visible notrace struct pt_regs *sync_regs(struct pt_regs *eregs) {struct pt_regs *regs = (struct pt_regs *)this_cpu_read (cpu_current_top_of_stack) -1 ;
@@ -634,6 +637,11 @@ struct bad_iret_stack { }; asmlinkage __visible notrace +/* +* Dark magic happening here, let's not instrument this function. +* Also avoid copying any metadata by using raw __memmove(). +*/ +__no_sanitize_memorystruct bad_iret_stack *fixup_bad_iret (struct bad_iret_stack *s) {/*
@@ -648,10 +656,10 @@ struct bad_iret_stack *fixup_bad_iret(struct bad_iret_stack *s) (struct bad_iret_stack *)this_cpu_read (cpu_tss_rw.x86_tss.sp0) -1 ;/* Copy the IRET target to the new stack. */ -memmove (&new_stack->regs.ip, (void *)s->regs.sp,5 *8 ); +__memmove (&new_stack->regs.ip, (void *)s->regs.sp,5 *8 );/* Copy the remainder of the stack from the current stack. */ - memmove(new_stack, s, offsetof(struct bad_iret_stack, regs.ip)); + __memmove(new_stack, s, offsetof(struct bad_iret_stack, regs.ip));BUG_ON (!user_mode (&new_stack->regs));return new_stack;
arch/x86/kernel/uprobes.c +6 -1
@@ -8,6 +8,7 @@* Jim Keniston */ #include <linux/kernel.h> +#include <linux/kmsan-checks.h> #include <linux/sched.h> #include <linux/ptrace.h> #include <linux/uprobes.h>
@@ -997,9 +998,13 @@ int arch_uprobe_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)int arch_uprobe_exception_notify (struct notifier_block *self,unsigned long val,void *data) {struct die_args *args = data; -struct pt_regs *regs = args->regs; +struct pt_regs *regs;int ret =NOTIFY_DONE ; +kmsan_unpoison_shadow (args,sizeof (*args)); + regs = args->regs; +if (regs) +kmsan_unpoison_shadow (regs,sizeof (*regs));/* We are only interested in userspace traps */ if (regs && !user_mode (regs))return NOTIFY_DONE ;
kernel/profile.c +1 -0
@@ -399,6 +399,7 @@ void profile_hits(int type, void *__pc, unsigned int nr_hits) }EXPORT_SYMBOL_GPL (profile_hits); +__no_sanitize_memory void profile_tick(int type) {struct pt_regs *regs =get_irq_regs ();
kernel/sched/core.c +11 -0
@@ -475,6 +475,8 @@ void wake_q_add_safe(struct wake_q_head *head, struct task_struct *task)put_task_struct (task); } +/* TODO(glider): context switching here. */ +__no_sanitize_memory void wake_up_q(struct wake_q_head *head) {struct wake_q_node *node = head->first;
@@ -3180,6 +3182,8 @@ prepare_task_switch(struct rq *rq, struct task_struct *prev,* past. prev == current is still correct but we need to recalculate this_rq * because prev may have moved to another CPU. */ +/* TODO(glider): |current| changes here. */ +__no_sanitize_memory static struct rq *finish_task_switch(struct task_struct *prev) __releases(rq->lock) {
@@ -3986,6 +3990,8 @@ pick_next_task(struct rq *rq, struct task_struct *prev, struct rq_flags *rf)* * WARNING: must be called with preemption disabled! */ +/* TODO(glider): |current| changes here. */ +__no_sanitize_memory static void __sched notrace __schedule(bool preempt) {struct task_struct *prev, *next;
@@ -4605,6 +4611,8 @@ int task_prio(const struct task_struct *p)* * Return: 1 if the CPU is currently idle. 0 otherwise. */ +/* TODO(glider): nothing to report here. */ +__no_sanitize_memory int idle_cpu(int cpu) {struct rq *rq =cpu_rq (cpu);
@@ -6544,6 +6552,7 @@ static struct kmem_cache *task_group_cache __read_mostly;DECLARE_PER_CPU (cpumask_var_t, load_balance_mask);DECLARE_PER_CPU (cpumask_var_t, select_idle_mask); +__no_sanitize_memory void __init sched_init(void) {unsigned long ptr =0 ;
@@ -6716,6 +6725,8 @@ static inline int preempt_count_equals(int preempt_offset)return (nested == preempt_offset); } +/* TODO(glider): the args are most certainly initialized. */ +__no_sanitize_memory void __might_sleep(const char *file, int line, int preempt_offset) {/*
Signature · 1 line
2.23.0.866.gb869b98d4c-goog
[email protected]
maintainerRFC PATCH 21/26 kmsan: unpoison |tlb| in arch_tlb_gather_mmu()
In reply to [email protected]This is a hack to reduce stackdepot pressure.
To: Alexander Potapenko <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]
---
Change-Id: I22a201e7e4f67ed74f8129072f12e5351b26103a
--- mm/mmu_gather.c | 10 ++++++++++ 1 file changed, 10 insertions(+)
mm/mmu_gather.c +10 -0
@@ -1,6 +1,7 @@#include <linux/gfp.h> #include <linux/highmem.h> #include <linux/kernel.h> +#include <linux/kmsan-checks.h> #include <linux/mmdebug.h> #include <linux/mm_types.h> #include <linux/pagemap.h>
@@ -206,6 +207,15 @@ void tlb_flush_mmu(struct mmu_gather *tlb)void tlb_gather_mmu (struct mmu_gather *tlb,struct mm_struct *mm,unsigned long start,unsigned long end) { +/* +* TODO(glider): struct mmu_gather contains 7 1-bit fields packed into a +* 32-bit unsigned int value. The remaining 25 bits remain uninitialized +* and are never used, but KMSAN updates the origin for them in +* zap_pXX_range() in mm/memory.c, thus creating very long origin +* chains. This is technically correct, but consumes too much memory. +* Unpoisoning the whole structure will prevent creating such chains. +*/ +kmsan_unpoison_shadow (tlb,sizeof (*tlb)); tlb->mm = mm;/* Is it from 0 to ~0? */
Signature · 1 line
2.23.0.866.gb869b98d4c-goog
[email protected]
maintainerRFC PATCH 22/26 kmsan: use __msan_memcpy() where possible.
In reply to [email protected]Unless stated otherwise (by explicitly calling __memcpy()) we want all memcpy() calls to call __msan_memcpy() so that shadow and origin values are updated accordingly.
To: Alexander Potapenko <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]
---
Change-Id: Ib2512ce5aa8d457453dd38caa12f58f002166813
--- arch/x86/include/asm/string_64.h | 9 ++++++++- include/linux/compiler.h | 9 ++++++++- include/linux/string.h | 2 ++ 3 files changed, 18 insertions(+), 2 deletions(-)
arch/x86/include/asm/string_64.h +8 -1
@@ -11,7 +11,13 @@function. */ #define __HAVE_ARCH_MEMCPY 1 +#if defined(CONFIG_KMSAN ) +#undef memcpy +/* __msan_memcpy() is defined in compiler.h */ +#define memcpy (dst, src, len) __msan_memcpy(dst, src, len) +#else extern void *memcpy (void *to,const void *from,size_t len); +#endif extern void *__memcpy (void *to,const void *from,size_t len);#define __HAVE_ARCH_MEMSET
@@ -64,7 +70,8 @@ char *strcpy(char *dest, const char *src);char *strcat (char *dest,const char *src);int strcmp (const char *cs,const char *ct); -#if defined(CONFIG_KASAN ) && !defined(__SANITIZE_ADDRESS__) +#if (defined(CONFIG_KASAN ) && !defined(__SANITIZE_ADDRESS__)) || \ + (defined(CONFIG_KMSAN ) && !defined(__SANITIZE_MEMORY__))/* * For files that not instrumented (e.g. mm/slub.c) we
include/linux/compiler.h +8 -1
@@ -179,6 +179,13 @@ void ftrace_likely_update(struct ftrace_likely_data *f, int val,#include <uapi/linux/types.h> +#ifdef CONFIG_KMSAN +void *__msan_memcpy (void *dst,const void *src,u64 size); +#define __DO_MEMCPY (res, p, size) __msan_memcpy(res, p, size) +#else +#define __DO_MEMCPY (res, p, size) __builtin_memcpy(res, p, size) +#endif +#define __READ_ONCE_SIZE \ ({ \ switch (size) { \
@@ -188,7 +195,7 @@ void ftrace_likely_update(struct ftrace_likely_data *f, int val,case 8 : *(__u64 *)res = *(volatile __u64 *)p;break ; \default : \barrier (); \ -__builtin_memcpy ((void *)res, (const void *)p, size); \ +__DO_MEMCPY ((void *)res, (const void *)p, size); \barrier (); \ } \ })
include/linux/string.h +2 -0
@@ -363,6 +363,7 @@ __FORTIFY_INLINE void *memset(void *p, int c, __kernel_size_t size)return __builtin_memset (p, c, size); } +#ifndef CONFIG_KMSAN __FORTIFY_INLINE void *memcpy(void *p, const void *q, __kernel_size_t size) {size_t p_size =__builtin_object_size (p,0 );
@@ -377,6 +378,7 @@ __FORTIFY_INLINE void *memcpy(void *p, const void *q, __kernel_size_t size)fortify_panic (__func__);return __builtin_memcpy (p, q, size); } +#endif __FORTIFY_INLINE void *memmove(void *p, const void *q, __kernel_size_t size) {
Signature · 1 line
2.23.0.866.gb869b98d4c-goog
[email protected]
maintainerRFC PATCH 23/26 kmsan: unpoisoning buffers from devices etc.
In reply to [email protected]When data is copied to memory from a device KMSAN should treat it as initialized. In most cases it's enough to just unpoison the buffer that is known to come from a device.
In the case with __do_page_cache_readahead() and bio_copy_user_iov() we have to mark the whole pages as ignored by KMSAN, as it's not obvious where these pages are read again.
To: Alexander Potapenko <[email protected]>
Cc: Andrew Morton <[email protected]>
Cc: Jens Axboe <[email protected]>
Cc: "Theodore Ts'o" <[email protected]>
Cc: Dmitry Torokhov <[email protected]>
Cc: Martin K. Petersen <[email protected]>
Cc: "Michael S. Tsirkin" <[email protected]>
Cc: Christoph Hellwig <[email protected]>
Cc: Eric Dumazet <[email protected]>
Cc: Eric Van Hensbergen <[email protected]>
Cc: Takashi Iwai <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]
---
Suggestions on how to avoid scattering too many annotations over the code are welcome. Perhaps annotating DMA reads/writes is enough to get rid of most of these annotations.
Change-Id: Id460e7a86ce564f1357469f53d0c7410ca08f0e9
--- block/bio.c | 20 ++++++++++++++++++++ block/partition-generic.c | 9 ++++++++- drivers/char/random.c | 2 ++ drivers/input/serio/libps2.c | 6 +++++- drivers/scsi/scsi_lib.c | 4 ++++ drivers/usb/core/message.c | 6 +++++- drivers/virtio/virtio_ring.c | 14 ++++++++++++++ fs/buffer.c | 7 ++++++- include/linux/dma-mapping.h | 2 ++ include/linux/skbuff.h | 5 ++++- mm/filemap.c | 3 +++ mm/readahead.c | 6 ++++++ net/9p/protocol.c | 2 ++ sound/core/oss/pcm_oss.c | 7 +++++++ 14 files changed, 88 insertions(+), 5 deletions(-)
block/bio.c +20 -0
@@ -11,6 +11,7 @@#include <linux/slab.h> #include <linux/init.h> #include <linux/kernel.h> +#include <linux/kmsan-checks.h> #include <linux/export.h> #include <linux/mempool.h> #include <linux/workqueue.h>
@@ -900,6 +901,13 @@ static int __bio_iov_iter_get_pages(struct bio *bio, struct iov_iter *iter)__bio_add_page (bio, page, len, offset); } offset =0 ; +/* +* TODO(glider): these pages will be soon passed to block +* device for reading, so consider them initialized. +*/ +if (iov_iter_rw (iter) ==READ ) +kmsan_unpoison_shadow (page_address (page), +PAGE_SIZE ); }iov_iter_advance (iter, size);
@@ -1274,11 +1282,16 @@ struct bio *bio_copy_user_iov(struct request_queue *q, i++; }else { +/* +* TODO(glider): KMSAN doesn't track the pages +* allocated for bio here. +*/ page =alloc_page (q->bounce_gfp | gfp_mask);if (!page) { ret = -ENOMEM ;break ; } +kmsan_ignore_page (page,/*order*/ 0 ); }if (bio_add_pc_page (q, bio, page, bytes, offset) < bytes) {
@@ -1574,6 +1587,13 @@ struct bio *bio_copy_kern(struct request_queue *q, void *data, unsigned int len,if (!page) goto cleanup; +/* +* TODO(glider): if we're about to read data from a SCSI device, +* assume the page allocated for that is already initialized. +*/ +if (reading) +kmsan_unpoison_shadow (page_address (page),PAGE_SIZE ); +if (!reading)memcpy (page_address (page), p, bytes);
block/partition-generic.c +8 -1
@@ -663,13 +663,20 @@ unsigned char *read_dev_sector(struct block_device *bdev, sector_t n, Sector *p) {struct address_space *mapping = bdev->bd_inode->i_mapping;struct page *page; +unsigned char *retval; page =read_mapping_page (mapping, (pgoff_t)(n >> (PAGE_SHIFT -9 )),NULL );if (!IS_ERR (page)) {if (PageError (page)) goto fail; p->v = page; -return (unsigned char *)page_address (page) + ((n & ((1 << (PAGE_SHIFT -9 )) -1 )) <<9 ); + retval = (unsigned char *)page_address (page) + + ((n & ((1 << (PAGE_SHIFT -9 )) -1 )) <<9 ); +/* +* Unpoison sector-sized chunk of memory coming from the device. +*/ +kmsan_unpoison_shadow (retval,SECTOR_SIZE ); +return retval; fail:put_page (page); }
drivers/char/random.c +2 -0
@@ -320,6 +320,7 @@#include <linux/fs.h> #include <linux/genhd.h> #include <linux/interrupt.h> +#include <linux/kmsan-checks.h> #include <linux/mm.h> #include <linux/nodemask.h> #include <linux/spinlock.h>
@@ -1061,6 +1062,7 @@ static void _extract_crng(struct crng_state *crng,spin_lock_irqsave (&crng->lock, flags);if (arch_get_random_long (&v)) crng->state[14 ] ^= v; +kmsan_unpoison_shadow (crng->state,sizeof (crng->state));chacha20_block (&crng->state[0 ], out);if (crng->state[12 ] ==0 ) crng->state[13 ]++;
drivers/input/serio/libps2.c +5 -1
@@ -8,6 +8,7 @@#include <linux/delay.h> +#include <linux/kmsan-checks.h> #include <linux/module.h> #include <linux/sched.h> #include <linux/interrupt.h>
@@ -30,6 +31,7 @@ static int ps2_do_sendbyte(struct ps2dev *ps2dev, u8 byte,int error;lockdep_assert_held (&ps2dev->serio->lock); +kmsan_check_memory (&byte,1 ); do { ps2dev->nak = 1;
@@ -294,9 +296,11 @@ int __ps2_command(struct ps2dev *ps2dev, u8 *param, unsigned int command)serio_pause_rx (ps2dev->serio); -if (param) +if (param) {for (i =0 ; i < receive; i++) param[i] = ps2dev->cmdbuf[(receive -1 ) - i]; +kmsan_unpoison_shadow (param, receive); + }if (ps2dev->cmdcnt && (command !=PS2_CMD_RESET_BAT || ps2dev->cmdcnt !=1 )) {
drivers/scsi/scsi_lib.c +4 -0
@@ -14,6 +14,7 @@#include <linux/blkdev.h> #include <linux/completion.h> #include <linux/kernel.h> +#include <linux/kmsan-checks.h> #include <linux/export.h> #include <linux/init.h> #include <linux/pci.h>
@@ -296,6 +297,9 @@ int __scsi_execute(struct scsi_device *sdev, const unsigned char *cmd, ret = rq->result; out:blk_put_request (req); +/* TODO(glider): this is a bit rough. */ +if (data_direction ==DMA_FROM_DEVICE ) +kmsan_unpoison_shadow (buffer, bufflen);return ret; }
drivers/usb/core/message.c +5 -1
@@ -9,6 +9,7 @@#include <linux/usb.h> #include <linux/module.h> #include <linux/slab.h> +#include <linux/kmsan-checks.h> #include <linux/mm.h> #include <linux/timer.h> #include <linux/ctype.h>
@@ -101,8 +102,11 @@ static int usb_internal_control_msg(struct usb_device *usb_dev, retv =usb_start_wait_urb (urb, timeout, &length);if (retv <0 )return retv; -else +else { +/* TODO(glider): USB initializes |length| bytes? */ +kmsan_unpoison_shadow (data, length);return length; + } }/**
drivers/virtio/virtio_ring.c +14 -0
@@ -11,6 +11,7 @@#include <linux/module.h> #include <linux/hrtimer.h> #include <linux/dma-mapping.h> +#include <linux/kmsan-checks.h> #include <xen/xen.h> #ifdef DEBUG
@@ -387,6 +388,12 @@ static void vring_unmap_one_split(const struct vring_virtqueue *vq, (flags &VRING_DESC_F_WRITE ) ? DMA_FROM_DEVICE : DMA_TO_DEVICE); } +/* +* Unmapping DMA memory after a transfer from device requires this +* memory to be unpoisoned. +*/ +if (flags &VRING_DESC_F_WRITE ) +kmsan_unpoison_shadow ((const void *)desc->addr, desc->len); } static struct vring_desc *alloc_indirect_split(struct virtqueue *_vq,
@@ -500,6 +507,13 @@ static inline int virtqueue_add_split(struct virtqueue *_vq, desc[i].flags =cpu_to_virtio16 (_vq->vdev,VRING_DESC_F_NEXT |VRING_DESC_F_WRITE ); desc[i].addr =cpu_to_virtio64 (_vq->vdev, addr); desc[i].len =cpu_to_virtio32 (_vq->vdev, sg->length); +/* +* It's hard to figure out the buffer's address upon +* receive. Instead we unpoison it once, when exposing +* it to the device, and hope nobody else will write to +* it. +*/ +kmsan_unpoison_shadow (sg_virt (sg), sg->length); prev = i; i =virtio16_to_cpu (_vq->vdev, desc[i].next); }
fs/buffer.c +6 -1
@@ -1170,14 +1170,17 @@ static struct buffer_head *__bread_slow(struct buffer_head *bh)lock_buffer (bh);if (buffer_uptodate (bh)) {unlock_buffer (bh); +kmsan_unpoison_shadow (bh->b_data, bh->b_size);return bh; }else {get_bh (bh); bh->b_end_io = end_buffer_read_sync;submit_bh (REQ_OP_READ ,0 , bh);wait_on_buffer (bh); -if (buffer_uptodate (bh)) +if (buffer_uptodate (bh)) { +kmsan_unpoison_shadow (bh->b_data, bh->b_size);return bh; + } }brelse (bh);return NULL ;
@@ -1320,6 +1323,8 @@ __getblk_gfp(struct block_device *bdev, sector_t block,might_sleep ();if (bh ==NULL ) bh =__getblk_slow (bdev, block, size, gfp); +if (bh) +kmsan_unpoison_shadow (bh->b_data, bh->b_size);return bh; }EXPORT_SYMBOL (__getblk_gfp);
include/linux/dma-mapping.h +2 -0
@@ -8,6 +8,7 @@#include <linux/err.h> #include <linux/dma-debug.h> #include <linux/dma-direction.h> +#include <linux/kmsan-checks.h> #include <linux/scatterlist.h> #include <linux/bug.h> #include <linux/mem_encrypt.h>
@@ -281,6 +282,7 @@ static inline dma_addr_t dma_map_page_attrs(struct device *dev,const struct dma_map_ops *ops =get_dma_ops (dev);dma_addr_t addr; +kmsan_unpoison_shadow (page_address (page), size);BUG_ON (!valid_dma_direction (dir));if (dma_is_direct (ops)) addr =dma_direct_map_page (dev, page, offset, size, dir, attrs);
include/linux/skbuff.h +4 -1
@@ -11,6 +11,7 @@#define _LINUX_SKBUFF_H#include <linux/kernel.h> +#include <linux/kmsan-checks.h> #include <linux/compiler.h> #include <linux/time.h> #include <linux/bug.h>
@@ -2214,6 +2215,8 @@ static inline void *skb_put_data(struct sk_buff *skb, const void *data, {void *tmp =skb_put (skb, len); +/* Unpoison the data received from the network device. */ +kmsan_unpoison_shadow (data, len);memcpy (tmp, data, len);return tmp;
@@ -2221,7 +2224,7 @@ static inline void *skb_put_data(struct sk_buff *skb, const void *data,static inline void skb_put_u8 (struct sk_buff *skb,u8 val) { - *(u8 *)skb_put (skb,1 ) = val; + *(u8 *)skb_put (skb,1 ) =KMSAN_INIT_VALUE (val); }void *skb_push (struct sk_buff *skb,unsigned int len);
mm/filemap.c +3 -0
@@ -18,6 +18,7 @@#include <linux/uaccess.h> #include <linux/capability.h> #include <linux/kernel_stat.h> +#include <linux/kmsan-checks.h> #include <linux/gfp.h> #include <linux/mm.h> #include <linux/swap.h>
@@ -2810,6 +2811,8 @@ static struct page *do_read_cache_page(struct address_space *mapping, page =wait_on_page_read (page);if (IS_ERR (page))return page; +/* Assume all pages in page cache are initialized. */ +kmsan_unpoison_shadow (page_address (page),PAGE_SIZE ); goto out; }if (PageUptodate (page))
mm/readahead.c +6 -0
@@ -193,7 +193,13 @@ unsigned int __do_page_cache_readahead(struct address_space *mapping,continue ; } +/* +* The easiest way to handle these pages is to mark them +* untracked by KMSAN, assuming they're never used by anything +* else. +*/ page =__page_cache_alloc (gfp_mask); +kmsan_ignore_page (page,/*order*/ 0 );if (!page)break ; page->index = page_offset;
net/9p/protocol.c +2 -0
@@ -13,6 +13,7 @@#include <linux/module.h> #include <linux/errno.h> #include <linux/kernel.h> +#include <linux/kmsan-checks.h> #include <linux/uaccess.h> #include <linux/slab.h> #include <linux/sched.h>
@@ -46,6 +47,7 @@ EXPORT_SYMBOL(p9stat_free);size_t pdu_read (struct p9_fcall *pdu,void *data,size_t size) {size_t len =min (pdu->size - pdu->offset, size); +kmsan_unpoison_shadow (&pdu->sdata[pdu->offset], len);memcpy (data, &pdu->sdata[pdu->offset], len); pdu->offset += len;return size - len;
sound/core/oss/pcm_oss.c +7 -0
@@ -12,6 +12,7 @@#endif #include <linux/init.h> +#include <linux/kmsan-checks.h> /* for kmsan_unpoison_shadow() */ #include <linux/slab.h> #include <linux/sched/signal.h> #include <linux/time.h>
@@ -1054,6 +1055,12 @@ static int snd_pcm_oss_change_params_locked(struct snd_pcm_substream *substream) err = -ENOMEM ; goto failure; } +/* +* Unpoison the freshly created buffer to prevent KMSAN from reporting +* uninit errors. +* TODO(glider): unpoison it only when it's actually initialized. +*/ +kmsan_unpoison_shadow (runtime->oss.buffer, runtime->oss.period_bytes); runtime->oss.params =0 ; runtime->oss.prepare =1 ;
Signature · 1 line
2.23.0.866.gb869b98d4c-goog
Re: [PATCH RFC v1 23/26] kmsan: unpoisoning buffers from devices etc.
Quoted 6 lines · On Fri, Oct 18, 2019 at 11:43:01AM +0200, [email protected] wrote:
When data is copied to memory from a device KMSAN should treat it as initialized. In most cases it's enough to just unpoison the buffer that is known to come from a device.
In the case with__do_page_cache_readahead()andbio_copy_user_iov()we have to mark the whole pages as ignored by KMSAN, as it's not obvious where these pages are read again.
I'm not sure how your annotations work because you only Cced me on this one patch. Don't do that.
Matthew Wilcox
maintainerRe: [PATCH RFC v1 23/26] kmsan: unpoisoning buffers from devices etc.
In reply to [email protected]Quoted 6 lines · On Fri, Oct 18, 2019 at 11:43:01AM +0200, [email protected] wrote:
When data is copied to memory from a device KMSAN should treat it as initialized. In most cases it's enough to just unpoison the buffer that is known to come from a device.
In the case with__do_page_cache_readahead()andbio_copy_user_iov()we have to mark the whole pages as ignored by KMSAN, as it's not obvious where these pages are read again.
...
Quoted 10 lines
+++
b/mm/filemap.cmm/filemap.c +1
-0@@ -18,6 +18,7 @@#include <linux/uaccess.h> #include <linux/capability.h> #include <linux/kernel_stat.h> +#include <linux/kmsan-checks.h> #include <linux/gfp.h> #include <linux/mm.h> #include <linux/swap.h>
mm/filemap.c static struct page *do_read_cache_page(struct address_space *mapping, · line 2815
Quoted 5 lines
mm/filemap.c +2
-0@@ -2810,6 +2811,8 @@ static struct page *do_read_cache_page(struct address_space *mapping, page =wait_on_page_read (page);if (IS_ERR (page))return page; +/* Assume all pages in page cache are initialized. */ +kmsan_unpoison_shadow (page_address (page),PAGE_SIZE );
Why would you do that? The page cache already keeps track of which pages are initialised -- the PageUptodate flag is set on them. Indeed, just adding a kmsan call to SetPageUptodate and __SetPageUptodate would probably be a very straightforward way of handling things, and probably means you can get rid of a lot of these other calls.
Alexander Potapenko
maintainerRe: [PATCH RFC v1 23/26] kmsan: unpoisoning buffers from devices etc.
Quoted 24 lines · Matthew Wilcox
Quoted 6 lines · On Fri, Oct 18, 2019 at 11:43:01AM +0200, [email protected] wrote:
When data is copied to memory from a device KMSAN should treat it as initialized. In most cases it's enough to just unpoison the buffer that is known to come from a device.
In the case with__do_page_cache_readahead()andbio_copy_user_iov()we have to mark the whole pages as ignored by KMSAN, as it's not obvious where these pages are read again....
Quoted 13 lines
+++
b/mm/filemap.cmm/filemap.c +1
-0@@ -18,6 +18,7 @@#include <linux/uaccess.h> #include <linux/capability.h> #include <linux/kernel_stat.h> +#include <linux/kmsan-checks.h> #include <linux/gfp.h> #include <linux/mm.h> #include <linux/swap.h> @@ -2810,6 +2811,8 @@ static struct page *do_read_cache_page(struct address_space *mapping, page =wait_on_page_read (page);if (IS_ERR (page))return page;
Quoted 8 lines
mm/filemap.c static struct page *do_read_cache_page(struct address_space *mapping, · line 2815
Quoted 2 lines
mm/filemap.c +2
-0+/* Assume all pages in page cache are initialized. */ +kmsan_unpoison_shadow (page_address (page),PAGE_SIZE );Why would you do that? The page cache already keeps track of which pages are initialised -- the PageUptodate flag is set on them. Indeed, just adding a kmsan call to SetPageUptodate and
__SetPageUptodatewould probably be a very straightforward way of handling things, and probably means you can get rid of a lot of these other calls.
This seems like a very good thing to do, I'll definitely try that. I however noticed that __SetPageUptodate is used when copying pages, not during disk I/O. Is that really so?
We basically need the following behavior: if a device writes to a page, the contents of that page are considered initialized. However when the kernel copies one page to another, we must explicitly copy the source shadow page to the destination.
On a related note, there seems to be a PG_dirty bit that indicates the page is to be flushed to disk.
What's the best place to check such pages for being initialized, so that we can also report writes of uninitialized data to the disk?
Signature · 10 lines
Alexander Potapenko Software Engineer Google Germany GmbH Erika-Mann-Straße, 33 80636 München Geschäftsführer: Paul Manicle, Halimah DeLaine Prado Registergericht und -nummer: Hamburg, HRB 86891 Sitz der Gesellschaft: Hamburg
Alexander Potapenko
maintainerRe: [PATCH RFC v1 23/26] kmsan: unpoisoning buffers from devices etc.
Quoted 34 lines · Alexander Potapenko
Quoted 24 lines · Matthew Wilcox
Quoted 6 lines · On Fri, Oct 18, 2019 at 11:43:01AM +0200, [email protected] wrote:
When data is copied to memory from a device KMSAN should treat it as initialized. In most cases it's enough to just unpoison the buffer that is known to come from a device.
In the case with__do_page_cache_readahead()andbio_copy_user_iov()we have to mark the whole pages as ignored by KMSAN, as it's not obvious where these pages are read again....
Quoted 13 lines
+++
b/mm/filemap.cmm/filemap.c +1
-0@@ -18,6 +18,7 @@#include <linux/uaccess.h> #include <linux/capability.h> #include <linux/kernel_stat.h> +#include <linux/kmsan-checks.h> #include <linux/gfp.h> #include <linux/mm.h> #include <linux/swap.h> @@ -2810,6 +2811,8 @@ static struct page *do_read_cache_page(struct address_space *mapping, page =wait_on_page_read (page);if (IS_ERR (page))return page;Quoted 8 lines
mm/filemap.c static struct page *do_read_cache_page(struct address_space *mapping, · line 2815
Quoted 2 lines
mm/filemap.c +2
-0+/* Assume all pages in page cache are initialized. */ +kmsan_unpoison_shadow (page_address (page),PAGE_SIZE );Why would you do that? The page cache already keeps track of which pages are initialised -- the PageUptodate flag is set on them. Indeed, just adding a kmsan call to SetPageUptodate and
__SetPageUptodatewould probably be a very straightforward way of handling things, and probably means you can get rid of a lot of these other calls.
Quoted 1 line
This seems like a very good thing to do, I'll definitely try that.
Hm, turns out there's no need for this particular call to kmsan_unpoison_memory(),
because the errors that it was suppressing are better addressed by making READ_ONCE_NOCHECK() return initialized memory.
On the other hand, unpoisoning memory in SetPageUptodate doesn't seem to address the same errors.
=====================================================
BUG: KMSAN: uninit-value in[< none >] get_wchan+0x2c6/0x410 arch/x86/kernel/process.c:851 CPU: 0 PID: 5159 Comm: ps Not tainted 5.4.0-rc5+ #3220 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.12.0-1 04/01/2014 Call Trace: [< inline >] __dump_stack lib/dump_stack.c:77 [< none >] dump_stack+0x196/0x1f0 lib/dump_stack.c:113 [< none >] kmsan_report+0x127/0x220 mm/kmsan/kmsan_report.c:108 [< none >] __msan_warning+0x73/0xe0 mm/kmsan/kmsan_instr.c:245 [< none >] get_wchan+0x2c6/0x410 arch/x86/kernel/process.c:851 [< none >] do_task_stat+0x16f3/0x30a0 fs/proc/array.c:522 [< none >] proc_tgid_stat+0xbe/0xf0 fs/proc/array.c:632 [< none >] proc_single_show+0x1a8/0x2b0 fs/proc/base.c:756 [< none >] seq_read+0xad1/0x1da0 fs/seq_file.c:229 [< none >] __vfs_read+0x1a9/0xc90 fs/read_write.c:425 [< none >] vfs_read+0x333/0x6a0 fs/read_write.c:461 [< none >] ksys_read+0x281/0x460 fs/read_write.c:587 [< inline >] __do_sys_read fs/read_write.c:597 [< none >] __se_sys_read+0x92/0xb0 fs/read_write.c:595 [< none >] __x64_sys_read+0x4a/0x70 fs/read_write.c:595 [< none >] do_syscall_64+0xb6/0x160 arch/x86/entry/common.c:291 [< none >] entry_SYSCALL_64_after_hwframe+0x63/0xe7 arch/x86/entry/entry_64.S:177 Local variable description: ----stat.i@__se_sys_newstat Variable was created at: [< inline >] __do_sys_newstat fs/stat.c:337 [< none >] __se_sys_newstat+0x71/0xac0 fs/stat.c:337 [< inline >] __do_sys_newstat fs/stat.c:337 [< none >] __se_sys_newstat+0x71/0xac0 fs/stat.c:337 =====================================================
Quoted 18 lines
I however noticed that
__SetPageUptodateis used when copying pages, not during disk I/O. Is that really so?We basically need the following behavior: if a device writes to a page, the contents of that page are considered initialized. However when the kernel copies one page to another, we must explicitly copy the source shadow page to the destination.
On a related note, there seems to be a
PG_dirtybit that indicates the page is to be flushed to disk.
What's the best place to check such pages for being initialized, so that we can also report writes of uninitialized data to the disk?--
Alexander Potapenko
Software Engineer
Quoted 7 lines
Google Germany GmbH
Erika-Mann-Straße, 33
80636 MünchenGeschäftsführer: Paul Manicle, Halimah DeLaine Prado Registergericht und -nummer: Hamburg, HRB 86891 Sitz der Gesellschaft: Hamburg
--
Alexander Potapenko
Software Engineer
Google Germany GmbH
Erika-Mann-Straße, 33
80636 München
Geschäftsführer: Paul Manicle, Halimah DeLaine Prado Registergericht und -nummer: Hamburg, HRB 86891 Sitz der Gesellschaft: Hamburg
[email protected]
maintainerRFC PATCH 24/26 kmsan: hooks for copy_to_user() and friends
In reply to [email protected]Memory that is copied from userspace must be unpoisoned. Before copying memory to userspace, check it and report an error if it contains uninitialized bits.
To: Alexander Potapenko <[email protected]>
Cc: Alexander Viro <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]
---
Change-Id: I38428b9c7d1909b8441dcec1749b080494a7af99
--- arch/x86/include/asm/uaccess.h | 12 ++++++++++++ include/asm-generic/cacheflush.h | 7 ++++++- include/asm-generic/uaccess.h | 12 ++++++++++-- include/linux/uaccess.h | 32 +++++++++++++++++++++++++++----- lib/iov_iter.c | 6 ++++++ lib/usercopy.c | 6 +++++- 6 files changed, 66 insertions(+), 9 deletions(-)
arch/x86/include/asm/uaccess.h +12 -0
@@ -6,6 +6,7 @@*/ #include <linux/compiler.h> #include <linux/kasan-checks.h> +#include <linux/kmsan-checks.h> #include <linux/string.h> #include <asm/asm.h> #include <asm/page.h>
@@ -174,6 +175,7 @@ __typeof__(__builtin_choose_expr(sizeof(x) > sizeof(0UL), 0ULL, 0UL)) ASM_CALL_CONSTRAINT \ : "0" (ptr), "i" (sizeof(*(ptr)))); \ (x) = (__force __typeof__(*(ptr))) __val_gu; \ +kmsan_unpoison_shadow (&(x),sizeof (*(ptr))); \__builtin_expect (__ret_gu,0 ); \ })
@@ -248,6 +250,7 @@ extern void __put_user_8(void);__chk_user_ptr (ptr); \might_fault (); \ __pu_val = x; \ +kmsan_check_memory (&(__pu_val),sizeof (*(ptr))); \switch (sizeof (*(ptr))) { \case 1 : \__put_user_x (1 , __pu_val, ptr, __ret_pu); \
@@ -270,7 +273,9 @@ extern void __put_user_8(void);#define __put_user_size (x, ptr, size, label) \ do { \ + __typeof__(*(ptr)) __pus_val = x; \ __chk_user_ptr(ptr); \ + kmsan_check_memory(&(__pus_val), size); \ switch (size) { \ case 1: \ __put_user_goto(x, ptr, "b", "b", "iq", label); \
@@ -295,7 +300,10 @@ do { \*/ #define __put_user_size_ex (x, ptr, size) \ do { \ + __typeof__(*(ptr)) __puse_val; \ __chk_user_ptr(ptr); \ + __puse_val = x; \ + kmsan_check_memory(&(__puse_val), size); \ switch (size) { \ case 1: \ __put_user_asm_ex(x, ptr, "b", "b", "iq"); \
@@ -363,6 +371,7 @@ do { \default : \ (x) =__get_user_bad (); \ } \ +kmsan_unpoison_shadow (&(x), size); \ }while (0 ) #define __get_user_asm(x, addr, err, itype, rtype, ltype, errret) \
@@ -413,6 +422,7 @@ do { \default : \ (x) =__get_user_bad (); \ } \ +kmsan_unpoison_shadow (&(x), size); \ }while (0 ) #define __get_user_asm_ex(x, addr, itype, rtype, ltype) \
@@ -428,11 +438,13 @@ do { \#define __put_user_nocheck (x, ptr, size) \ ({ \ __label__ __pu_label; \ + __typeof__(*(ptr)) __pun_val = x; \ int __pu_err = -EFAULT; \ __typeof__(*(ptr)) __pu_val = (x); \ __typeof__(ptr) __pu_ptr = (ptr); \ __typeof__(size) __pu_size = (size); \ __uaccess_begin(); \ + kmsan_check_memory(&(__pun_val), size); \ __put_user_size(__pu_val, __pu_ptr, __pu_size, __pu_label); \ __pu_err = 0; \ __pu_label: \
include/asm-generic/cacheflush.h +6 -1
@@ -4,6 +4,7 @@/* Keep includes the same across arches. */ #include <linux/mm.h> +#include <linux/kmsan-checks.h> #define ARCH_IMPLEMENTS_FLUSH_DCACHE_PAGE 0
@@ -72,10 +73,14 @@ static inline void flush_cache_vunmap(unsigned long start, unsigned long end)#define copy_to_user_page (vma, page, vaddr, dst, src, len) \ do { \ + kmsan_check_memory(src, len); \ memcpy(dst, src, len); \ flush_icache_user_range(vma, page, vaddr, len); \ } while (0)#define copy_from_user_page (vma, page, vaddr, dst, src, len) \ - memcpy(dst, src, len) + do { \ + memcpy(dst, src, len); \ + kmsan_unpoison_shadow(dst, len); \ + } while (0)#endif /* __ASM_CACHEFLUSH_H */
include/asm-generic/uaccess.h +10 -2
@@ -142,7 +142,11 @@ static inline int __access_ok(unsigned long addr, unsigned long size)static inline int __put_user_fn (size_t size,void __user *ptr,void *x) { -return unlikely (raw_copy_to_user (ptr, x, size)) ? -EFAULT :0 ; +int n; + + n =raw_copy_to_user (ptr, x, size); +kmsan_copy_to_user (ptr, x, size, n); +return unlikely (n) ? -EFAULT :0 ; }#define __put_user_fn (sz, u, k) __put_user_fn(sz, u, k)
@@ -203,7 +207,11 @@ extern int __put_user_bad(void) __attribute__((noreturn));#ifndef __get_user_fnstatic inline int __get_user_fn (size_t size,const void __user *ptr,void *x) { -return unlikely (raw_copy_from_user (x, ptr, size)) ? -EFAULT :0 ; +int copied, to_copy = size; + + copied =raw_copy_from_user (x, ptr, size); +kmsan_unpoison_shadow (to, to_copy - res); +return unlikely (copied) ? -EFAULT :0 ; }#define __get_user_fn (sz, u, k) __get_user_fn(sz, u, k)
include/linux/uaccess.h +27 -5
@@ -5,6 +5,7 @@#include <linux/sched.h> #include <linux/thread_info.h> #include <linux/kasan-checks.h> +#include <linux/kmsan-checks.h> #define uaccess_kernel () segment_eq(get_fs(), KERNEL_DS)
@@ -58,18 +59,26 @@static __always_inline __must_check unsigned long __copy_from_user_inatomic (void *to,const void __user *from,unsigned long n) { +unsigned long to_copy = n; +kasan_check_write (to, n);check_object_size (to, n, false); -return raw_copy_from_user (to, from, n); + n =raw_copy_from_user (to, from, n); +kmsan_unpoison_shadow (to, to_copy - n); +return n; }static __always_inline __must_check unsigned long __copy_from_user (void *to,const void __user *from,unsigned long n) { +unsigned long to_copy = n; +might_fault ();kasan_check_write (to, n);check_object_size (to, n, false); -return raw_copy_from_user (to, from, n); + n =raw_copy_from_user (to, from, n); +kmsan_unpoison_shadow (to, to_copy - n); +return n; }/**
@@ -88,29 +97,39 @@ __copy_from_user(void *to, const void __user *from, unsigned long n)static __always_inline __must_check unsigned long __copy_to_user_inatomic (void __user *to,const void *from,unsigned long n) { +unsigned long to_copy = n; +kasan_check_read (from, n);check_object_size (from, n, true); -return raw_copy_to_user (to, from, n); + n =raw_copy_to_user (to, from, n); +kmsan_copy_to_user (to, from, to_copy, n); +return n; }static __always_inline __must_check unsigned long __copy_to_user (void __user *to,const void *from,unsigned long n) { +unsigned long to_copy = n; +might_fault ();kasan_check_read (from, n);check_object_size (from, n, true); -return raw_copy_to_user (to, from, n); + n =raw_copy_to_user (to, from, n); +kmsan_copy_to_user (to, from, to_copy, n); +return n; }#ifdef INLINE_COPY_FROM_USER static inline __must_check unsigned long _copy_from_user (void *to,const void __user *from,unsigned long n) { -unsigned long res = n; +unsigned long res = n, to_copy = n; +might_fault ();if (likely (access_ok (from, n))) {kasan_check_write (to, n); res =raw_copy_from_user (to, from, n); +kmsan_unpoison_shadow (to, to_copy - res); }if (unlikely (res))memset (to + (n - res),0 , res);
@@ -125,10 +144,13 @@ _copy_from_user(void *, const void __user *, unsigned long);static inline __must_check unsigned long _copy_to_user (void __user *to,const void *from,unsigned long n) { +unsigned long to_copy = n; +might_fault ();if (access_ok (to, n)) {kasan_check_read (from, n); n =raw_copy_to_user (to, from, n); +kmsan_copy_to_user (to, from, to_copy, n); }return n; }
lib/iov_iter.c +6 -0
@@ -137,18 +137,24 @@static int copyout (void __user *to,const void *from,size_t n) { +size_t to_copy = n; +if (access_ok (to, n)) {kasan_check_read (from, n); n =raw_copy_to_user (to, from, n); +kmsan_copy_to_user (to, from, to_copy, n); }return n; }static int copyin (void *to,const void __user *from,size_t n) { +size_t to_copy = n; +if (access_ok (from, n)) {kasan_check_write (to, n); n =raw_copy_from_user (to, from, n); +kmsan_unpoison_shadow (to, to_copy - n); }return n; }
lib/usercopy.c +5 -1
@@ -1,4 +1,5 @@// SPDX-License-Identifier: GPL-2.0 +#include <linux/kmsan-checks.h> #include <linux/uaccess.h> #include <linux/bitops.h>
@@ -7,11 +8,12 @@#ifndef INLINE_COPY_FROM_USER unsigned long _copy_from_user (void *to,const void __user *from,unsigned long n) { -unsigned long res = n; +unsigned long res = n, to_copy = n;might_fault ();if (likely (access_ok (from, n))) {kasan_check_write (to, n); res =raw_copy_from_user (to, from, n); +kmsan_unpoison_shadow (to, to_copy - res); }if (unlikely (res))memset (to + (n - res),0 , res);
@@ -23,10 +25,12 @@ EXPORT_SYMBOL(_copy_from_user);#ifndef INLINE_COPY_TO_USER unsigned long _copy_to_user (void __user *to,const void *from,unsigned long n) { +unsigned long to_copy = n;might_fault ();if (likely (access_ok (to, n))) {kasan_check_read (from, n); n =raw_copy_to_user (to, from, n); +kmsan_copy_to_user (to, from, to_copy, n); }return n; }
Signature · 1 line
2.23.0.866.gb869b98d4c-goog
[email protected]
maintainerRFC PATCH 25/26 kmsan: disable strscpy() optimization under KMSAN
In reply to [email protected]Disable the efficient 8-byte reading under KMSAN to avoid false positives.
To: Alexander Potapenko <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]
---
Change-Id: I25d1acf5c3df6eff85894cd94f5ddbe93308271c
--- lib/string.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
lib/string.c +4 -1
@@ -186,7 +186,10 @@ ssize_t strscpy(char *dest, const char *src, size_t count)if (count ==0 ||WARN_ON_ONCE (count >INT_MAX ))return -E2BIG ; -#ifdef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS +/** +* Disable the efficient 8-byte reading under KMSAN to avoid false positives. +*/ +#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS ) && !defined(CONFIG_KMSAN )/* * If src is unaligned, don't cross a page boundary, * since we don't know if the next page is mapped.
Signature · 1 line
2.23.0.866.gb869b98d4c-goog
[email protected]
maintainerRFC PATCH 26/26 net: kasan: kmsan: support CONFIG_GENERIC_CSUM on x86, enable it for KASAN/KMSAN
In reply to [email protected]This is needed to allow memory tools like KASAN and KMSAN see the memory accesses from the checksum code. Without CONFIG_GENERIC_CSUM the tools can't see memory accesses originating from handwritten assembly code.
For KASAN it's a question of detecting more bugs, for KMSAN using the C implementation also helps avoid false positives originating from seemingly uninitialized checksum values.
To: Alexander Potapenko <[email protected]>
Cc: Arnd Bergmann <[email protected]>
Cc: Michal Simek <[email protected]>
Cc: Andrey Ryabinin <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]
---
Change-Id: I645e2c097253a8d5717ad87e2e2df6f6f67251f3
--- arch/x86/Kconfig | 5 +++++ arch/x86/include/asm/checksum.h | 10 +++++++--- arch/x86/lib/Makefile | 2 ++ 3 files changed, 14 insertions(+), 3 deletions(-)
arch/x86/Kconfig +5 -0
@@ -272,6 +272,11 @@ config GENERIC_ISA_DMA def_bool y depends on ISA_DMA_API +config GENERIC_CSUM + bool + default y if KMSAN || KASAN + default n + config GENERIC_BUG def_bool y depends on BUG
arch/x86/include/asm/checksum.h +7 -3
@@ -1,6 +1,10 @@/* SPDX-License-Identifier: GPL-2.0 */ -#ifdef CONFIG_X86_32 -# include <asm/checksum_32.h> +#ifdef CONFIG_GENERIC_CSUM +# include <asm-generic/checksum.h> #else -# include <asm/checksum_64.h> +# ifdef CONFIG_X86_32 +# include <asm/checksum_32.h> +# else +# include <asm/checksum_64.h> +# endif #endif
arch/x86/lib/Makefile +2 -0
@@ -55,7 +55,9 @@ endif lib-$(CONFIG_X86_USE_3DNOW) += mmx_32.o else obj-y += iomap_copy_64.o +ifneq ($(CONFIG_GENERIC_CSUM),y) lib-y += csum-partial_64.o csum-copy_64.o csum-wrappers_64.o +endif lib-y += clear_page_64.o copy_page_64.o lib-y += memmove_64.o memset_64.o lib-y += copy_user_64.o
Signature · 1 line
2.23.0.866.gb869b98d4c-goog
Randy Dunlap
reviewerRe: [PATCH RFC v1 26/26] net: kasan: kmsan: support CONFIG_GENERIC_CSUM on x86, enable it for KASAN/KMSAN
Hi,
arch/x86/Kconfig config GENERIC_ISA_DMA · line 278
Quoted 12 lines · On 10/18/19 2:43 AM, [email protected] wrote:
arch/x86/Kconfig +4
-0@@ -272,6 +272,11 @@ config GENERIC_ISA_DMA def_bool y depends on ISA_DMA_API +config GENERIC_CSUM + bool + default y if KMSAN || KASAN + default n
Drop the "default n". It's already the default default and we prefer not to repeat the default default.
arch/x86/Kconfig config GENERIC_ISA_DMA · line 282
Quoted 4 lines
arch/x86/Kconfig +1
-0+ config GENERIC_BUG def_bool y depends on BUG
ta.
Signature · 1 line
~Randy