Add KernelMemorySanitizer infrastructure
Subsystems (43)
Alexander Potapenko
maintainerv2 cover 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 in the userspace [1]) 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 escapes to userspace, USB or DMA.
KMSAN has reported more than 300 bugs in the past few years (recently fixed bugs: [2]), most of them with the help of syzkaller. Such bugs keep getting introduced into the kernel despite new compiler warnings and other analyses (the 5.16 cycle already resulted in several KMSAN-reported bugs, e.g. [3]). Mitigations like total stack and heap initialization are unfortunately very far from being deployable.
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:
1. Changes and refactorings of existing code required to add KMSAN:
- [1/48] x86: add missing include to sparsemem.h
- [2/48] stackdepot: reserve 5 extra bits in depot_stack_handle_t
- [3/48] kasan: common: adapt to the new prototype of __stack_depot_save()
- [4/48] instrumented.h: allow instrumenting both sides of copy_from_user()
- [5/48] x86: asm: instrument usercopy in get_user() and __put_user_size()
- [6/48] asm-generic: instrument usercopy in cacheflush.h
- [11/48] libnvdimm/pfn_dev: increase MAX_STRUCT_PAGE_SIZE
- [12/48] kcsan: clang: retire CONFIG_KCSAN_KCOV_BROKEN
2. KMSAN-related declarations in generic code, KMSAN runtime library, docs and configs:
- [7/48] kmsan: add ReST documentation
- [8/48] kmsan: introduce __no_sanitize_memory and __no_kmsan_checks
- [10/48] x86: kmsan: pgtable: reduce vmalloc space
- [13/48] kmsan: add KMSAN runtime core
- [16/48] MAINTAINERS: add entry for KMSAN
- [30/48] kmsan: add tests for KMSAN
- [38/48] objtool: kmsan: list KMSAN API functions as uaccess-safe
- [43/48] x86: kmsan: use __msan_ string functions where possible.
- [48/48] x86: kmsan: enable KMSAN builds for x86
3. Adding hooks from different subsystems to notify KMSAN about memory state changes:
- [17/48] kmsan: mm: maintain KMSAN metadata for page operations
- [18/48] kmsan: mm: call KMSAN hooks from SLUB code
- [19/48] kmsan: handle task creation and exiting
- [20/48] kmsan: init: call KMSAN initialization routines
- [21/48] instrumented.h: add KMSAN support
- [23/48] kmsan: add iomap support
- [24/48] Input: libps2: mark data received in __ps2_command() as initialized
- [25/48] kmsan: dma: unpoison DMA mappings
- [42/48] x86: kmsan: handle open-coded assembly in lib/iomem.c
- [44/48] x86: kmsan: sync metadata pages on page fault
4. Changes that prevent false reports by explicitly initializing memory, disabling optimized code that may trick KMSAN, selectively skipping instrumentation:
- [14/48] kmsan: implement kmsan_init(), initialize READ_ONCE_NOCHECK()
- [15/48] kmsan: disable instrumentation of unsupported common kernel code
- [22/48] kmsan: unpoison @tlb in arch_tlb_gather_mmu()
- [26/48] kmsan: virtio: check/unpoison scatterlist in vring_map_one_sg()
- [27/48] kmsan: handle memory sent to/from USB
- [31/48] kernel: kmsan: don't instrument stacktrace.c
- [32/48] kmsan: disable strscpy() optimization under KMSAN
- [33/48] crypto: kmsan: disable accelerated configs under KMSAN
- [34/48] kmsan: disable physical page merging in biovec
- [35/48] kmsan: block: skip bio block merging logic for KMSAN
- [36/48] kmsan: kcov: unpoison area->list in kcov_remote_area_put()
- [37/48] security: kmsan: fix interoperability with auto-initialization
- [39/48] x86: kmsan: make READ_ONCE_TASK_STACK() return initialized values
- [40/48] x86: kmsan: disable instrumentation of unsupported code
- [41/48] x86: kmsan: skip shadow checks in __switch_to()
- [45/48] x86: kasan: kmsan: support CONFIG_GENERIC_CSUM on x86, enable it for KASAN/KMSAN
- [46/48] x86: fs: kmsan: disable CONFIG_DCACHE_WORD_ACCESS
5. Noinstr handling:
- [9/48] kmsan: mark noinstr as __no_sanitize_memory
- [28/48] kmsan: instrumentation.h: add instrumentation_begin_with_regs()
- [29/48] kmsan: entry: handle register passing from uninstrumented code
- [47/48] x86: kmsan: handle register passing from uninstrumented code
This patchset allows one to boot and run a defconfig+KMSAN kernel on a QEMU without known 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 a large config.
The patchset was generated relative to Linux v5.17. The most up-to-date KMSAN tree currently resides at
https://github.com/google/kmsan/.
One may find it handy to review these patches in Gerrit:
https://linux-review.googlesource.com/c/linux/kernel/git/torvalds/linux/+/12604/25
A huge thanks goes to the reviewers of the RFC patch series sent to LKML in 2020
(https://lore.kernel.org/all/[email protected]/).
[1] https://clang.llvm.org/docs/MemorySanitizer.html
[2] https://syzkaller.appspot.com/upstream/fixed?manager=ci-upstream-kmsan-gce
[3] https://lore.kernel.org/all/[email protected]/
Alexander Potapenko (47):
stackdepot: reserve 5 extra bits in depot_stack_handle_t
kasan: common: adapt to the new prototype of __stack_depot_save() instrumented.h: allow instrumenting both sides of copy_from_user()
x86: asm: instrument usercopy in get_user() and __put_user_size()
asm-generic: instrument usercopy in cacheflush.h
kmsan: add ReST documentation
kmsan: introduce __no_sanitize_memory and __no_kmsan_checks
kmsan: mark noinstr as __no_sanitize_memory
x86: kmsan: pgtable: reduce vmalloc space libnvdimm/pfn_dev: increase MAX_STRUCT_PAGE_SIZE
kcsan: clang: retire CONFIG_KCSAN_KCOV_BROKEN
kmsan: add KMSAN runtime core
kmsan: implement kmsan_init(), initialize READ_ONCE_NOCHECK()
kmsan: disable instrumentation of unsupported common kernel code
MAINTAINERS: add entry for KMSAN
kmsan: mm: maintain KMSAN metadata for page operations
kmsan: mm: call KMSAN hooks from SLUB code
kmsan: handle task creation and exiting
kmsan: init: call KMSAN initialization routines instrumented.h: add KMSAN support
kmsan: unpoison @tlb in arch_tlb_gather_mmu()
kmsan: add iomap support
Input: libps2: mark data received in __ps2_command() as initialized
kmsan: dma: unpoison DMA mappings
kmsan: virtio: check/unpoison scatterlist in vring_map_one_sg()
kmsan: handle memory sent to/from USB
kmsan: instrumentation.h: add instrumentation_begin_with_regs()
kmsan: entry: handle register passing from uninstrumented code
kmsan: add tests for KMSAN
kernel: kmsan: don't instrument stacktrace.c
kmsan: disable strscpy() optimization under KMSAN
crypto: kmsan: disable accelerated configs under KMSAN
kmsan: disable physical page merging in biovec
kmsan: block: skip bio block merging logic for KMSAN
kmsan: kcov: unpoison area->list in kcov_remote_area_put()
security: kmsan: fix interoperability with auto-initialization
objtool: kmsan: list KMSAN API functions as uaccess-safe
x86: kmsan: make READ_ONCE_TASK_STACK() return initialized values
x86: kmsan: disable instrumentation of unsupported code
x86: kmsan: skip shadow checks in __switch_to()
x86: kmsan: handle open-coded assembly in lib/iomem.c
x86: kmsan: use __msan_ string functions where possible.
x86: kmsan: sync metadata pages on page fault
x86: kasan: kmsan: support CONFIG_GENERIC_CSUM on x86, enable it for
KASAN/KMSAN
x86: fs: kmsan: disable CONFIG_DCACHE_WORD_ACCESS
x86: kmsan: handle register passing from uninstrumented code
x86: kmsan: enable KMSAN builds for x86
Dmitry Vyukov (1):
x86: add missing include to sparsemem.h
Documentation/dev-tools/index.rst | 1 + Documentation/dev-tools/kmsan.rst | 414 ++++++++++++++++++ MAINTAINERS | 12 + Makefile | 1 + arch/x86/Kconfig | 9 +- arch/x86/boot/Makefile | 1 + arch/x86/boot/compressed/Makefile | 1 + arch/x86/entry/common.c | 3 +- arch/x86/entry/vdso/Makefile | 3 + arch/x86/include/asm/checksum.h | 16 +- arch/x86/include/asm/idtentry.h | 10 +- arch/x86/include/asm/page_64.h | 13 + arch/x86/include/asm/pgtable_64_types.h | 41 +- arch/x86/include/asm/sparsemem.h | 2 + arch/x86/include/asm/string_64.h | 23 +- arch/x86/include/asm/uaccess.h | 7 + arch/x86/include/asm/unwind.h | 23 +- arch/x86/kernel/Makefile | 2 + arch/x86/kernel/cpu/Makefile | 1 + arch/x86/kernel/cpu/mce/core.c | 2 +- arch/x86/kernel/kvm.c | 2 +- arch/x86/kernel/nmi.c | 2 +- arch/x86/kernel/process_64.c | 1 + arch/x86/kernel/sev.c | 4 +- arch/x86/kernel/traps.c | 14 +- arch/x86/lib/Makefile | 2 + arch/x86/lib/iomem.c | 5 + arch/x86/mm/Makefile | 2 + arch/x86/mm/fault.c | 25 +- arch/x86/mm/init_64.c | 2 +- arch/x86/mm/ioremap.c | 3 + arch/x86/realmode/rm/Makefile | 1 + block/bio.c | 2 + block/blk.h | 7 + crypto/Kconfig | 30 ++ drivers/firmware/efi/libstub/Makefile | 1 + drivers/input/serio/libps2.c | 5 +- drivers/net/Kconfig | 1 + drivers/nvdimm/nd.h | 2 +- drivers/nvdimm/pfn_devs.c | 2 +- drivers/usb/core/urb.c | 2 + drivers/virtio/virtio_ring.c | 10 +- include/asm-generic/cacheflush.h | 9 +- include/asm-generic/rwonce.h | 5 +- include/linux/compiler-clang.h | 23 + include/linux/compiler-gcc.h | 6 + include/linux/compiler_types.h | 3 +- include/linux/fortify-string.h | 2 + include/linux/highmem.h | 3 + include/linux/instrumentation.h | 6 + include/linux/instrumented.h | 26 +- include/linux/kmsan-checks.h | 123 ++++++ include/linux/kmsan.h | 359 ++++++++++++++++ include/linux/mm_types.h | 12 + include/linux/sched.h | 5 + include/linux/stackdepot.h | 8 + include/linux/uaccess.h | 19 +- init/main.c | 3 + kernel/Makefile | 6 + kernel/dma/mapping.c | 9 +- kernel/entry/common.c | 22 +- kernel/exit.c | 2 + kernel/fork.c | 2 + kernel/kcov.c | 7 + kernel/locking/Makefile | 3 +- lib/Kconfig.debug | 1 + lib/Kconfig.kcsan | 11 - lib/Kconfig.kmsan | 39 ++ lib/Makefile | 1 + lib/iomap.c | 40 ++ lib/iov_iter.c | 9 +- lib/stackdepot.c | 29 +- lib/string.c | 8 + lib/usercopy.c | 3 +- mm/Makefile | 1 + mm/internal.h | 6 + mm/kasan/common.c | 2 +- mm/kmsan/Makefile | 26 ++ mm/kmsan/annotations.c | 28 ++ mm/kmsan/core.c | 463 ++++++++++++++++++++ mm/kmsan/hooks.c | 384 +++++++++++++++++ mm/kmsan/init.c | 240 +++++++++++ mm/kmsan/instrumentation.c | 267 ++++++++++++ mm/kmsan/kmsan.h | 188 +++++++++ mm/kmsan/kmsan_test.c | 536 ++++++++++++++++++++++++ mm/kmsan/report.c | 211 ++++++++++ mm/kmsan/shadow.c | 336 +++++++++++++++ mm/memory.c | 2 + mm/mmu_gather.c | 10 + mm/page_alloc.c | 18 + mm/slab.h | 1 + mm/slub.c | 21 +- mm/vmalloc.c | 20 +- scripts/Makefile.kmsan | 1 + scripts/Makefile.lib | 9 + security/Kconfig.hardening | 4 + tools/objtool/check.c | 19 + 97 files changed, 4209 insertions(+), 98 deletions(-) create mode 100644 Documentation/dev-tools/kmsan.rst 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/annotations.c create mode 100644 mm/kmsan/core.c create mode 100644 mm/kmsan/hooks.c create mode 100644 mm/kmsan/init.c create mode 100644 mm/kmsan/instrumentation.c create mode 100644 mm/kmsan/kmsan.h create mode 100644 mm/kmsan/kmsan_test.c create mode 100644 mm/kmsan/report.c create mode 100644 mm/kmsan/shadow.c create mode 100644 scripts/Makefile.kmsan
Signature · 1 line
2.35.1.1021.g381101b075-goog
Alexander Potapenko
maintainerPATCH v2 1/48 x86: add missing include to sparsemem.h
From: Dmitry Vyukov <[email protected]>
sparsemem.h:34:32: error: unknown type name 'phys_addr_t'
extern int phys_to_target_node(phys_addr_t start);
^
sparsemem.h:36:39: error: unknown type name 'u64'
extern int memory_add_physaddr_to_nid(u64 start);
^---
--- arch/x86/include/asm/sparsemem.h | 2 ++ 1 file changed, 2 insertions(+)
arch/x86/include/asm/sparsemem.h +2 -0
@@ -2,6 +2,8 @@#ifndef _ASM_X86_SPARSEMEM_H#define _ASM_X86_SPARSEMEM_H +#include <linux/types.h> +#ifdef CONFIG_SPARSEMEM /* * generic non-linear memory support:
Signature · 1 line
2.35.1.1021.g381101b075-goog
Alexander Potapenko
maintainerPATCH v2 2/48 stackdepot: reserve 5 extra bits in depot_stack_handle_t
In reply to Alexander PotapenkoSome users (currently only KMSAN) may want to use spare bits in depot_stack_handle_t. Let them do so by adding @extra_bits to __stack_depot_save() to store arbitrary flags, and providing stack_depot_get_extra_bits() to retrieve those flags.
---
--- include/linux/stackdepot.h | 8 ++++++++ lib/stackdepot.c | 29 ++++++++++++++++++++++++----- 2 files changed, 32 insertions(+), 5 deletions(-)
include/linux/stackdepot.h +8 -0
@@ -14,9 +14,15 @@#include <linux/gfp.h> typedef 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, +unsigned int extra_bits,gfp_t gfp_flags,bool can_alloc);/*
@@ -41,6 +47,8 @@ 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 stack_depot_get_extra_bits (depot_stack_handle_t handle); +int stack_depot_snprint (depot_stack_handle_t handle,char *buf,size_t size,int spaces);
lib/stackdepot.c +24 -5
@@ -42,7 +42,8 @@#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) ? \
@@ -55,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; }; };
@@ -73,6 +75,14 @@ static int next_slab_inited;static size_t depot_offset;static DEFINE_RAW_SPINLOCK (depot_lock); +unsigned int stack_depot_get_extra_bits (depot_stack_handle_t handle) +{ +union handle_parts parts = { .handle = handle }; + +return parts.extra; +} +EXPORT_SYMBOL (stack_depot_get_extra_bits); +static bool init_stack_slab (void **prealloc) {if (!*prealloc)
@@ -136,6 +146,7 @@ depot_alloc_stack(unsigned long *entries, int size, u32 hash, void **prealloc) 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,flex_array_size (stack, entries, size)); depot_offset += required_size;
@@ -320,6 +331,7 @@ EXPORT_SYMBOL_GPL(stack_depot_fetch);* * @entries: Pointer to storage array * @nr_entries: Size of the storage array +* @extra_bits: Flags to store in unused bits of depot_stack_handle_t * @alloc_flags: Allocation gfp flags * @can_alloc: Allocate stack slabs (increased chance of failure if false) *
@@ -331,6 +343,10 @@ EXPORT_SYMBOL_GPL(stack_depot_fetch);* If the stack trace in @entries is from an interrupt, only the portion up to * interrupt entry is saved. * +* Additional opaque flags can be passed in @extra_bits, stored in the unused +* bits of the stack handle, and retrieved using stack_depot_get_extra_bits() +* without calling stack_depot_fetch(). +* * Context: Any context, but setting @can_alloc to %false is required if * alloc_pages() cannot be used from the current context. Currently * this is the case from contexts where neither %GFP_ATOMIC nor
@@ -340,10 +356,11 @@ EXPORT_SYMBOL_GPL(stack_depot_fetch);*/ depot_stack_handle_t __stack_depot_save (unsigned long *entries,unsigned int nr_entries, +unsigned int extra_bits,gfp_t alloc_flags,bool can_alloc) {struct stack_record *found =NULL , **bucket; -depot_stack_handle_t retval =0 ; +union handle_parts retval = { .handle =0 };struct page *page =NULL ;void *prealloc =NULL ;unsigned long flags;
@@ -427,9 +444,11 @@ depot_stack_handle_t __stack_depot_save(unsigned long *entries,free_pages ((unsigned long )prealloc,STACK_ALLOC_ORDER ); }if (found) - retval = found->handle.handle; + retval.handle = found->handle.handle; fast_exit: -return retval; + retval.extra = extra_bits; + +return retval.handle; }EXPORT_SYMBOL_GPL (__stack_depot_save);
@@ -449,6 +468,6 @@ depot_stack_handle_t stack_depot_save(unsigned long *entries, unsigned int nr_entries, gfp_t alloc_flags) { - return __stack_depot_save(entries, nr_entries, alloc_flags, true); +return __stack_depot_save (entries, nr_entries,0 , alloc_flags, true); }EXPORT_SYMBOL_GPL (stack_depot_save);
Signature · 1 line
2.35.1.1021.g381101b075-goog
Alexander Potapenko
maintainerPATCH v2 3/48 kasan: common: adapt to the new prototype of __stack_depot_save()
In reply to Alexander PotapenkoPass extra_bits=0, as KASAN does not intend to store additional information in the stack handle. No functional change.
---
--- mm/kasan/common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
mm/kasan/common.c +1 -1
@@ -36,7 +36,7 @@ depot_stack_handle_t kasan_save_stack(gfp_t flags, bool can_alloc)unsigned int nr_entries; nr_entries =stack_trace_save (entries,ARRAY_SIZE (entries),0 ); -return __stack_depot_save (entries, nr_entries, flags, can_alloc); +return __stack_depot_save (entries, nr_entries,0 , flags, can_alloc); }void kasan_set_track (struct kasan_track *track,gfp_t flags)
Signature · 1 line
2.35.1.1021.g381101b075-goog
Alexander Potapenko
maintainerPATCH v2 4/48 instrumented.h: allow instrumenting both sides of copy_from_user()
In reply to Alexander PotapenkoIntroduce instrument_copy_from_user_before() and instrument_copy_from_user_after() hooks to be invoked before and after the call to copy_from_user().
KASAN and KCSAN will be only using instrument_copy_from_user_before(), but for KMSAN we'll need to insert code after copy_from_user().
---
--- include/linux/instrumented.h | 21 +++++++++++++++++++-- include/linux/uaccess.h | 19 ++++++++++++++----- lib/iov_iter.c | 9 ++++++--- lib/usercopy.c | 3 ++- 4 files changed, 41 insertions(+), 11 deletions(-)
include/linux/instrumented.h +19 -2
@@ -120,7 +120,7 @@ instrument_copy_to_user(void __user *to, const void *from, unsigned long n) }/** -* instrument_copy_from_user - instrument writes of copy_from_user +* instrument_copy_from_user_before - add instrumentation before copy_from_user * * Instrument writes to kernel memory, that are due to copy_from_user (and * variants). The instrumentation should be inserted before the accesses.
@@ -130,10 +130,27 @@ instrument_copy_to_user(void __user *to, const void *from, unsigned long n)* @n number of bytes to copy */ static __always_inline void -instrument_copy_from_user (const void *to,const void __user *from,unsigned long n) +instrument_copy_from_user_before (const void *to,const void __user *from,unsigned long n) {kasan_check_write (to, n);kcsan_check_write (to, n); } +/** +* instrument_copy_from_user_after - add instrumentation after copy_from_user +* +* Instrument writes to kernel memory, that are due to copy_from_user (and +* variants). The instrumentation should be inserted after the accesses. +* +* @to destination address +* @from source address +* @n number of bytes to copy +* @left number of bytes not copied (as returned by copy_from_user) +*/ +static __always_inline void +instrument_copy_from_user_after (const void *to,const void __user *from, +unsigned long n,unsigned long left) +{ +} +#endif /* _LINUX_INSTRUMENTED_H */
include/linux/uaccess.h +14 -5
@@ -98,20 +98,28 @@ static inline void force_uaccess_end(mm_segment_t oldfs)static __always_inline __must_check unsigned long __copy_from_user_inatomic (void *to,const void __user *from,unsigned long n) { -instrument_copy_from_user (to, from, n); +unsigned long res; + +instrument_copy_from_user_before (to, from, n);check_object_size (to, n, false); -return raw_copy_from_user (to, from, n); + res =raw_copy_from_user (to, from, n); +instrument_copy_from_user_after (to, from, n, res); +return res; }static __always_inline __must_check unsigned long __copy_from_user (void *to,const void __user *from,unsigned long n) { +unsigned long res; +might_fault (); +instrument_copy_from_user_before (to, from, n);if (should_fail_usercopy ())return n; -instrument_copy_from_user (to, from, n);check_object_size (to, n, false); -return raw_copy_from_user (to, from, n); + res =raw_copy_from_user (to, from, n); +instrument_copy_from_user_after (to, from, n, res); +return res; }/**
@@ -155,8 +163,9 @@ _copy_from_user(void *to, const void __user *from, unsigned long n)unsigned long res = n;might_fault ();if (!should_fail_usercopy () &&likely (access_ok (from, n))) { -instrument_copy_from_user (to, from, n); +instrument_copy_from_user_before (to, from, n); res =raw_copy_from_user (to, from, n); +instrument_copy_from_user_after (to, from, n, res); }if (unlikely (res))memset (to + (n - res),0 , res);
lib/iov_iter.c +6 -3
@@ -159,13 +159,16 @@ static int copyout(void __user *to, const void *from, size_t n)static int copyin (void *to,const void __user *from,size_t n) { +size_t res = n; +if (should_fail_usercopy ())return n;if (access_ok (from, n)) { -instrument_copy_from_user (to, from, n); - n =raw_copy_from_user (to, from, n); +instrument_copy_from_user_before (to, from, n); + res =raw_copy_from_user (to, from, n); +instrument_copy_from_user_after (to, from, n, res); } -return n; +return res; } static size_t copy_page_to_iter_iovec(struct page *page, size_t offset, size_t bytes,
lib/usercopy.c +2 -1
@@ -12,8 +12,9 @@ unsigned long _copy_from_user(void *to, const void __user *from, unsigned long nunsigned long res = n;might_fault ();if (!should_fail_usercopy () &&likely (access_ok (from, n))) { -instrument_copy_from_user (to, from, n); +instrument_copy_from_user_before (to, from, n); res =raw_copy_from_user (to, from, n); +instrument_copy_from_user_after (to, from, n, res); }if (unlikely (res))memset (to + (n - res),0 , res);
Signature · 1 line
2.35.1.1021.g381101b075-goog
Alexander Potapenko
maintainerPATCH v2 5/48 x86: asm: instrument usercopy in get_user() and __put_user_size()
In reply to Alexander PotapenkoUse hooks from instrumented.h to notify bug detection tools about usercopy events in get_user() and put_user_size().
It's still unclear how to instrument put_user(), which assumes that instrumentation code doesn't clobber RAX.
---
--- arch/x86/include/asm/uaccess.h | 7 +++++++ 1 file changed, 7 insertions(+)
arch/x86/include/asm/uaccess.h +7 -0
@@ -5,6 +5,7 @@* User space memory access functions */ #include <linux/compiler.h> +#include <linux/instrumented.h> #include <linux/kasan-checks.h> #include <linux/string.h> #include <asm/asm.h>
@@ -126,11 +127,13 @@ extern int __get_user_bad(void);int __ret_gu; \ register __inttype(*(ptr)) __val_gu asm("%"_ASM_DX); \__chk_user_ptr (ptr); \ +instrument_copy_from_user_before ((void *)&(x), ptr,sizeof (*(ptr))); \ asm volatile("call __" #fn "_%P4" \ : "=a" (__ret_gu), "=r" (__val_gu), \ ASM_CALL_CONSTRAINT \ : "0" (ptr), "i" (sizeof(*(ptr)))); \ (x) = (__force __typeof__(*(ptr))) __val_gu; \ + instrument_copy_from_user_after((void *)&(x), ptr, sizeof(*(ptr)), 0); \ __builtin_expect(__ret_gu, 0); \ })
@@ -275,7 +278,9 @@ extern void __put_user_nocheck_8(void);#define __put_user_size (x, ptr, size, label) \ do { \ + __typeof__(*(ptr)) __pus_val = x; \ __chk_user_ptr(ptr); \ + instrument_copy_to_user(ptr, &(__pus_val), size); \ switch (size) { \ case 1: \ __put_user_goto(x, ptr, "b", "iq", label); \
@@ -313,6 +318,7 @@ do { \#define __get_user_size (x, ptr, size, label) \ do { \ __chk_user_ptr(ptr); \ + instrument_copy_from_user_before((void *)&(x), ptr, size); \ switch (size) { \ case 1: { \ unsigned char x_u8__; \
@@ -332,6 +338,7 @@ do { \default : \ (x) =__get_user_bad (); \ } \ +instrument_copy_from_user_after ((void *)&(x), ptr, size,0 ); \ }while (0 ) #define __get_user_asm(x, addr, itype, ltype, label) \
Signature · 1 line
2.35.1.1021.g381101b075-goog
Alexander Potapenko
maintainerPATCH v2 6/48 asm-generic: instrument usercopy in cacheflush.h
In reply to Alexander PotapenkoNotify memory tools about usercopy events in copy_to_user_page() and copy_from_user_page().
---
--- include/asm-generic/cacheflush.h | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-)
include/asm-generic/cacheflush.h +8 -1
@@ -2,6 +2,8 @@#ifndef _ASM_GENERIC_CACHEFLUSH_H#define _ASM_GENERIC_CACHEFLUSH_H +#include <linux/instrumented.h> +struct mm_struct ;struct vm_area_struct ;struct page ;
@@ -105,6 +107,7 @@ static inline void flush_cache_vunmap(unsigned long start, unsigned long end)#ifndef copy_to_user_page#define copy_to_user_page (vma, page, vaddr, dst, src, len) \ do { \ + instrument_copy_to_user(dst, src, len); \ memcpy(dst, src, len); \ flush_icache_user_page(vma, page, vaddr, len); \ } while (0)
@@ -112,7 +115,11 @@ static inline void flush_cache_vunmap(unsigned long start, unsigned long end)#ifndef copy_from_user_page#define copy_from_user_page (vma, page, vaddr, dst, src, len) \ - memcpy(dst, src, len) + do { \ + instrument_copy_from_user_before(dst, src, len); \ + memcpy(dst, src, len); \ + instrument_copy_from_user_after(dst, src, len, 0); \ + } while (0)#endif #endif /* _ASM_GENERIC_CACHEFLUSH_H */
Signature · 1 line
2.35.1.1021.g381101b075-goog
Alexander Potapenko
maintainerPATCH v2 7/48 kmsan: add ReST documentation
In reply to Alexander PotapenkoAdd Documentation/dev-tools/kmsan.rst and reference it in the dev-tools index.
---
v2:
-- added a note that KMSAN is not intended for production use
--- Documentation/dev-tools/index.rst | 1 + Documentation/dev-tools/kmsan.rst | 414 ++++++++++++++++++++++++++++++ 2 files changed, 415 insertions(+) create mode 100644 Documentation/dev-tools/kmsan.rst
Documentation/dev-tools/index.rst +1 -0
@@ -24,6 +24,7 @@ Documentation/dev-tools/testing-overview.rst kcov gcov kasan + kmsan ubsan kmemleak kcsan
Documentation/dev-tools/kmsan.rst new file +414 -0
414 lines not on this page · raw message
Signature · 1 line
2.35.1.1021.g381101b075-goog
Alexander Potapenko
maintainerPATCH v2 8/48 kmsan: introduce __no_sanitize_memory and __no_kmsan_checks
In reply to Alexander Potapenko__no_sanitize_memory is a function attribute that instructs KMSAN to skip a function during instrumentation. This is needed to e.g. implement the noinstr functions.
__no_kmsan_checks 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. This behavior is overridden by __always_inline.
__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.
---
--- include/linux/compiler-clang.h | 23 +++++++++++++++++++++++ include/linux/compiler-gcc.h | 6 ++++++ 2 files changed, 29 insertions(+)
include/linux/compiler-clang.h +23 -0
@@ -51,6 +51,29 @@#define __no_sanitize_undefined#endif +#if __has_feature (memory_sanitizer) +#define __SANITIZE_MEMORY__ +/* +* Unlike other sanitizers, KMSAN still inserts code into functions marked with +* no_sanitize("kernel-memory"). Using disable_sanitizer_instrumentation +* provides the behavior consistent with other __no_sanitize_ attributes, +* guaranteeing that __no_sanitize_memory functions remain uninstrumented. +*/ +#define __no_sanitize_memory __disable_sanitizer_instrumentation + +/* +* The __no_kmsan_checks attribute ensures that a function does not produce +* false positive reports by: +* - initializing all local variables and memory stores in this function; +* - skipping all shadow checks; +* - passing initialized arguments to this function's callees. +*/ +#define __no_kmsan_checks __attribute__((no_sanitize("kernel-memory"))) +#else +#define __no_sanitize_memory +#define __no_kmsan_checks +#endif +/* * Support for __has_feature(coverage_sanitizer) was added in Clang 13 together * with no_sanitize("coverage"). Prior versions of Clang support coverage
include/linux/compiler-gcc.h +6 -0
@@ -129,6 +129,12 @@#define __SANITIZE_ADDRESS__#endif +/* +* GCC does not support KMSAN. +*/ +#define __no_sanitize_memory +#define __no_kmsan_checks +/* * Turn individual warnings and errors on and off locally, depending * on version.
Signature · 1 line
2.35.1.1021.g381101b075-goog
Alexander Potapenko
maintainerPATCH v2 9/48 kmsan: mark noinstr as __no_sanitize_memory
In reply to Alexander Potapenkonoinstr functions should never be instrumented, so make KMSAN skip them by applying the __no_sanitize_memory attribute.
---
v2:
-- moved this patch earlier in the series per Mark Rutland's request
--- include/linux/compiler_types.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
include/linux/compiler_types.h +2 -1
@@ -221,7 +221,8 @@ struct ftrace_likely_data {/* Section for code which can't be instrumented at all */ #define noinstr \ noinline notrace __attribute((__section__(".noinstr.text"))) \ - __no_kcsan __no_sanitize_address __no_profile __no_sanitize_coverage + __no_kcsan __no_sanitize_address __no_profile __no_sanitize_coverage \ + __no_sanitize_memory#endif /* __KERNEL__ */
Signature · 1 line
2.35.1.1021.g381101b075-goog
Alexander Potapenko
maintainerPATCH v2 10/48 x86: kmsan: pgtable: reduce vmalloc space
In reply to Alexander PotapenkoKMSAN 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.
---
v2:
-- added x86: to the title
--- arch/x86/include/asm/pgtable_64_types.h | 41 ++++++++++++++++++++++++- arch/x86/mm/init_64.c | 2 +- 2 files changed, 41 insertions(+), 2 deletions(-)
arch/x86/include/asm/pgtable_64_types.h +40 -1
@@ -139,7 +139,46 @@ extern unsigned int ptrs_per_p4d;# define VMEMMAP_START __VMEMMAP_BASE_L4#endif /* CONFIG_DYNAMIC_MEMORY_LAYOUT */ -#define VMALLOC_END (VMALLOC_START + (VMALLOC_SIZE_TB << 40) - 1) +#define VMEMORY_END (VMALLOC_START + (VMALLOC_SIZE_TB << 40) - 1) + +#ifndef CONFIG_KMSAN +#define VMALLOC_END VMEMORY_END +#else +/* +* In KMSAN builds vmalloc area is four times smaller, and the remaining 3/4 +* are used to keep the metadata for virtual pages. The memory formerly +* belonging to vmalloc area is now laid out as follows: +* +* 1st quarter: VMALLOC_START to VMALLOC_END - new vmalloc area +* 2nd quarter: KMSAN_VMALLOC_SHADOW_START to +* VMALLOC_END+KMSAN_VMALLOC_SHADOW_OFFSET - vmalloc area shadow +* 3rd quarter: KMSAN_VMALLOC_ORIGIN_START to +* VMALLOC_END+KMSAN_VMALLOC_ORIGIN_OFFSET - vmalloc area origins +* 4th quarter: KMSAN_MODULES_SHADOW_START to KMSAN_MODULES_ORIGIN_START +* - shadow for modules, +* KMSAN_MODULES_ORIGIN_START to +* KMSAN_MODULES_ORIGIN_START + MODULES_LEN - origins for modules. +*/ +#define VMALLOC_QUARTER_SIZE ((VMALLOC_SIZE_TB << 40) >> 2) +#define VMALLOC_END (VMALLOC_START + VMALLOC_QUARTER_SIZE - 1) + +/* +* vmalloc metadata addresses are calculated by adding shadow/origin offsets +* to vmalloc address. +*/ +#define KMSAN_VMALLOC_SHADOW_OFFSET VMALLOC_QUARTER_SIZE +#define KMSAN_VMALLOC_ORIGIN_OFFSET (VMALLOC_QUARTER_SIZE << 1) + +#define KMSAN_VMALLOC_SHADOW_START (VMALLOC_START + KMSAN_VMALLOC_SHADOW_OFFSET) +#define KMSAN_VMALLOC_ORIGIN_START (VMALLOC_START + KMSAN_VMALLOC_ORIGIN_OFFSET) + +/* +* The shadow/origin for modules are placed one by one in the last 1/4 of +* vmalloc space. +*/ +#define KMSAN_MODULES_SHADOW_START (VMALLOC_END + KMSAN_VMALLOC_ORIGIN_OFFSET + 1) +#define KMSAN_MODULES_ORIGIN_START (KMSAN_MODULES_SHADOW_START + MODULES_LEN) +#endif /* CONFIG_KMSAN */ #define MODULES_VADDR (__START_KERNEL_map + KERNEL_IMAGE_SIZE)/* The module sections ends with the start of the fixmap */
arch/x86/mm/init_64.c +1 -1
@@ -1287,7 +1287,7 @@ static void __init preallocate_vmalloc_pages(void)unsigned long addr;const char *lvl; -for (addr =VMALLOC_START ; addr <=VMALLOC_END ; addr =ALIGN (addr +1 ,PGDIR_SIZE )) { +for (addr =VMALLOC_START ; addr <=VMEMORY_END ; addr =ALIGN (addr +1 ,PGDIR_SIZE )) {pgd_t *pgd =pgd_offset_k (addr);p4d_t *p4d;pud_t *pud;
Signature · 1 line
2.35.1.1021.g381101b075-goog
Alexander Potapenko
maintainerPATCH v2 11/48 libnvdimm/pfn_dev: increase MAX_STRUCT_PAGE_SIZE
In reply to Alexander PotapenkoKMSAN adds extra metadata fields to struct page, so it does not fit into 64 bytes anymore.
---
--- drivers/nvdimm/nd.h | 2 +- drivers/nvdimm/pfn_devs.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-)
drivers/nvdimm/nd.h +1 -1
@@ -663,7 +663,7 @@ void devm_namespace_disable(struct device *dev, struct nd_namespace_common *ndns);#if IS_ENABLED (CONFIG_ND_CLAIM )/* max struct page size independent of kernel config */ -#define MAX_STRUCT_PAGE_SIZE 64 +#define MAX_STRUCT_PAGE_SIZE 128int nvdimm_setup_pfn (struct nd_pfn *nd_pfn,struct dev_pagemap *pgmap);#else static inline int nvdimm_setup_pfn(struct nd_pfn *nd_pfn,
drivers/nvdimm/pfn_devs.c +1 -1
@@ -785,7 +785,7 @@ static int nd_pfn_init(struct nd_pfn *nd_pfn)* when populating the vmemmap. This *should* be equal to * PMD_SIZE for most architectures. * -* Also make sure size of struct page is less than 64. We +* Also make sure size of struct page is less than 128. We * want to make sure we use large enough size here so that * we don't have a dynamic reserve space depending on * struct page size. But we also want to make sure we notice
Signature · 1 line
2.35.1.1021.g381101b075-goog
Alexander Potapenko
maintainerPATCH v2 12/48 kcsan: clang: retire CONFIG_KCSAN_KCOV_BROKEN
In reply to Alexander Potapenkokcov used to be broken prior to Clang 11, but right now that version is already the minimum required to build with KCSAN, because no prior compiler has "-tsan-distinguish-volatile=1".
Therefore KCSAN_KCOV_BROKEN is not needed anymore.
---
--- lib/Kconfig.kcsan | 11 ----------- 1 file changed, 11 deletions(-)
lib/Kconfig.kcsan +0 -11
@@ -10,21 +10,10 @@ config HAVE_KCSAN_COMPILER For the list of compilers that support KCSAN, please see <file:Documentation/dev-tools/kcsan.rst>. -config KCSAN_KCOV_BROKEN - def_bool KCOV && CC_HAS_SANCOV_TRACE_PC - depends on CC_IS_CLANG - depends on !$(cc-option,-Werror=unused-command-line-argument -fsanitize=thread -fsanitize-coverage=trace-pc) - help - Some versions of clang support either KCSAN and KCOV but not the - combination of the two. - See https://bugs.llvm.org/show_bug.cgi?id=45831 for the status - in newer releases. - menuconfig KCSAN bool "KCSAN: dynamic data race detector" depends on HAVE_ARCH_KCSAN && HAVE_KCSAN_COMPILER depends on DEBUG_KERNEL && !KASAN - depends on !KCSAN_KCOV_BROKEN select STACKTRACE help The Kernel Concurrency Sanitizer (KCSAN) is a dynamic
Signature · 1 line
2.35.1.1021.g381101b075-goog
Marco Elver
maintainerRe: [PATCH v2 12/48] kcsan: clang: retire CONFIG_KCSAN_KCOV_BROKEN
Quoted 9 lines · Alexander Potapenko
kcov used to be broken prior to Clang 11, but right now that version is already the minimum required to build with KCSAN, because no prior compiler has "-tsan-distinguish-volatile=1".
Therefore
KCSAN_KCOV_BROKENis not needed anymore.Suggested-by Marco Elver <[email protected]>Signed-off-by Alexander Potapenko <[email protected]>
FYI, this is superseded by
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=b027471adaf955efde6153d67f391fe1604b7292
Quoted 35 lines
---
--- lib/Kconfig.kcsan | 11 ----------- 1 file changed, 11 deletions(-)lib/Kconfig.kcsan +0
-12@@ -10,21 +10,10 @@ config HAVE_KCSAN_COMPILER For the list of compilers that support KCSAN, please see <file:Documentation/dev-tools/kcsan.rst>. -config KCSAN_KCOV_BROKEN - def_bool KCOV && CC_HAS_SANCOV_TRACE_PC - depends on CC_IS_CLANG - depends on !$(cc-option,-Werror=unused-command-line-argument -fsanitize=thread -fsanitize-coverage=trace-pc) - help - Some versions of clang support either KCSAN and KCOV but not the - combination of the two. - See https://bugs.llvm.org/show_bug.cgi?id=45831 for the status - in newer releases. - menuconfig KCSAN bool "KCSAN: dynamic data race detector" depends on HAVE_ARCH_KCSAN && HAVE_KCSAN_COMPILER depends on DEBUG_KERNEL && !KASAN - depends on !KCSAN_KCOV_BROKEN select STACKTRACE help The Kernel Concurrency Sanitizer (KCSAN) is a dynamic --2.35.1.1021.g381101b075-goog
Alexander Potapenko
maintainerPATCH v2 13/48 kmsan: add KMSAN runtime core
In reply to Alexander PotapenkoFor each memory location KernelMemorySanitizer maintains two types of metadata:
1. The so-called shadow of that location - а byte:byte mapping describing whether or not individual bits of memory are initialized (shadow is 0) or not (shadow is 1).
2. The origins of that location - а 4-byte:4-byte mapping containing 4-byte IDs of the stack traces where uninitialized values were created.
Each struct page now contains pointers to two struct pages holding KMSAN metadata (shadow and origins) for the original struct page. Utility routines in mm/kmsan/core.c and mm/kmsan/shadow.c handle the metadata creation, addressing, copying and checking. mm/kmsan/report.c performs error reporting in the cases an uninitialized value is used in a way that leads to undefined behavior.
KMSAN compiler instrumentation is responsible for tracking the metadata along with the kernel memory. mm/kmsan/instrumentation.c provides the implementation for instrumentation hooks that are called from files compiled with -fsanitize=kernel-memory.
To aid parameter passing (also done at instrumentation level), each task_struct now contains a struct kmsan_task_state used to track the metadata of function parameters and return values for that task.
Finally, this patch provides CONFIG_KMSAN that enables KMSAN, and declares CFLAGS_KMSAN, which are applied to files compiled with KMSAN. The KMSAN_SANITIZE:=n Makefile directive can be used to completely disable KMSAN instrumentation for certain files.
Similarly, KMSAN_ENABLE_CHECKS:=n disables KMSAN checks and makes newly created stack memory initialized.
Users can also use functions from include/linux/kmsan-checks.h to mark certain memory regions as uninitialized or initialized (this is called "poisoning" and "unpoisoning") or check that a particular region is initialized.
---
v2:
-- as requested by Greg K-H, moved hooks for different subsystems to respective patches,
rewrote the patch description;
-- addressed comments by Dmitry Vyukov;
-- added a note about KMSAN being not intended for production use. -- fix case of unaligned dst in kmsan_internal_memmove_metadata()
--- Makefile | 1 + include/linux/kmsan-checks.h | 64 +++++ include/linux/kmsan.h | 47 ++++ include/linux/mm_types.h | 12 + include/linux/sched.h | 5 + lib/Kconfig.debug | 1 + lib/Kconfig.kmsan | 23 ++ mm/Makefile | 1 + mm/kmsan/Makefile | 18 ++ mm/kmsan/core.c | 453 +++++++++++++++++++++++++++++++++++ mm/kmsan/hooks.c | 66 +++++ mm/kmsan/instrumentation.c | 267 +++++++++++++++++++++ mm/kmsan/kmsan.h | 183 ++++++++++++++ mm/kmsan/report.c | 211 ++++++++++++++++ mm/kmsan/shadow.c | 186 ++++++++++++++ scripts/Makefile.kmsan | 1 + scripts/Makefile.lib | 9 + 17 files changed, 1548 insertions(+) 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/core.c create mode 100644 mm/kmsan/hooks.c create mode 100644 mm/kmsan/instrumentation.c create mode 100644 mm/kmsan/kmsan.h create mode 100644 mm/kmsan/report.c create mode 100644 mm/kmsan/shadow.c create mode 100644 scripts/Makefile.kmsan
Makefile +1 -0
@@ -1006,6 +1006,7 @@ include-y := scripts/Makefile.extrawarn include-$(CONFIG_DEBUG_INFO) += scripts/Makefile.debug include-$(CONFIG_KASAN) += scripts/Makefile.kasan include-$(CONFIG_KCSAN) += scripts/Makefile.kcsan +include-$(CONFIG_KMSAN) += scripts/Makefile.kmsan include-$(CONFIG_UBSAN) += scripts/Makefile.ubsan include-$(CONFIG_KCOV) += scripts/Makefile.kcov include-$(CONFIG_GCC_PLUGINS) += scripts/Makefile.gcc-plugins
include/linux/kmsan-checks.h new file +64 -0
64 lines not on this page · raw message
include/linux/kmsan.h new file +47 -0
47 lines not on this page · raw message
include/linux/mm_types.h +12 -0
@@ -206,6 +206,18 @@ struct page {not kmapped, ie. highmem) */ #endif /* WANT_PAGE_VIRTUAL */ +#ifdef CONFIG_KMSAN +/* +* KMSAN metadata for this page: +* - shadow page: every bit indicates whether the corresponding +* bit of the original page is initialized (0) or not (1); +* - origin page: every 4 bytes contain an id of the stack trace +* where the uninitialized value was created. +*/ +struct page *kmsan_shadow; +struct page *kmsan_origin; +#endif +#ifdef LAST_CPUPID_NOT_IN_PAGE_FLAGS int _last_cpupid;#endif
include/linux/sched.h +5 -0
@@ -14,6 +14,7 @@#include <linux/pid.h> #include <linux/sem.h> #include <linux/shm.h> +#include <linux/kmsan.h> #include <linux/mutex.h> #include <linux/plist.h> #include <linux/hrtimer.h>
@@ -1349,6 +1350,10 @@ struct task_struct {#endif #endif +#ifdef CONFIG_KMSAN +struct kmsan_ctx kmsan_ctx; +#endif +#if IS_ENABLED (CONFIG_KUNIT )struct kunit *kunit_test;#endif
lib/Kconfig.debug +1 -0
@@ -968,6 +968,7 @@ config DEBUG_STACKOVERFLOW source "lib/Kconfig.kasan" source "lib/Kconfig.kfence" +source "lib/Kconfig.kmsan" endmenu # "Memory Debugging"
lib/Kconfig.kmsan new file +23 -0
@@ -0,0 +1,23 @@ +config HAVE_ARCH_KMSAN + bool + +config HAVE_KMSAN_COMPILER + def_bool (CC_IS_CLANG && $(cc-option,-fsanitize=kernel-memory -mllvm -msan-disable-checks=1)) + +config KMSAN + bool "KMSAN: detector of uninitialized values use" + depends on HAVE_ARCH_KMSAN && HAVE_KMSAN_COMPILER + depends on SLUB && DEBUG_KERNEL && !KASAN && !KCSAN + depends on CC_IS_CLANG && CLANG_VERSION >= 140000 + select STACKDEPOT + select STACKDEPOT_ALWAYS_INIT + help + KernelMemorySanitizer (KMSAN) is a dynamic detector of uses of + uninitialized values in the kernel. It is based on compiler + instrumentation provided by Clang and thus requires Clang to build. + + An important note is that KMSAN is not intended for production use, + because it drastically increases kernel memory footprint and slows + the whole system down. + + See <file:Documentation/dev-tools/kmsan.rst> for more details.
mm/Makefile +1 -0
@@ -89,6 +89,7 @@ obj-$(CONFIG_SLAB) += slab.o obj-$(CONFIG_SLUB) += slub.o obj-$(CONFIG_KASAN) += kasan/ obj-$(CONFIG_KFENCE) += kfence/ +obj-$(CONFIG_KMSAN) += kmsan/ obj-$(CONFIG_FAILSLAB) += failslab.o obj-$(CONFIG_MEMTEST) += memtest.o obj-$(CONFIG_MIGRATION) += migrate.o
mm/kmsan/Makefile new file +18 -0
@@ -0,0 +1,18 @@ +obj-y := core.o instrumentation.o hooks.o report.o shadow.o + +KMSAN_SANITIZE := n +KCOV_INSTRUMENT := n +UBSAN_SANITIZE := n + +# Disable instrumentation of KMSAN runtime with other tools. +CC_FLAGS_KMSAN_RUNTIME := -fno-stack-protector +CC_FLAGS_KMSAN_RUNTIME += $(call cc-option,-fno-conserve-stack) +CC_FLAGS_KMSAN_RUNTIME += -DDISABLE_BRANCH_PROFILING + +CFLAGS_REMOVE.o = $(CC_FLAGS_FTRACE) + +CFLAGS_core.o := $(CC_FLAGS_KMSAN_RUNTIME) +CFLAGS_hooks.o := $(CC_FLAGS_KMSAN_RUNTIME) +CFLAGS_instrumentation.o := $(CC_FLAGS_KMSAN_RUNTIME) +CFLAGS_report.o := $(CC_FLAGS_KMSAN_RUNTIME) +CFLAGS_shadow.o := $(CC_FLAGS_KMSAN_RUNTIME)
mm/kmsan/core.c new file +453 -0
453 lines not on this page · raw message
mm/kmsan/hooks.c new file +66 -0
66 lines not on this page · raw message
mm/kmsan/instrumentation.c new file +267 -0
267 lines not on this page · raw message
mm/kmsan/kmsan.h new file +183 -0
183 lines not on this page · raw message
mm/kmsan/report.c new file +211 -0
211 lines not on this page · raw message
mm/kmsan/shadow.c new file +186 -0
186 lines not on this page · raw message
scripts/Makefile.kmsan new file +1 -0
@@ -0,0 +1 @@ +export CFLAGS_KMSAN := -fsanitize=kernel-memory
scripts/Makefile.lib +9 -0
@@ -162,6 +162,15 @@ _c_flags += $(if $(patsubst n%,, \ endif endif +ifeq ($(CONFIG_KMSAN),y) +_c_flags += $(if $(patsubst n%,, \ + $(KMSAN_SANITIZE_$(basetarget).o)$(KMSAN_SANITIZE)y), \ + $(CFLAGS_KMSAN)) +_c_flags += $(if $(patsubst n%,, \ + $(KMSAN_ENABLE_CHECKS_$(basetarget).o)$(KMSAN_ENABLE_CHECKS)y), \ + , -mllvm -msan-disable-checks=1) +endif + ifeq ($(CONFIG_UBSAN),y) _c_flags += $(if $(patsubst n%,, \ $(UBSAN_SANITIZE_$(basetarget).o)$(UBSAN_SANITIZE)$(CONFIG_UBSAN_SANITIZE_ALL)), \
Signature · 1 line
2.35.1.1021.g381101b075-goog
Peter Zijlstra
maintainerRe: [PATCH v2 13/48] kmsan: add KMSAN runtime core
Quoted 48 lines · Alexander Potapenko
mm/kmsan/instrumentation.c new file +48
-0+/* Handle llvm.memmove intrinsic. */ +void *__msan_memmove (void *dst,const void *src,uintptr_t n) +{ +void *result; + + result =__memmove (dst, src, n); +if (!n) +/* Some people call memmove() with zero length. */ +return result; +if (!kmsan_enabled ||kmsan_in_runtime ()) +return result; + +kmsan_internal_memmove_metadata (dst, (void *)src, n); + +return result; +} +EXPORT_SYMBOL (__msan_memmove); + +/* Handle llvm.memcpy intrinsic. */ +void *__msan_memcpy (void *dst,const void *src,uintptr_t n) +{ +void *result; + + result =__memcpy (dst, src, n); +if (!n) +/* Some people call memcpy() with zero length. */ +return result; + +if (!kmsan_enabled ||kmsan_in_runtime ()) +return result; + +/* Using memmove instead of memcpy doesn't affect correctness. */ +kmsan_internal_memmove_metadata (dst, (void *)src, n); + +return result; +} +EXPORT_SYMBOL (__msan_memcpy); + +/* Handle llvm.memset intrinsic. */ +void *__msan_memset (void *dst,int c,uintptr_t n) +{ +void *result; + + result =__memset (dst, c, n); +if (!kmsan_enabled ||kmsan_in_runtime ()) +return result; + +kmsan_enter_runtime ();
mm/kmsan/instrumentation.c line 170
Quoted 10 lines
mm/kmsan/instrumentation.c new file +10
-0+/* +* Clang doesn't pass parameter metadata here, so it is impossible to +* use shadow of @c to set up the shadow for @dst. +*/ +kmsan_internal_unpoison_memory (dst, n,/*checked*/ false); +kmsan_leave_runtime (); + +return result; +} +EXPORT_SYMBOL (__msan_memset);
This, we need this same for KASAN. KASAN must be changed to have the mem*() intrinsics emit __asan_mem*(), such that we can have uninstrumented base functions.
Currently we seem to have the problem that when a noinstr function trips one of those instrinsics it'll emit a call to an instrumented function, which is a complete no-no.
Also see:
https://lore.kernel.org/all/[email protected]/T/#m2049a14be400d4ae2b54a1f7da3ede28b7fd7564
Given the helpful feedback there, Mark and me are going to unilaterally break Kasan by deleting the existing wrappers.
Peter Zijlstra
maintainerRe: [PATCH v2 13/48] kmsan: add KMSAN runtime core
Quoted 68 lines · Peter Zijlstra
Quoted 48 lines · Alexander Potapenko
(quoted diff) +48
-0+/* Handle llvm.memmove intrinsic. */ +void *__msan_memmove(void *dst, const void *src, uintptr_t n) +{ + void *result; + + result = __memmove(dst, src, n); + if (!n) + /* Some people call memmove() with zero length. */ + return result; + if (!kmsan_enabled || kmsan_in_runtime()) + return result; + + kmsan_internal_memmove_metadata(dst, (void *)src, n); + + return result; +} +EXPORT_SYMBOL(__msan_memmove); + +/* Handle llvm.memcpy intrinsic. */ +void *__msan_memcpy(void *dst, const void *src, uintptr_t n) +{ + void *result; + + result = __memcpy(dst, src, n); + if (!n) + /* Some people call memcpy() with zero length. */ + return result; + + if (!kmsan_enabled || kmsan_in_runtime()) + return result; + + /* Using memmove instead of memcpy doesn't affect correctness. */ + kmsan_internal_memmove_metadata(dst, (void *)src, n); + + return result; +} +EXPORT_SYMBOL(__msan_memcpy); + +/* Handle llvm.memset intrinsic. */ +void *__msan_memset(void *dst, int c, uintptr_t n) +{ + void *result; + + result = __memset(dst, c, n); + if (!kmsan_enabled || kmsan_in_runtime()) + return result; + + kmsan_enter_runtime();Review
Quoted 10 lines
(quoted diff) +10
-0+ /* + * Clang doesn't pass parameter metadata here, so it is impossible to + * use shadow of @c to set up the shadow for @dst. + */ + kmsan_internal_unpoison_memory(dst, n, /*checked*/ false); + kmsan_leave_runtime(); + + return result; +} +EXPORT_SYMBOL(__msan_memset);This, we need this same for KASAN. KASAN must be changed to have the mem*() intrinsics emit __asan_mem*(), such that we can have uninstrumented base functions.
Currently we seem to have the problem that when a noinstr function trips one of those instrinsics it'll emit a call to an instrumented function, which is a complete no-no.
Quoted 6 lines
Also see:
https://lore.kernel.org/all/[email protected]/T/#m2049a14be400d4ae2b54a1f7da3ede28b7fd7564
Given the helpful feedback there, Mark and me are going to unilaterally break Kasan by deleting the existing wrappers.
specifically, I was thinking of something like the below...
(potentially more architectures are affected)
---
arch/arm64/Kconfig +1 -0
@@ -149,6 +149,7 @@ config ARM64 select HAVE_ARCH_JUMP_LABEL_RELATIVE select HAVE_ARCH_KASAN if !(ARM64_16K_PAGES && ARM64_VA_BITS_48) select HAVE_ARCH_KASAN_VMALLOC if HAVE_ARCH_KASAN + select HAVE_ARCH_KASAN_NOINSTR if HAVE_ARCH_KASAN select HAVE_ARCH_KASAN_SW_TAGS if HAVE_ARCH_KASAN select HAVE_ARCH_KASAN_HW_TAGS if (HAVE_ARCH_KASAN && ARM64_MTE) # Some instrumentation may be unsound, hence EXPERT
arch/arm64/lib/memcpy.S +2 -2
@@ -242,12 +242,12 @@ SYM_FUNC_END(__pi_memcpy) SYM_FUNC_ALIAS(__memcpy, __pi_memcpy) EXPORT_SYMBOL(__memcpy) -SYM_FUNC_ALIAS_WEAK(memcpy, __memcpy) +SYM_FUNC_ALIAS(memcpy, __memcpy) EXPORT_SYMBOL(memcpy) SYM_FUNC_ALIAS(__pi_memmove, __pi_memcpy) SYM_FUNC_ALIAS(__memmove, __pi_memmove) EXPORT_SYMBOL(__memmove) -SYM_FUNC_ALIAS_WEAK(memmove, __memmove) +SYM_FUNC_ALIAS(memmove, __memmove) EXPORT_SYMBOL(memmove)
arch/arm64/lib/memset.S +1 -1
@@ -206,5 +206,5 @@ SYM_FUNC_END(__pi_memset) SYM_FUNC_ALIAS(__memset, __pi_memset) EXPORT_SYMBOL(__memset) -SYM_FUNC_ALIAS_WEAK(memset, __pi_memset) +SYM_FUNC_ALIAS(memset, __pi_memset) EXPORT_SYMBOL(memset)
arch/x86/Kconfig +1 -0
@@ -166,6 +166,7 @@ config X86 select HAVE_ARCH_JUMP_LABEL_RELATIVE select HAVE_ARCH_KASAN if X86_64 select HAVE_ARCH_KASAN_VMALLOC if X86_64 + select HAVE_ARCH_KASAN_NOINSTR if X86_64 select HAVE_ARCH_KFENCE select HAVE_ARCH_KGDB select HAVE_ARCH_MMAP_RND_BITS if MMU
arch/x86/lib/memcpy_64.S +1 -1
@@ -42,7 +42,7 @@ SYM_FUNC_START(__memcpy) SYM_FUNC_END(__memcpy) EXPORT_SYMBOL(__memcpy) -SYM_FUNC_ALIAS_WEAK(memcpy, __memcpy) +SYM_FUNC_ALIAS(memcpy, __memcpy) EXPORT_SYMBOL(memcpy) /*
arch/x86/lib/memmove_64.S +1 -1
@@ -208,5 +208,5 @@ SYM_FUNC_START(__memmove) SYM_FUNC_END(__memmove) EXPORT_SYMBOL(__memmove) -SYM_FUNC_ALIAS_WEAK(memmove, __memmove) +SYM_FUNC_ALIAS(memmove, __memmove) EXPORT_SYMBOL(memmove)
arch/x86/lib/memset_64.S +1 -1
@@ -43,7 +43,7 @@ SYM_FUNC_START(__memset) SYM_FUNC_END(__memset) EXPORT_SYMBOL(__memset) -SYM_FUNC_ALIAS_WEAK(memset, __memset) +SYM_FUNC_ALIAS(memset, __memset) EXPORT_SYMBOL(memset) /*
lib/Kconfig.kasan +3 -0
@@ -12,6 +12,9 @@ config HAVE_ARCH_KASAN_HW_TAGS config HAVE_ARCH_KASAN_VMALLOC bool +config HAVE_ARCH_KASAN_NOINSTR + bool + config ARCH_DISABLE_KASAN_INLINE bool help
mm/kasan/shadow.c +40 -0
@@ -38,6 +38,44 @@ bool __kasan_check_write(const volatile void *p, unsigned int size) }EXPORT_SYMBOL (__kasan_check_write); +/* +* noinstr archs require uninstrumented base functions, as such their kasan +* implementation must use __asan_mem*() functions if they want +* instrumentation. +*/ +#ifdef HAVE_ARCH_KASAN_NOINSTR + +void *__asan_memset (void *addr,int c,size_t len) +{ +if (!kasan_check_range ((unsigned long )addr, len, true, _RET_IP_)) +return NULL ; + +return __memset (addr, c, len); +} + +#ifdef __HAVE_ARCH_MEMMOVE +#undef memmove +void *__asan_memmove (void *dest,const void *src,size_t len) +{ +if (!kasan_check_range ((unsigned long )src, len, false, _RET_IP_) || + !kasan_check_range ((unsigned long )dest, len, true, _RET_IP_)) +return NULL ; + +return __memmove (dest, src, len); +} +#endif + +#undef memcpy +void *__asan_memcpy (void *dest,const void *src,size_t len) +{ +if (!kasan_check_range ((unsigned long )src, len, false, _RET_IP_) || + !kasan_check_range ((unsigned long )dest, len, true, _RET_IP_)) +return NULL ; + +return __memcpy (dest, src, len); +} +#else +#undef memsetvoid *memset (void *addr,int c,size_t len) {
@@ -69,6 +107,8 @@ void *memcpy(void *dest, const void *src, size_t len) return __memcpy(dest, src, len); } +#endif +void kasan_poison (const void *addr,size_t size,u8 value,bool init) {void *shadow_start, *shadow_end;
Alexander Potapenko
maintainerRe: [PATCH v2 13/48] kmsan: add KMSAN runtime core
In reply to Alexander Potapenkomm/kmsan/core.c line 71
Quoted 8 lines
mm/kmsan/core.c new file +8
-0+depot_stack_handle_t kmsan_save_stack_with_flags (gfp_t flags, +unsigned int extra) +{ +unsigned long entries[KMSAN_STACK_DEPTH ]; +unsigned int nr_entries; + + nr_entries =stack_trace_save (entries,KMSAN_STACK_DEPTH ,0 ); + nr_entries =filter_irq_stacks (entries, nr_entries);
This is redundant, __stack_depot_save() below already calls filter_irq_stacks().
Quoted 10 lines
mm/kmsan/core.c new file +10
-0+ +if (depth >=MAX_CHAIN_DEPTH ) { +static atomic_long_t kmsan_skipped_origins; +long skipped =atomic_long_inc_return (&kmsan_skipped_origins); + +if (skipped %NUM_SKIPPED_TO_WARN ==0 ) { +pr_warn ("not chained %ld origins\n" , skipped); +dump_stack (); +kmsan_print_origin (id); + }
mm/kmsan/core.c line 246
Quoted 10 lines
mm/kmsan/core.c new file +10
-0+return id; + } + depth++; + extra_bits =kmsan_extra_bits (depth, uaf); + + entries[0 ] =KMSAN_CHAIN_MAGIC_ORIGIN ; + entries[1 ] =kmsan_save_stack_with_flags (GFP_ATOMIC ,0 ); + entries[2 ] = id; +return __stack_depot_save (entries,ARRAY_SIZE (entries), extra_bits, +GFP_ATOMIC , true);
@entries is initialized in non-instrumented code, so passing it to filter_irq_stacks() etc. will result in false positives, unless we explicitly unpoison it.
(right now KMSAN does not instrument kernel/stacktrace.c, but it probably should)
Alexander Potapenko
maintainerPATCH v2 14/48 kmsan: implement kmsan_init(), initialize READ_ONCE_NOCHECK()
In reply to Alexander Potapenkokmsan_init() is a macro that takes a possibly uninitialized value and returns an initialized value of the same type. It can be used e.g. in cases when a value comes from non-instrumented code to avoid false positive reports.
In particular, we use kmsan_init() in READ_ONCE_NOCHECK() so that it returns initialized values. This helps defeat false positives e.g. from leftover stack contents accessed by stack unwinders.
---
--- include/asm-generic/rwonce.h | 5 +++-- include/linux/kmsan-checks.h | 40 ++++++++++++++++++++++++++++++++++++ mm/kmsan/Makefile | 5 ++++- mm/kmsan/annotations.c | 28 +++++++++++++++++++++++++ 4 files changed, 75 insertions(+), 3 deletions(-) create mode 100644 mm/kmsan/annotations.c
include/asm-generic/rwonce.h +3 -2
@@ -25,6 +25,7 @@#include <linux/compiler_types.h> #include <linux/kasan-checks.h> #include <linux/kcsan-checks.h> +#include <linux/kmsan-checks.h> /* * Yes, this permits 64-bit accesses on 32-bit architectures. These will
@@ -69,14 +70,14 @@ unsigned long __read_once_word_nocheck(const void *addr)/* * Use READ_ONCE_NOCHECK() instead of READ_ONCE() if you need to load a -* word from memory atomically but without telling KASAN/KCSAN. This is +* word from memory atomically but without telling KASAN/KCSAN/KMSAN. This is * usually used by unwinding code when walking the stack of a running process. */ #define READ_ONCE_NOCHECK (x) \ ({ \ compiletime_assert(sizeof(x) == sizeof(unsigned long), \ "Unsupported access size for READ_ONCE_NOCHECK()."); \ - (typeof(x))__read_once_word_nocheck(&(x)); \ + kmsan_init((typeof(x))__read_once_word_nocheck(&(x))); \ }) static __no_kasan_or_inline
include/linux/kmsan-checks.h +40 -0
@@ -14,6 +14,44 @@#ifdef CONFIG_KMSAN +/* +* Helper functions that mark the return value initialized. +* See mm/kmsan/annotations.c. +*/ +u8 kmsan_init_1 (u8 value); +u16 kmsan_init_2 (u16 value); +u32 kmsan_init_4 (u32 value); +u64 kmsan_init_8 (u64 value); + +static inline void *kmsan_init_ptr (void *ptr) +{ +return (void *)kmsan_init_8 ((u64 )ptr); +} + +static inline char kmsan_init_char (char value) +{ +return (u8 )kmsan_init_1 ((u8 )value); +} + +#define __decl_kmsan_init_type (type, fn) unsigned type : fn, signed type : fn + +/** +* kmsan_init - Make the value initialized. +* @val: 1-, 2-, 4- or 8-byte integer that may be treated as uninitialized by +* KMSAN. +* +* Return: value of @val that KMSAN treats as initialized. +*/ +#define kmsan_init (val) \ + ( \ + (typeof(val))(_Generic((val), \ + __decl_kmsan_init_type(char, kmsan_init_1), \ + __decl_kmsan_init_type(short, kmsan_init_2), \ + __decl_kmsan_init_type(int, kmsan_init_4), \ + __decl_kmsan_init_type(long, kmsan_init_8), \ + char : kmsan_init_char, \ + void * : kmsan_init_ptr)(val))) +/** * kmsan_poison_memory() - Mark the memory range as uninitialized. * @address: address to start with.
@@ -48,6 +86,8 @@ void kmsan_check_memory(const void *address, size_t size);#else +#define kmsan_init (value) (value) +static inline void kmsan_poison_memory (const void *address,size_t size,gfp_t flags) {
mm/kmsan/Makefile +4 -1
@@ -1,9 +1,11 @@ -obj-y := core.o instrumentation.o hooks.o report.o shadow.o +obj-y := core.o instrumentation.o hooks.o report.o shadow.o annotations.o KMSAN_SANITIZE := n KCOV_INSTRUMENT := n UBSAN_SANITIZE := n +KMSAN_SANITIZE_kmsan_annotations.o := y + # Disable instrumentation of KMSAN runtime with other tools. CC_FLAGS_KMSAN_RUNTIME := -fno-stack-protector CC_FLAGS_KMSAN_RUNTIME += $(call cc-option,-fno-conserve-stack)
@@ -11,6 +13,7 @@ CC_FLAGS_KMSAN_RUNTIME += -DDISABLE_BRANCH_PROFILING CFLAGS_REMOVE.o = $(CC_FLAGS_FTRACE) +CFLAGS_annotations.o := $(CC_FLAGS_KMSAN_RUNTIME) CFLAGS_core.o := $(CC_FLAGS_KMSAN_RUNTIME) CFLAGS_hooks.o := $(CC_FLAGS_KMSAN_RUNTIME) CFLAGS_instrumentation.o := $(CC_FLAGS_KMSAN_RUNTIME)
mm/kmsan/annotations.c new file +28 -0
@@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-2.0 +/* +* KMSAN annotations. +* +* The kmsan_init_SIZE functions reside in a separate translation unit to +* prevent inlining them. Clang may inline functions marked with +* __no_sanitize_memory attribute into functions without it, which effectively +* results in ignoring the attribute. +* +* Copyright (C) 2017-2022 Google LLC +* Author: Alexander Potapenko <[email protected]> +* +*/ + +#include <linux/export.h> +#include <linux/kmsan-checks.h> + +#define DECLARE_KMSAN_INIT (size, t) \ + __no_sanitize_memory t kmsan_init_##size(t value) \ + { \ + return value; \ + } \ + EXPORT_SYMBOL(kmsan_init_##size) + +DECLARE_KMSAN_INIT (1 , u8); +DECLARE_KMSAN_INIT (2 , u16); +DECLARE_KMSAN_INIT (4 , u32); +DECLARE_KMSAN_INIT (8 , u64);
Signature · 1 line
2.35.1.1021.g381101b075-goog
Alexander Potapenko
maintainerPATCH v2 15/48 kmsan: disable instrumentation of unsupported common kernel code
In reply to Alexander PotapenkoEFI stub cannot be linked with KMSAN runtime, so we disable instrumentation for it.
Instrumenting kcov, stackdepot or lockdep leads to infinite recursion caused by instrumentation hooks calling instrumented code again.
This patch was previously part of "kmsan: disable KMSAN instrumentation for certain kernel parts", but was split away per Mark Rutland's request.
---
--- drivers/firmware/efi/libstub/Makefile | 1 + kernel/Makefile | 1 + kernel/locking/Makefile | 3 ++- lib/Makefile | 1 + 4 files changed, 5 insertions(+), 1 deletion(-)
drivers/firmware/efi/libstub/Makefile +1 -0
@@ -46,6 +46,7 @@ GCOV_PROFILE := n # Sanitizer runtimes are unavailable and cannot be linked here. KASAN_SANITIZE := n KCSAN_SANITIZE := n +KMSAN_SANITIZE := n UBSAN_SANITIZE := n OBJECT_FILES_NON_STANDARD := y
kernel/Makefile +1 -0
@@ -39,6 +39,7 @@ KCOV_INSTRUMENT_kcov.o := n KASAN_SANITIZE_kcov.o := n KCSAN_SANITIZE_kcov.o := n UBSAN_SANITIZE_kcov.o := n +KMSAN_SANITIZE_kcov.o := n CFLAGS_kcov.o := $(call cc-option, -fno-conserve-stack) -fno-stack-protector # Don't instrument error handlers
kernel/locking/Makefile +2 -1
@@ -5,8 +5,9 @@ KCOV_INSTRUMENT := n obj-y += mutex.o semaphore.o rwsem.o percpu-rwsem.o -# Avoid recursion lockdep -> KCSAN -> ... -> lockdep. +# Avoid recursion lockdep -> sanitizer -> ... -> lockdep. KCSAN_SANITIZE_lockdep.o := n +KMSAN_SANITIZE_lockdep.o := n ifdef CONFIG_FUNCTION_TRACER CFLAGS_REMOVE_lockdep.o = $(CC_FLAGS_FTRACE)
lib/Makefile +1 -0
@@ -269,6 +269,7 @@ obj-$(CONFIG_IRQ_POLL) += irq_poll.o CFLAGS_stackdepot.o += -fno-builtin obj-$(CONFIG_STACKDEPOT) += stackdepot.o KASAN_SANITIZE_stackdepot.o := n +KMSAN_SANITIZE_stackdepot.o := n KCOV_INSTRUMENT_stackdepot.o := n obj-$(CONFIG_REF_TRACKER) += ref_tracker.o
Signature · 1 line
2.35.1.1021.g381101b075-goog
Alexander Potapenko
maintainerPATCH v2 16/48 MAINTAINERS: add entry for KMSAN
In reply to Alexander PotapenkoAdd entry for KMSAN maintainers/reviewers.
---
--- MAINTAINERS | 12 ++++++++++++ 1 file changed, 12 insertions(+)
MAINTAINERS +12 -0
@@ -10721,6 +10721,18 @@ F: kernel/kmod.c F: lib/test_kmod.c F: tools/testing/selftests/kmod/ +KMSAN +M: Alexander Potapenko <[email protected]> +R: Marco Elver <[email protected]> +R: Dmitry Vyukov <[email protected]> +L: [email protected] +S: Maintained +F: Documentation/dev-tools/kmsan.rst +F: include/linux/kmsan*.h +F: lib/Kconfig.kmsan +F: mm/kmsan/ +F: scripts/Makefile.kmsan + KPROBES M: Naveen N. Rao <[email protected]> M: Anil S Keshavamurthy <[email protected]>
Signature · 1 line
2.35.1.1021.g381101b075-goog
Alexander Potapenko
maintainerPATCH v2 17/48 kmsan: mm: maintain KMSAN metadata for page operations
In reply to Alexander PotapenkoInsert KMSAN hooks that make the necessary bookkeeping changes:
- poison page shadow and origins in alloc_pages()/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();
---
v2:
-- move page metadata hooks implementation here
-- remove call to kmsan_memblock_free_pages()
--- arch/x86/include/asm/page_64.h | 13 ++++ arch/x86/mm/ioremap.c | 3 + include/linux/highmem.h | 3 + include/linux/kmsan.h | 123 +++++++++++++++++++++++++++++++++ mm/internal.h | 6 ++ mm/kmsan/hooks.c | 87 +++++++++++++++++++++++ mm/kmsan/shadow.c | 114 ++++++++++++++++++++++++++++++ mm/memory.c | 2 + mm/page_alloc.c | 11 +++ mm/vmalloc.c | 20 +++++- 10 files changed, 380 insertions(+), 2 deletions(-)
arch/x86/include/asm/page_64.h +13 -0
@@ -45,14 +45,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_unpoison_memory (const void *addr,size_t size); +#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_unpoison_memory (page_copy,PAGE_SIZE ); +#endif }void copy_page (void *to,void *from);
arch/x86/mm/ioremap.c +3 -0
@@ -17,6 +17,7 @@#include <linux/cc_platform.h> #include <linux/efi.h> #include <linux/pgtable.h> +#include <linux/kmsan.h> #include <asm/set_memory.h> #include <asm/e820/api.h>
@@ -474,6 +475,8 @@ void iounmap(volatile void __iomem *addr)return ; } +kmsan_iounmap_page_range ((unsigned long )addr, + (unsigned long )addr +get_vm_area_size (p));memtype_free (p->phys_addr, p->phys_addr +get_vm_area_size (p));/* Finally remove it */
include/linux/highmem.h +3 -0
@@ -6,6 +6,7 @@#include <linux/kernel.h> #include <linux/bug.h> #include <linux/cacheflush.h> +#include <linux/kmsan.h> #include <linux/mm.h> #include <linux/uaccess.h> #include <linux/hardirq.h>
@@ -277,6 +278,7 @@ static inline void copy_user_highpage(struct page *to, struct page *from, vfrom =kmap_local_page (from); vto =kmap_local_page (to);copy_user_page (vto, vfrom, vaddr, to); +kmsan_unpoison_memory (page_address (to),PAGE_SIZE );kunmap_local (vto);kunmap_local (vfrom); }
@@ -292,6 +294,7 @@ static inline void copy_highpage(struct page *to, struct page *from) vfrom =kmap_local_page (from); vto =kmap_local_page (to);copy_page (vto, vfrom); +kmsan_copy_page_meta (to, from);kunmap_local (vto);kunmap_local (vfrom); }
include/linux/kmsan.h +123 -0
129 lines not on this page · raw message
mm/internal.h +6 -0
@@ -713,8 +713,14 @@ int vmap_pages_range_noflush(unsigned long addr, unsigned long end, }#endif +int __vmap_pages_range_noflush (unsigned long addr,unsigned long end, +pgprot_t prot,struct page **pages, +unsigned int page_shift); +void vunmap_range_noflush (unsigned long start,unsigned long end); +void __vunmap_range_noflush (unsigned long start,unsigned long end); +int numa_migrate_prep (struct page *page,struct vm_area_struct *vma,unsigned long addr,int page_nid,int *flags);
mm/kmsan/hooks.c +87 -0
93 lines not on this page · raw message
mm/kmsan/shadow.c +114 -0
117 lines not on this page · raw message
mm/memory.c +2 -0
@@ -52,6 +52,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>
@@ -3026,6 +3027,7 @@ static vm_fault_t wp_page_copy(struct vm_fault *vmf)put_page (old_page);return 0 ; } +kmsan_copy_page_meta (new_page, old_page); }if (mem_cgroup_charge (page_folio (new_page), mm,GFP_KERNEL ))
mm/page_alloc.c +11 -0
@@ -27,6 +27,7 @@#include <linux/compiler.h> #include <linux/kernel.h> #include <linux/kasan.h> +#include <linux/kmsan.h> #include <linux/module.h> #include <linux/suspend.h> #include <linux/pagevec.h>
@@ -1301,6 +1302,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);if (unlikely (PageHWPoison (page)) && !order) {/*
@@ -3679,6 +3681,14 @@ static struct page *rmqueue_pcplist(struct zone *preferred_zone,/* * Allocate a page from the given zone. Use pcplists for order-0 allocations. */ + +/* +* Do not instrument rmqueue() with KMSAN. This function 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,
@@ -5409,6 +5419,7 @@ struct page *__alloc_pages(gfp_t gfp, unsigned int order, int preferred_nid, }trace_mm_page_alloc (page, order, alloc_gfp, ac.migratetype); +kmsan_alloc_page (page, order, alloc_gfp);return page; }
mm/vmalloc.c +18 -2
@@ -321,6 +321,9 @@ int ioremap_page_range(unsigned long addr, unsigned long end, err =vmap_range_noflush (addr, end, phys_addr,pgprot_nx (prot), ioremap_max_page_shift);flush_cache_vmap (addr, end); +if (!err) +kmsan_ioremap_page_range (addr, end, phys_addr, prot, + ioremap_max_page_shift);return err; }
@@ -420,7 +423,7 @@ static void vunmap_p4d_range(pgd_t *pgd, unsigned long addr, unsigned long end,* * This is an internal function only. Do not use outside mm/. */ -void vunmap_range_noflush (unsigned long start,unsigned long end) +void __vunmap_range_noflush (unsigned long start,unsigned long end) {unsigned long next;pgd_t *pgd;
@@ -442,6 +445,12 @@ void vunmap_range_noflush(unsigned long start, unsigned long end)arch_sync_kernel_mappings (start, end); } +void vunmap_range_noflush (unsigned long start,unsigned long end) +{ +kmsan_vunmap_range_noflush (start, end); +__vunmap_range_noflush (start, end); +} +/** * vunmap_range - unmap kernel virtual addresses * @addr: start of the VM area to unmap
@@ -576,7 +585,7 @@ static int vmap_small_pages_range_noflush(unsigned long addr, unsigned long end,* * This is an internal function only. Do not use outside mm/. */ -int vmap_pages_range_noflush (unsigned long addr,unsigned long end, +int __vmap_pages_range_noflush (unsigned long addr,unsigned long end,pgprot_t prot,struct page **pages,unsigned int page_shift) {unsigned int i, nr = (end - addr) >>PAGE_SHIFT ;
@@ -602,6 +611,13 @@ int vmap_pages_range_noflush(unsigned long addr, unsigned long end,return 0 ; } +int vmap_pages_range_noflush (unsigned long addr,unsigned long end, +pgprot_t prot,struct page **pages,unsigned int page_shift) +{ +kmsan_vmap_pages_range_noflush (addr, end, prot, pages, page_shift); +return __vmap_pages_range_noflush (addr, end, prot, pages, page_shift); +} +/** * vmap_pages_range - map pages to a kernel virtual address * @addr: start of the VM area to map
Signature · 1 line
2.35.1.1021.g381101b075-goog
Alexander Potapenko
maintainerPATCH v2 18/48 kmsan: mm: call KMSAN hooks from SLUB code
In reply to Alexander PotapenkoIn 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.
---
v2:
-- move the implementation of SLUB hooks here
--- include/linux/kmsan.h | 57 ++++++++++++++++++++++++++++++ mm/kmsan/hooks.c | 80 +++++++++++++++++++++++++++++++++++++++++++ mm/slab.h | 1 + mm/slub.c | 21 ++++++++++-- 4 files changed, 157 insertions(+), 2 deletions(-)
include/linux/kmsan.h +57 -0
@@ -16,6 +16,7 @@#include <linux/vmalloc.h> struct page ; +struct kmem_cache ;#ifdef CONFIG_KMSAN
@@ -73,6 +74,44 @@ void kmsan_free_page(struct page *page, unsigned int order);*/ void kmsan_copy_page_meta (struct page *dst,struct page *src); +/** +* kmsan_slab_alloc() - Notify KMSAN about a slab allocation. +* @s: slab cache the object belongs to. +* @object: object pointer. +* @flags: GFP flags passed to the allocator. +* +* Depending on cache flags and GFP flags, KMSAN sets up the metadata of the +* newly created object, marking it as initialized or uninitialized. +*/ +void kmsan_slab_alloc (struct kmem_cache *s,void *object,gfp_t flags); + +/** +* kmsan_slab_free() - Notify KMSAN about a slab deallocation. +* @s: slab cache the object belongs to. +* @object: object pointer. +* +* KMSAN marks the freed object as uninitialized. +*/ +void kmsan_slab_free (struct kmem_cache *s,void *object); + +/** +* kmsan_kmalloc_large() - Notify KMSAN about a large slab allocation. +* @ptr: object pointer. +* @size: object size. +* @flags: GFP flags passed to the allocator. +* +* Similar to kmsan_slab_alloc(), but for large allocations. +*/ +void kmsan_kmalloc_large (const void *ptr,size_t size,gfp_t flags); + +/** +* kmsan_kfree_large() - Notify KMSAN about a large slab deallocation. +* @ptr: object pointer. +* +* Similar to kmsan_slab_free(), but for large allocations. +*/ +void kmsan_kfree_large (const void *ptr); +/** * kmsan_map_kernel_range_noflush() - Notify KMSAN about a vmap. * @start: start of vmapped range.
@@ -139,6 +178,24 @@ static inline void kmsan_copy_page_meta(struct page *dst, struct page *src) { } +static inline void kmsan_slab_alloc (struct kmem_cache *s,void *object, +gfp_t flags) +{ +} + +static inline void kmsan_slab_free (struct kmem_cache *s,void *object) +{ +} + +static inline void kmsan_kmalloc_large (const void *ptr,size_t size, +gfp_t flags) +{ +} + +static inline void kmsan_kfree_large (const void *ptr) +{ +} + static inline void kmsan_vmap_pages_range_noflush(unsigned long start, unsigned long end, pgprot_t prot,
mm/kmsan/hooks.c +80 -0
86 lines not on this page · raw message
mm/slab.h +1 -0
@@ -734,6 +734,7 @@ static inline void slab_post_alloc_hook(struct kmem_cache *s,memset (p[i],0 , s->object_size);kmemleak_alloc_recursive (p[i], s->object_size,1 , s->flags, flags); +kmsan_slab_alloc (s, p[i], flags); }memcg_slab_post_alloc_hook (s, objcg, flags, size, p);
mm/slub.c +19 -2
@@ -22,6 +22,7 @@#include <linux/proc_fs.h> #include <linux/seq_file.h> #include <linux/kasan.h> +#include <linux/kmsan.h> #include <linux/cpu.h> #include <linux/cpuset.h> #include <linux/mempolicy.h>
@@ -357,18 +358,28 @@ static void prefetch_freepointer(const struct kmem_cache *s, void *object)prefetchw (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() 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_static ()) -return get_freepointer (s, object); +return kmsan_init (get_freepointer (s, object)); object =kasan_reset_tag (object); freepointer_addr = (unsigned long )object + s->offset;copy_from_kernel_nofault (&p, (void **)freepointer_addr,sizeof (p)); -return freelist_ptr (s, p, freepointer_addr); +return kmsan_init (freelist_ptr (s, p, freepointer_addr)); }static inline void set_freepointer (struct kmem_cache *s,void *object,void *fp)
@@ -1683,6 +1694,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; }
@@ -1690,12 +1702,14 @@ static __always_inline void kfree_hook(void *x) {kmemleak_free (x);kasan_kfree_large (x); +kmsan_kfree_large (x); } static __always_inline bool slab_free_hook(struct kmem_cache *s,void *x,bool init) {kmemleak_free_recursive (x, s->flags); +kmsan_slab_free (s, x);debug_check_no_locks_freed (x, s->object_size);
@@ -3729,6 +3743,7 @@ int kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t size,*/ slab_post_alloc_hook (s, objcg, flags, size, p,slab_want_init_on_alloc (flags, s)); +return i; error:slub_put_cpu_ptr (s->cpu_slab);
@@ -5910,6 +5925,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_memory (name, p - name);return name; }
@@ -6011,6 +6027,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_memory (al,sizeof (struct saved_alias ));return 0 ; }
Signature · 1 line
2.35.1.1021.g381101b075-goog
Alexander Potapenko
maintainerPATCH v2 19/48 kmsan: handle task creation and exiting
In reply to Alexander PotapenkoTell KMSAN that a new task is created, so the tool creates a backing metadata structure for that task.
---
v2:
-- move implementation of kmsan_task_create() and kmsan_task_exit() here
--- include/linux/kmsan.h | 17 +++++++++++++++++ kernel/exit.c | 2 ++ kernel/fork.c | 2 ++ mm/kmsan/core.c | 10 ++++++++++ mm/kmsan/hooks.c | 19 +++++++++++++++++++ mm/kmsan/kmsan.h | 2 ++ 6 files changed, 52 insertions(+)
include/linux/kmsan.h +17 -0
@@ -17,6 +17,7 @@struct page ;struct kmem_cache ; +struct task_struct ;#ifdef CONFIG_KMSAN
@@ -43,6 +44,14 @@ struct kmsan_ctx {bool allow_reporting; }; +void kmsan_task_create (struct task_struct *task); + +/** +* kmsan_task_exit() - Notify KMSAN that a task has exited. +* @task: task about to finish. +*/ +void kmsan_task_exit (struct task_struct *task); +/** * kmsan_alloc_page() - Notify KMSAN about an alloc_pages() call. * @page: struct page pointer returned by alloc_pages().
@@ -164,6 +173,14 @@ void kmsan_iounmap_page_range(unsigned long start, unsigned long end);#else +static inline void kmsan_task_create (struct task_struct *task) +{ +} + +static inline void kmsan_task_exit (struct task_struct *task) +{ +} +static inline int kmsan_alloc_page (struct page *page,unsigned int order,gfp_t flags) {
kernel/exit.c +2 -0
@@ -59,6 +59,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>
@@ -752,6 +753,7 @@ void __noreturn do_exit(long code)force_uaccess_begin ();kcov_task_exit (tsk); +kmsan_task_exit (tsk);coredump_task_exit (tsk);ptrace_event (PTRACE_EVENT_EXIT , code);
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>
@@ -956,6 +957,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);kmap_local_fork (tsk);#ifdef CONFIG_FAULT_INJECTION
mm/kmsan/core.c +10 -0
@@ -44,6 +44,16 @@ bool kmsan_enabled __read_mostly;*/ DEFINE_PER_CPU(struct kmsan_ctx, kmsan_percpu_ctx); +void kmsan_internal_task_create (struct task_struct *task) +{ +struct kmsan_ctx *ctx = &task->kmsan_ctx; + +__memset (ctx,0 ,sizeof (struct kmsan_ctx )); + ctx->allow_reporting = true; +kmsan_internal_unpoison_memory (current_thread_info (), +sizeof (struct thread_info ), false); +} +void kmsan_internal_poison_memory (void *address,size_t size,gfp_t flags,unsigned int poison_flags) {
mm/kmsan/hooks.c +19 -0
@@ -26,6 +26,25 @@* skipping effects of functions like memset() inside instrumented code. */ +void kmsan_task_create (struct task_struct *task) +{ +kmsan_enter_runtime (); +kmsan_internal_task_create (task); +kmsan_leave_runtime (); +} +EXPORT_SYMBOL (kmsan_task_create); + +void kmsan_task_exit (struct task_struct *task) +{ +struct kmsan_ctx *ctx = &task->kmsan_ctx; + +if (!kmsan_enabled ||kmsan_in_runtime ()) +return ; + + ctx->allow_reporting = false; +} +EXPORT_SYMBOL (kmsan_task_exit); +void kmsan_slab_alloc (struct kmem_cache *s,void *object,gfp_t flags) {if (unlikely (object ==NULL ))
mm/kmsan/kmsan.h +2 -0
@@ -172,6 +172,8 @@ void kmsan_internal_set_shadow_origin(void *address, size_t size, int b, u32 origin, bool checked);depot_stack_handle_t kmsan_internal_chain_origin (depot_stack_handle_t id); +void kmsan_internal_task_create (struct task_struct *task); +bool kmsan_metadata_is_contiguous (void *addr,size_t size);void kmsan_internal_check_memory (void *addr,size_t size,const void *user_addr,int reason);
Signature · 1 line
2.35.1.1021.g381101b075-goog
Alexander Potapenko
maintainerPATCH v2 20/48 kmsan: init: call KMSAN initialization routines
In reply to Alexander Potapenkokmsan_init_shadow() scans the mappings created at boot time and creates metadata pages for those mappings.
When the memblock allocator returns pages to pagealloc, we reserve 2/3 of those pages and use them as metadata for the remaining 1/3. Once KMSAN starts, every page allocated by pagealloc has its associated shadow and origin pages.
kmsan_initialize() initializes the bookkeeping for init_task and enables KMSAN.
---
v2:
-- move mm/kmsan/init.c and kmsan_memblock_free_pages() to this patch -- print a warning that KMSAN is a debugging tool (per Greg K-H's
request)
--- include/linux/kmsan.h | 48 +++++++++ init/main.c | 3 + mm/kmsan/Makefile | 3 +- mm/kmsan/init.c | 240 ++++++++++++++++++++++++++++++++++++++++++ mm/kmsan/kmsan.h | 3 + mm/kmsan/shadow.c | 36 +++++++ mm/page_alloc.c | 3 + 7 files changed, 335 insertions(+), 1 deletion(-) create mode 100644 mm/kmsan/init.c
include/linux/kmsan.h +48 -0
@@ -52,6 +52,40 @@ void kmsan_task_create(struct task_struct *task);*/ void kmsan_task_exit (struct task_struct *task); +/** +* kmsan_init_shadow() - Initialize KMSAN shadow at boot time. +* +* Allocate and initialize KMSAN metadata for early allocations. +*/ +void __init kmsan_init_shadow (void ); + +/** +* kmsan_init_runtime() - Initialize KMSAN state and enable KMSAN. +*/ +void __init kmsan_init_runtime (void ); + +/** +* kmsan_memblock_free_pages() - handle freeing of memblock pages. +* @page: struct page to free. +* @order: order of @page. +* +* Freed pages are either returned to buddy allocator or held back to be used +* as metadata pages. +*/ +bool __init kmsan_memblock_free_pages (struct page *page,unsigned int order); + +/** +* kmsan_task_create() - Initialize KMSAN state for the task. +* @task: task to initialize. +*/ +void kmsan_task_create (struct task_struct *task); + +/** +* kmsan_task_exit() - Notify KMSAN that a task has exited. +* @task: task about to finish. +*/ +void kmsan_task_exit (struct task_struct *task); +/** * kmsan_alloc_page() - Notify KMSAN about an alloc_pages() call. * @page: struct page pointer returned by alloc_pages().
@@ -173,6 +207,20 @@ void kmsan_iounmap_page_range(unsigned long start, unsigned long end);#else +static inline void kmsan_init_shadow (void ) +{ +} + +static inline void kmsan_init_runtime (void ) +{ +} + +static inline bool kmsan_memblock_free_pages (struct page *page, +unsigned int order) +{ +return true; +} +static inline void kmsan_task_create (struct task_struct *task) { }
init/main.c +3 -0
@@ -34,6 +34,7 @@#include <linux/percpu.h> #include <linux/kmod.h> #include <linux/kprobes.h> +#include <linux/kmsan.h> #include <linux/vmalloc.h> #include <linux/kernel_stat.h> #include <linux/start_kernel.h>
@@ -834,6 +835,7 @@ static void __init mm_init(void)init_mem_debugging_and_hardening ();kfence_alloc_pool ();report_meminit (); +kmsan_init_shadow ();stack_depot_early_init ();mem_init ();mem_init_print_info ();
@@ -851,6 +853,7 @@ static void __init mm_init(void)init_espfix_bsp ();/* Should be run after espfix64 is set up. */ pti_init (); +kmsan_init_runtime (); }#ifdef CONFIG_HAVE_ARCH_RANDOMIZE_KSTACK_OFFSET
mm/kmsan/Makefile +2 -1
@@ -1,4 +1,4 @@ -obj-y := core.o instrumentation.o hooks.o report.o shadow.o annotations.o +obj-y := core.o instrumentation.o init.o hooks.o report.o shadow.o annotations.o KMSAN_SANITIZE := n KCOV_INSTRUMENT := n
@@ -16,6 +16,7 @@ CFLAGS_REMOVE.o = $(CC_FLAGS_FTRACE) CFLAGS_annotations.o := $(CC_FLAGS_KMSAN_RUNTIME) CFLAGS_core.o := $(CC_FLAGS_KMSAN_RUNTIME) CFLAGS_hooks.o := $(CC_FLAGS_KMSAN_RUNTIME) +CFLAGS_init.o := $(CC_FLAGS_KMSAN_RUNTIME) CFLAGS_instrumentation.o := $(CC_FLAGS_KMSAN_RUNTIME) CFLAGS_report.o := $(CC_FLAGS_KMSAN_RUNTIME) CFLAGS_shadow.o := $(CC_FLAGS_KMSAN_RUNTIME)
mm/kmsan/init.c new file +240 -0
240 lines not on this page · raw message
mm/kmsan/kmsan.h +3 -0
@@ -66,6 +66,7 @@ struct shadow_origin_ptr {struct shadow_origin_ptr kmsan_get_shadow_origin_ptr (void *addr,u64 size,bool store);void *kmsan_get_metadata (void *addr,bool is_origin); +void __init kmsan_init_alloc_meta_for_range (void *start,void *end);enum kmsan_bug_reason {REASON_ANY ,
@@ -181,5 +182,7 @@ bool kmsan_internal_is_module_addr(void *vaddr);bool kmsan_internal_is_vmalloc_addr (void *addr);struct page *kmsan_vmalloc_to_page_or_null (void *vaddr); +void kmsan_setup_meta (struct page *page,struct page *shadow, +struct page *origin,int order);#endif /* __MM_KMSAN_KMSAN_H */
mm/kmsan/shadow.c +36 -0
@@ -298,3 +298,39 @@ void kmsan_vmap_pages_range_noflush(unsigned long start, unsigned long end,kfree (s_pages);kfree (o_pages); } + +/* Allocate metadata for pages allocated at boot time. */ +void __init kmsan_init_alloc_meta_for_range (void *start,void *end) +{ +struct page *shadow_p, *origin_p; +void *shadow, *origin; +struct page *page; +u64 addr, size; + + start = (void *)ALIGN_DOWN ((u64 )start,PAGE_SIZE ); + size =ALIGN ((u64 )end - (u64 )start,PAGE_SIZE ); + shadow =memblock_alloc (size,PAGE_SIZE ); + origin =memblock_alloc (size,PAGE_SIZE ); +for (addr =0 ; addr < size; addr +=PAGE_SIZE ) { + page =virt_to_page_or_null ((char *)start + addr); + shadow_p =virt_to_page_or_null ((char *)shadow + addr); +set_no_shadow_origin_page (shadow_p); +shadow_page_for (page) = shadow_p; + origin_p =virt_to_page_or_null ((char *)origin + addr); +set_no_shadow_origin_page (origin_p); +origin_page_for (page) = origin_p; + } +} + +void kmsan_setup_meta (struct page *page,struct page *shadow, +struct page *origin,int order) +{ +int i; + +for (i =0 ; i < (1 << order); i++) { +set_no_shadow_origin_page (&shadow[i]); +set_no_shadow_origin_page (&origin[i]); +shadow_page_for (&page[i]) = &shadow[i]; +origin_page_for (&page[i]) = &origin[i]; + } +}
mm/page_alloc.c +3 -0
@@ -1751,6 +1751,9 @@ void __init memblock_free_pages(struct page *page, unsigned long pfn, {if (early_page_uninitialised (pfn))return ; +if (!kmsan_memblock_free_pages (page, order)) +/* KMSAN will take care of these pages. */ +return ;__free_pages_core (page, order); }
Signature · 1 line
2.35.1.1021.g381101b075-goog
Alexander Potapenko
maintainerPATCH v2 21/48 instrumented.h: add KMSAN support
In reply to Alexander PotapenkoTo avoid false positives, KMSAN needs to unpoison the data copied from the userspace. To detect infoleaks - check the memory buffer passed to copy_to_user().
---
v2:
-- move implementation of kmsan_copy_to_user() here
--- include/linux/instrumented.h | 5 ++++- include/linux/kmsan-checks.h | 19 ++++++++++++++++++ mm/kmsan/hooks.c | 38 ++++++++++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 1 deletion(-)
include/linux/instrumented.h +4 -1
@@ -2,7 +2,7 @@/* * This header provides generic wrappers for memory access instrumentation that -* the compiler cannot emit for: KASAN, KCSAN. +* the compiler cannot emit for: KASAN, KCSAN, KMSAN. */ #ifndef _LINUX_INSTRUMENTED_H#define _LINUX_INSTRUMENTED_H
@@ -10,6 +10,7 @@#include <linux/compiler.h> #include <linux/kasan-checks.h> #include <linux/kcsan-checks.h> +#include <linux/kmsan-checks.h> #include <linux/types.h> /**
@@ -117,6 +118,7 @@ instrument_copy_to_user(void __user *to, const void *from, unsigned long n) {kasan_check_read (from, n);kcsan_check_read (from, n); +kmsan_copy_to_user (to, from, n,0 ); }/**
@@ -151,6 +153,7 @@ static __always_inline void instrument_copy_from_user_after(const void *to, const void __user *from, unsigned long n, unsigned long left) { + kmsan_unpoison_memory(to, n - left); }#endif /* _LINUX_INSTRUMENTED_H */
include/linux/kmsan-checks.h +19 -0
@@ -84,6 +84,21 @@ void kmsan_unpoison_memory(const void *address, size_t size);*/ void kmsan_check_memory (const void *address,size_t size); +/** +* kmsan_copy_to_user() - Notify KMSAN about a data transfer to userspace. +* @to: destination address in the userspace. +* @from: source address in the kernel. +* @to_copy: number of bytes to copy. +* @left: number of bytes not copied. +* +* If this is a real userspace data transfer, KMSAN checks the bytes that were +* actually copied to ensure there was no information leak. If @to belongs to +* the kernel space (which is possible for compat syscalls), KMSAN just copies +* the metadata. +*/ +void kmsan_copy_to_user (void __user *to,const void *from,size_t to_copy, +size_t left); +#else #define kmsan_init (value) (value)
@@ -98,6 +113,10 @@ static inline void kmsan_unpoison_memory(const void *address, size_t size)static inline void kmsan_check_memory (const void *address,size_t size) { } +static inline void kmsan_copy_to_user (void __user *to,const void *from, +size_t to_copy,size_t left) +{ +}#endif
mm/kmsan/hooks.c +38 -0
@@ -212,6 +212,44 @@ void kmsan_iounmap_page_range(unsigned long start, unsigned long end) }EXPORT_SYMBOL (kmsan_iounmap_page_range); +void kmsan_copy_to_user (void __user *to,const void *from,size_t to_copy, +size_t left) +{ +unsigned long ua_flags; + +if (!kmsan_enabled ||kmsan_in_runtime ()) +return ; +/* +* At this point we've copied the memory already. It's hard to check it +* before copying, as the size of actually copied buffer is unknown. +*/ + +/* copy_to_user() may copy zero bytes. No need to check. */ +if (!to_copy) +return ; +/* Or maybe copy_to_user() failed to copy anything. */ +if (to_copy <= left) +return ; + + ua_flags =user_access_save (); +if ((u64 )to <TASK_SIZE ) { +/* This is a user memory access, check it. */ +kmsan_internal_check_memory ((void *)from, to_copy - left, to, +REASON_COPY_TO_USER ); +user_access_restore (ua_flags); +return ; + } +/* Otherwise this is a kernel memory access. This happens when a compat +* syscall passes an argument allocated on the kernel stack to a real +* syscall. +* Don't check anything, just copy the shadow of the copied bytes. +*/ +kmsan_internal_memmove_metadata ((void *)to, (void *)from, + to_copy - left); +user_access_restore (ua_flags); +} +EXPORT_SYMBOL (kmsan_copy_to_user); +/* Functions from kmsan-checks.h follow. */ void kmsan_poison_memory (const void *address,size_t size,gfp_t flags) {
Signature · 1 line
2.35.1.1021.g381101b075-goog
Alexander Potapenko
maintainerPATCH v2 22/48 kmsan: unpoison @tlb in arch_tlb_gather_mmu()
In reply to Alexander PotapenkoThis is a hack to reduce stackdepot pressure.
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.
---
--- 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/mm_inline.h>
@@ -253,6 +254,15 @@ void tlb_flush_mmu(struct mmu_gather *tlb)static void __tlb_gather_mmu (struct mmu_gather *tlb,struct mm_struct *mm,bool fullmm) { +/* +* 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_memory (tlb,sizeof (*tlb)); tlb->mm = mm; tlb->fullmm = fullmm;
Signature · 1 line
2.35.1.1021.g381101b075-goog
Alexander Potapenko
maintainerPATCH v2 23/48 kmsan: add iomap support
In reply to Alexander PotapenkoFunctions from lib/iomap.c interact with hardware, so KMSAN must ensure that:
- every read function returns an initialized value
- every write function checks values before sending them to hardware.
---
--- lib/iomap.c | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+)
lib/iomap.c +40 -0
@@ -6,6 +6,7 @@*/ #include <linux/pci.h> #include <linux/io.h> +#include <linux/kmsan-checks.h> #include <linux/export.h>
@@ -70,26 +71,31 @@ static void bad_io_access(unsigned long port, const char *access)#define mmio_read64be (addr) swab64(readq(addr))#endif +__no_sanitize_memory unsigned int ioread8(const void __iomem *addr) { IO_COND(addr, return inb(port), return readb(addr));return 0xff ; } +__no_sanitize_memory unsigned int ioread16(const void __iomem *addr) { IO_COND(addr, return inw(port), return readw(addr));return 0xffff ; } +__no_sanitize_memory unsigned int ioread16be(const void __iomem *addr) { IO_COND(addr, return pio_read16be(port), return mmio_read16be(addr));return 0xffff ; } +__no_sanitize_memory unsigned int ioread32(const void __iomem *addr) { IO_COND(addr, return inl(port), return readl(addr));return 0xffffffff ; } +__no_sanitize_memory unsigned int ioread32be(const void __iomem *addr) { IO_COND(addr, return pio_read32be(port), return mmio_read32be(addr));
@@ -142,18 +148,21 @@ static u64 pio_read64be_hi_lo(unsigned long port)return lo | (hi <<32 ); } +__no_sanitize_memory u64 ioread64_lo_hi(const void __iomem *addr) { IO_COND(addr, return pio_read64_lo_hi(port), return readq(addr));return 0xffffffffffffffffULL ; } +__no_sanitize_memory u64 ioread64_hi_lo(const void __iomem *addr) { IO_COND(addr, return pio_read64_hi_lo(port), return readq(addr));return 0xffffffffffffffffULL ; } +__no_sanitize_memory u64 ioread64be_lo_hi(const void __iomem *addr) { IO_COND(addr, return pio_read64be_lo_hi(port),
@@ -161,6 +170,7 @@ u64 ioread64be_lo_hi(const void __iomem *addr)return 0xffffffffffffffffULL ; } +__no_sanitize_memory u64 ioread64be_hi_lo(const void __iomem *addr) { IO_COND(addr, return pio_read64be_hi_lo(port),
@@ -188,22 +198,32 @@ EXPORT_SYMBOL(ioread64be_hi_lo);void iowrite8 (u8 val,void __iomem *addr) { +/* Make sure uninitialized memory isn't copied to devices. */ +kmsan_check_memory (&val,sizeof (val));IO_COND (addr,outb (val,port),writeb (val, addr)); }void iowrite16 (u16 val,void __iomem *addr) { +/* Make sure uninitialized memory isn't copied to devices. */ +kmsan_check_memory (&val,sizeof (val));IO_COND (addr,outw (val,port),writew (val, addr)); }void iowrite16be (u16 val,void __iomem *addr) { +/* Make sure uninitialized memory isn't copied to devices. */ +kmsan_check_memory (&val,sizeof (val));IO_COND (addr,pio_write16be (val,port),mmio_write16be (val, addr)); }void iowrite32 (u32 val,void __iomem *addr) { +/* Make sure uninitialized memory isn't copied to devices. */ +kmsan_check_memory (&val,sizeof (val));IO_COND (addr,outl (val,port),writel (val, addr)); }void iowrite32be (u32 val,void __iomem *addr) { +/* Make sure uninitialized memory isn't copied to devices. */ +kmsan_check_memory (&val,sizeof (val));IO_COND (addr,pio_write32be (val,port),mmio_write32be (val, addr)); }EXPORT_SYMBOL (iowrite8);
@@ -239,24 +259,32 @@ static void pio_write64be_hi_lo(u64 val, unsigned long port)void iowrite64_lo_hi (u64 val,void __iomem *addr) { +/* Make sure uninitialized memory isn't copied to devices. */ +kmsan_check_memory (&val,sizeof (val));IO_COND (addr,pio_write64_lo_hi (val, port),writeq (val, addr)); }void iowrite64_hi_lo (u64 val,void __iomem *addr) { +/* Make sure uninitialized memory isn't copied to devices. */ +kmsan_check_memory (&val,sizeof (val));IO_COND (addr,pio_write64_hi_lo (val, port),writeq (val, addr)); }void iowrite64be_lo_hi (u64 val,void __iomem *addr) { +/* Make sure uninitialized memory isn't copied to devices. */ +kmsan_check_memory (&val,sizeof (val));IO_COND (addr,pio_write64be_lo_hi (val, port),mmio_write64be (val, addr)); }void iowrite64be_hi_lo (u64 val,void __iomem *addr) { +/* Make sure uninitialized memory isn't copied to devices. */ +kmsan_check_memory (&val,sizeof (val));IO_COND (addr,pio_write64be_hi_lo (val, port),mmio_write64be (val, addr)); }
@@ -328,14 +356,20 @@ static inline void mmio_outsl(void __iomem *addr, const u32 *src, int count)void ioread8_rep (const void __iomem *addr,void *dst,unsigned long count) {IO_COND (addr,insb (port,dst,count),mmio_insb (addr, dst, count)); +/* KMSAN must treat values read from devices as initialized. */ +kmsan_unpoison_memory (dst, count); }void ioread16_rep (const void __iomem *addr,void *dst,unsigned long count) {IO_COND (addr,insw (port,dst,count),mmio_insw (addr, dst, count)); +/* KMSAN must treat values read from devices as initialized. */ +kmsan_unpoison_memory (dst, count *2 ); }void ioread32_rep (const void __iomem *addr,void *dst,unsigned long count) {IO_COND (addr,insl (port,dst,count),mmio_insl (addr, dst, count)); +/* KMSAN must treat values read from devices as initialized. */ +kmsan_unpoison_memory (dst, count *4 ); }EXPORT_SYMBOL (ioread8_rep);EXPORT_SYMBOL (ioread16_rep);
@@ -343,14 +377,20 @@ EXPORT_SYMBOL(ioread32_rep);void iowrite8_rep (void __iomem *addr,const void *src,unsigned long count) { +/* Make sure uninitialized memory isn't copied to devices. */ +kmsan_check_memory (src, count);IO_COND (addr,outsb (port, src, count),mmio_outsb (addr, src, count)); }void iowrite16_rep (void __iomem *addr,const void *src,unsigned long count) { +/* Make sure uninitialized memory isn't copied to devices. */ +kmsan_check_memory (src, count *2 );IO_COND (addr,outsw (port, src, count),mmio_outsw (addr, src, count)); }void iowrite32_rep (void __iomem *addr,const void *src,unsigned long count) { +/* Make sure uninitialized memory isn't copied to devices. */ +kmsan_check_memory (src, count *4 );IO_COND (addr,outsl (port, src,count),mmio_outsl (addr, src, count)); }EXPORT_SYMBOL (iowrite8_rep);
Signature · 1 line
2.35.1.1021.g381101b075-goog
Alexander Potapenko
maintainerPATCH v2 24/48 Input: libps2: mark data received in __ps2_command() as initialized
In reply to Alexander PotapenkoKMSAN does not know that the device initializes certain bytes in ps2dev->cmdbuf. Call kmsan_unpoison_memory() to explicitly mark them as initialized.
---
--- drivers/input/serio/libps2.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
drivers/input/serio/libps2.c +4 -1
@@ -12,6 +12,7 @@#include <linux/sched.h> #include <linux/interrupt.h> #include <linux/input.h> +#include <linux/kmsan-checks.h> #include <linux/serio.h> #include <linux/i8042.h> #include <linux/libps2.h>
@@ -294,9 +295,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_memory (param, receive); + }if (ps2dev->cmdcnt && (command !=PS2_CMD_RESET_BAT || ps2dev->cmdcnt !=1 )) {
Signature · 1 line
2.35.1.1021.g381101b075-goog
Alexander Potapenko
maintainerPATCH v2 25/48 kmsan: dma: unpoison DMA mappings
In reply to Alexander PotapenkoKMSAN doesn't know about DMA memory writes performed by devices. We unpoison such memory when it's mapped to avoid false positive reports.
---
v2:
-- move implementation of kmsan_handle_dma() and kmsan_handle_dma_sg() here
--- include/linux/kmsan.h | 41 +++++++++++++++++++++++++++++ kernel/dma/mapping.c | 9 ++++--- mm/kmsan/hooks.c | 61 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 108 insertions(+), 3 deletions(-)
include/linux/kmsan.h +41 -0
@@ -9,6 +9,7 @@#ifndef _LINUX_KMSAN_H#define _LINUX_KMSAN_H +#include <linux/dma-direction.h> #include <linux/gfp.h> #include <linux/kmsan-checks.h> #include <linux/stackdepot.h>
@@ -18,6 +19,7 @@struct page ;struct kmem_cache ;struct task_struct ; +struct scatterlist ;#ifdef CONFIG_KMSAN
@@ -205,6 +207,35 @@ void kmsan_ioremap_page_range(unsigned long addr, unsigned long end,*/ void kmsan_iounmap_page_range (unsigned long start,unsigned long end); +/** +* kmsan_handle_dma() - Handle a DMA data transfer. +* @page: first page of the buffer. +* @offset: offset of the buffer within the first page. +* @size: buffer size. +* @dir: one of possible dma_data_direction values. +* +* Depending on @direction, KMSAN: +* * checks the buffer, if it is copied to device; +* * initializes the buffer, if it is copied from device; +* * does both, if this is a DMA_BIDIRECTIONAL transfer. +*/ +void kmsan_handle_dma (struct page *page,size_t offset,size_t size, +enum dma_data_direction dir); + +/** +* kmsan_handle_dma_sg() - Handle a DMA transfer using scatterlist. +* @sg: scatterlist holding DMA buffers. +* @nents: number of scatterlist entries. +* @dir: one of possible dma_data_direction values. +* +* Depending on @direction, KMSAN: +* * checks the buffers in the scatterlist, if they are copied to device; +* * initializes the buffers, if they are copied from device; +* * does both, if this is a DMA_BIDIRECTIONAL transfer. +*/ +void kmsan_handle_dma_sg (struct scatterlist *sg,int nents, +enum dma_data_direction dir); +#else static inline void kmsan_init_shadow (void )
@@ -287,6 +318,16 @@ static inline void kmsan_iounmap_page_range(unsigned long start, { } +static inline void kmsan_handle_dma (struct page *page,size_t offset, +size_t size,enum dma_data_direction dir) +{ +} + +static inline void kmsan_handle_dma_sg (struct scatterlist *sg,int nents, +enum dma_data_direction dir) +{ +} +#endif #endif /* _LINUX_KMSAN_H */
kernel/dma/mapping.c +6 -3
@@ -156,6 +156,7 @@ dma_addr_t dma_map_page_attrs(struct device *dev, struct page *page, addr =dma_direct_map_page (dev, page, offset, size, dir, attrs); else addr = ops->map_page (dev, page, offset, size, dir, attrs); +kmsan_handle_dma (page, offset, size, dir);debug_dma_map_page (dev, page, offset, size, dir, addr, attrs);return addr;
@@ -194,11 +195,13 @@ static int __dma_map_sg_attrs(struct device *dev, struct scatterlist *sg,else ents = ops->map_sg (dev, sg, nents, dir, attrs); -if (ents >0 ) +if (ents >0 ) { +kmsan_handle_dma_sg (sg, nents, dir);debug_dma_map_sg (dev, sg, nents, ents, dir, attrs); -else if (WARN_ON_ONCE (ents != -EINVAL && ents != -ENOMEM && - ents != -EIO )) + }else if (WARN_ON_ONCE (ents != -EINVAL && ents != -ENOMEM && + ents != -EIO )) {return -EIO ; + }return ents; }
mm/kmsan/hooks.c +61 -0
76 lines not on this page · raw message
Signature · 1 line
2.35.1.1021.g381101b075-goog
Alexander Potapenko
maintainerPATCH v2 26/48 kmsan: virtio: check/unpoison scatterlist in vring_map_one_sg()
In reply to Alexander PotapenkoIf vring doesn't use the DMA API, KMSAN is unable to tell whether the memory is initialized by hardware. Explicitly call kmsan_handle_dma() from vring_map_one_sg() in this case to prevent false positives.
---
--- drivers/virtio/virtio_ring.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-)
drivers/virtio/virtio_ring.c +9 -1
@@ -11,6 +11,7 @@#include <linux/module.h> #include <linux/hrtimer.h> #include <linux/dma-mapping.h> +#include <linux/kmsan-checks.h> #include <linux/spinlock.h> #include <xen/xen.h>
@@ -331,8 +332,15 @@ static dma_addr_t vring_map_one_sg(const struct vring_virtqueue *vq, struct scatterlist *sg, enum dma_data_direction direction) { -if (!vq->use_dma_api) +if (!vq->use_dma_api) { +/* +* If DMA is not used, KMSAN doesn't know that the scatterlist +* is initialized by the hardware. Explicitly check/unpoison it +* depending on the direction. +*/ +kmsan_handle_dma (sg_page (sg), sg->offset, sg->length, direction);return (dma_addr_t )sg_phys (sg); + }/* * We can't use dma_map_sg, because we don't use scatterlists in
Signature · 1 line
2.35.1.1021.g381101b075-goog
Alexander Potapenko
maintainerPATCH v2 27/48 kmsan: handle memory sent to/from USB
In reply to Alexander PotapenkoDepending on the value of is_out kmsan_handle_urb() KMSAN either marks the data copied to the kernel from a USB device as initialized, or checks the data sent to the device for being initialized.
---
v2:
-- move kmsan_handle_urb() implementation to this patch
--- drivers/usb/core/urb.c | 2 ++ include/linux/kmsan.h | 15 +++++++++++++++ mm/kmsan/hooks.c | 17 +++++++++++++++++ 3 files changed, 34 insertions(+)
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>
@@ -426,6 +427,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/kmsan.h +15 -0
@@ -20,6 +20,7 @@ struct page;struct kmem_cache ;struct task_struct ;struct scatterlist ; +struct urb ;#ifdef CONFIG_KMSAN
@@ -236,6 +237,16 @@ void kmsan_handle_dma(struct page *page, size_t offset, size_t size,void kmsan_handle_dma_sg (struct scatterlist *sg,int nents,enum dma_data_direction dir); +/** +* kmsan_handle_urb() - Handle a USB data transfer. +* @urb: struct urb pointer. +* @is_out: data transfer direction (true means output to hardware). +* +* If @is_out is true, KMSAN checks the transfer buffer of @urb. Otherwise, +* KMSAN initializes the transfer buffer. +*/ +void kmsan_handle_urb (const struct urb *urb,bool is_out); +#else static inline void kmsan_init_shadow (void )
@@ -328,6 +339,10 @@ static inline void kmsan_handle_dma_sg(struct scatterlist *sg, int nents, { } +static inline void kmsan_handle_urb (const struct urb *urb,bool is_out) +{ +} +#endif #endif /* _LINUX_KMSAN_H */
mm/kmsan/hooks.c +17 -0
@@ -17,6 +17,7 @@#include <linux/scatterlist.h> #include <linux/slab.h> #include <linux/uaccess.h> +#include <linux/usb.h> #include "../internal.h" #include "../slab.h"
@@ -252,6 +253,22 @@ void kmsan_copy_to_user(void __user *to, const void *from, size_t to_copy, }EXPORT_SYMBOL (kmsan_copy_to_user); +/* Helper function to check an URB. */ +void kmsan_handle_urb (const struct urb *urb,bool is_out) +{ +if (!urb) +return ; +if (is_out) +kmsan_internal_check_memory (urb->transfer_buffer, + urb->transfer_buffer_length, +/*user_addr*/ 0 ,REASON_SUBMIT_URB ); +else +kmsan_internal_unpoison_memory (urb->transfer_buffer, + urb->transfer_buffer_length, +/*checked*/ false); +} +EXPORT_SYMBOL (kmsan_handle_urb); +static void kmsan_handle_dma_page (const void *addr,size_t size,enum dma_data_direction dir) {
Signature · 1 line
2.35.1.1021.g381101b075-goog
Alexander Potapenko
maintainerPATCH v2 28/48 kmsan: instrumentation.h: add instrumentation_begin_with_regs()
In reply to Alexander PotapenkoWhen calling KMSAN-instrumented functions from non-instrumented functions, function parameters may not be initialized properly, leading to false positive reports. In particular, this happens all the time when calling interrupt handlers from noinstr IDT entries.
We introduce instrumentation_begin_with_regs(), which calls instrumentation_begin() and notifies KMSAN about the beginning of the potentially instrumented region by calling kmsan_instrumentation_begin(), which:
- wipes the current KMSAN state at the beginning of the region, ensuring that the first call of an instrumented function receives initialized parameters (this is a pretty good approximation of having all other instrumented functions receive initialized parameters);
- unpoisons the struct pt_regs set up by the non-instrumented assembly code.
---
--- include/linux/instrumentation.h | 6 ++++++ include/linux/kmsan.h | 11 +++++++++++ mm/kmsan/hooks.c | 16 ++++++++++++++++ 3 files changed, 33 insertions(+)
include/linux/instrumentation.h +6 -0
@@ -15,6 +15,11 @@ })#define instrumentation_begin () __instrumentation_begin(__COUNTER__) +#define instrumentation_begin_with_regs (regs) do { \ + __instrumentation_begin(__COUNTER__); \ + kmsan_instrumentation_begin(regs); \ +} while (0) +/* * Because instrumentation_{begin,end}() can nest, objtool validation considers * _begin() a +1 and _end() a -1 and computes a sum over the instructions.
@@ -55,6 +60,7 @@#define instrumentation_end () __instrumentation_end(__COUNTER__)#else # define instrumentation_begin () do { } while(0) +# define instrumentation_begin_with_regs (regs) kmsan_instrumentation_begin(regs)# define instrumentation_end () do { } while(0)#endif
include/linux/kmsan.h +11 -0
@@ -247,6 +247,13 @@ void kmsan_handle_dma_sg(struct scatterlist *sg, int nents,*/ void kmsan_handle_urb (const struct urb *urb,bool is_out); +/** +* kmsan_instrumentation_begin() - handle instrumentation_begin(). +* @regs: pointer to struct pt_regs that non-instrumented code passes to +* instrumented code. +*/ +void kmsan_instrumentation_begin (struct pt_regs *regs); +#else static inline void kmsan_init_shadow (void )
@@ -343,6 +350,10 @@ static inline void kmsan_handle_urb(const struct urb *urb, bool is_out) { } +static inline void kmsan_instrumentation_begin (struct pt_regs *regs) +{ +} +#endif #endif /* _LINUX_KMSAN_H */
mm/kmsan/hooks.c +16 -0
@@ -366,3 +366,19 @@ void kmsan_check_memory(const void *addr, size_t size) REASON_ANY); }EXPORT_SYMBOL (kmsan_check_memory); + +void kmsan_instrumentation_begin (struct pt_regs *regs) +{ +struct kmsan_context_state *state = &kmsan_get_context ()->cstate; + +if (state) +__memset (state,0 ,sizeof (struct kmsan_context_state )); +if (!kmsan_enabled || !regs) +return ; +/* +* @regs may reside in cpu_entry_area, for which KMSAN does not allocate +* metadata. Do not force an error in that case. +*/ +kmsan_internal_unpoison_memory (regs,sizeof (*regs),/*checked*/ false); +} +EXPORT_SYMBOL (kmsan_instrumentation_begin);
Signature · 1 line
2.35.1.1021.g381101b075-goog
Alexander Potapenko
maintainerPATCH v2 29/48 kmsan: entry: handle register passing from uninstrumented code
In reply to Alexander PotapenkoReplace instrumentation_begin() with instrumentation_begin_with_regs()
to let KMSAN handle the non-instrumented code and unpoison pt_regs passed from the instrumented part.
---
--- kernel/entry/common.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-)
kernel/entry/common.c +11 -11
@@ -21,7 +21,7 @@ static __always_inline void __enter_from_user_mode(struct pt_regs *regs)CT_WARN_ON (ct_state () !=CONTEXT_USER );user_exit_irqoff (); -instrumentation_begin (); +instrumentation_begin_with_regs (regs);trace_hardirqs_off_finish ();instrumentation_end (); }
@@ -103,7 +103,7 @@ noinstr long syscall_enter_from_user_mode(struct pt_regs *regs, long syscall)__enter_from_user_mode (regs); -instrumentation_begin (); +instrumentation_begin_with_regs (regs);local_irq_enable (); ret =__syscall_enter_from_user_work (regs, syscall);instrumentation_end ();
@@ -114,7 +114,7 @@ noinstr long syscall_enter_from_user_mode(struct pt_regs *regs, long syscall) noinstr void syscall_enter_from_user_mode_prepare(struct pt_regs *regs) {__enter_from_user_mode (regs); -instrumentation_begin (); +instrumentation_begin_with_regs (regs);local_irq_enable ();instrumentation_end (); }
@@ -296,7 +296,7 @@ void syscall_exit_to_user_mode_work(struct pt_regs *regs) __visible noinstr void syscall_exit_to_user_mode(struct pt_regs *regs) { -instrumentation_begin (); +instrumentation_begin_with_regs (regs);__syscall_exit_to_user_mode_work (regs);instrumentation_end ();__exit_to_user_mode ();
@@ -309,7 +309,7 @@ noinstr void irqentry_enter_from_user_mode(struct pt_regs *regs) noinstr void irqentry_exit_to_user_mode(struct pt_regs *regs) { -instrumentation_begin (); +instrumentation_begin_with_regs (regs);exit_to_user_mode_prepare (regs);instrumentation_end ();__exit_to_user_mode ();
@@ -357,7 +357,7 @@ noinstr irqentry_state_t irqentry_enter(struct pt_regs *regs)*/ lockdep_hardirqs_off (CALLER_ADDR0 );rcu_irq_enter (); -instrumentation_begin (); +instrumentation_begin_with_regs (regs);trace_hardirqs_off_finish ();instrumentation_end ();
@@ -372,7 +372,7 @@ noinstr irqentry_state_t irqentry_enter(struct pt_regs *regs)* in having another one here. */ lockdep_hardirqs_off (CALLER_ADDR0 ); -instrumentation_begin (); +instrumentation_begin_with_regs (regs);rcu_irq_enter_check_tick ();trace_hardirqs_off_finish ();instrumentation_end ();
@@ -409,7 +409,7 @@ noinstr void irqentry_exit(struct pt_regs *regs, irqentry_state_t state)* and RCU as the return to user mode path. */ if (state.exit_rcu) { -instrumentation_begin (); +instrumentation_begin_with_regs (regs);/* Tell the tracer that IRET will enable interrupts */ trace_hardirqs_on_prepare ();lockdep_hardirqs_on_prepare (CALLER_ADDR0 );
@@ -419,7 +419,7 @@ noinstr void irqentry_exit(struct pt_regs *regs, irqentry_state_t state)return ; } -instrumentation_begin (); +instrumentation_begin_with_regs (regs);if (IS_ENABLED (CONFIG_PREEMPTION )) {#ifdef CONFIG_PREEMPT_DYNAMIC static_call (irqentry_exit_cond_resched)();
@@ -451,7 +451,7 @@ irqentry_state_t noinstr irqentry_nmi_enter(struct pt_regs *regs)lockdep_hardirq_enter ();rcu_nmi_enter (); -instrumentation_begin (); +instrumentation_begin_with_regs (regs);trace_hardirqs_off_finish ();ftrace_nmi_enter ();instrumentation_end ();
@@ -461,7 +461,7 @@ irqentry_state_t noinstr irqentry_nmi_enter(struct pt_regs *regs) void noinstr irqentry_nmi_exit(struct pt_regs *regs, irqentry_state_t irq_state) { -instrumentation_begin (); +instrumentation_begin_with_regs (regs);ftrace_nmi_exit ();if (irq_state.lockdep) {trace_hardirqs_on_prepare ();
Signature · 1 line
2.35.1.1021.g381101b075-goog
Alexander Potapenko
maintainerPATCH v2 30/48 kmsan: add tests for KMSAN
In reply to Alexander PotapenkoThe testing module triggers KMSAN warnings in different cases and checks that the errors are properly reported, using console probes to capture the tool's output.
---
v2:
-- add memcpy tests
--- lib/Kconfig.kmsan | 16 ++ mm/kmsan/Makefile | 4 + mm/kmsan/kmsan_test.c | 536 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 556 insertions(+) create mode 100644 mm/kmsan/kmsan_test.c
lib/Kconfig.kmsan +16 -0
@@ -21,3 +21,19 @@ config KMSAN the whole system down. See <file:Documentation/dev-tools/kmsan.rst> for more details. + +if KMSAN + +config KMSAN_KUNIT_TEST + tristate "KMSAN integration test suite" if !KUNIT_ALL_TESTS + default KUNIT_ALL_TESTS + depends on TRACEPOINTS && KUNIT + help + Test suite for KMSAN, testing various error detection scenarios, + and checking that reports are correctly output to console. + + Say Y here if you want the test to be built into the kernel and run + during boot; say M if you want the test to build as a module; say N + if you are unsure. + +endif
mm/kmsan/Makefile +4 -0
@@ -20,3 +20,7 @@ CFLAGS_init.o := $(CC_FLAGS_KMSAN_RUNTIME) CFLAGS_instrumentation.o := $(CC_FLAGS_KMSAN_RUNTIME) CFLAGS_report.o := $(CC_FLAGS_KMSAN_RUNTIME) CFLAGS_shadow.o := $(CC_FLAGS_KMSAN_RUNTIME) + +obj-$(CONFIG_KMSAN_KUNIT_TEST) += kmsan_test.o +KMSAN_SANITIZE_kmsan_test.o := y +CFLAGS_kmsan_test.o += $(call cc-disable-warning, uninitialized)
mm/kmsan/kmsan_test.c new file +536 -0
536 lines not on this page · raw message
Signature · 1 line
2.35.1.1021.g381101b075-goog
Alexander Potapenko
maintainerPATCH v2 31/48 kernel: kmsan: don't instrument stacktrace.c
In reply to Alexander PotapenkoWhen unwinding stack traces, the kernel may pick uninitialized data from the stack. To avoid false reports on that data, we do not instrument stacktrace.c
---
--- kernel/Makefile | 5 +++++ 1 file changed, 5 insertions(+)
kernel/Makefile +5 -0
@@ -40,6 +40,11 @@ KASAN_SANITIZE_kcov.o := n KCSAN_SANITIZE_kcov.o := n UBSAN_SANITIZE_kcov.o := n KMSAN_SANITIZE_kcov.o := n + +# Code in stactrace.c may branch on random values taken from the stack. +# Prevent KMSAN false positives by not instrumenting this file. +KMSAN_SANITIZE_stacktrace.o := n + CFLAGS_kcov.o := $(call cc-option, -fno-conserve-stack) -fno-stack-protector # Don't instrument error handlers
Signature · 1 line
2.35.1.1021.g381101b075-goog
Alexander Potapenko
maintainerRe: [PATCH v2 31/48] kernel: kmsan: don't instrument stacktrace.c
Quoted 4 lines · Alexander Potapenko
When unwinding stack traces, the kernel may pick uninitialized data from the stack. To avoid false reports on that data, we do not instrument
stacktrace.c
This patch is not needed anymore if we unpoison the stack traces passed to __stack_depot_save() from KMSAN core.
Alexander Potapenko
maintainerPATCH v2 32/48 kmsan: disable strscpy() optimization under KMSAN
In reply to Alexander PotapenkoDisable the efficient 8-byte reading under KMSAN to avoid false positives.
---
--- lib/string.c | 8 ++++++++ 1 file changed, 8 insertions(+)
lib/string.c +8 -0
@@ -197,6 +197,14 @@ ssize_t strscpy(char *dest, const char *src, size_t count) max =0 ;#endif +/* +* read_word_at_a_time() below may read uninitialized bytes after the +* trailing zero and use them in comparisons. Disable this optimization +* under KMSAN to prevent false positive reports. +*/ +if (IS_ENABLED (CONFIG_KMSAN )) + max =0 ; +while (max >=sizeof (unsigned long )) {unsigned long c, data;
Signature · 1 line
2.35.1.1021.g381101b075-goog
Alexander Potapenko
maintainerPATCH v2 33/48 crypto: kmsan: disable accelerated configs under KMSAN
In reply to Alexander PotapenkoKMSAN is unable to understand when initialized values come from assembly. Disable accelerated configs in KMSAN builds to prevent false positive reports.
---
--- crypto/Kconfig | 30 ++++++++++++++++++++++++++++++ drivers/net/Kconfig | 1 + 2 files changed, 31 insertions(+)
crypto/Kconfig +30 -0
@@ -290,6 +290,7 @@ config CRYPTO_CURVE25519 config CRYPTO_CURVE25519_X86 tristate "x86_64 accelerated Curve25519 scalar multiplication library" depends on X86 && 64BIT + depends on !KMSAN # avoid false positives from assembly select CRYPTO_LIB_CURVE25519_GENERIC select CRYPTO_ARCH_HAVE_LIB_CURVE25519
@@ -338,11 +339,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 # avoid false positives from assembly default y config CRYPTO_AEGIS128_AESNI_SSE2 tristate "AEGIS-128 AEAD algorithm (x86_64 AESNI+SSE2 implementation)" depends on X86 && 64BIT + depends on !KMSAN # avoid false positives from assembly select CRYPTO_AEAD select CRYPTO_SIMD help
@@ -478,6 +481,7 @@ config CRYPTO_NHPOLY1305 config CRYPTO_NHPOLY1305_SSE2 tristate "NHPoly1305 hash function (x86_64 SSE2 implementation)" depends on X86 && 64BIT + depends on !KMSAN # avoid false positives from assembly select CRYPTO_NHPOLY1305 help SSE2 optimized implementation of the hash function used by the
@@ -486,6 +490,7 @@ config CRYPTO_NHPOLY1305_SSE2 config CRYPTO_NHPOLY1305_AVX2 tristate "NHPoly1305 hash function (x86_64 AVX2 implementation)" depends on X86 && 64BIT + depends on !KMSAN # avoid false positives from assembly select CRYPTO_NHPOLY1305 help AVX2 optimized implementation of the hash function used by the
@@ -599,6 +604,7 @@ config CRYPTO_CRC32C config CRYPTO_CRC32C_INTEL tristate "CRC32c INTEL hardware acceleration" depends on X86 + depends on !KMSAN # avoid false positives from assembly select CRYPTO_HASH help In Intel processor with SSE4.2 supported, the processor will
@@ -639,6 +645,7 @@ config CRYPTO_CRC32 config CRYPTO_CRC32_PCLMUL tristate "CRC32 PCLMULQDQ hardware acceleration" depends on X86 + depends on !KMSAN # avoid false positives from assembly select CRYPTO_HASH select CRC32 help
@@ -704,6 +711,7 @@ config CRYPTO_BLAKE2S config CRYPTO_BLAKE2S_X86 tristate "BLAKE2s digest algorithm (x86 accelerated version)" depends on X86 && 64BIT + depends on !KMSAN # avoid false positives from assembly select CRYPTO_LIB_BLAKE2S_GENERIC select CRYPTO_ARCH_HAVE_LIB_BLAKE2S
@@ -718,6 +726,7 @@ config CRYPTO_CRCT10DIF config CRYPTO_CRCT10DIF_PCLMUL tristate "CRCT10DIF PCLMULQDQ hardware acceleration" depends on X86 && 64BIT && CRC_T10DIF + depends on !KMSAN # avoid false positives from assembly select CRYPTO_HASH help For x86_64 processors with SSE4.2 and PCLMULQDQ supported,
@@ -765,6 +774,7 @@ config CRYPTO_POLY1305 config CRYPTO_POLY1305_X86_64 tristate "Poly1305 authenticator algorithm (x86_64/SSE2/AVX2)" depends on X86 && 64BIT + depends on !KMSAN # avoid false positives from assembly select CRYPTO_LIB_POLY1305_GENERIC select CRYPTO_ARCH_HAVE_LIB_POLY1305 help
@@ -853,6 +863,7 @@ config CRYPTO_SHA1 config CRYPTO_SHA1_SSSE3 tristate "SHA1 digest algorithm (SSSE3/AVX/AVX2/SHA-NI)" depends on X86 && 64BIT + depends on !KMSAN # avoid false positives from assembly select CRYPTO_SHA1 select CRYPTO_HASH help
@@ -864,6 +875,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 # avoid false positives from assembly select CRYPTO_SHA256 select CRYPTO_HASH help
@@ -876,6 +888,7 @@ config CRYPTO_SHA256_SSSE3 config CRYPTO_SHA512_SSSE3 tristate "SHA512 digest algorithm (SSSE3/AVX/AVX2)" depends on X86 && 64BIT + depends on !KMSAN # avoid false positives from assembly select CRYPTO_SHA512 select CRYPTO_HASH help
@@ -1034,6 +1047,7 @@ config CRYPTO_WP512 config CRYPTO_GHASH_CLMUL_NI_INTEL tristate "GHASH hash function (CLMUL-NI accelerated)" depends on X86 && 64BIT + depends on !KMSAN # avoid false positives from assembly select CRYPTO_CRYPTD help This is the x86_64 CLMUL-NI accelerated implementation of
@@ -1084,6 +1098,7 @@ config CRYPTO_AES_TI config CRYPTO_AES_NI_INTEL tristate "AES cipher algorithms (AES-NI)" depends on X86 + depends on !KMSAN # avoid false positives from assembly select CRYPTO_AEAD select CRYPTO_LIB_AES select CRYPTO_ALGAPI
@@ -1208,6 +1223,7 @@ config CRYPTO_BLOWFISH_COMMON config CRYPTO_BLOWFISH_X86_64 tristate "Blowfish cipher algorithm (x86_64)" depends on X86 && 64BIT + depends on !KMSAN # avoid false positives from assembly select CRYPTO_SKCIPHER select CRYPTO_BLOWFISH_COMMON imply CRYPTO_CTR
@@ -1238,6 +1254,7 @@ config CRYPTO_CAMELLIA config CRYPTO_CAMELLIA_X86_64 tristate "Camellia cipher algorithm (x86_64)" depends on X86 && 64BIT + depends on !KMSAN # avoid false positives from assembly select CRYPTO_SKCIPHER imply CRYPTO_CTR help
@@ -1254,6 +1271,7 @@ config CRYPTO_CAMELLIA_X86_64 config CRYPTO_CAMELLIA_AESNI_AVX_X86_64 tristate "Camellia cipher algorithm (x86_64/AES-NI/AVX)" depends on X86 && 64BIT + depends on !KMSAN # avoid false positives from assembly select CRYPTO_SKCIPHER select CRYPTO_CAMELLIA_X86_64 select CRYPTO_SIMD
@@ -1272,6 +1290,7 @@ config CRYPTO_CAMELLIA_AESNI_AVX_X86_64 config CRYPTO_CAMELLIA_AESNI_AVX2_X86_64 tristate "Camellia cipher algorithm (x86_64/AES-NI/AVX2)" depends on X86 && 64BIT + depends on !KMSAN # avoid false positives from assembly select CRYPTO_CAMELLIA_AESNI_AVX_X86_64 help Camellia cipher algorithm module (x86_64/AES-NI/AVX2).
@@ -1317,6 +1336,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 # avoid false positives from assembly select CRYPTO_SKCIPHER select CRYPTO_CAST5 select CRYPTO_CAST_COMMON
@@ -1340,6 +1360,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 # avoid false positives from assembly select CRYPTO_SKCIPHER select CRYPTO_CAST6 select CRYPTO_CAST_COMMON
@@ -1373,6 +1394,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 # avoid false positives from assembly select CRYPTO_SKCIPHER select CRYPTO_LIB_DES imply CRYPTO_CTR
@@ -1430,6 +1452,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 # avoid false positives from assembly select CRYPTO_SKCIPHER select CRYPTO_LIB_CHACHA_GENERIC select CRYPTO_ARCH_HAVE_LIB_CHACHA
@@ -1473,6 +1496,7 @@ config CRYPTO_SERPENT config CRYPTO_SERPENT_SSE2_X86_64 tristate "Serpent cipher algorithm (x86_64/SSE2)" depends on X86 && 64BIT + depends on !KMSAN # avoid false positives from assembly select CRYPTO_SKCIPHER select CRYPTO_SERPENT select CRYPTO_SIMD
@@ -1492,6 +1516,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 # avoid false positives from assembly select CRYPTO_SKCIPHER select CRYPTO_SERPENT select CRYPTO_SIMD
@@ -1511,6 +1536,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 # avoid false positives from assembly select CRYPTO_SKCIPHER select CRYPTO_SERPENT select CRYPTO_SIMD
@@ -1531,6 +1557,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 # avoid false positives from assembly select CRYPTO_SERPENT_AVX_X86_64 help Serpent cipher algorithm, by Anderson, Biham & Knudsen.
@@ -1672,6 +1699,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 # avoid false positives from assembly select CRYPTO_ALGAPI select CRYPTO_TWOFISH_COMMON imply CRYPTO_CTR
@@ -1689,6 +1717,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 # avoid false positives from assembly select CRYPTO_SKCIPHER select CRYPTO_TWOFISH_COMMON select CRYPTO_TWOFISH_X86_64
@@ -1709,6 +1738,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 # avoid false positives from assembly select CRYPTO_SKCIPHER select CRYPTO_SIMD select CRYPTO_TWOFISH_COMMON
drivers/net/Kconfig +1 -0
@@ -76,6 +76,7 @@ config WIREGUARD tristate "WireGuard secure network tunnel" depends on NET && INET depends on IPV6 || !IPV6 + depends on !KMSAN # KMSAN doesn't support the crypto configs below select NET_UDP_TUNNEL select DST_CACHE select CRYPTO
Signature · 1 line
2.35.1.1021.g381101b075-goog
Alexander Potapenko
maintainerPATCH v2 34/48 kmsan: disable physical page merging in biovec
In reply to Alexander PotapenkoKMSAN metadata for adjacent physical pages may not be adjacent, therefore accessing such pages together may lead to metadata corruption.
We disable merging pages in biovec to prevent such corruptions.
---
--- block/blk.h | 7 +++++++ 1 file changed, 7 insertions(+)
block/blk.h +7 -0
@@ -93,6 +93,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; +/* +* Merging adjacent physical pages may not work correctly under KMSAN +* if their metadata pages aren't adjacent. Just disable merging. +*/ +if (IS_ENABLED (CONFIG_KMSAN )) +return false; +if (addr1 + vec1->bv_len != addr2)return false;if (xen_domain () && !xen_biovec_phys_mergeable (vec1, vec2->bv_page))
Signature · 1 line
2.35.1.1021.g381101b075-goog
Alexander Potapenko
maintainerPATCH v2 35/48 kmsan: block: skip bio block merging logic for KMSAN
In reply to Alexander PotapenkoKMSAN doesn't allow treating adjacent memory pages as such, if they were allocated by different alloc_pages() calls.
The block layer however does so: adjacent pages end up being used together. To prevent this, make page_is_mergeable() return false under KMSAN.
---
--- block/bio.c | 2 ++ 1 file changed, 2 insertions(+)
block/bio.c +2 -0
@@ -808,6 +808,8 @@ static inline bool page_is_mergeable(const struct bio_vec *bv,return false; *same_page = ((vec_end_addr &PAGE_MASK ) == page_addr); +if (!*same_page &&IS_ENABLED (CONFIG_KMSAN )) +return false;if (*same_page)return true;return (bv->bv_page + bv_end /PAGE_SIZE ) == (page + off /PAGE_SIZE );
Signature · 1 line
2.35.1.1021.g381101b075-goog
Alexander Potapenko
maintainerPATCH v2 36/48 kmsan: kcov: unpoison area->list in kcov_remote_area_put()
In reply to Alexander PotapenkoKMSAN does not instrument kernel/kcov.c for performance reasons (with CONFIG_KCOV=y virtually every place in the kernel invokes kcov instrumentation). Therefore the tool may miss writes from kcov.c that initialize memory.
When CONFIG_DEBUG_LIST is enabled, list pointers from kernel/kcov.c are passed to instrumented helpers in lib/list_debug.c, resulting in false positives.
To work around these reports, we unpoison the contents of area->list after initializing it.
---
--- kernel/kcov.c | 7 +++++++ 1 file changed, 7 insertions(+)
kernel/kcov.c +7 -0
@@ -11,6 +11,7 @@#include <linux/fs.h> #include <linux/hashtable.h> #include <linux/init.h> +#include <linux/kmsan-checks.h> #include <linux/mm.h> #include <linux/preempt.h> #include <linux/printk.h>
@@ -152,6 +153,12 @@ static void kcov_remote_area_put(struct kcov_remote_area *area,INIT_LIST_HEAD (&area->list); area->size = size;list_add (&area->list, &kcov_remote_areas); +/* +* KMSAN doesn't instrument this file, so it may not know area->list +* is initialized. Unpoison it explicitly to avoid reports in +* kcov_remote_area_get(). +*/ +kmsan_unpoison_memory (&area->list,sizeof (struct list_head )); } static notrace bool check_kcov_mode(enum kcov_mode needed_mode, struct task_struct *t)
Signature · 1 line
2.35.1.1021.g381101b075-goog
Alexander Potapenko
maintainerPATCH v2 37/48 security: kmsan: fix interoperability with auto-initialization
In reply to Alexander PotapenkoHeap and stack initialization is great, but not when we are trying uses of uninitialized memory. When the kernel is built with KMSAN, having kernel memory initialization enabled may introduce false negatives.
We disable CONFIG_INIT_STACK_ALL_PATTERN and CONFIG_INIT_STACK_ALL_ZERO under CONFIG_KMSAN, making it impossible to auto-initialize stack variables in KMSAN builds. We also disable CONFIG_INIT_ON_ALLOC_DEFAULT_ON and CONFIG_INIT_ON_FREE_DEFAULT_ON to prevent accidental use of heap auto-initialization.
We however still let the users enable heap auto-initialization at boot-time (by setting init_on_alloc=1 or init_on_free=1), in which case a warning is printed.
---
--- mm/page_alloc.c | 4 ++++ security/Kconfig.hardening | 4 ++++ 2 files changed, 8 insertions(+)
mm/page_alloc.c +4 -0
@@ -868,6 +868,10 @@ void init_mem_debugging_and_hardening(void)else static_branch_disable (&init_on_free); +if (IS_ENABLED (CONFIG_KMSAN ) && + (_init_on_alloc_enabled_early || _init_on_free_enabled_early)) +pr_info ("mem auto-init: please make sure init_on_alloc and init_on_free are disabled when running KMSAN\n" ); +#ifdef CONFIG_DEBUG_PAGEALLOC if (!debug_pagealloc_enabled ())return ;
security/Kconfig.hardening +4 -0
@@ -106,6 +106,7 @@ choice config INIT_STACK_ALL_PATTERN bool "pattern-init everything (strongest)" depends on CC_HAS_AUTO_VAR_INIT_PATTERN + depends on !KMSAN help Initializes everything on the stack (including padding) with a specific debug value. This is intended to eliminate
@@ -124,6 +125,7 @@ choice config INIT_STACK_ALL_ZERO bool "zero-init everything (strongest and safest)" depends on CC_HAS_AUTO_VAR_INIT_ZERO + depends on !KMSAN help Initializes everything on the stack (including padding) with a zero value. This is intended to eliminate all
@@ -208,6 +210,7 @@ config STACKLEAK_RUNTIME_DISABLE config INIT_ON_ALLOC_DEFAULT_ON bool "Enable heap memory zeroing on allocation by default" + depends on !KMSAN help This has the effect of setting "init_on_alloc=1" on the kernel command line. This can be disabled with "init_on_alloc=0".
@@ -220,6 +223,7 @@ config INIT_ON_ALLOC_DEFAULT_ON config INIT_ON_FREE_DEFAULT_ON bool "Enable heap memory zeroing on free by default" + depends on !KMSAN help This has the effect of setting "init_on_free=1" on the kernel command line. This can be disabled with "init_on_free=0".
Signature · 1 line
2.35.1.1021.g381101b075-goog
Alexander Potapenko
maintainerPATCH v2 38/48 objtool: kmsan: list KMSAN API functions as uaccess-safe
In reply to Alexander PotapenkoKMSAN inserts API function calls in a lot of places (function entries and exits, local variables, memory accesses), so they may get called from the uaccess regions as well.
---
--- tools/objtool/check.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+)
tools/objtool/check.c +19 -0
@@ -943,6 +943,25 @@ static const char *uaccess_safe_builtin[] = {"__sanitizer_cov_trace_cmp4" ,"__sanitizer_cov_trace_cmp8" ,"__sanitizer_cov_trace_switch" , +/* KMSAN */ +"kmsan_copy_to_user" , +"kmsan_report" , +"kmsan_unpoison_memory" , +"__msan_chain_origin" , +"__msan_get_context_state" , +"__msan_instrument_asm_store" , +"__msan_metadata_ptr_for_load_1" , +"__msan_metadata_ptr_for_load_2" , +"__msan_metadata_ptr_for_load_4" , +"__msan_metadata_ptr_for_load_8" , +"__msan_metadata_ptr_for_load_n" , +"__msan_metadata_ptr_for_store_1" , +"__msan_metadata_ptr_for_store_2" , +"__msan_metadata_ptr_for_store_4" , +"__msan_metadata_ptr_for_store_8" , +"__msan_metadata_ptr_for_store_n" , +"__msan_poison_alloca" , +"__msan_warning" ,/* UBSAN */ "ubsan_type_mismatch_common" , "__ubsan_handle_type_mismatch",
Signature · 1 line
2.35.1.1021.g381101b075-goog
Peter Zijlstra
maintainerRe: [PATCH v2 38/48] objtool: kmsan: list KMSAN API functions as uaccess-safe
Quoted 3 lines · Alexander Potapenko
KMSAN inserts API function calls in a lot of places (function entries and exits, local variables, memory accesses), so they may get called from the uaccess regions as well.
That's insufficient. Explain how you did the right thing and made these functions actually safe to be called in this context.
Quoted 40 lines
Signed-off-by Alexander Potapenko <[email protected]>---
--- tools/objtool/check.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+)tools/objtool/check.c +19
-0@@ -943,6 +943,25 @@ static const char *uaccess_safe_builtin[] = {"__sanitizer_cov_trace_cmp4" ,"__sanitizer_cov_trace_cmp8" ,"__sanitizer_cov_trace_switch" , +/* KMSAN */ +"kmsan_copy_to_user" , +"kmsan_report" , +"kmsan_unpoison_memory" , +"__msan_chain_origin" , +"__msan_get_context_state" , +"__msan_instrument_asm_store" , +"__msan_metadata_ptr_for_load_1" , +"__msan_metadata_ptr_for_load_2" , +"__msan_metadata_ptr_for_load_4" , +"__msan_metadata_ptr_for_load_8" , +"__msan_metadata_ptr_for_load_n" , +"__msan_metadata_ptr_for_store_1" , +"__msan_metadata_ptr_for_store_2" , +"__msan_metadata_ptr_for_store_4" , +"__msan_metadata_ptr_for_store_8" , +"__msan_metadata_ptr_for_store_n" , +"__msan_poison_alloca" , +"__msan_warning" ,/* UBSAN */ "ubsan_type_mismatch_common" , "__ubsan_handle_type_mismatch",Signature · 1 line
2.35.1.1021.g381101b075-goog
Alexander Potapenko
maintainerRe: [PATCH v2 38/48] objtool: kmsan: list KMSAN API functions as uaccess-safe
Quoted 9 lines · Peter Zijlstra
Quoted 3 lines · Alexander Potapenko
KMSAN inserts API function calls in a lot of places (function entries and exits, local variables, memory accesses), so they may get called from the uaccess regions as well.
That's insufficient. Explain how you did the right thing and made these functions actually safe to be called in this context.
KMSAN API functions are used to update the metadata (shadow/origin pages)
for kernel memory accesses.
The metadata pages for kernel pointers are also located in the kernel memory, so touching them is not a problem.
For userspace pointers, no metadata is allocated.
If an API function is supposed to read or modify the metadata, it does so for kernel pointers and ignores userspace pointers.
If an API function is supposed to return a pair of metadata pointers for the instrumentation to use (like all __msan_metadata_ptr_for_TYPE_SIZE() functions do), it returns the allocated metadata for kernel pointers and special dummy buffers residing in the kernel memory for userspace pointers.
As a result, none of KMSAN API functions perform userspace accesses, but since they might be called from UACCESS regions they
use user_access_save/restore().
Does this make sense?
Quoted 42 lines
Quoted 3 lines
Signed-off-by Alexander Potapenko <[email protected]>---
Link:https://linux-review.googlesource.com/id/I242bc9816273fecad4ea3d977393784396bb3c35
Quoted 37 lines
--- tools/objtool/check.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+)tools/objtool/check.c +19
-1@@ -943,6 +943,25 @@ static const char *uaccess_safe_builtin[] = {"__sanitizer_cov_trace_cmp4" ,"__sanitizer_cov_trace_cmp8" ,"__sanitizer_cov_trace_switch" , +/* KMSAN */ +"kmsan_copy_to_user" , +"kmsan_report" , +"kmsan_unpoison_memory" , +"__msan_chain_origin" , +"__msan_get_context_state" , +"__msan_instrument_asm_store" , +"__msan_metadata_ptr_for_load_1" , +"__msan_metadata_ptr_for_load_2" , +"__msan_metadata_ptr_for_load_4" , +"__msan_metadata_ptr_for_load_8" , +"__msan_metadata_ptr_for_load_n" , +"__msan_metadata_ptr_for_store_1" , +"__msan_metadata_ptr_for_store_2" , +"__msan_metadata_ptr_for_store_4" , +"__msan_metadata_ptr_for_store_8" , +"__msan_metadata_ptr_for_store_n" , +"__msan_poison_alloca" , +"__msan_warning" ,/* UBSAN */ "ubsan_type_mismatch_common" , "__ubsan_handle_type_mismatch", --2.35.1.1021.g381101b075-goog
Signature · 20 lines
Alexander Potapenko Software Engineer Google Germany GmbH Erika-Mann-Straße, 33 80636 München Geschäftsführer: Paul Manicle, Liana Sebastian Registergericht und -nummer: Hamburg, HRB 86891 Sitz der Gesellschaft: Hamburg Diese E-Mail ist vertraulich. Falls Sie diese fälschlicherweise erhalten haben sollten, leiten Sie diese bitte nicht an jemand anderes weiter, löschen Sie alle Kopien und Anhänge davon und lassen Sie mich bitte wissen, dass die E-Mail an die falsche Person gesendet wurde. This e-mail is confidential. If you received this communication by mistake, please don't forward it to anyone else, please erase all copies and attachments, and please let me know that it has gone to the wrong person.
Peter Zijlstra
maintainerRe: [PATCH v2 38/48] objtool: kmsan: list KMSAN API functions as uaccess-safe
Quoted 8 lines · Alexander Potapenko
Quoted 5 lines · Peter Zijlstra
Quoted 3 lines · Alexander Potapenko
KMSAN inserts API function calls in a lot of places (function entries and exits, local variables, memory accesses), so they may get called from the uaccess regions as well.
Quoted 7 lines
Quoted 3 lines
That's insufficient. Explain how you did the right thing and made these functions actually safe to be called in this context.
As a result, none of KMSAN API functions perform userspace accesses, but since they might be called from UACCESS regions they
use user_access_save/restore().
^ That.. very good.
Thanks!
Alexander Potapenko
maintainerPATCH v2 39/48 x86: kmsan: make READ_ONCE_TASK_STACK() return initialized values
In reply to Alexander PotapenkoTo avoid false positives, assume that reading from the task stack always produces initialized values.
---
--- arch/x86/include/asm/unwind.h | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-)
arch/x86/include/asm/unwind.h +12 -11
@@ -129,18 +129,19 @@ unsigned long unwind_recover_ret_addr(struct unwind_state *state, }/* -* 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) \ -({ \ - unsigned long val; \ - if (task == current) \ - val = READ_ONCE(x); \ - else \ - val = READ_ONCE_NOCHECK(x); \ - val; \ +#define READ_ONCE_TASK_STACK (task, x) \ +({ \ + unsigned long val; \ + if (task == current && !IS_ENABLED(CONFIG_KMSAN)) \ + val = READ_ONCE(x); \ + else \ + val = READ_ONCE_NOCHECK(x); \ + val; \ })static inline bool task_on_another_cpu (struct task_struct *task)
Signature · 1 line
2.35.1.1021.g381101b075-goog
Alexander Potapenko
maintainerPATCH v2 40/48 x86: kmsan: disable instrumentation of unsupported code
In reply to Alexander PotapenkoInstrumenting 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).
Completely omit KMSAN instrumentation 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.
---
v2:
- moved the patch earlier in the series so that KMSAN can compile
- split off the non-x86 part into a separate patch
--- arch/x86/boot/Makefile | 1 + arch/x86/boot/compressed/Makefile | 1 + arch/x86/entry/vdso/Makefile | 3 +++ arch/x86/kernel/Makefile | 2 ++ arch/x86/kernel/cpu/Makefile | 1 + arch/x86/mm/Makefile | 2 ++ arch/x86/realmode/rm/Makefile | 1 + 7 files changed, 11 insertions(+)
arch/x86/boot/Makefile +1 -0
@@ -12,6 +12,7 @@ # Sanitizer runtimes are unavailable and cannot be linked for early boot code. KASAN_SANITIZE := n KCSAN_SANITIZE := n +KMSAN_SANITIZE := n OBJECT_FILES_NON_STANDARD := y # Kernel does not boot with kcov instrumentation here.
arch/x86/boot/compressed/Makefile +1 -0
@@ -20,6 +20,7 @@ # Sanitizer runtimes are unavailable and cannot be linked for early boot code. KASAN_SANITIZE := n KCSAN_SANITIZE := n +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 # Sanitizer runtimes are unavailable and cannot be linked here. KASAN_SANITIZE := n +KMSAN_SANITIZE_vclock_gettime.o := n +KMSAN_SANITIZE_vgetcpu.o := n + UBSAN_SANITIZE := n KCSAN_SANITIZE := n OBJECT_FILES_NON_STANDARD := y
arch/x86/kernel/Makefile +2 -0
@@ -35,6 +35,8 @@ KASAN_SANITIZE_cc_platform.o := n # With some compiler versions the generated code results in boot hangs, caused # by several compilation units. To be safe, disable all instrumentation. KCSAN_SANITIZE := n +KMSAN_SANITIZE_head$(BITS).o := n +KMSAN_SANITIZE_nmi.o := n OBJECT_FILES_NON_STANDARD_test_nx.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 # As above, instrumenting secondary CPU boot code causes boot hangs. KCSAN_SANITIZE_common.o := n
arch/x86/mm/Makefile +2 -0
@@ -12,6 +12,8 @@ KASAN_SANITIZE_mem_encrypt_identity.o := n # Disable KCSAN entirely, because otherwise we get warnings that some functions # reference __initdata sections. KCSAN_SANITIZE := n +# Avoid recursion by not calling KMSAN hooks for CEA code. +KMSAN_SANITIZE_cpu_entry_area.o := n ifdef CONFIG_FUNCTION_TRACER CFLAGS_REMOVE_mem_encrypt.o = -pg
arch/x86/realmode/rm/Makefile +1 -0
@@ -10,6 +10,7 @@ # Sanitizer runtimes are unavailable and cannot be linked here. KASAN_SANITIZE := n KCSAN_SANITIZE := n +KMSAN_SANITIZE := n OBJECT_FILES_NON_STANDARD := y # Prevents link failures: __sanitizer_cov_trace_pc() is not linked in.
Signature · 1 line
2.35.1.1021.g381101b075-goog
Alexander Potapenko
maintainerPATCH v2 41/48 x86: kmsan: skip shadow checks in __switch_to()
In reply to Alexander PotapenkoWhen instrumenting functions, KMSAN obtains the per-task state (mostly pointers to metadata for function arguments and return values) once per function at its beginning, using the current pointer.
Every time the instrumented function calls another function, this state (struct kmsan_context_state) is updated with shadow/origin data of the passed and returned values.
When current changes in the low-level arch code, instrumented code can not notice that, and will still refer to the old state, possibly corrupting it or using stale data. This may result in false positive reports.
To deal with that, we need to apply __no_kmsan_checks to the functions performing context switching - this will result in skipping all KMSAN shadow checks and marking newly created values as initialized, preventing all false positive reports in those functions. False negatives are still possible, but we expect them to be rare and impersistent.
---
v2:
-- This patch was previously called "kmsan: skip shadow checks in files
doing context switches". Per Mark Rutland's suggestion, we now only skip checks in low-level arch-specific code, as context switches in common code should be invisible to KMSAN. We also apply the checks to precisely the functions performing the context switch instead of the whole file.
--- arch/x86/kernel/process_64.c | 1 + 1 file changed, 1 insertion(+)
arch/x86/kernel/process_64.c +1 -0
@@ -553,6 +553,7 @@ void compat_start_thread(struct pt_regs *regs, u32 new_ip, u32 new_sp, bool x32)* Kprobes not supported here. Set the probe on schedule instead. * Function graph tracer not supported too. */ +__no_kmsan_checks __visible __notrace_funcgraph struct task_struct *__switch_to (struct task_struct *prev_p,struct task_struct *next_p) {
Signature · 1 line
2.35.1.1021.g381101b075-goog
Alexander Potapenko
maintainerPATCH v2 42/48 x86: kmsan: handle open-coded assembly in lib/iomem.c
In reply to Alexander PotapenkoKMSAN cannot intercept memory accesses within asm() statements. That's why we add kmsan_unpoison_memory() and kmsan_check_memory() to hint it how to handle memory copied from/to I/O memory.
---
--- arch/x86/lib/iomem.c | 5 +++++ 1 file changed, 5 insertions(+)
arch/x86/lib/iomem.c +5 -0
@@ -1,6 +1,7 @@#include <linux/string.h> #include <linux/module.h> #include <linux/io.h> +#include <linux/kmsan-checks.h> #define movs (type,to,from) \ asm volatile("movs" type:"=&D" (to), "=&S" (from):"0" (to), "1" (from):"memory")
@@ -37,6 +38,8 @@ void memcpy_fromio(void *to, const volatile void __iomem *from, size_t n) n-=2 ; }rep_movs (to, (const void *)from, n); +/* KMSAN must treat values read from devices as initialized. */ +kmsan_unpoison_memory (to, n); }EXPORT_SYMBOL (memcpy_fromio);
@@ -45,6 +48,8 @@ void memcpy_toio(volatile void __iomem *to, const void *from, size_t n)if (unlikely (!n))return ; +/* Make sure uninitialized memory isn't copied to devices. */ +kmsan_check_memory (from, n);/* Align any unaligned destination IO */ if (unlikely (1 & (unsigned long )to)) {movs ("b" , to, from);
Signature · 1 line
2.35.1.1021.g381101b075-goog
Alexander Potapenko
maintainerPATCH v2 43/48 x86: kmsan: use __msan_ string functions where possible.
In reply to Alexander PotapenkoUnless stated otherwise (by explicitly calling __memcpy(), __memset() or __memmove()) we want all string functions to call their __msan_ versions (e.g. __msan_memcpy() instead of memcpy()), so that shadow and origin values are updated accordingly.
Bootloader must still use the default string functions to avoid crashes.
---
--- arch/x86/include/asm/string_64.h | 23 +++++++++++++++++++++-- include/linux/fortify-string.h | 2 ++ 2 files changed, 23 insertions(+), 2 deletions(-)
arch/x86/include/asm/string_64.h +21 -2
@@ -11,11 +11,23 @@function. */ #define __HAVE_ARCH_MEMCPY 1 +#if defined(__SANITIZE_MEMORY__) +#undef memcpy +void *__msan_memcpy (void *dst,const void *src,size_t size); +#define memcpy __msan_memcpy +#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 +#if defined(__SANITIZE_MEMORY__) +extern void *__msan_memset (void *s,int c,size_t n); +#undef memset +#define memset __msan_memset +#else void *memset (void *s,int c,size_t n); +#endif void *__memset (void *s,int c,size_t n);#define __HAVE_ARCH_MEMSET16
@@ -55,7 +67,13 @@ static inline void *memset64(uint64_t *s, uint64_t v, size_t n) }#define __HAVE_ARCH_MEMMOVE +#if defined(__SANITIZE_MEMORY__) +#undef memmove +void *__msan_memmove (void *dest,const void *src,size_t len); +#define memmove __msan_memmove +#else void *memmove (void *dest,const void *src,size_t count); +#endif void *__memmove (void *dest,const void *src,size_t count);int memcmp (const void *cs,const void *ct,size_t count);
@@ -64,8 +82,7 @@ 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__))/* * For files that not instrumented (e.g. mm/slub.c) we * should use not instrumented version of mem* functions.
@@ -73,7 +90,9 @@ int strcmp(const char *cs, const char *ct);#undef memcpy#define memcpy (dst, src, len) __memcpy(dst, src, len) +#undef memmove#define memmove (dst, src, len) __memmove(dst, src, len) +#undef memset#define memset (s, c, n) __memset(s, c, n)#ifndef __NO_FORTIFY
include/linux/fortify-string.h +2 -0
@@ -198,6 +198,7 @@ __FORTIFY_INLINE char *strncat(char *p, const char *q, __kernel_size_t count)return p; } +#ifndef CONFIG_KMSAN __FORTIFY_INLINE void *memset(void *p, int c, __kernel_size_t size) {size_t p_size =__builtin_object_size (p,0 );
@@ -240,6 +241,7 @@ __FORTIFY_INLINE void *memmove(void *p, const void *q, __kernel_size_t size)fortify_panic (__func__);return __underlying_memmove (p, q, size); } +#endif extern void *__real_memscan (void *,int ,__kernel_size_t )__RENAME (memscan); __FORTIFY_INLINE void *memscan(void *p, int c, __kernel_size_t size)
Signature · 1 line
2.35.1.1021.g381101b075-goog
Alexander Potapenko
maintainerPATCH v2 44/48 x86: kmsan: sync metadata pages on page fault
In reply to Alexander PotapenkoKMSAN assumes shadow and origin pages for every allocated page are accessible. For pages between [VMALLOC_START, VMALLOC_END] those metadata pages start at KMSAN_VMALLOC_SHADOW_START and KMSAN_VMALLOC_ORIGIN_START, therefore we must sync a bigger memory region.
---
v2:
-- addressed reports from kernel test robot <[email protected]>
--- arch/x86/mm/fault.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-)
arch/x86/mm/fault.c +22 -1
@@ -260,7 +260,7 @@ static noinline int vmalloc_fault(unsigned long address) }NOKPROBE_SYMBOL (vmalloc_fault); -void arch_sync_kernel_mappings (unsigned long start,unsigned long end) +static void __arch_sync_kernel_mappings (unsigned long start,unsigned long end) {unsigned long addr;
@@ -284,6 +284,27 @@ void arch_sync_kernel_mappings(unsigned long start, unsigned long end) } } +void arch_sync_kernel_mappings (unsigned long start,unsigned long end) +{ +__arch_sync_kernel_mappings (start, end); +#ifdef CONFIG_KMSAN +/* +* KMSAN maintains two additional metadata page mappings for the +* [VMALLOC_START, VMALLOC_END) range. These mappings start at +* KMSAN_VMALLOC_SHADOW_START and KMSAN_VMALLOC_ORIGIN_START and +* have to be synced together with the vmalloc memory mapping. +*/ +if (start >=VMALLOC_START && end <VMALLOC_END ) { +__arch_sync_kernel_mappings ( + start -VMALLOC_START +KMSAN_VMALLOC_SHADOW_START , + end -VMALLOC_START +KMSAN_VMALLOC_SHADOW_START ); +__arch_sync_kernel_mappings ( + start -VMALLOC_START +KMSAN_VMALLOC_ORIGIN_START , + end -VMALLOC_START +KMSAN_VMALLOC_ORIGIN_START ); + } +#endif +} +static bool low_pfn (unsigned long pfn) {return pfn < max_low_pfn;
Signature · 1 line
2.35.1.1021.g381101b075-goog
Alexander Potapenko
maintainerPATCH v2 45/48 x86: kasan: kmsan: support CONFIG_GENERIC_CSUM on x86, enable it for KASAN/KMSAN
In reply to Alexander PotapenkoThis 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.
---
--- arch/x86/Kconfig | 4 ++++ arch/x86/include/asm/checksum.h | 16 ++++++++++------ arch/x86/lib/Makefile | 2 ++ 3 files changed, 16 insertions(+), 6 deletions(-)
arch/x86/Kconfig +4 -0
@@ -315,6 +315,10 @@ config GENERIC_ISA_DMA def_bool y depends on ISA_DMA_API +config GENERIC_CSUM + bool + default y if KMSAN || KASAN + config GENERIC_BUG def_bool y depends on BUG
arch/x86/include/asm/checksum.h +10 -6
@@ -1,9 +1,13 @@/* SPDX-License-Identifier: GPL-2.0 */ -#define _HAVE_ARCH_COPY_AND_CSUM_FROM_USER 1 -#define HAVE_CSUM_COPY_USER -#define _HAVE_ARCH_CSUM_AND_COPY -#ifdef CONFIG_X86_32 -# include <asm/checksum_32.h> +#ifdef CONFIG_GENERIC_CSUM +# include <asm-generic/checksum.h> #else -# include <asm/checksum_64.h> +# define _HAVE_ARCH_COPY_AND_CSUM_FROM_USER 1 +# define HAVE_CSUM_COPY_USER +# define _HAVE_ARCH_CSUM_AND_COPY +# ifdef CONFIG_X86_32 +# include <asm/checksum_32.h> +# else +# include <asm/checksum_64.h> +# endif #endif
arch/x86/lib/Makefile +2 -0
@@ -65,7 +65,9 @@ ifneq ($(CONFIG_X86_CMPXCHG64),y) endif 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.35.1.1021.g381101b075-goog
Alexander Potapenko
maintainerPATCH v2 46/48 x86: fs: kmsan: disable CONFIG_DCACHE_WORD_ACCESS
In reply to Alexander Potapenkodentry_string_cmp() calls read_word_at_a_time(), which might read uninitialized bytes to optimize string comparisons. Disabling CONFIG_DCACHE_WORD_ACCESS should prohibit this optimization, as well as (probably) similar ones.
---
--- arch/x86/Kconfig | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
arch/x86/Kconfig +3 -1
@@ -126,7 +126,9 @@ config X86 select CLKEVT_I8253 select CLOCKSOURCE_VALIDATE_LAST_CYCLE select CLOCKSOURCE_WATCHDOG - select DCACHE_WORD_ACCESS + # Word-size accesses may read uninitialized data past the trailing \0 + # in strings and cause false KMSAN reports. + select DCACHE_WORD_ACCESS if !KMSAN select DYNAMIC_SIGFRAME select EDAC_ATOMIC_SCRUB select EDAC_SUPPORT
Signature · 1 line
2.35.1.1021.g381101b075-goog
Alexander Potapenko
maintainerPATCH v2 47/48 x86: kmsan: handle register passing from uninstrumented code
In reply to Alexander PotapenkoReplace instrumentation_begin() with instrumentation_begin_with_regs() to let KMSAN handle the non-instrumented code and unpoison pt_regs passed from the instrumented part. This is done to reduce the number of false positive reports.
---
v2:
-- this patch was previously called "x86: kmsan: handle register
passing from uninstrumented code". Instead of adding KMSAN-specific code to every instrumentation_begin()/instrumentation_end() section, we changed instrumentation_begin() to instrumentation_begin_with_regs() where applicable.
--- arch/x86/entry/common.c | 3 ++- arch/x86/include/asm/idtentry.h | 10 +++++----- arch/x86/kernel/cpu/mce/core.c | 2 +- arch/x86/kernel/kvm.c | 2 +- arch/x86/kernel/nmi.c | 2 +- arch/x86/kernel/sev.c | 4 ++-- arch/x86/kernel/traps.c | 14 +++++++------- arch/x86/mm/fault.c | 2 +- 8 files changed, 20 insertions(+), 19 deletions(-)
arch/x86/entry/common.c +2 -1
@@ -14,6 +14,7 @@#include <linux/mm.h> #include <linux/smp.h> #include <linux/errno.h> +#include <linux/kmsan.h> #include <linux/ptrace.h> #include <linux/export.h> #include <linux/nospec.h>
@@ -75,7 +76,7 @@ __visible noinstr void do_syscall_64(struct pt_regs *regs, int nr)add_random_kstack_offset (); nr =syscall_enter_from_user_mode (regs, nr); -instrumentation_begin (); +instrumentation_begin_with_regs (regs);if (!do_syscall_x64 (regs, nr) && !do_syscall_x32 (regs, nr) && nr !=-1 ) {/* Invalid system call, but still a system call. */
arch/x86/include/asm/idtentry.h +5 -5
@@ -51,7 +51,7 @@ __visible noinstr void func(struct pt_regs *regs) \ { \irqentry_state_t state =irqentry_enter (regs); \ \ -instrumentation_begin (); \ +instrumentation_begin_with_regs (regs); \ __##func (regs); \ instrumentation_end(); \ irqentry_exit(regs, state); \
@@ -98,7 +98,7 @@ __visible noinstr void func(struct pt_regs *regs, \ { \irqentry_state_t state =irqentry_enter (regs); \ \ -instrumentation_begin (); \ +instrumentation_begin_with_regs (regs); \ __##func (regs, error_code); \ instrumentation_end(); \ irqentry_exit(regs, state); \
@@ -195,7 +195,7 @@ __visible noinstr void func(struct pt_regs *regs, \irqentry_state_t state =irqentry_enter (regs); \u32 vector = (u32 )(u8 )error_code; \ \ -instrumentation_begin (); \ +instrumentation_begin_with_regs (regs); \kvm_set_cpu_l1tf_flush_l1d (); \ run_irq_on_irqstack_cond(__##func, regs, vector); \ instrumentation_end(); \
@@ -235,7 +235,7 @@ __visible noinstr void func(struct pt_regs *regs) \ { \irqentry_state_t state =irqentry_enter (regs); \ \ -instrumentation_begin (); \ +instrumentation_begin_with_regs (regs); \kvm_set_cpu_l1tf_flush_l1d (); \ run_sysvec_on_irqstack_cond(__##func, regs); \instrumentation_end (); \
@@ -262,7 +262,7 @@ __visible noinstr void func(struct pt_regs *regs) \ { \irqentry_state_t state =irqentry_enter (regs); \ \ -instrumentation_begin (); \ +instrumentation_begin_with_regs (regs); \__irq_enter_raw (); \kvm_set_cpu_l1tf_flush_l1d (); \ __##func (regs); \
arch/x86/kernel/cpu/mce/core.c +1 -1
@@ -1355,7 +1355,7 @@ static void queue_task_work(struct mce *m, char *msg, void (*func)(struct callba/* Handle unconfigured int18 (should never happen) */ static noinstr void unexpected_machine_check(struct pt_regs *regs) { -instrumentation_begin (); +instrumentation_begin_with_regs (regs);pr_err ("CPU#%d: Unexpected int18 (Machine Check)\n" ,smp_processor_id ());instrumentation_end ();
arch/x86/kernel/kvm.c +1 -1
@@ -249,7 +249,7 @@ noinstr bool __kvm_handle_async_pf(struct pt_regs *regs, u32 token)return false; state =irqentry_enter (regs); -instrumentation_begin (); +instrumentation_begin_with_regs (regs);/* * If the host managed to inject an async #PF into an interrupt
arch/x86/kernel/nmi.c +1 -1
@@ -329,7 +329,7 @@ static noinstr void default_do_nmi(struct pt_regs *regs)__this_cpu_write (last_nmi_rip, regs->ip); -instrumentation_begin (); +instrumentation_begin_with_regs (regs); handled =nmi_handle (NMI_LOCAL , regs);__this_cpu_add (nmi_stats.normal, handled);
arch/x86/kernel/sev.c +2 -2
@@ -1330,7 +1330,7 @@ DEFINE_IDTENTRY_VC_KERNEL(exc_vmm_communication) irq_state =irqentry_nmi_enter (regs); -instrumentation_begin (); +instrumentation_begin_with_regs (regs);if (!vc_raw_handle_exception (regs, error_code)) {/* Show some debug info */
@@ -1362,7 +1362,7 @@ DEFINE_IDTENTRY_VC_USER(exc_vmm_communication) }irqentry_enter_from_user_mode (regs); -instrumentation_begin (); +instrumentation_begin_with_regs (regs);if (!vc_raw_handle_exception (regs, error_code)) {/*
arch/x86/kernel/traps.c +7 -7
@@ -229,7 +229,7 @@ static noinstr bool handle_bug(struct pt_regs *regs)/* * All lies, just get the WARN/BUG out. */ -instrumentation_begin (); +instrumentation_begin_with_regs (regs);/* * Since we're emulating a CALL with exceptions, restore the interrupt * state to what it was at the exception site.
@@ -260,7 +260,7 @@ DEFINE_IDTENTRY_RAW(exc_invalid_op)return ; state =irqentry_enter (regs); -instrumentation_begin (); +instrumentation_begin_with_regs (regs);handle_invalid_op (regs);instrumentation_end ();irqentry_exit (regs, state);
@@ -414,7 +414,7 @@ DEFINE_IDTENTRY_DF(exc_double_fault)#endif irqentry_nmi_enter (regs); -instrumentation_begin (); +instrumentation_begin_with_regs (regs);notify_die (DIE_TRAP , str, regs, error_code,X86_TRAP_DF ,SIGSEGV ); tsk->thread.error_code = error_code;
@@ -690,14 +690,14 @@ DEFINE_IDTENTRY_RAW(exc_int3)*/ if (user_mode (regs)) {irqentry_enter_from_user_mode (regs); -instrumentation_begin (); +instrumentation_begin_with_regs (regs);do_int3_user (regs);instrumentation_end ();irqentry_exit_to_user_mode (regs); }else {irqentry_state_t irq_state =irqentry_nmi_enter (regs); -instrumentation_begin (); +instrumentation_begin_with_regs (regs);if (!do_int3 (regs))die ("int3" , regs,0 );instrumentation_end ();
@@ -896,7 +896,7 @@ static __always_inline void exc_debug_kernel(struct pt_regs *regs,*/ unsigned long dr7 =local_db_save ();irqentry_state_t irq_state =irqentry_nmi_enter (regs); -instrumentation_begin (); +instrumentation_begin_with_regs (regs);/* * If something gets miswired and we end up here for a user mode
@@ -975,7 +975,7 @@ static __always_inline void exc_debug_user(struct pt_regs *regs,*/ irqentry_enter_from_user_mode (regs); -instrumentation_begin (); +instrumentation_begin_with_regs (regs);/* * Start the virtual/ptrace DR6 value with just the DR_STEP mask
arch/x86/mm/fault.c +1 -1
@@ -1557,7 +1557,7 @@ DEFINE_IDTENTRY_RAW_ERRORCODE(exc_page_fault)*/ state =irqentry_enter (regs); -instrumentation_begin (); +instrumentation_begin_with_regs (regs);handle_page_fault (regs, error_code, address);instrumentation_end ();
Signature · 1 line
2.35.1.1021.g381101b075-goog
Alexander Potapenko
maintainerPATCH v2 48/48 x86: kmsan: enable KMSAN builds for x86
In reply to Alexander PotapenkoMake KMSAN usable by adding the necessary Kconfig bits.
---
--- arch/x86/Kconfig | 1 + 1 file changed, 1 insertion(+)
arch/x86/Kconfig +1 -0
@@ -165,6 +165,7 @@ config X86 select HAVE_ARCH_KASAN if X86_64 select HAVE_ARCH_KASAN_VMALLOC if X86_64 select HAVE_ARCH_KFENCE + 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
Signature · 1 line
2.35.1.1021.g381101b075-goog