Skip to contentSkip to conversation

Add KernelMemorySanitizer infrastructure

RFC PATCH Superseded [email protected] 7 participants 54 messages Oct 18, 2019 – Oct 30, 2019

Subsystems (47)

v1 of 5 Superseded Latest is v5 →

lore
Share & tools
lore

[email protected]

author maintainer
#0 lore raw

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: https://clang.llvm.org/docs/MemorySanitizer.html)
and tracks the state of every bit of kernel memory, being able to report an error if uninitialized value is used in a condition, dereferenced or copied to userspace, USB or network.

KMSAN has reported more than 200 bugs in the past two years, most of them with the help of syzkaller (http://syzkaller.appspot.com).

The proposed patchset contains KMSAN runtime implementation together with small changes to other subsystems needed to make KMSAN work. The latter changes fall into several categories:
- nice-to-have features that are independent from KMSAN but simplify its implementation (stackdepot changes, CONFIG_GENERIC_CSUM etc.);
- Kconfig changes that prohibit options incompatible with KMSAN;
- calls to KMSAN runtime functions that help KMSAN do the bookkeeping (e.g. tell it to allocate, copy or delete the metadata);
- calls to KMSAN runtime functions that tell KMSAN to check memory escaping the kernel for uninitialized values. These are required to increase the number of true positive error reports;
- calls to runtime functions that tell KMSAN to ignore certain memory ranges to avoid false negative reports. Most certainly there can be better ways to deal with every such report.

This patchset allows one to boot and run a defconfig+KMSAN kernel on a QEMU without known major false positives. It however doesn't guarantee there are no false positives in drivers of certain devices or less tested subsystems, although KMSAN is actively tested on syzbot with quite a rich config.

One may find it handy to review these patches in Gerrit:
https://linux-review.googlesource.com/c/linux/kernel/git/torvalds/linux/+/1081
I've ensured the Change-Id: tags stay away from commit descriptions.

Note: checkpatch.pl complains a lot about the use of BUG_ON in KMSAN source. I don't have a strong opinion on this, but KMSAN is a debugging tool, so any runtime invariant violation in it renders the tool useless. Therefore it doesn't make much sense to not terminate after a bug in KMSAN.

Alexander Potapenko (26):
stackdepot: check depot_index before accessing the stack slab
stackdepot: prevent Clang from optimizing away stackdepot_memcmp()
kasan: stackdepot: move filter_irq_stacks() to stackdepot.c
stackdepot: reserve 5 extra bits in depot_stack_handle_t
printk_safe: externalize printk_context
kasan: compiler.h: rename __no_kasan_or_inline into
__no_memory_tool_or_inline
kmsan: add ReST documentation
kmsan: gfp: introduce __GFP_NO_KMSAN_SHADOW
kmsan: introduce __no_sanitize_memory and __SANITIZE_MEMORY__
kmsan: reduce vmalloc space
kmsan: add KMSAN runtime
kmsan: x86: sync metadata pages on page fault
kmsan: add tests for KMSAN
kmsan: make READ_ONCE_TASK_STACK() return initialized values
kmsan: Kconfig changes to disable options incompatible with KMSAN
kmsan: Changing existing files to enable KMSAN builds
kmsan: disable KMSAN instrumentation for certain kernel parts
kmsan: mm: call KMSAN hooks from SLUB code
kmsan: call KMSAN hooks where needed
kmsan: disable instrumentation of certain functions
kmsan: unpoison |tlb| in arch_tlb_gather_mmu()
kmsan: use __msan_memcpy() where possible.
kmsan: unpoisoning buffers from devices etc.
kmsan: hooks for copy_to_user() and friends
kmsan: disable strscpy() optimization under KMSAN
net: kasan: kmsan: support CONFIG_GENERIC_CSUM on x86, enable it for
KASAN/KMSAN

To: Alexander Potapenko <[email protected]>
Cc: Alexander Viro <[email protected]>
Cc: Andrew Morton <[email protected]>
Cc: Andrey Ryabinin <[email protected]>
Cc: Andy Lutomirski <[email protected]>
Cc: Ard Biesheuvel <[email protected]>
Cc: Arnd Bergmann <[email protected]>
Cc: Christoph Hellwig <[email protected]>
Cc: Dmitry Torokhov <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: Eric Dumazet <[email protected]>
Cc: Eric Van Hensbergen <[email protected]>
Cc: Greg Kroah-Hartman <[email protected]>
Cc: Harry Wentland <[email protected]>
Cc: Herbert Xu <[email protected]>
Cc: Ingo Molnar <[email protected]>
Cc: Jens Axboe <[email protected]>
Cc: Martin K. Petersen <[email protected]>
Cc: Martin Schwidefsky <[email protected]>
Cc: "Michael S. Tsirkin" <[email protected]>
Cc: Michal Simek <[email protected]>
Cc: Petr Mladek <[email protected]>
Cc: Sergey Senozhatsky <[email protected]>
Cc: Steven Rostedt <[email protected]>
Cc: Takashi Iwai <[email protected]>
Cc: "Theodore Ts'o" <[email protected]>
Cc: Thomas Gleixner <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Wolfram Sang <[email protected]>
Cc: Vasily Gorbik <[email protected]>
Cc: Ilya Leoshkevich <[email protected]>
Cc: Mark Rutland <[email protected]>
Cc: [email protected]

 Documentation/dev-tools/index.rst       |   1 +
 Documentation/dev-tools/kmsan.rst       | 418 +++++++++++++++++
 Makefile                                |   3 +-
 arch/Kconfig                            |   2 +-
 arch/s390/include/asm/processor.h       |   4 +-
 arch/x86/Kconfig                        |   7 +
 arch/x86/Kconfig.debug                  |   3 +
 arch/x86/boot/Makefile                  |   2 +
 arch/x86/boot/compressed/Makefile       |   2 +
 arch/x86/boot/compressed/misc.h         |   1 +
 arch/x86/entry/common.c                 |   1 +
 arch/x86/entry/entry_64.S               |  18 +
 arch/x86/entry/vdso/Makefile            |   3 +
 arch/x86/include/asm/checksum.h         |  10 +-
 arch/x86/include/asm/irq_regs.h         |   1 +
 arch/x86/include/asm/kmsan.h            | 129 ++++++
 arch/x86/include/asm/page_64.h          |  13 +
 arch/x86/include/asm/page_64_types.h    |  12 +-
 arch/x86/include/asm/pgtable_64_types.h |  15 +
 arch/x86/include/asm/string_64.h        |   9 +-
 arch/x86/include/asm/syscall_wrapper.h  |   1 +
 arch/x86/include/asm/uaccess.h          |  12 +
 arch/x86/include/asm/unwind.h           |   9 +-
 arch/x86/kernel/Makefile                |   4 +
 arch/x86/kernel/apic/apic.c             |   2 +
 arch/x86/kernel/cpu/Makefile            |   1 +
 arch/x86/kernel/dumpstack_64.c          |   1 +
 arch/x86/kernel/process_64.c            |   5 +
 arch/x86/kernel/traps.c                 |  12 +-
 arch/x86/kernel/uprobes.c               |   7 +-
 arch/x86/lib/Makefile                   |   2 +
 arch/x86/mm/Makefile                    |   2 +
 arch/x86/mm/fault.c                     |  20 +
 arch/x86/mm/ioremap.c                   |   3 +
 arch/x86/realmode/rm/Makefile           |   2 +
 block/bio.c                             |  20 +
 block/blk.h                             |   7 +
 block/partition-generic.c               |   9 +-
 crypto/Kconfig                          |  26 ++
 drivers/char/random.c                   |   2 +
 drivers/firmware/efi/libstub/Makefile   |   1 +
 drivers/gpu/drm/amd/display/Kconfig     |   2 +-
 drivers/i2c/i2c-core-base.c             |   2 +
 drivers/input/serio/libps2.c            |   6 +-
 drivers/scsi/scsi_lib.c                 |   4 +
 drivers/usb/core/message.c              |   6 +-
 drivers/usb/core/urb.c                  |   2 +
 drivers/virtio/virtio_ring.c            |  14 +
 fs/buffer.c                             |   7 +-
 include/asm-generic/cacheflush.h        |   7 +-
 include/asm-generic/uaccess.h           |  12 +-
 include/linux/compiler-clang.h          |   8 +
 include/linux/compiler-gcc.h            |   5 +
 include/linux/compiler.h                |  21 +-
 include/linux/dma-mapping.h             |   2 +
 include/linux/gfp.h                     |   4 +-
 include/linux/highmem.h                 |   4 +
 include/linux/kmsan-checks.h            | 123 +++++
 include/linux/kmsan.h                   | 143 ++++++
 include/linux/mm_types.h                |   9 +
 include/linux/sched.h                   |   5 +
 include/linux/skbuff.h                  |   5 +-
 include/linux/stackdepot.h              |  10 +
 include/linux/string.h                  |   2 +
 include/linux/uaccess.h                 |  32 +-
 init/main.c                             |   3 +
 kernel/Makefile                         |   1 +
 kernel/exit.c                           |   2 +
 kernel/fork.c                           |   2 +
 kernel/kthread.c                        |   2 +
 kernel/printk/printk.c                  |   8 +-
 kernel/printk/printk_safe.c             |   2 +-
 kernel/profile.c                        |   1 +
 kernel/sched/core.c                     |  11 +
 kernel/softirq.c                        |   5 +
 lib/Kconfig.debug                       |   5 +
 lib/Kconfig.kmsan                       |  22 +
 lib/Makefile                            |   2 +
 lib/ioremap.c                           |   5 +
 lib/iov_iter.c                          |   6 +
 lib/stackdepot.c                        |  72 ++-
 lib/string.c                            |   5 +-
 lib/test_kmsan.c                        | 221 +++++++++
 lib/usercopy.c                          |   6 +-
 mm/Makefile                             |   1 +
 mm/compaction.c                         |   9 +
 mm/filemap.c                            |   3 +
 mm/gup.c                                |   3 +
 mm/kasan/common.c                       |  23 -
 mm/kmsan/Makefile                       |   4 +
 mm/kmsan/kmsan.c                        | 570 ++++++++++++++++++++++++
 mm/kmsan/kmsan.h                        | 147 ++++++
 mm/kmsan/kmsan_entry.c                  | 130 ++++++
 mm/kmsan/kmsan_hooks.c                  | 416 +++++++++++++++++
 mm/kmsan/kmsan_init.c                   |  88 ++++
 mm/kmsan/kmsan_instr.c                  | 259 +++++++++++
 mm/kmsan/kmsan_report.c                 | 135 ++++++
 mm/kmsan/kmsan_shadow.c                 | 543 ++++++++++++++++++++++
 mm/kmsan/kmsan_shadow.h                 |  30 ++
 mm/memory.c                             |   2 +
 mm/mmu_gather.c                         |  10 +
 mm/page_alloc.c                         |  16 +
 mm/readahead.c                          |   6 +
 mm/slub.c                               |  37 +-
 mm/vmalloc.c                            |  23 +-
 net/9p/protocol.c                       |   2 +
 net/sched/sch_generic.c                 |   2 +
 scripts/Makefile.kmsan                  |  12 +
 scripts/Makefile.lib                    |   6 +
 sound/core/oss/pcm_oss.c                |   7 +
 110 files changed, 4022 insertions(+), 80 deletions(-)
 create mode 100644 Documentation/dev-tools/kmsan.rst
 create mode 100644 arch/x86/include/asm/kmsan.h
 create mode 100644 include/linux/kmsan-checks.h
 create mode 100644 include/linux/kmsan.h
 create mode 100644 lib/Kconfig.kmsan
 create mode 100644 lib/test_kmsan.c
 create mode 100644 mm/kmsan/Makefile
 create mode 100644 mm/kmsan/kmsan.c
 create mode 100644 mm/kmsan/kmsan.h
 create mode 100644 mm/kmsan/kmsan_entry.c
 create mode 100644 mm/kmsan/kmsan_hooks.c
 create mode 100644 mm/kmsan/kmsan_init.c
 create mode 100644 mm/kmsan/kmsan_instr.c
 create mode 100644 mm/kmsan/kmsan_report.c
 create mode 100644 mm/kmsan/kmsan_shadow.c
 create mode 100644 mm/kmsan/kmsan_shadow.h
 create mode 100644 scripts/Makefile.kmsan
Signature · 1 line
2.23.0.866.gb869b98d4c-goog
author maintainer
#1 lore raw

RFC PATCH 1/26 stackdepot: check depot_index before accessing the stack slab

Avoid crashes on corrupted stack ids.

To: Alexander Potapenko <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]

---

Change-Id: I0a0b38ed5057090696a2c6ff0be7cfcc24ae6738

---
 lib/stackdepot.c | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)
lib/stackdepot.c +15 -2
index ed717dd08ff3..0d00d2ac0c4b 100644
@@ -198,9 +198,22 @@ unsigned int stack_depot_fetch(depot_stack_handle_t handle,
 			       unsigned long **entries)
 {
 	union handle_parts parts = { .handle = handle };
-	void *slab = stack_slabs[parts.slabindex];
+	void *slab;
 	size_t offset = parts.offset << STACK_ALLOC_ALIGN;
-	struct stack_record *stack = slab + offset;
+	struct stack_record *stack;
+
+	if (parts.slabindex > depot_index) {
+		WARN(1, "slab index %d out of bounds (%d) for stack id %08x\n",
+			parts.slabindex, depot_index, handle);
+		__memset(trace, 0, sizeof(*trace));
+		return;
+	}
+	slab = stack_slabs[parts.slabindex];
+	stack = slab + offset;
+	if (!stack) {
+		entries = NULL;
+		return 0;
+	}
 
 	*entries = stack->entries;
 	return stack->size;
Signature · 1 line
2.23.0.866.gb869b98d4c-goog
author maintainer
#2 lore raw

RFC PATCH 2/26 stackdepot: prevent Clang from optimizing away stackdepot_memcmp()

In reply to [email protected]

Clang may replace stackdepot_memcmp() with a call to instrumented bcmp(), which is exactly what we wanted to avoid creating stackdepot_memcmp(). Add a compiler barrier() to prevent optimizations.

To: Alexander Potapenko <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]
---

Change-Id: I4495b617b15c0ab003a61c1f0d54d0026fa8b144

---
 lib/stackdepot.c | 5 +++++
 1 file changed, 5 insertions(+)
lib/stackdepot.c +5 -0
index 0d00d2ac0c4b..785839298e08 100644
@@ -163,6 +163,11 @@ int stackdepot_memcmp(const unsigned long *u1, const unsigned long *u2,
 			unsigned int n)
 {
 	for ( ; n-- ; u1++, u2++) {
+		/*
+		 * Prevent Clang from replacing this function with a bcmp()
+		 * call.
+		 */
+		barrier();
 		if (*u1 != *u2)
 			return 1;
 	}
Signature · 1 line
2.23.0.866.gb869b98d4c-goog
author maintainer
#3 lore raw

RFC PATCH 3/26 kasan: stackdepot: move filter_irq_stacks() to stackdepot.c

In reply to [email protected]

filter_irq_stacks() can be used by other tools (e.g. KMSAN), so it needs to be moved to a common location.
lib/stackdepot.c seems a good place, as filter_irq_stacks() is usually applied to the output of stack_trace_save().

To: Alexander Potapenko <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: Andrey Ryabinin <[email protected]>
Cc: [email protected]
---

Change-Id: I65acecf64930a3658e8c2aa7423801082ded8602

---
 include/linux/stackdepot.h |  2 ++
 lib/stackdepot.c           | 23 +++++++++++++++++++++++
 mm/kasan/common.c          | 23 -----------------------
 3 files changed, 25 insertions(+), 23 deletions(-)
include/linux/stackdepot.h +2 -0
index 3efa97d482cb..24d49c732341 100644
@@ -19,4 +19,6 @@ depot_stack_handle_t stack_depot_save(unsigned long *entries,
 unsigned int stack_depot_fetch(depot_stack_handle_t handle,
 			       unsigned long **entries);
 
+unsigned int filter_irq_stacks(unsigned long *entries, unsigned int nr_entries);
+
 #endif
lib/stackdepot.c +23 -0
index 785839298e08..decb1e073b58 100644
@@ -20,6 +20,7 @@
  */
 
 #include <linux/gfp.h>
+#include <linux/interrupt.h>
 #include <linux/jhash.h>
 #include <linux/kernel.h>
 #include <linux/mm.h>
@@ -319,3 +320,25 @@ depot_stack_handle_t stack_depot_save(unsigned long *entries,
 	return retval;
 }
 EXPORT_SYMBOL_GPL(stack_depot_save);
+
+static inline int in_irqentry_text(unsigned long ptr)
+{
+	return (ptr >= (unsigned long)&__irqentry_text_start &&
+		ptr < (unsigned long)&__irqentry_text_end) ||
+		(ptr >= (unsigned long)&__softirqentry_text_start &&
+		 ptr < (unsigned long)&__softirqentry_text_end);
+}
+
+unsigned int filter_irq_stacks(unsigned long *entries,
+					     unsigned int nr_entries)
+{
+	unsigned int i;
+
+	for (i = 0; i < nr_entries; i++) {
+		if (in_irqentry_text(entries[i])) {
+			/* Include the irqentry function into the stack. */
+			return i + 1;
+		}
+	}
+	return nr_entries;
+}
mm/kasan/common.c +0 -23
index 6814d6d6a023..154eba5700d8 100644
@@ -15,7 +15,6 @@
  */
 
 #include <linux/export.h>
-#include <linux/interrupt.h>
 #include <linux/init.h>
 #include <linux/kasan.h>
 #include <linux/kernel.h>
@@ -39,28 +38,6 @@
 #include "kasan.h"
 #include "../slab.h"
 
-static inline int in_irqentry_text(unsigned long ptr)
-{
-	return (ptr >= (unsigned long)&__irqentry_text_start &&
-		ptr < (unsigned long)&__irqentry_text_end) ||
-		(ptr >= (unsigned long)&__softirqentry_text_start &&
-		 ptr < (unsigned long)&__softirqentry_text_end);
-}
-
-static inline unsigned int filter_irq_stacks(unsigned long *entries,
-					     unsigned int nr_entries)
-{
-	unsigned int i;
-
-	for (i = 0; i < nr_entries; i++) {
-		if (in_irqentry_text(entries[i])) {
-			/* Include the irqentry function into the stack. */
-			return i + 1;
-		}
-	}
-	return nr_entries;
-}
-
 static inline depot_stack_handle_t save_stack(gfp_t flags)
 {
 	unsigned long entries[KASAN_STACK_DEPTH];
Signature · 1 line
2.23.0.866.gb869b98d4c-goog
author maintainer
#4 lore raw

RFC PATCH 4/26 stackdepot: reserve 5 extra bits in depot_stack_handle_t

In reply to [email protected]

Some users (currently only KMSAN) may want to use spare bits in depot_stack_handle_t. Let them do so and provide get_dsh_extra_bits() and set_dsh_extra_bits() to access those bits.

To: Alexander Potapenko <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]
---

Change-Id: I23580dbde85908eeda0bdd8f83a8c3882ab3e012

---
 include/linux/stackdepot.h |  8 ++++++++
 lib/stackdepot.c           | 24 +++++++++++++++++++++++-
 2 files changed, 31 insertions(+), 1 deletion(-)
include/linux/stackdepot.h +8 -0
index 24d49c732341..ac1b5a78d7f6 100644
@@ -12,6 +12,11 @@
 #define _LINUX_STACKDEPOT_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 5
 
 depot_stack_handle_t stack_depot_save(unsigned long *entries,
 				      unsigned int nr_entries, gfp_t gfp_flags);
@@ -20,5 +25,8 @@ unsigned int stack_depot_fetch(depot_stack_handle_t handle,
 			       unsigned long **entries);
 
 unsigned int filter_irq_stacks(unsigned long *entries, unsigned int nr_entries);
+depot_stack_handle_t set_dsh_extra_bits(depot_stack_handle_t handle,
+					unsigned int bits);
+unsigned int get_dsh_extra_bits(depot_stack_handle_t handle);
 
 #endif
lib/stackdepot.c +23 -1
index decb1e073b58..460efd3ef742 100644
@@ -40,8 +40,10 @@
 #define STACK_ALLOC_ALIGN 4
 #define STACK_ALLOC_OFFSET_BITS (STACK_ALLOC_ORDER + PAGE_SHIFT - \
 					STACK_ALLOC_ALIGN)
+
 #define STACK_ALLOC_INDEX_BITS (DEPOT_STACK_BITS - \
-		STACK_ALLOC_NULL_PROTECTION_BITS - STACK_ALLOC_OFFSET_BITS)
+		STACK_ALLOC_NULL_PROTECTION_BITS - \
+		STACK_ALLOC_OFFSET_BITS - STACK_DEPOT_EXTRA_BITS)
 #define STACK_ALLOC_SLABS_CAP 8192
 #define STACK_ALLOC_MAX_SLABS \
 	(((1LL << (STACK_ALLOC_INDEX_BITS)) < STACK_ALLOC_SLABS_CAP) ? \
@@ -54,6 +56,7 @@ union handle_parts {
 		u32 slabindex : STACK_ALLOC_INDEX_BITS;
 		u32 offset : STACK_ALLOC_OFFSET_BITS;
 		u32 valid : STACK_ALLOC_NULL_PROTECTION_BITS;
+		u32 extra : STACK_DEPOT_EXTRA_BITS;
 	};
 };
 
@@ -72,6 +75,24 @@ static int next_slab_inited;
 static size_t depot_offset;
 static DEFINE_SPINLOCK(depot_lock);
 
+depot_stack_handle_t set_dsh_extra_bits(depot_stack_handle_t handle,
+					u32 bits)
+{
+	union handle_parts parts = { .handle = handle };
+
+	parts.extra = bits & ((1U << STACK_DEPOT_EXTRA_BITS) - 1);
+	return parts.handle;
+}
+EXPORT_SYMBOL_GPL(set_dsh_extra_bits);
+
+u32 get_dsh_extra_bits(depot_stack_handle_t handle)
+{
+	union handle_parts parts = { .handle = handle };
+
+	return parts.extra;
+}
+EXPORT_SYMBOL_GPL(get_dsh_extra_bits);
+
 static bool init_stack_slab(void **prealloc)
 {
 	if (!*prealloc)
@@ -132,6 +153,7 @@ static struct stack_record *depot_alloc_stack(unsigned long *entries, int size,
 	stack->handle.slabindex = depot_index;
 	stack->handle.offset = depot_offset >> STACK_ALLOC_ALIGN;
 	stack->handle.valid = 1;
+	stack->handle.extra = 0;
 	memcpy(stack->entries, entries, size * sizeof(unsigned long));
 	depot_offset += required_size;
 
Signature · 1 line
2.23.0.866.gb869b98d4c-goog
author maintainer
#5 lore raw

RFC PATCH 5/26 printk_safe: externalize printk_context

In reply to [email protected]

Petr Mladek suggested we use
this_cpu_read(printk_context) & PRINTK_SAFE_CONTEXT_MASK
instead of checking the spinlock status in kmsan_pr_err()

This appears to be less intrusive, although we'll need to declare printk_context in some printk header.

To: Alexander Potapenko <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Petr Mladek <[email protected]>
Cc: Sergey Senozhatsky <[email protected]>
Cc: Steven Rostedt <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]
---

Change-Id: Idb5e7c7cedf6ab6ba635a64423d58d702ad3eca3

---
 kernel/printk/printk_safe.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/printk/printk_safe.c +1 -1
index b4045e782743..5ebfc8bc5693 100644
@@ -42,7 +42,7 @@ struct printk_safe_seq_buf {
 };
 
 static DEFINE_PER_CPU(struct printk_safe_seq_buf, safe_print_seq);
-static DEFINE_PER_CPU(int, printk_context);
+DEFINE_PER_CPU(int, printk_context);
 
 #ifdef CONFIG_PRINTK_NMI
 static DEFINE_PER_CPU(struct printk_safe_seq_buf, nmi_print_seq);
Signature · 1 line
2.23.0.866.gb869b98d4c-goog
maintainer
#6 lore raw

Re: [PATCH RFC v1 05/26] printk_safe: externalize printk_context

Quoted 3 lines · On Fri 2019-10-18 11:42:43, [email protected] wrote:

Petr Mladek suggested we use
this_cpu_read(printk_context) & PRINTK_SAFE_CONTEXT_MASK
instead of checking the spinlock status in kmsan_pr_err()

I would like to understand why the check is needed.

My guess is that it prevents a infinite recursion. Is this the case? It might be possible to debug this using trace_printk().

It is important. If it is the recursion then there are two possibilities how to prevent it. Either prevent calling the recursive printk(). Or prevent calling the memory checker recursive. I am not sure what makes more sense.

Is printk() the only piece of code where you need to avoid recursion? I wonder how it is prevented in the other cases.

Quoted 2 lines

This appears to be less intrusive, although we'll need to declare printk_context in some printk header.

It is easier than the original approach. But the main motivation is that it is more reliable. The spinlock is a global lock. But it seems that it is enough to check the state of the local CPU.

Finally, could you please CC me to the patch(es) that are using this variable? I would actually prefer to be in CC of entire patchsets so that I see the full context.

Best Regards,
Petr

author maintainer
#7 lore raw

Re: [PATCH RFC v1 05/26] printk_safe: externalize printk_context

Quoted 11 lines · Petr Mladek
Quoted 3 lines · On Fri 2019-10-18 11:42:43, [email protected] wrote:

Petr Mladek suggested we use
this_cpu_read(printk_context) & PRINTK_SAFE_CONTEXT_MASK
instead of checking the spinlock status in kmsan_pr_err()

I would like to understand why the check is needed.

My guess is that it prevents a infinite recursion. Is this the case? It might be possible to debug this using trace_printk().

This indeed used to prevent infinite recursion, however looks like the check isn't needed anymore.
When I remove it, KMSAN doesn't hang, probably because printk is doing a better job at deadlock prevention.

What I'm seeing now is that e.g. in the following case:
ptr = kmalloc(sizeof(int), GFP_KERNEL);
if (ptr)

    pr_info("true\n");
  else
    pr_info("false\n");

KMSAN detects errors within pr_info(), namely in vprintk_store(). If I understand correctly, printing from that point causes printk to use the per-cpu buffer, which is flushed once we're done with the original printing:

[   58.984971][ T8390] BUG: KMSAN: uninit-value in
kmsan_handle_vprintk+0xa0/0xf0
...
[   59.061976][    C0] BUG: KMSAN: uninit-value in vsnprintf+0x3276/0x32b0
...
[   59.062457][    C0] BUG: KMSAN: uninit-value in format_decode+0x17f/0x1900
...
[   59.062961][    C0] BUG: KMSAN: uninit-value in format_decode+0x17f/0x1900
...
[   59.063338][    C0] Lost 6207 message(s)!

So it turns out we'd better disable reporting under logbuf_lock, otherwise these lost messages will confuse the developers. Because the first pr_info() isn't called by KMSAN itself, the tool still needs a way to know it's running inside printk.

Quoted 23 lines

It is important. If it is the recursion then there are two possibilities how to prevent it. Either prevent calling the recursive printk(). Or prevent calling the memory checker recursive. I am not sure what makes more sense.

Is printk() the only piece of code where you need to avoid recursion? I wonder how it is prevented in the other cases.

Quoted 2 lines

This appears to be less intrusive, although we'll need to declare printk_context in some printk header.

It is easier than the original approach. But the main motivation is that it is more reliable. The spinlock is a global lock. But it seems that it is enough to check the state of the local CPU.

Finally, could you please CC me to the patch(es) that are using this variable? I would actually prefer to be in CC of entire patchsets so that I see the full context.

Best Regards,
Petr

Signature · 10 lines
Alexander Potapenko
Software Engineer

Google Germany GmbH
Erika-Mann-Straße, 33
80636 München

Geschäftsführer: Paul Manicle, Halimah DeLaine Prado
Registergericht und -nummer: Hamburg, HRB 86891
Sitz der Gesellschaft: Hamburg
author maintainer
#8 lore raw

Re: [PATCH RFC v1 05/26] printk_safe: externalize printk_context

Quoted 41 lines · Alexander Potapenko
Quoted 11 lines · Petr Mladek
Quoted 3 lines · On Fri 2019-10-18 11:42:43, [email protected] wrote:

Petr Mladek suggested we use
this_cpu_read(printk_context) & PRINTK_SAFE_CONTEXT_MASK
instead of checking the spinlock status in kmsan_pr_err()

I would like to understand why the check is needed.

My guess is that it prevents a infinite recursion. Is this the case? It might be possible to debug this using trace_printk().

This indeed used to prevent infinite recursion, however looks like the check isn't needed anymore.
When I remove it, KMSAN doesn't hang, probably because printk is doing a better job at deadlock prevention.

What I'm seeing now is that e.g. in the following case:
ptr = kmalloc(sizeof(int), GFP_KERNEL);
if (ptr)

    pr_info("true\n");
  else
    pr_info("false\n");

KMSAN detects errors within pr_info(), namely in vprintk_store(). If I understand correctly, printing from that point causes printk to use the per-cpu buffer, which is flushed once we're done with the original printing:

[   58.984971][ T8390] BUG: KMSAN: uninit-value in
kmsan_handle_vprintk+0xa0/0xf0
...
[   59.061976][    C0] BUG: KMSAN: uninit-value in vsnprintf+0x3276/0x32b0
...
[   59.062457][    C0] BUG: KMSAN: uninit-value in format_decode+0x17f/0x1900
...
[   59.062961][    C0] BUG: KMSAN: uninit-value in format_decode+0x17f/0x1900
...
[   59.063338][    C0] Lost 6207 message(s)!
Quoted 25 lines

So it turns out we'd better disable reporting under logbuf_lock, otherwise these lost messages will confuse the developers. Because the first pr_info() isn't called by KMSAN itself, the tool still needs a way to know it's running inside printk.

Quoted 20 lines

It is important. If it is the recursion then there are two possibilities how to prevent it. Either prevent calling the recursive printk(). Or prevent calling the memory checker recursive. I am not sure what makes more sense.

Is printk() the only piece of code where you need to avoid recursion? I wonder how it is prevented in the other cases.

Quoted 2 lines

This appears to be less intrusive, although we'll need to declare printk_context in some printk header.

It is easier than the original approach. But the main motivation is that it is more reliable. The spinlock is a global lock. But it seems that it is enough to check the state of the local CPU.

Finally, could you please CC me to the patch(es) that are using this variable? I would actually prefer to be in CC of entire patchsets so that I see the full context.

I am very sorry about this. I'm going to re-send the patches soon and CC everyone on every patch.
What I thought to be reducing the email burden on every reviewer turned out to be really inconvenient.

Quoted 16 lines
Quoted 2 lines

Best Regards,
Petr

--
Alexander Potapenko
Software Engineer

Google Germany GmbH
Erika-Mann-Straße, 33
80636 München

Geschäftsführer: Paul Manicle, Halimah DeLaine Prado Registergericht und -nummer: Hamburg, HRB 86891 Sitz der Gesellschaft: Hamburg

Signature · 10 lines
Alexander Potapenko
Software Engineer

Google Germany GmbH
Erika-Mann-Straße, 33
80636 München

Geschäftsführer: Paul Manicle, Halimah DeLaine Prado
Registergericht und -nummer: Hamburg, HRB 86891
Sitz der Gesellschaft: Hamburg
maintainer
#9 lore raw

Re: [PATCH RFC v1 05/26] printk_safe: externalize printk_context

In reply to Alexander Potapenko
Quoted 9 lines · Alexander Potapenko
Quoted 8 lines · Petr Mladek
Quoted 3 lines · On Fri 2019-10-18 11:42:43, [email protected] wrote:

Petr Mladek suggested we use
this_cpu_read(printk_context) & PRINTK_SAFE_CONTEXT_MASK
instead of checking the spinlock status in kmsan_pr_err()

I would like to understand why the check is needed.

Quoted 7 lines
Quoted 3 lines

My guess is that it prevents a infinite recursion. Is this the case? It might be possible to debug this using trace_printk().

This indeed used to prevent infinite recursion, however looks like the check isn't needed anymore.
When I remove it, KMSAN doesn't hang, probably because printk is doing a better job at deadlock prevention.

Great.

Quoted 12 lines

What I'm seeing now is that e.g. in the following case:
ptr = kmalloc(sizeof(int), GFP_KERNEL);
if (ptr)

    pr_info("true\n");
  else
    pr_info("false\n");

KMSAN detects errors within pr_info(), namely in vprintk_store(). If I understand correctly, printing from that point causes printk to use the per-cpu buffer, which is flushed once we're done with the original printing:

Quoted 8 lines
[   58.984971][ T8390] BUG: KMSAN: uninit-value in
kmsan_handle_vprintk+0xa0/0xf0
...
[   59.061976][    C0] BUG: KMSAN: uninit-value in vsnprintf+0x3276/0x32b0
...
[   59.062457][    C0] BUG: KMSAN: uninit-value in format_decode+0x17f/0x1900
...
[   59.062961][    C0] BUG: KMSAN: uninit-value in format_decode+0x17f/0x1900

Please, do you have an explanation where the uninitialized values come from? Is it a false positive? Or is there really a bug how the printk() parameters are passed?

Quoted 2 lines

...

[   59.063338][    C0] Lost 6207 message(s)!

This message comes from report_message_lost(). It means that many recursive messages did not fit into the limited per-CPU buffers.

Quoted 4 lines

So it turns out we'd better disable reporting under logbuf_lock, otherwise these lost messages will confuse the developers. Because the first pr_info() isn't called by KMSAN itself, the tool still needs a way to know it's running inside printk.

OK. Well, I still would like to understand why the recursive messages are printed at all. Is it a bug in the printk() code or a false positive?

There should be no recursive messages if we fix the bug or make some annotations to prevent the false positives.

That said, I am not completely against disabling the recursive messages. But it would be pity to hide some real bugs.

Best Regards,
Petr

author maintainer
#10 lore raw

Re: [PATCH RFC v1 05/26] printk_safe: externalize printk_context

Quoted 42 lines · Petr Mladek
Quoted 9 lines · Alexander Potapenko
Quoted 8 lines · Petr Mladek
Quoted 3 lines · On Fri 2019-10-18 11:42:43, [email protected] wrote:

Petr Mladek suggested we use
this_cpu_read(printk_context) & PRINTK_SAFE_CONTEXT_MASK
instead of checking the spinlock status in kmsan_pr_err()

I would like to understand why the check is needed.

Quoted 7 lines
Quoted 3 lines

My guess is that it prevents a infinite recursion. Is this the case? It might be possible to debug this using trace_printk().

This indeed used to prevent infinite recursion, however looks like the check isn't needed anymore.
When I remove it, KMSAN doesn't hang, probably because printk is doing a better job at deadlock prevention.

Great.

Quoted 12 lines

What I'm seeing now is that e.g. in the following case:
ptr = kmalloc(sizeof(int), GFP_KERNEL);
if (ptr)

    pr_info("true\n");
  else
    pr_info("false\n");

KMSAN detects errors within pr_info(), namely in vprintk_store(). If I understand correctly, printing from that point causes printk to use the per-cpu buffer, which is flushed once we're done with the original printing:

Quoted 8 lines
[   58.984971][ T8390] BUG: KMSAN: uninit-value in
kmsan_handle_vprintk+0xa0/0xf0
...
[   59.061976][    C0] BUG: KMSAN: uninit-value in vsnprintf+0x3276/0x32b0
...
[   59.062457][    C0] BUG: KMSAN: uninit-value in format_decode+0x17f/0x1900
...
[   59.062961][    C0] BUG: KMSAN: uninit-value in format_decode+0x17f/0x1900
Quoted 3 lines

Please, do you have an explanation where the uninitialized values come from? Is it a false positive? Or is there really a bug how the printk() parameters are passed?

I believe these are true bugs.
The problem is, when we pass an uninitialized argument into printk(), KMSAN will report an error every time this uninitialized argument is used.
E.g. for an uninitialized format string there'll be at least strlen(fmt) reports in format_decode(), followed by several reports in vsnprintf().
Although these reports seem to be real, printing only the first of them should be more than enough.
In the future we'll actually want KMSAN to check every printk() argument (which will require parsing the format string to figure out the arguments' lengths), but disable reports within printk.

Quoted 14 lines
Quoted 2 lines

...

[   59.063338][    C0] Lost 6207 message(s)!

This message comes from report_message_lost(). It means that many recursive messages did not fit into the limited per-CPU buffers.

Quoted 4 lines

So it turns out we'd better disable reporting under logbuf_lock, otherwise these lost messages will confuse the developers. Because the first pr_info() isn't called by KMSAN itself, the tool still needs a way to know it's running inside printk.

OK. Well, I still would like to understand why the recursive messages are printed at all. Is it a bug in the printk() code or a false positive?

Quoted 8 lines

There should be no recursive messages if we fix the bug or make some annotations to prevent the false positives.

That said, I am not completely against disabling the recursive messages. But it would be pity to hide some real bugs.

Best Regards,
Petr

--
Alexander Potapenko
Software Engineer

Google Germany GmbH
Erika-Mann-Straße, 33
80636 München

Geschäftsführer: Paul Manicle, Halimah DeLaine Prado Registergericht und -nummer: Hamburg, HRB 86891 Sitz der Gesellschaft: Hamburg

maintainer
#11 lore raw

Re: [PATCH RFC v1 05/26] printk_safe: externalize printk_context

Quoted 23 lines · Alexander Potapenko
Quoted 22 lines · Petr Mladek
Quoted 20 lines · Alexander Potapenko

What I'm seeing now is that e.g. in the following case:
ptr = kmalloc(sizeof(int), GFP_KERNEL);
if (ptr)

    pr_info("true\n");
  else
    pr_info("false\n");

KMSAN detects errors within pr_info(), namely in vprintk_store(). If I understand correctly, printing from that point causes printk to use the per-cpu buffer, which is flushed once we're done with the original printing:

[   58.984971][ T8390] BUG: KMSAN: uninit-value in
kmsan_handle_vprintk+0xa0/0xf0
...
[   59.061976][    C0] BUG: KMSAN: uninit-value in vsnprintf+0x3276/0x32b0
...
[   59.062457][    C0] BUG: KMSAN: uninit-value in format_decode+0x17f/0x1900
...
[   59.062961][    C0] BUG: KMSAN: uninit-value in format_decode+0x17f/0x1900
Quoted 7 lines
Quoted 3 lines

Please, do you have an explanation where the uninitialized values come from? Is it a false positive? Or is there really a bug how the printk() parameters are passed?

I believe these are true bugs.
The problem is, when we pass an uninitialized argument into printk(), KMSAN will report an error every time this uninitialized argument is used.

I see, thanks for explanation.

Quoted 5 lines

E.g. for an uninitialized format string there'll be at least strlen(fmt) reports in format_decode(), followed by several reports in vsnprintf().
Although these reports seem to be real, printing only the first of them should be more than enough.

Isn't this a generic problem? I mean that uninitialized values can be passed and used also in many other locations.

printk() is special because this problem causes infinite loop. But it would make sense to report also any other problematic value only once.

Quoted 3 lines

In the future we'll actually want KMSAN to check every printk() argument (which will require parsing the format string to figure out the arguments' lengths), but disable reports within printk.

What is the motivation for this, please?

It looks to me that you want to do very paranoid checks of variables passed to printk()? Do you want to prevent printk() crashes? Or do you want to make sure that printk() produces correct values?

From my POV, printk() is debugging tool. It is used to show values that people are interested in. On one hand, it might make sense to warn people that a particular value was not initalized. On the other hand, printk() is not important for the kernel behavior. It just reads values and does not affect any behavior.

I would like to understand how many printk-related code is worth the effort.

Best Regards,
Petr

author maintainer
#12 lore raw

Re: [PATCH RFC v1 05/26] printk_safe: externalize printk_context

Quoted 35 lines · Petr Mladek
Quoted 23 lines · Alexander Potapenko
Quoted 22 lines · Petr Mladek
Quoted 20 lines · Alexander Potapenko

What I'm seeing now is that e.g. in the following case:
ptr = kmalloc(sizeof(int), GFP_KERNEL);
if (ptr)

    pr_info("true\n");
  else
    pr_info("false\n");

KMSAN detects errors within pr_info(), namely in vprintk_store(). If I understand correctly, printing from that point causes printk to use the per-cpu buffer, which is flushed once we're done with the original printing:

[   58.984971][ T8390] BUG: KMSAN: uninit-value in
kmsan_handle_vprintk+0xa0/0xf0
...
[   59.061976][    C0] BUG: KMSAN: uninit-value in vsnprintf+0x3276/0x32b0
...
[   59.062457][    C0] BUG: KMSAN: uninit-value in format_decode+0x17f/0x1900
...
[   59.062961][    C0] BUG: KMSAN: uninit-value in format_decode+0x17f/0x1900
Quoted 7 lines
Quoted 3 lines

Please, do you have an explanation where the uninitialized values come from? Is it a false positive? Or is there really a bug how the printk() parameters are passed?

I believe these are true bugs.
The problem is, when we pass an uninitialized argument into printk(), KMSAN will report an error every time this uninitialized argument is used.

I see, thanks for explanation.

Quoted 8 lines
Quoted 5 lines

E.g. for an uninitialized format string there'll be at least strlen(fmt) reports in format_decode(), followed by several reports in vsnprintf().
Although these reports seem to be real, printing only the first of them should be more than enough.

Isn't this a generic problem? I mean that uninitialized values can be passed and used also in many other locations.

More or less, yes.

Quoted 2 lines

printk() is special because this problem causes infinite loop. But it would make sense to report also any other problematic value only once.

Yes, printk is a bit different, because finding errors in it causes other printk() calls which now don't cause infinite loops, but may produce out-of-order error messages.

Quoted 10 lines
Quoted 4 lines

In the future we'll actually want KMSAN to check every printk() argument (which will require parsing the format string to figure out the arguments' lengths), but disable reports within printk.

What is the motivation for this, please?

It looks to me that you want to do very paranoid checks of variables passed to printk()? Do you want to prevent printk() crashes? Or do you want to make sure that printk() produces correct values?

Simply passing an uninitialized value to printk() may result in cryptic error messages from printk guts, which may be harder to understand without knowing how printk works.
The idea is to check the arguments right before printk, so that we can print a really simple diagnostic message.

Quoted 8 lines

From my POV, printk() is debugging tool. It is used to show values that people are interested in. On one hand, it might make sense to warn people that a particular value was not initalized. On the other hand, printk() is not important for the kernel behavior. It just reads values and does not affect any behavior.

I would like to understand how many printk-related code is worth the effort.

I think overall you're right.
The feature I'm talking about isn't a critical part of KMSAN functionality, so in order to keep the code simple I'd better drop it, as long as we're able to report bugs when uninitialized memory is passed to printk.

The only change to printk that I'll probably still have to make is to initialize the result of vscnprintf() here:
https://github.com/google/kmsan/blob/master/kernel/printk/printk.c#L1921
Passing uninitialized data to vscnprintf() causes its result to also be uninitialized, which causes an avalanche of new reports in vprintk_store()
I'll send the updated patch (together with other KMSAN patches) your way today.

Quoted 2 lines

Best Regards,
Petr

Signature · 10 lines
Alexander Potapenko
Software Engineer

Google Germany GmbH
Erika-Mann-Straße, 33
80636 München

Geschäftsführer: Paul Manicle, Halimah DeLaine Prado
Registergericht und -nummer: Hamburg, HRB 86891
Sitz der Gesellschaft: Hamburg
author maintainer
#13 lore raw

RFC PATCH 6/26 kasan: compiler.h: rename __no_kasan_or_inline into __no_memory_tool_or_inline

In reply to [email protected]

As we use it for KMSAN as well now, let's change the name to be more generic.

To: Alexander Potapenko <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Martin Schwidefsky <[email protected]>
Cc: Andrey Ryabinin <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]
---
Note that __no_kasan_or_inline is currently used only on S390, and KMSAN patches don't introduce uses of __no_memory_tool_or_inline, so this patch can be landed later if needed.

Change-Id: I842d88b841bf1c75ab659b0016a808c6dcf7cdae

---
 arch/s390/include/asm/processor.h |  4 ++--
 include/linux/compiler.h          | 12 +++++++-----
 2 files changed, 9 insertions(+), 7 deletions(-)
arch/s390/include/asm/processor.h +2 -2
index 51a0e4a2dc96..6bee92331fcd 100644
@@ -214,7 +214,7 @@ static inline unsigned long current_stack_pointer(void)
 	return sp;
 }
 
-static __no_kasan_or_inline unsigned short stap(void)
+static __no_memory_tool_or_inline unsigned short stap(void)
 {
 	unsigned short cpu_address;
 
@@ -253,7 +253,7 @@ static inline void __load_psw(psw_t psw)
  * Set PSW mask to specified value, while leaving the
  * PSW addr pointing to the next instruction.
  */
-static __no_kasan_or_inline void __load_psw_mask(unsigned long mask)
+static __no_memory_tool_or_inline void __load_psw_mask(unsigned long mask)
 {
 	unsigned long addr;
 	psw_t psw;
include/linux/compiler.h +7 -5
index 5e88e7e33abe..4a594bd1bc47 100644
@@ -199,19 +199,21 @@ void __read_once_size(const volatile void *p, void *res, int size)
 	__READ_ONCE_SIZE;
 }
 
-#ifdef CONFIG_KASAN
+#if defined(CONFIG_KASAN)
 /*
  * We can't declare function 'inline' because __no_sanitize_address confilcts
  * with inlining. Attempt to inline it may cause a build failure.
  * 	https://gcc.gnu.org/bugzilla/show_bug.cgi?id=67368
  * '__maybe_unused' allows us to avoid defined-but-not-used warnings.
  */
-# define __no_kasan_or_inline __no_sanitize_address notrace __maybe_unused
+# define __no_memory_tool_or_inline __no_sanitize_address notrace __maybe_unused
+#elif defined(CONFIG_KMSAN)
+# define __no_memory_tool_or_inline __no_sanitize_memory notrace __maybe_unused
 #else
-# define __no_kasan_or_inline __always_inline
+# define __no_memory_tool_or_inline __always_inline
 #endif
 
-static __no_kasan_or_inline
+static __no_memory_tool_or_inline
 void __read_once_size_nocheck(const volatile void *p, void *res, int size)
 {
 	__READ_ONCE_SIZE;
@@ -274,7 +276,7 @@ static __always_inline void __write_once_size(volatile void *p, void *res, int s
  */
 #define READ_ONCE_NOCHECK(x) __READ_ONCE(x, 0)
 
-static __no_kasan_or_inline
+static __no_memory_tool_or_inline
 unsigned long read_word_at_a_time(const void *addr)
 {
 	kasan_check_read(addr, 1);
Signature · 1 line
2.23.0.866.gb869b98d4c-goog
author maintainer
#14 lore raw

RFC PATCH 7/26 kmsan: add ReST documentation

In reply to [email protected]

Add Documentation/dev-tools/kmsan.rst and reference it in the dev-tools index.

To: Alexander Potapenko <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]

---

Change-Id: Iac6345065e6804ef811f1124fdf779c67ff1530e

---
 Documentation/dev-tools/index.rst |   1 +
 Documentation/dev-tools/kmsan.rst | 418 ++++++++++++++++++++++++++++++
 2 files changed, 419 insertions(+)
 create mode 100644 Documentation/dev-tools/kmsan.rst
Documentation/dev-tools/index.rst +1 -0
index b0522a4dd107..bc5e3fd87efa 100644
@@ -19,6 +19,7 @@ whole; patches welcome!
    kcov
    gcov
    kasan
+   kmsan
    ubsan
    kmemleak
    gdb-kernel-debugging
Documentation/dev-tools/kmsan.rst new file +418 -0

418 lines not on this page · raw message

Signature · 1 line
2.23.0.866.gb869b98d4c-goog
author maintainer
#15 lore raw

RFC PATCH 8/26 kmsan: gfp: introduce __GFP_NO_KMSAN_SHADOW

In reply to [email protected]

This flag is to be used by KMSAN runtime to mark that newly created memory pages don't need KMSAN metadata backing them.

To: Alexander Potapenko <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Andrew Morton <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]

---
We can't decide what to do here:
- do we need to conditionally define ___GFP_NO_KMSAN_SHADOW depending on CONFIG_KMSAN like LOCKDEP does?
- if KMSAN is defined, and LOCKDEP is not, do we want to "compactify" the GFP bits?

Change-Id: If5d0352fd5711ad103328e2c185eb885e826423a

---
 include/linux/gfp.h | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
include/linux/gfp.h +3 -1
index fb07b503dc45..b4e7963cd94b 100644
@@ -44,6 +44,7 @@ struct vm_area_struct;
 #else
 #define ___GFP_NOLOCKDEP	0
 #endif
+#define ___GFP_NO_KMSAN_SHADOW  0x1000000u
 /* If the above are modified, __GFP_BITS_SHIFT may need updating */
 
 /*
@@ -212,12 +213,13 @@ struct vm_area_struct;
 #define __GFP_NOWARN	((__force gfp_t)___GFP_NOWARN)
 #define __GFP_COMP	((__force gfp_t)___GFP_COMP)
 #define __GFP_ZERO	((__force gfp_t)___GFP_ZERO)
+#define __GFP_NO_KMSAN_SHADOW  ((__force gfp_t)___GFP_NO_KMSAN_SHADOW)
 
 /* Disable lockdep for GFP context tracking */
 #define __GFP_NOLOCKDEP ((__force gfp_t)___GFP_NOLOCKDEP)
 
 /* Room for N __GFP_FOO bits */
-#define __GFP_BITS_SHIFT (23 + IS_ENABLED(CONFIG_LOCKDEP))
+#define __GFP_BITS_SHIFT (25)
 #define __GFP_BITS_MASK ((__force gfp_t)((1 << __GFP_BITS_SHIFT) - 1))
 
 /**
Signature · 1 line
2.23.0.866.gb869b98d4c-goog
author maintainer
#16 lore raw

RFC PATCH 9/26 kmsan: introduce __no_sanitize_memory and __SANITIZE_MEMORY__

In reply to [email protected]

__no_sanitize_memory is a function attribute that makes KMSAN ignore the uninitialized values coming from the function's inputs, and initialize the function's outputs.

Functions marked with this attribute can't be inlined into functions not marked with it, and vice versa.

__SANITIZE_MEMORY__ is a macro that's defined iff the file is instrumented with KMSAN. This is not the same as CONFIG_KMSAN, which is defined for every file.

To: Alexander Potapenko <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]

---

Change-Id: I1f1672652c8392f15f7ca8ac26cd4e71f9cc1e4b

---
 include/linux/compiler-clang.h | 8 ++++++++
 include/linux/compiler-gcc.h   | 5 +++++
 2 files changed, 13 insertions(+)
include/linux/compiler-clang.h +8 -0
index 333a6695a918..edba13a069a6 100644
@@ -24,6 +24,14 @@
 #define __no_sanitize_address
 #endif
 
+/* KMSAN is a Clang-only tool, thus putting the defines here */
+#if __has_feature(memory_sanitizer)
+# define __SANITIZE_MEMORY__
+# define __no_sanitize_memory __attribute__((no_sanitize("kernel-memory")))
+#else
+# define __no_sanitize_memory
+#endif
+
 /*
  * Not all versions of clang implement the the type-generic versions
  * of the builtin overflow checkers. Fortunately, clang implements
include/linux/compiler-gcc.h +5 -0
index d7ee4c6bad48..e5ebc788dde4 100644
@@ -145,6 +145,11 @@
 #define __no_sanitize_address
 #endif
 
+/*
+ * GCC doesn't support KMSAN.
+ */
+#define __no_sanitize_memory
+
 #if GCC_VERSION >= 50100
 #define COMPILER_HAS_GENERIC_BUILTIN_OVERFLOW 1
 #endif
Signature · 1 line
2.23.0.866.gb869b98d4c-goog
author maintainer
#17 lore raw

RFC PATCH 10/26 kmsan: reduce vmalloc space

In reply to [email protected]

KMSAN is going to use 3/4 of existing vmalloc space to hold the metadata, therefore we lower VMALLOC_END to make sure vmalloc() doesn't allocate past the first 1/4.

To: Alexander Potapenko <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Andrew Morton <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]

---

Change-Id: Iaa5e8e0fc2aa66c956f937f5a1de6e5ef40d57cc

---
 arch/x86/include/asm/pgtable_64_types.h | 15 +++++++++++++++
 1 file changed, 15 insertions(+)
arch/x86/include/asm/pgtable_64_types.h +15 -0
index 52e5f5f2240d..586629e20436 100644
@@ -139,7 +139,22 @@ extern unsigned int ptrs_per_p4d;
 # define VMEMMAP_START		__VMEMMAP_BASE_L4
 #endif /* CONFIG_DYNAMIC_MEMORY_LAYOUT */
 
+#ifndef CONFIG_KMSAN
 #define VMALLOC_END		(VMALLOC_START + (VMALLOC_SIZE_TB << 40) - 1)
+#else
+/*
+ * In KMSAN builds vmalloc area is four times smaller, and the remaining 3/4
+ * are used to keep the metadata for virtual pages.
+ */
+#define VMALLOC_QUARTER_SIZE	((VMALLOC_SIZE_TB << 40) >> 2)
+#define VMALLOC_END		(VMALLOC_START + VMALLOC_QUARTER_SIZE - 1)
+#define VMALLOC_SHADOW_OFFSET	VMALLOC_QUARTER_SIZE
+#define VMALLOC_ORIGIN_OFFSET	(VMALLOC_QUARTER_SIZE * 2)
+#define VMALLOC_META_END	(VMALLOC_END + VMALLOC_ORIGIN_OFFSET)
+#define MODULES_SHADOW_START	(VMALLOC_META_END + 1)
+#define MODULES_ORIGIN_START	(MODULES_SHADOW_START + MODULES_LEN)
+#define MODULES_ORIGIN_END	(MODULES_ORIGIN_START + MODULES_LEN)
+#endif
 
 #define MODULES_VADDR		(__START_KERNEL_map + KERNEL_IMAGE_SIZE)
 /* The module sections ends with the start of the fixmap */
Signature · 1 line
2.23.0.866.gb869b98d4c-goog
author maintainer
#18 lore raw

RFC PATCH 11/26 kmsan: add KMSAN runtime

In reply to [email protected]

This patch adds the KernelMemorySanitizer runtime and associated files:

- arch/x86/include/asm/kmsan.h: assembly definitions for hooking

    interrupt handlers;

- include/linux/kmsan-checks.h: user API to enable/disable KMSAN, poison/unpoison memory etc.
- include/linux/kmsan.h: declarations of KMSAN memory hooks to be referenced outside KMSAN runtime
- lib/Kconfig.kmsan: declarations for CONFIG_KMSAN and CONFIG_TEST_KMSAN
- mm/kmsan/Makefile: boilerplate Makefile
- mm/kmsan/kmsan.h: internal KMSAN declarations
- mm/kmsan/kmsan.c: core functions that operate with shadow and origin memory and perform checks, utility functions
- mm/kmsan/kmsan_entry.c: KMSAN hooks for entry_64.S
- mm/kmsan/kmsan_hooks.c: KMSAN hooks for kernel subsystems
- mm/kmsan/kmsan_init.c: KMSAN initialization routines
- mm/kmsan/kmsan_instr.c: functions called by KMSAN instrumentation
- scripts/Makefile.kmsan: CFLAGS_KMSAN

To: Alexander Potapenko <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]

---

Change-Id: I4b3a7aba6d5804afac4f5f7274cadf8675b6e119

---
 arch/x86/Kconfig             |   1 +
 arch/x86/include/asm/kmsan.h | 129 ++++++++
 include/linux/kmsan-checks.h | 123 ++++++++
 include/linux/kmsan.h        | 143 +++++++++
 lib/Kconfig.debug            |   2 +
 lib/Kconfig.kmsan            |  22 ++
 mm/kmsan/Makefile            |   4 +
 mm/kmsan/kmsan.c             | 570 +++++++++++++++++++++++++++++++++++
 mm/kmsan/kmsan.h             | 147 +++++++++
 mm/kmsan/kmsan_entry.c       | 130 ++++++++
 mm/kmsan/kmsan_hooks.c       | 416 +++++++++++++++++++++++++
 mm/kmsan/kmsan_init.c        |  88 ++++++
 mm/kmsan/kmsan_instr.c       | 259 ++++++++++++++++
 mm/kmsan/kmsan_report.c      | 135 +++++++++
 mm/kmsan/kmsan_shadow.c      | 543 +++++++++++++++++++++++++++++++++
 mm/kmsan/kmsan_shadow.h      |  30 ++
 scripts/Makefile.kmsan       |  12 +
 17 files changed, 2754 insertions(+)
 create mode 100644 arch/x86/include/asm/kmsan.h
 create mode 100644 include/linux/kmsan-checks.h
 create mode 100644 include/linux/kmsan.h
 create mode 100644 lib/Kconfig.kmsan
 create mode 100644 mm/kmsan/Makefile
 create mode 100644 mm/kmsan/kmsan.c
 create mode 100644 mm/kmsan/kmsan.h
 create mode 100644 mm/kmsan/kmsan_entry.c
 create mode 100644 mm/kmsan/kmsan_hooks.c
 create mode 100644 mm/kmsan/kmsan_init.c
 create mode 100644 mm/kmsan/kmsan_instr.c
 create mode 100644 mm/kmsan/kmsan_report.c
 create mode 100644 mm/kmsan/kmsan_shadow.c
 create mode 100644 mm/kmsan/kmsan_shadow.h
 create mode 100644 scripts/Makefile.kmsan
arch/x86/Kconfig +1 -0
index d6e1faa28c58..3f83a5c53808 100644
@@ -135,6 +135,7 @@ config X86
 	select HAVE_ARCH_JUMP_LABEL
 	select HAVE_ARCH_JUMP_LABEL_RELATIVE
 	select HAVE_ARCH_KASAN			if X86_64
+	select HAVE_ARCH_KMSAN			if X86_64
 	select HAVE_ARCH_KGDB
 	select HAVE_ARCH_MMAP_RND_BITS		if MMU
 	select HAVE_ARCH_MMAP_RND_COMPAT_BITS	if MMU && COMPAT
arch/x86/include/asm/kmsan.h new file +129 -0

129 lines not on this page · raw message

include/linux/kmsan-checks.h new file +123 -0

123 lines not on this page · raw message

include/linux/kmsan.h new file +143 -0

143 lines not on this page · raw message

lib/Kconfig.debug +2 -0
index 93d97f9b0157..75c36318943d 100644
@@ -756,6 +756,8 @@ config DEBUG_STACKOVERFLOW
 
 source "lib/Kconfig.kasan"
 
+source "lib/Kconfig.kmsan"
+
 endmenu # "Memory Debugging"
 
 config ARCH_HAS_KCOV
lib/Kconfig.kmsan new file +22 -0
new file mode 100644
index 000000000000..187dddfcf220
@@ -0,0 +1,22 @@
+config HAVE_ARCH_KMSAN
+	bool
+
+if HAVE_ARCH_KMSAN
+
+config KMSAN
+	bool "KMSAN: detector of uninitialized memory use"
+	depends on SLUB && !KASAN
+	select STACKDEPOT
+	help
+	  KMSAN is a dynamic detector of uses of uninitialized memory in the
+	  kernel. It is based on compiler instrumentation provided by Clang
+	  and thus requires Clang 10.0.0+ to build.
+
+config TEST_KMSAN
+	tristate "Module for testing KMSAN for bug detection"
+	depends on m && KMSAN
+	help
+	  Test module that can trigger various uses of uninitialized memory
+	  detectable by KMSAN.
+
+endif
mm/kmsan/Makefile new file +4 -0
new file mode 100644
index 000000000000..ccf6d2d00a7a
@@ -0,0 +1,4 @@
+obj-y := kmsan.o kmsan_instr.o kmsan_init.o kmsan_entry.o kmsan_hooks.o kmsan_report.o kmsan_shadow.o
+
+KMSAN_SANITIZE := n
+KCOV_INSTRUMENT := n
mm/kmsan/kmsan.c new file +570 -0

570 lines not on this page · raw message

mm/kmsan/kmsan.h new file +147 -0

147 lines not on this page · raw message

mm/kmsan/kmsan_entry.c new file +130 -0

130 lines not on this page · raw message

mm/kmsan/kmsan_hooks.c new file +416 -0

416 lines not on this page · raw message

mm/kmsan/kmsan_init.c new file +88 -0

88 lines not on this page · raw message

mm/kmsan/kmsan_instr.c new file +259 -0

259 lines not on this page · raw message

mm/kmsan/kmsan_report.c new file +135 -0

135 lines not on this page · raw message

mm/kmsan/kmsan_shadow.c new file +543 -0

543 lines not on this page · raw message

mm/kmsan/kmsan_shadow.h new file +30 -0
new file mode 100644
index 000000000000..eaa7f771b6a5
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * KMSAN shadow API.
+ *
+ * This should be agnostic to shadow implementation details.
+ *
+ * Copyright (C) 2017-2019 Google LLC
+ * Author: Alexander Potapenko <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#ifndef __MM_KMSAN_KMSAN_SHADOW_H
+#define __MM_KMSAN_KMSAN_SHADOW_H
+
+#include <asm/cpu_entry_area.h>  /* for CPU_ENTRY_AREA_MAP_SIZE */
+
+struct shadow_origin_ptr {
+	void *s, *o;
+};
+
+struct shadow_origin_ptr kmsan_get_shadow_origin_ptr(void *addr, u64 size,
+						     bool store);
+void *kmsan_get_metadata(void *addr, size_t size, bool is_origin);
+void __init kmsan_init_alloc_meta_for_range(void *start, void *end);
+
+#endif  /* __MM_KMSAN_KMSAN_SHADOW_H */
scripts/Makefile.kmsan new file +12 -0
new file mode 100644
index 000000000000..8b3844b66b22
@@ -0,0 +1,12 @@
+ifdef CONFIG_KMSAN
+
+CFLAGS_KMSAN := -fsanitize=kernel-memory
+
+ifeq ($(call cc-option, $(CFLAGS_KMSAN) -Werror),)
+   ifneq ($(CONFIG_COMPILE_TEST),y)
+        $(warning Cannot use CONFIG_KMSAN: \
+            -fsanitize=kernel-memory is not supported by compiler)
+   endif
+endif
+
+endif
Signature · 1 line
2.23.0.866.gb869b98d4c-goog
author maintainer
#19 lore raw

RFC PATCH 12/26 kmsan: x86: sync metadata pages on page fault

In reply to [email protected]

KMSAN assumes shadow and origin pages for every allocated page are accessible. For pages in vmalloc region those metadata pages reside in [VMALLOC_END, VMALLOC_META_END), therefore we must sync a bigger memory region.

To: Alexander Potapenko <[email protected]>
Cc: Ingo Molnar <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]

---

Change-Id: I0d54855489870ef1180b37fe2120b601da464bf7

---
 arch/x86/mm/fault.c | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)
arch/x86/mm/fault.c +20 -0
index 9ceacd1156db..d582337ba45d 100644
@@ -320,7 +320,17 @@ static void dump_pagetable(unsigned long address)
 
 void vmalloc_sync_all(void)
 {
+#ifdef CONFIG_KMSAN
+	/*
+	 * For KMSAN, make sure metadata pages for vmalloc area and modules are
+	 * also synced.
+	 */
+	sync_global_pgds(VMALLOC_START & PGDIR_MASK, VMALLOC_META_END);
+	sync_global_pgds(MODULES_SHADOW_START & PGDIR_MASK,
+		MODULES_ORIGIN_END);
+#else
 	sync_global_pgds(VMALLOC_START & PGDIR_MASK, VMALLOC_END);
+#endif
 }
 
 /*
@@ -337,7 +347,17 @@ static noinline int vmalloc_fault(unsigned long address)
 	pte_t *pte;
 
 	/* Make sure we are in vmalloc area: */
+#ifdef CONFIG_KMSAN
+	/*
+	 * For KMSAN, make sure metadata pages for vmalloc area and modules are
+	 * also synced.
+	 */
+	if (!(address >= VMALLOC_START && address < VMALLOC_META_END) &&
+		!(address >= MODULES_SHADOW_START &&
+		  address < MODULES_ORIGIN_END))
+#else
 	if (!(address >= VMALLOC_START && address < VMALLOC_END))
+#endif
 		return -1;
 
 	/*
Signature · 1 line
2.23.0.866.gb869b98d4c-goog
author maintainer
#20 lore raw

RFC PATCH 13/26 kmsan: add tests for KMSAN

In reply to [email protected]

The initial commit adds several tests that trigger KMSAN warnings in simple cases.
To use, build the kernel with CONFIG_TEST_KMSAN and do insmod test_kmsan.ko

To: Alexander Potapenko <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]
---

Change-Id: I287e86ae83a82b770f2baa46e5bbdce1dfa65195

---
 lib/Makefile     |   1 +
 lib/test_kmsan.c | 221 +++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 222 insertions(+)
 create mode 100644 lib/test_kmsan.c
lib/Makefile +1 -0
index c5892807e06f..cb44262c38ee 100644
@@ -65,6 +65,7 @@ CFLAGS_test_kasan.o += $(call cc-disable-warning, vla)
 obj-$(CONFIG_TEST_UBSAN) += test_ubsan.o
 CFLAGS_test_ubsan.o += $(call cc-disable-warning, vla)
 UBSAN_SANITIZE_test_ubsan.o := y
+obj-$(CONFIG_TEST_KMSAN) += test_kmsan.o
 obj-$(CONFIG_TEST_KSTRTOX) += test-kstrtox.o
 obj-$(CONFIG_TEST_LIST_SORT) += test_list_sort.o
 obj-$(CONFIG_TEST_LKM) += test_module.o
lib/test_kmsan.c new file +221 -0

221 lines not on this page · raw message

Signature · 1 line
2.23.0.866.gb869b98d4c-goog
author maintainer
#21 lore raw

RFC PATCH 14/26 kmsan: make READ_ONCE_TASK_STACK() return initialized values

In reply to [email protected]

To avoid false positives, assume that reading from the task stack always produces initialized values.

To: Alexander Potapenko <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]
---

Change-Id: Ie73e5a41fdc8195699928e65f5cbe0d3d3c9e2fa

---
 arch/x86/include/asm/unwind.h | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)
arch/x86/include/asm/unwind.h +5 -4
index 499578f7e6d7..f60c2bd1ddf2 100644
@@ -100,9 +100,10 @@ void unwind_module_init(struct module *mod, void *orc_ip, size_t orc_ip_size,
 #endif
 
 /*
- * This disables KASAN checking when reading a value from another task's stack,
- * since the other task could be running on another CPU and could have poisoned
- * the stack in the meantime.
+ * This disables KASAN/KMSAN checking when reading a value from another task's
+ * stack, since the other task could be running on another CPU and could have
+ * poisoned the stack in the meantime. Frame pointers are uninitialized by
+ * default, so for KMSAN we mark the return value initialized unconditionally.
  */
 #define READ_ONCE_TASK_STACK(task, x)			\
 ({							\
@@ -111,7 +112,7 @@ void unwind_module_init(struct module *mod, void *orc_ip, size_t orc_ip_size,
 		val = READ_ONCE(x);			\
 	else						\
 		val = READ_ONCE_NOCHECK(x);		\
-	val;						\
+	KMSAN_INIT_VALUE(val);				\
 })
 
 static inline bool task_on_another_cpu(struct task_struct *task)
Signature · 1 line
2.23.0.866.gb869b98d4c-goog
author maintainer
#22 lore raw

RFC PATCH 15/26 kmsan: Kconfig changes to disable options incompatible with KMSAN

In reply to [email protected]

Certain config options are currently incompatible with KMSAN, disable them.

To: Alexander Potapenko <[email protected]>
Cc: Herbert Xu <[email protected]>
Cc: Harry Wentland <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]

---

Change-Id: I4521f5c764cfaeba2fae79c954925c302c5e3bc0

---
 arch/Kconfig                        |  2 +-
 arch/x86/Kconfig                    |  1 +
 arch/x86/Kconfig.debug              |  3 +++
 crypto/Kconfig                      | 26 ++++++++++++++++++++++++++
 drivers/gpu/drm/amd/display/Kconfig |  2 +-
 lib/Kconfig.debug                   |  3 +++
 6 files changed, 35 insertions(+), 2 deletions(-)
arch/Kconfig +1 -1
index 5f8a5d84dbbe..d5ca838ae8ad 100644
@@ -843,7 +843,7 @@ config HAVE_ARCH_VMAP_STACK
 config VMAP_STACK
 	default y
 	bool "Use a virtually-mapped stack"
-	depends on HAVE_ARCH_VMAP_STACK && !KASAN
+	depends on HAVE_ARCH_VMAP_STACK && !KASAN && !KMSAN
 	---help---
 	  Enable this if you want the use virtually-mapped kernel stacks
 	  with guard pages.  This causes kernel stack overflows to be
arch/x86/Kconfig +1 -0
index 3f83a5c53808..6d2b0fb3a871 100644
@@ -2143,6 +2143,7 @@ config RELOCATABLE
 config RANDOMIZE_BASE
 	bool "Randomize the address of the kernel image (KASLR)"
 	depends on RELOCATABLE
+	depends on !KMSAN
 	default y
 	---help---
 	  In support of Kernel Address Space Layout Randomization (KASLR),
arch/x86/Kconfig.debug +3 -0
index bf9cd83de777..db3cd6147829 100644
@@ -292,6 +292,9 @@ choice
 config UNWINDER_ORC
 	bool "ORC unwinder"
 	depends on X86_64
+	# KMSAN doesn't support UNWINDER_ORC yet,
+	# see https://github.com/google/kmsan/issues/48.
+	depends on !KMSAN
 	select STACK_VALIDATION
 	---help---
 	  This option enables the ORC (Oops Rewind Capability) unwinder for
crypto/Kconfig +26 -0
index 9e524044d312..37a22711012c 100644
@@ -309,11 +309,13 @@ config CRYPTO_AEGIS128
 config CRYPTO_AEGIS128_SIMD
 	bool "Support SIMD acceleration for AEGIS-128"
 	depends on CRYPTO_AEGIS128 && ((ARM || ARM64) && KERNEL_MODE_NEON)
+	depends on !KMSAN
 	default y
 
 config CRYPTO_AEGIS128_AESNI_SSE2
 	tristate "AEGIS-128 AEAD algorithm (x86_64 AESNI+SSE2 implementation)"
 	depends on X86 && 64BIT
+	depends on !KMSAN
 	select CRYPTO_AEAD
 	select CRYPTO_SIMD
 	help
@@ -571,6 +573,7 @@ config CRYPTO_CRC32C
 config CRYPTO_CRC32C_INTEL
 	tristate "CRC32c INTEL hardware acceleration"
 	depends on X86
+	depends on !KMSAN
 	select CRYPTO_HASH
 	help
 	  In Intel processor with SSE4.2 supported, the processor will
@@ -611,6 +614,7 @@ config CRYPTO_CRC32
 config CRYPTO_CRC32_PCLMUL
 	tristate "CRC32 PCLMULQDQ hardware acceleration"
 	depends on X86
+	depends on !KMSAN
 	select CRYPTO_HASH
 	select CRC32
 	help
@@ -649,6 +653,7 @@ config CRYPTO_CRCT10DIF
 config CRYPTO_CRCT10DIF_PCLMUL
 	tristate "CRCT10DIF PCLMULQDQ hardware acceleration"
 	depends on X86 && 64BIT && CRC_T10DIF
+	depends on !KMSAN
 	select CRYPTO_HASH
 	help
 	  For x86_64 processors with SSE4.2 and PCLMULQDQ supported,
@@ -695,6 +700,7 @@ config CRYPTO_POLY1305
 config CRYPTO_POLY1305_X86_64
 	tristate "Poly1305 authenticator algorithm (x86_64/SSE2/AVX2)"
 	depends on X86 && 64BIT
+	depends on !KMSAN
 	select CRYPTO_POLY1305
 	help
 	  Poly1305 authenticator algorithm, RFC7539.
@@ -814,6 +820,7 @@ config CRYPTO_SHA1
 config CRYPTO_SHA1_SSSE3
 	tristate "SHA1 digest algorithm (SSSE3/AVX/AVX2/SHA-NI)"
 	depends on X86 && 64BIT
+	depends on !KMSAN
 	select CRYPTO_SHA1
 	select CRYPTO_HASH
 	help
@@ -825,6 +832,7 @@ config CRYPTO_SHA1_SSSE3
 config CRYPTO_SHA256_SSSE3
 	tristate "SHA256 digest algorithm (SSSE3/AVX/AVX2/SHA-NI)"
 	depends on X86 && 64BIT
+	depends on !KMSAN
 	select CRYPTO_SHA256
 	select CRYPTO_HASH
 	help
@@ -837,6 +845,7 @@ config CRYPTO_SHA256_SSSE3
 config CRYPTO_SHA512_SSSE3
 	tristate "SHA512 digest algorithm (SSSE3/AVX/AVX2)"
 	depends on X86 && 64BIT
+	depends on !KMSAN
 	select CRYPTO_SHA512
 	select CRYPTO_HASH
 	help
@@ -1011,6 +1020,7 @@ config CRYPTO_WP512
 config CRYPTO_GHASH_CLMUL_NI_INTEL
 	tristate "GHASH hash function (CLMUL-NI accelerated)"
 	depends on X86 && 64BIT
+	depends on !KMSAN
 	select CRYPTO_CRYPTD
 	help
 	  This is the x86_64 CLMUL-NI accelerated implementation of
@@ -1064,6 +1074,7 @@ config CRYPTO_AES_TI
 config CRYPTO_AES_NI_INTEL
 	tristate "AES cipher algorithms (AES-NI)"
 	depends on X86
+	depends on !KMSAN
 	select CRYPTO_AEAD
 	select CRYPTO_LIB_AES
 	select CRYPTO_ALGAPI
@@ -1190,6 +1201,7 @@ config CRYPTO_BLOWFISH_COMMON
 config CRYPTO_BLOWFISH_X86_64
 	tristate "Blowfish cipher algorithm (x86_64)"
 	depends on X86 && 64BIT
+	depends on !KMSAN
 	select CRYPTO_BLKCIPHER
 	select CRYPTO_BLOWFISH_COMMON
 	help
@@ -1221,6 +1233,7 @@ config CRYPTO_CAMELLIA_X86_64
 	tristate "Camellia cipher algorithm (x86_64)"
 	depends on X86 && 64BIT
 	depends on CRYPTO
+	depends on !KMSAN
 	select CRYPTO_BLKCIPHER
 	select CRYPTO_GLUE_HELPER_X86
 	help
@@ -1238,6 +1251,7 @@ config CRYPTO_CAMELLIA_AESNI_AVX_X86_64
 	tristate "Camellia cipher algorithm (x86_64/AES-NI/AVX)"
 	depends on X86 && 64BIT
 	depends on CRYPTO
+	depends on !KMSAN
 	select CRYPTO_BLKCIPHER
 	select CRYPTO_CAMELLIA_X86_64
 	select CRYPTO_GLUE_HELPER_X86
@@ -1258,6 +1272,7 @@ config CRYPTO_CAMELLIA_AESNI_AVX2_X86_64
 	tristate "Camellia cipher algorithm (x86_64/AES-NI/AVX2)"
 	depends on X86 && 64BIT
 	depends on CRYPTO
+	depends on !KMSAN
 	select CRYPTO_CAMELLIA_AESNI_AVX_X86_64
 	help
 	  Camellia cipher algorithm module (x86_64/AES-NI/AVX2).
@@ -1303,6 +1318,7 @@ config CRYPTO_CAST5
 config CRYPTO_CAST5_AVX_X86_64
 	tristate "CAST5 (CAST-128) cipher algorithm (x86_64/AVX)"
 	depends on X86 && 64BIT
+	depends on !KMSAN
 	select CRYPTO_BLKCIPHER
 	select CRYPTO_CAST5
 	select CRYPTO_CAST_COMMON
@@ -1325,6 +1341,7 @@ config CRYPTO_CAST6
 config CRYPTO_CAST6_AVX_X86_64
 	tristate "CAST6 (CAST-256) cipher algorithm (x86_64/AVX)"
 	depends on X86 && 64BIT
+	depends on !KMSAN
 	select CRYPTO_BLKCIPHER
 	select CRYPTO_CAST6
 	select CRYPTO_CAST_COMMON
@@ -1360,6 +1377,7 @@ config CRYPTO_DES_SPARC64
 config CRYPTO_DES3_EDE_X86_64
 	tristate "Triple DES EDE cipher algorithm (x86-64)"
 	depends on X86 && 64BIT
+	depends on !KMSAN
 	select CRYPTO_BLKCIPHER
 	select CRYPTO_LIB_DES
 	help
@@ -1426,6 +1444,7 @@ config CRYPTO_CHACHA20
 config CRYPTO_CHACHA20_X86_64
 	tristate "ChaCha stream cipher algorithms (x86_64/SSSE3/AVX2/AVX-512VL)"
 	depends on X86 && 64BIT
+	depends on !KMSAN
 	select CRYPTO_BLKCIPHER
 	select CRYPTO_CHACHA20
 	help
@@ -1462,6 +1481,7 @@ config CRYPTO_SERPENT
 config CRYPTO_SERPENT_SSE2_X86_64
 	tristate "Serpent cipher algorithm (x86_64/SSE2)"
 	depends on X86 && 64BIT
+	depends on !KMSAN
 	select CRYPTO_BLKCIPHER
 	select CRYPTO_GLUE_HELPER_X86
 	select CRYPTO_SERPENT
@@ -1481,6 +1501,7 @@ config CRYPTO_SERPENT_SSE2_X86_64
 config CRYPTO_SERPENT_SSE2_586
 	tristate "Serpent cipher algorithm (i586/SSE2)"
 	depends on X86 && !64BIT
+	depends on !KMSAN
 	select CRYPTO_BLKCIPHER
 	select CRYPTO_GLUE_HELPER_X86
 	select CRYPTO_SERPENT
@@ -1500,6 +1521,7 @@ config CRYPTO_SERPENT_SSE2_586
 config CRYPTO_SERPENT_AVX_X86_64
 	tristate "Serpent cipher algorithm (x86_64/AVX)"
 	depends on X86 && 64BIT
+	depends on !KMSAN
 	select CRYPTO_BLKCIPHER
 	select CRYPTO_GLUE_HELPER_X86
 	select CRYPTO_SERPENT
@@ -1520,6 +1542,7 @@ config CRYPTO_SERPENT_AVX_X86_64
 config CRYPTO_SERPENT_AVX2_X86_64
 	tristate "Serpent cipher algorithm (x86_64/AVX2)"
 	depends on X86 && 64BIT
+	depends on !KMSAN
 	select CRYPTO_SERPENT_AVX_X86_64
 	help
 	  Serpent cipher algorithm, by Anderson, Biham & Knudsen.
@@ -1615,6 +1638,7 @@ config CRYPTO_TWOFISH_586
 config CRYPTO_TWOFISH_X86_64
 	tristate "Twofish cipher algorithm (x86_64)"
 	depends on (X86 || UML_X86) && 64BIT
+	depends on !KMSAN
 	select CRYPTO_ALGAPI
 	select CRYPTO_TWOFISH_COMMON
 	help
@@ -1631,6 +1655,7 @@ config CRYPTO_TWOFISH_X86_64
 config CRYPTO_TWOFISH_X86_64_3WAY
 	tristate "Twofish cipher algorithm (x86_64, 3-way parallel)"
 	depends on X86 && 64BIT
+	depends on !KMSAN
 	select CRYPTO_BLKCIPHER
 	select CRYPTO_TWOFISH_COMMON
 	select CRYPTO_TWOFISH_X86_64
@@ -1652,6 +1677,7 @@ config CRYPTO_TWOFISH_X86_64_3WAY
 config CRYPTO_TWOFISH_AVX_X86_64
 	tristate "Twofish cipher algorithm (x86_64/AVX)"
 	depends on X86 && 64BIT
+	depends on !KMSAN
 	select CRYPTO_BLKCIPHER
 	select CRYPTO_GLUE_HELPER_X86
 	select CRYPTO_SIMD
drivers/gpu/drm/amd/display/Kconfig +1 -1
index 71991a28a775..99585eaf4dd0 100644
@@ -6,7 +6,7 @@ config DRM_AMD_DC
 	bool "AMD DC - Enable new display engine"
 	default y
 	select SND_HDA_COMPONENT if SND_HDA_CORE
-	select DRM_AMD_DC_DCN1_0 if X86 && !(KCOV_INSTRUMENT_ALL && KCOV_ENABLE_COMPARISONS)
+	select DRM_AMD_DC_DCN1_0 if X86 && !(KCOV_INSTRUMENT_ALL && KCOV_ENABLE_COMPARISONS) && !KMSAN
 	help
 	  Choose this option if you want to use the new display engine
 	  support for AMDGPU. This adds required support for Vega and
lib/Kconfig.debug +3 -0
index 75c36318943d..a3f6f5d68593 100644
@@ -1068,6 +1068,9 @@ menu "Lock Debugging (spinlocks, mutexes, etc...)"
 config LOCK_DEBUGGING_SUPPORT
 	bool
 	depends on TRACE_IRQFLAGS_SUPPORT && STACKTRACE_SUPPORT && LOCKDEP_SUPPORT
+	# KMSAN is incompatible with lockdep,
+	# see https://github.com/google/kmsan/issues/57.
+	depends on !KMSAN
 	default y
 
 config PROVE_LOCKING
Signature · 1 line
2.23.0.866.gb869b98d4c-goog
Acked-by on v1 15/26
#23 lore raw

Re: [PATCH RFC v1 15/26] kmsan: Kconfig changes to disable options incompatible with KMSAN

Quoted 10 lines · On 2019-10-18 5:42 a.m., [email protected] wrote:

Certain config options are currently incompatible with KMSAN, disable them.

To: Alexander Potapenko <[email protected]>
Cc: Herbert Xu <[email protected]>
Cc: Harry Wentland <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]

Harry

Quoted 290 lines

---

Change-Id: I4521f5c764cfaeba2fae79c954925c302c5e3bc0

---
 arch/Kconfig                        |  2 +-
 arch/x86/Kconfig                    |  1 +
 arch/x86/Kconfig.debug              |  3 +++
 crypto/Kconfig                      | 26 ++++++++++++++++++++++++++
 drivers/gpu/drm/amd/display/Kconfig |  2 +-
 lib/Kconfig.debug                   |  3 +++
 6 files changed, 35 insertions(+), 2 deletions(-)
arch/Kconfig +1 -1
index 5f8a5d84dbbe..d5ca838ae8ad 100644
@@ -843,7 +843,7 @@ config HAVE_ARCH_VMAP_STACK
 config VMAP_STACK
 	default y
 	bool "Use a virtually-mapped stack"
-	depends on HAVE_ARCH_VMAP_STACK && !KASAN
+	depends on HAVE_ARCH_VMAP_STACK && !KASAN && !KMSAN
 	---help---
 	  Enable this if you want the use virtually-mapped kernel stacks
 	  with guard pages.  This causes kernel stack overflows to be
arch/x86/Kconfig +1 -0
index 3f83a5c53808..6d2b0fb3a871 100644
@@ -2143,6 +2143,7 @@ config RELOCATABLE
 config RANDOMIZE_BASE
 	bool "Randomize the address of the kernel image (KASLR)"
 	depends on RELOCATABLE
+	depends on !KMSAN
 	default y
 	---help---
 	  In support of Kernel Address Space Layout Randomization (KASLR),
arch/x86/Kconfig.debug +3 -0
index bf9cd83de777..db3cd6147829 100644
@@ -292,6 +292,9 @@ choice
 config UNWINDER_ORC
 	bool "ORC unwinder"
 	depends on X86_64
+	# KMSAN doesn't support UNWINDER_ORC yet,
+	# see https://github.com/google/kmsan/issues/48.
+	depends on !KMSAN
 	select STACK_VALIDATION
 	---help---
 	  This option enables the ORC (Oops Rewind Capability) unwinder for
crypto/Kconfig +26 -0
index 9e524044d312..37a22711012c 100644
@@ -309,11 +309,13 @@ config CRYPTO_AEGIS128
 config CRYPTO_AEGIS128_SIMD
 	bool "Support SIMD acceleration for AEGIS-128"
 	depends on CRYPTO_AEGIS128 && ((ARM || ARM64) && KERNEL_MODE_NEON)
+	depends on !KMSAN
 	default y
 
 config CRYPTO_AEGIS128_AESNI_SSE2
 	tristate "AEGIS-128 AEAD algorithm (x86_64 AESNI+SSE2 implementation)"
 	depends on X86 && 64BIT
+	depends on !KMSAN
 	select CRYPTO_AEAD
 	select CRYPTO_SIMD
 	help
@@ -571,6 +573,7 @@ config CRYPTO_CRC32C
 config CRYPTO_CRC32C_INTEL
 	tristate "CRC32c INTEL hardware acceleration"
 	depends on X86
+	depends on !KMSAN
 	select CRYPTO_HASH
 	help
 	  In Intel processor with SSE4.2 supported, the processor will
@@ -611,6 +614,7 @@ config CRYPTO_CRC32
 config CRYPTO_CRC32_PCLMUL
 	tristate "CRC32 PCLMULQDQ hardware acceleration"
 	depends on X86
+	depends on !KMSAN
 	select CRYPTO_HASH
 	select CRC32
 	help
@@ -649,6 +653,7 @@ config CRYPTO_CRCT10DIF
 config CRYPTO_CRCT10DIF_PCLMUL
 	tristate "CRCT10DIF PCLMULQDQ hardware acceleration"
 	depends on X86 && 64BIT && CRC_T10DIF
+	depends on !KMSAN
 	select CRYPTO_HASH
 	help
 	  For x86_64 processors with SSE4.2 and PCLMULQDQ supported,
@@ -695,6 +700,7 @@ config CRYPTO_POLY1305
 config CRYPTO_POLY1305_X86_64
 	tristate "Poly1305 authenticator algorithm (x86_64/SSE2/AVX2)"
 	depends on X86 && 64BIT
+	depends on !KMSAN
 	select CRYPTO_POLY1305
 	help
 	  Poly1305 authenticator algorithm, RFC7539.
@@ -814,6 +820,7 @@ config CRYPTO_SHA1
 config CRYPTO_SHA1_SSSE3
 	tristate "SHA1 digest algorithm (SSSE3/AVX/AVX2/SHA-NI)"
 	depends on X86 && 64BIT
+	depends on !KMSAN
 	select CRYPTO_SHA1
 	select CRYPTO_HASH
 	help
@@ -825,6 +832,7 @@ config CRYPTO_SHA1_SSSE3
 config CRYPTO_SHA256_SSSE3
 	tristate "SHA256 digest algorithm (SSSE3/AVX/AVX2/SHA-NI)"
 	depends on X86 && 64BIT
+	depends on !KMSAN
 	select CRYPTO_SHA256
 	select CRYPTO_HASH
 	help
@@ -837,6 +845,7 @@ config CRYPTO_SHA256_SSSE3
 config CRYPTO_SHA512_SSSE3
 	tristate "SHA512 digest algorithm (SSSE3/AVX/AVX2)"
 	depends on X86 && 64BIT
+	depends on !KMSAN
 	select CRYPTO_SHA512
 	select CRYPTO_HASH
 	help
@@ -1011,6 +1020,7 @@ config CRYPTO_WP512
 config CRYPTO_GHASH_CLMUL_NI_INTEL
 	tristate "GHASH hash function (CLMUL-NI accelerated)"
 	depends on X86 && 64BIT
+	depends on !KMSAN
 	select CRYPTO_CRYPTD
 	help
 	  This is the x86_64 CLMUL-NI accelerated implementation of
@@ -1064,6 +1074,7 @@ config CRYPTO_AES_TI
 config CRYPTO_AES_NI_INTEL
 	tristate "AES cipher algorithms (AES-NI)"
 	depends on X86
+	depends on !KMSAN
 	select CRYPTO_AEAD
 	select CRYPTO_LIB_AES
 	select CRYPTO_ALGAPI
@@ -1190,6 +1201,7 @@ config CRYPTO_BLOWFISH_COMMON
 config CRYPTO_BLOWFISH_X86_64
 	tristate "Blowfish cipher algorithm (x86_64)"
 	depends on X86 && 64BIT
+	depends on !KMSAN
 	select CRYPTO_BLKCIPHER
 	select CRYPTO_BLOWFISH_COMMON
 	help
@@ -1221,6 +1233,7 @@ config CRYPTO_CAMELLIA_X86_64
 	tristate "Camellia cipher algorithm (x86_64)"
 	depends on X86 && 64BIT
 	depends on CRYPTO
+	depends on !KMSAN
 	select CRYPTO_BLKCIPHER
 	select CRYPTO_GLUE_HELPER_X86
 	help
@@ -1238,6 +1251,7 @@ config CRYPTO_CAMELLIA_AESNI_AVX_X86_64
 	tristate "Camellia cipher algorithm (x86_64/AES-NI/AVX)"
 	depends on X86 && 64BIT
 	depends on CRYPTO
+	depends on !KMSAN
 	select CRYPTO_BLKCIPHER
 	select CRYPTO_CAMELLIA_X86_64
 	select CRYPTO_GLUE_HELPER_X86
@@ -1258,6 +1272,7 @@ config CRYPTO_CAMELLIA_AESNI_AVX2_X86_64
 	tristate "Camellia cipher algorithm (x86_64/AES-NI/AVX2)"
 	depends on X86 && 64BIT
 	depends on CRYPTO
+	depends on !KMSAN
 	select CRYPTO_CAMELLIA_AESNI_AVX_X86_64
 	help
 	  Camellia cipher algorithm module (x86_64/AES-NI/AVX2).
@@ -1303,6 +1318,7 @@ config CRYPTO_CAST5
 config CRYPTO_CAST5_AVX_X86_64
 	tristate "CAST5 (CAST-128) cipher algorithm (x86_64/AVX)"
 	depends on X86 && 64BIT
+	depends on !KMSAN
 	select CRYPTO_BLKCIPHER
 	select CRYPTO_CAST5
 	select CRYPTO_CAST_COMMON
@@ -1325,6 +1341,7 @@ config CRYPTO_CAST6
 config CRYPTO_CAST6_AVX_X86_64
 	tristate "CAST6 (CAST-256) cipher algorithm (x86_64/AVX)"
 	depends on X86 && 64BIT
+	depends on !KMSAN
 	select CRYPTO_BLKCIPHER
 	select CRYPTO_CAST6
 	select CRYPTO_CAST_COMMON
@@ -1360,6 +1377,7 @@ config CRYPTO_DES_SPARC64
 config CRYPTO_DES3_EDE_X86_64
 	tristate "Triple DES EDE cipher algorithm (x86-64)"
 	depends on X86 && 64BIT
+	depends on !KMSAN
 	select CRYPTO_BLKCIPHER
 	select CRYPTO_LIB_DES
 	help
@@ -1426,6 +1444,7 @@ config CRYPTO_CHACHA20
 config CRYPTO_CHACHA20_X86_64
 	tristate "ChaCha stream cipher algorithms (x86_64/SSSE3/AVX2/AVX-512VL)"
 	depends on X86 && 64BIT
+	depends on !KMSAN
 	select CRYPTO_BLKCIPHER
 	select CRYPTO_CHACHA20
 	help
@@ -1462,6 +1481,7 @@ config CRYPTO_SERPENT
 config CRYPTO_SERPENT_SSE2_X86_64
 	tristate "Serpent cipher algorithm (x86_64/SSE2)"
 	depends on X86 && 64BIT
+	depends on !KMSAN
 	select CRYPTO_BLKCIPHER
 	select CRYPTO_GLUE_HELPER_X86
 	select CRYPTO_SERPENT
@@ -1481,6 +1501,7 @@ config CRYPTO_SERPENT_SSE2_X86_64
 config CRYPTO_SERPENT_SSE2_586
 	tristate "Serpent cipher algorithm (i586/SSE2)"
 	depends on X86 && !64BIT
+	depends on !KMSAN
 	select CRYPTO_BLKCIPHER
 	select CRYPTO_GLUE_HELPER_X86
 	select CRYPTO_SERPENT
@@ -1500,6 +1521,7 @@ config CRYPTO_SERPENT_SSE2_586
 config CRYPTO_SERPENT_AVX_X86_64
 	tristate "Serpent cipher algorithm (x86_64/AVX)"
 	depends on X86 && 64BIT
+	depends on !KMSAN
 	select CRYPTO_BLKCIPHER
 	select CRYPTO_GLUE_HELPER_X86
 	select CRYPTO_SERPENT
@@ -1520,6 +1542,7 @@ config CRYPTO_SERPENT_AVX_X86_64
 config CRYPTO_SERPENT_AVX2_X86_64
 	tristate "Serpent cipher algorithm (x86_64/AVX2)"
 	depends on X86 && 64BIT
+	depends on !KMSAN
 	select CRYPTO_SERPENT_AVX_X86_64
 	help
 	  Serpent cipher algorithm, by Anderson, Biham & Knudsen.
@@ -1615,6 +1638,7 @@ config CRYPTO_TWOFISH_586
 config CRYPTO_TWOFISH_X86_64
 	tristate "Twofish cipher algorithm (x86_64)"
 	depends on (X86 || UML_X86) && 64BIT
+	depends on !KMSAN
 	select CRYPTO_ALGAPI
 	select CRYPTO_TWOFISH_COMMON
 	help
@@ -1631,6 +1655,7 @@ config CRYPTO_TWOFISH_X86_64
 config CRYPTO_TWOFISH_X86_64_3WAY
 	tristate "Twofish cipher algorithm (x86_64, 3-way parallel)"
 	depends on X86 && 64BIT
+	depends on !KMSAN
 	select CRYPTO_BLKCIPHER
 	select CRYPTO_TWOFISH_COMMON
 	select CRYPTO_TWOFISH_X86_64
@@ -1652,6 +1677,7 @@ config CRYPTO_TWOFISH_X86_64_3WAY
 config CRYPTO_TWOFISH_AVX_X86_64
 	tristate "Twofish cipher algorithm (x86_64/AVX)"
 	depends on X86 && 64BIT
+	depends on !KMSAN
 	select CRYPTO_BLKCIPHER
 	select CRYPTO_GLUE_HELPER_X86
 	select CRYPTO_SIMD
drivers/gpu/drm/amd/display/Kconfig +1 -1
index 71991a28a775..99585eaf4dd0 100644
@@ -6,7 +6,7 @@ config DRM_AMD_DC
 	bool "AMD DC - Enable new display engine"
 	default y
 	select SND_HDA_COMPONENT if SND_HDA_CORE
-	select DRM_AMD_DC_DCN1_0 if X86 && !(KCOV_INSTRUMENT_ALL && KCOV_ENABLE_COMPARISONS)
+	select DRM_AMD_DC_DCN1_0 if X86 && !(KCOV_INSTRUMENT_ALL && KCOV_ENABLE_COMPARISONS) && !KMSAN
 	help
 	  Choose this option if you want to use the new display engine
 	  support for AMDGPU. This adds required support for Vega and
lib/Kconfig.debug +3 -0
index 75c36318943d..a3f6f5d68593 100644
@@ -1068,6 +1068,9 @@ menu "Lock Debugging (spinlocks, mutexes, etc...)"
 config LOCK_DEBUGGING_SUPPORT
 	bool
 	depends on TRACE_IRQFLAGS_SUPPORT && STACKTRACE_SUPPORT && LOCKDEP_SUPPORT
+	# KMSAN is incompatible with lockdep,
+	# see https://github.com/google/kmsan/issues/57.
+	depends on !KMSAN
 	default y
 
 config PROVE_LOCKING
author maintainer
#24 lore raw

RFC PATCH 16/26 kmsan: Changing existing files to enable KMSAN builds

In reply to [email protected]

Do the following to make KMSAN usable:
- add KMSAN declarations to struct page and struct task_struct;
- add Makefile.kmsan to top-level Makefile;
- call KMSAN initialization from init/main.c
- add asm hooks to arch/x86/entry/entry_64.S;
- increase task stack size under KMSAN;
- disable page merging in block/blk.h;
- disable CONFIG_KMSAN in arch/x86/boot/compressed/misc.h to use default string functions instead of KMSAN ones.

To: Alexander Potapenko <[email protected]>
Cc: Jens Axboe <[email protected]>
Cc: Andy Lutomirski <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]

---

Change-Id: I37e0b7f2d2f2b0aeac5753ff9d6b411485fc374e

---
 Makefile                             |  3 ++-
 arch/x86/boot/compressed/misc.h      |  1 +
 arch/x86/entry/entry_64.S            | 18 ++++++++++++++++++
 arch/x86/include/asm/page_64_types.h | 12 +++++++-----
 block/blk.h                          |  7 +++++++
 include/linux/mm_types.h             |  9 +++++++++
 include/linux/sched.h                |  5 +++++
 init/main.c                          |  3 +++
 lib/stackdepot.c                     |  7 ++++---
 mm/Makefile                          |  1 +
 scripts/Makefile.lib                 |  6 ++++++
 11 files changed, 63 insertions(+), 9 deletions(-)
Makefile +2 -1
index ffd7a912fc46..5041e908f051 100644
@@ -478,7 +478,7 @@ export KBUILD_HOSTCXXFLAGS KBUILD_HOSTLDFLAGS KBUILD_HOSTLDLIBS LDFLAGS_MODULE
 
 export KBUILD_CPPFLAGS NOSTDINC_FLAGS LINUXINCLUDE OBJCOPYFLAGS KBUILD_LDFLAGS
 export KBUILD_CFLAGS CFLAGS_KERNEL CFLAGS_MODULE
-export CFLAGS_KASAN CFLAGS_KASAN_NOSANITIZE CFLAGS_UBSAN
+export CFLAGS_KASAN CFLAGS_KASAN_NOSANITIZE CFLAGS_UBSAN CFLAGS_KMSAN
 export KBUILD_AFLAGS AFLAGS_KERNEL AFLAGS_MODULE
 export KBUILD_AFLAGS_MODULE KBUILD_CFLAGS_MODULE KBUILD_LDFLAGS_MODULE
 export KBUILD_AFLAGS_KERNEL KBUILD_CFLAGS_KERNEL
@@ -898,6 +898,7 @@ KBUILD_CFLAGS += $(call cc-option,-fcf-protection=none)
 endif
 
 include scripts/Makefile.kasan
+include scripts/Makefile.kmsan
 include scripts/Makefile.extrawarn
 include scripts/Makefile.ubsan
 
arch/x86/boot/compressed/misc.h +1 -0
index c8181392f70d..dd4bd8c5d97a 100644
@@ -12,6 +12,7 @@
 #undef CONFIG_PARAVIRT_XXL
 #undef CONFIG_PARAVIRT_SPINLOCKS
 #undef CONFIG_KASAN
+#undef CONFIG_KMSAN
 
 /* cpu_feature_enabled() cannot be used this early */
 #define USE_EARLY_PGTABLE_L5
arch/x86/entry/entry_64.S +18 -0
index b7c3ea4cb19d..1218672bad85 100644
@@ -35,6 +35,7 @@
 #include <asm/asm.h>
 #include <asm/smap.h>
 #include <asm/pgtable_types.h>
+#include <asm/kmsan.h>
 #include <asm/export.h>
 #include <asm/frame.h>
 #include <asm/nospec-branch.h>
@@ -168,12 +169,14 @@ GLOBAL(entry_SYSCALL_64_after_hwframe)
 	PUSH_AND_CLEAR_REGS rax=$-ENOSYS
 
 	TRACE_IRQS_OFF
+	KMSAN_SYSCALL_ENTER
 
 	/* IRQs are off. */
 	movq	%rax, %rdi
 	movq	%rsp, %rsi
 	call	do_syscall_64		/* returns with IRQs disabled */
 
+	KMSAN_SYSCALL_EXIT
 	TRACE_IRQS_IRETQ		/* we're about to change IF */
 
 	/*
@@ -575,6 +578,7 @@ ENTRY(interrupt_entry)
 
 1:
 	ENTER_IRQ_STACK old_rsp=%rdi save_ret=1
+	KMSAN_INTERRUPT_ENTER
 	/* We entered an interrupt context - irqs are off: */
 	TRACE_IRQS_OFF
 
@@ -604,12 +608,14 @@ common_interrupt:
 	addq	$-0x80, (%rsp)			/* Adjust vector to [-256, -1] range */
 	call	interrupt_entry
 	UNWIND_HINT_REGS indirect=1
+	KMSAN_UNPOISON_PT_REGS
 	call	do_IRQ	/* rdi points to pt_regs */
 	/* 0(%rsp): old RSP */
 ret_from_intr:
 	DISABLE_INTERRUPTS(CLBR_ANY)
 	TRACE_IRQS_OFF
 
+	KMSAN_INTERRUPT_EXIT
 	LEAVE_IRQ_STACK
 
 	testb	$3, CS(%rsp)
@@ -802,6 +808,7 @@ ENTRY(\sym)
 .Lcommon_\sym:
 	call	interrupt_entry
 	UNWIND_HINT_REGS indirect=1
+	KMSAN_UNPOISON_PT_REGS
 	call	\do_sym	/* rdi points to pt_regs */
 	jmp	ret_from_intr
 END(\sym)
@@ -909,15 +916,18 @@ apicinterrupt IRQ_WORK_VECTOR			irq_work_interrupt		smp_irq_work_interrupt
 
 	.if \shift_ist != -1
 	subq	$\ist_offset, CPU_TSS_IST(\shift_ist)
+	KMSAN_IST_ENTER(\shift_ist)
 	.endif
 
 	.if \read_cr2
 	movq	%r12, %rdx			/* Move CR2 into 3rd argument */
 	.endif
 
+	KMSAN_UNPOISON_PT_REGS
 	call	\do_sym
 
 	.if \shift_ist != -1
+	KMSAN_IST_EXIT(\shift_ist)
 	addq	$\ist_offset, CPU_TSS_IST(\shift_ist)
 	.endif
 
@@ -1079,7 +1089,9 @@ ENTRY(do_softirq_own_stack)
 	pushq	%rbp
 	mov	%rsp, %rbp
 	ENTER_IRQ_STACK regs=0 old_rsp=%r11
+	KMSAN_SOFTIRQ_ENTER
 	call	__do_softirq
+	KMSAN_SOFTIRQ_EXIT
 	LEAVE_IRQ_STACK regs=0
 	leaveq
 	ret
@@ -1466,9 +1478,12 @@ ENTRY(nmi)
 	 * done with the NMI stack.
 	 */
 
+	KMSAN_NMI_ENTER
 	movq	%rsp, %rdi
 	movq	$-1, %rsi
+	KMSAN_UNPOISON_PT_REGS
 	call	do_nmi
+	KMSAN_NMI_EXIT
 
 	/*
 	 * Return back to user mode.  We must *not* do the normal exit
@@ -1678,10 +1693,13 @@ end_repeat_nmi:
 	call	paranoid_entry
 	UNWIND_HINT_REGS
 
+	KMSAN_NMI_ENTER
 	/* paranoidentry do_nmi, 0; without TRACE_IRQS_OFF */
 	movq	%rsp, %rdi
 	movq	$-1, %rsi
+	KMSAN_UNPOISON_PT_REGS
 	call	do_nmi
+	KMSAN_NMI_EXIT
 
 	/* Always restore stashed CR3 value (see paranoid_entry) */
 	RESTORE_CR3 scratch_reg=%r15 save_reg=%r14
arch/x86/include/asm/page_64_types.h +7 -5
index 288b065955b7..ea9fbf09f43b 100644
@@ -7,18 +7,20 @@
 #endif
 
 #ifdef CONFIG_KASAN
-#define KASAN_STACK_ORDER 1
+#define EXTRA_STACK_ORDER 1
+#elif defined(CONFIG_KMSAN)
+#define EXTRA_STACK_ORDER 2
 #else
-#define KASAN_STACK_ORDER 0
+#define EXTRA_STACK_ORDER 0
 #endif
 
-#define THREAD_SIZE_ORDER	(2 + KASAN_STACK_ORDER)
+#define THREAD_SIZE_ORDER	(2 + EXTRA_STACK_ORDER)
 #define THREAD_SIZE  (PAGE_SIZE << THREAD_SIZE_ORDER)
 
-#define EXCEPTION_STACK_ORDER (0 + KASAN_STACK_ORDER)
+#define EXCEPTION_STACK_ORDER (0 + EXTRA_STACK_ORDER)
 #define EXCEPTION_STKSZ (PAGE_SIZE << EXCEPTION_STACK_ORDER)
 
-#define IRQ_STACK_ORDER (2 + KASAN_STACK_ORDER)
+#define IRQ_STACK_ORDER (2 + EXTRA_STACK_ORDER)
 #define IRQ_STACK_SIZE (PAGE_SIZE << IRQ_STACK_ORDER)
 
 /*
block/blk.h +7 -0
index 47fba9362e60..9ee271a22423 100644
@@ -78,6 +78,13 @@ static inline bool biovec_phys_mergeable(struct request_queue *q,
 	phys_addr_t addr1 = page_to_phys(vec1->bv_page) + vec1->bv_offset;
 	phys_addr_t addr2 = page_to_phys(vec2->bv_page) + vec2->bv_offset;
 
+#ifdef CONFIG_KMSAN
+	/*
+	 * Merging consequent physical pages may not work correctly under KMSAN
+	 * if their metadata pages aren't consequent. Just disable merging.
+	 */
+	return false;
+#endif
 	if (addr1 + vec1->bv_len != addr2)
 		return false;
 	if (xen_domain() && !xen_biovec_phys_mergeable(vec1, vec2->bv_page))
include/linux/mm_types.h +9 -0
index 2222fa795284..c87c5416a802 100644
@@ -216,6 +216,15 @@ struct page {
 					   not kmapped, ie. highmem) */
 #endif /* WANT_PAGE_VIRTUAL */
 
+#ifdef CONFIG_KMSAN
+	/*
+	 * Bits in struct page are scarce, so the LSB in *shadow is used to
+	 * indicate whether the page should be ignored by KMSAN or not.
+	 */
+	struct page *shadow;
+	struct page *origin;
+#endif
+
 #ifdef LAST_CPUPID_NOT_IN_PAGE_FLAGS
 	int _last_cpupid;
 #endif
include/linux/sched.h +5 -0
index 7417e0207b48..e0f6e95c22ed 100644
@@ -15,6 +15,7 @@
 #include <linux/sem.h>
 #include <linux/shm.h>
 #include <linux/kcov.h>
+#include <linux/kmsan.h>
 #include <linux/mutex.h>
 #include <linux/plist.h>
 #include <linux/hrtimer.h>
@@ -1172,6 +1173,10 @@ struct task_struct {
 	unsigned int			kasan_depth;
 #endif
 
+#ifdef CONFIG_KMSAN
+	struct kmsan_task_state		kmsan;
+#endif
+
 #ifdef CONFIG_FUNCTION_GRAPH_TRACER
 	/* Index of current stored address in ret_stack: */
 	int				curr_ret_stack;
init/main.c +3 -0
index 91f6ebb30ef0..afcca2a38348 100644
@@ -32,6 +32,7 @@
 #include <linux/nmi.h>
 #include <linux/percpu.h>
 #include <linux/kmod.h>
+#include <linux/kmsan.h>
 #include <linux/vmalloc.h>
 #include <linux/kernel_stat.h>
 #include <linux/start_kernel.h>
@@ -554,6 +555,7 @@ static void __init mm_init(void)
 	 */
 	page_ext_init_flatmem();
 	report_meminit();
+	kmsan_initialize_shadow();
 	mem_init();
 	kmem_cache_init();
 	kmemleak_init();
@@ -625,6 +627,7 @@ asmlinkage __visible void __init start_kernel(void)
 	sort_main_extable();
 	trap_init();
 	mm_init();
+	kmsan_initialize();
 
 	ftrace_init();
 
lib/stackdepot.c +4 -3
index 460efd3ef742..529a86eb1dd1 100644
@@ -233,8 +233,8 @@ unsigned int stack_depot_fetch(depot_stack_handle_t handle,
 	if (parts.slabindex > depot_index) {
 		WARN(1, "slab index %d out of bounds (%d) for stack id %08x\n",
 			parts.slabindex, depot_index, handle);
-		__memset(trace, 0, sizeof(*trace));
-		return;
+		*entries = NULL;
+		return 0;
 	}
 	slab = stack_slabs[parts.slabindex];
 	stack = slab + offset;
@@ -299,7 +299,8 @@ depot_stack_handle_t stack_depot_save(unsigned long *entries,
 		 * contexts and I/O.
 		 */
 		alloc_flags &= ~GFP_ZONEMASK;
-		alloc_flags &= (GFP_ATOMIC | GFP_KERNEL);
+		alloc_flags &= (GFP_ATOMIC | GFP_KERNEL |
+				__GFP_NO_KMSAN_SHADOW);
 		alloc_flags |= __GFP_NOWARN;
 		page = alloc_pages(alloc_flags, STACK_ALLOC_ORDER);
 		if (page)
mm/Makefile +1 -0
index d996846697ef..419e6e02dfaf 100644
@@ -71,6 +71,7 @@ obj-$(CONFIG_PAGE_POISONING) += page_poison.o
 obj-$(CONFIG_SLAB) += slab.o
 obj-$(CONFIG_SLUB) += slub.o
 obj-$(CONFIG_KASAN)	+= kasan/
+obj-$(CONFIG_KMSAN)	+= kmsan/
 obj-$(CONFIG_FAILSLAB) += failslab.o
 obj-$(CONFIG_MEMORY_HOTPLUG) += memory_hotplug.o
 obj-$(CONFIG_MEMTEST)		+= memtest.o
scripts/Makefile.lib +6 -0
index 179d55af5852..f9f38f7c1cd4 100644
@@ -140,6 +140,12 @@ _c_flags += $(if $(patsubst n%,, \
 		$(CFLAGS_KASAN), $(CFLAGS_KASAN_NOSANITIZE))
 endif
 
+ifeq ($(CONFIG_KMSAN),y)
+_c_flags += $(if $(patsubst n%,, \
+		$(KMSAN_SANITIZE_$(basetarget).o)$(KMSAN_SANITIZE)y), \
+		$(CFLAGS_KMSAN))
+endif
+
 ifeq ($(CONFIG_UBSAN),y)
 _c_flags += $(if $(patsubst n%,, \
 		$(UBSAN_SANITIZE_$(basetarget).o)$(UBSAN_SANITIZE)$(CONFIG_UBSAN_SANITIZE_ALL)), \
Signature · 1 line
2.23.0.866.gb869b98d4c-goog
author maintainer
#25 lore raw

RFC PATCH 17/26 kmsan: disable KMSAN instrumentation for certain kernel parts

In reply to [email protected]

Instrumenting some files with KMSAN will result in kernel being unable to link, boot or crashing at runtime for various reasons (e.g. infinite recursion caused by instrumentation hooks calling instrumented code again).

Disable KMSAN in the following places:
- arch/x86/boot and arch/x86/realmode/rm, as KMSAN doesn't work for i386;
- arch/x86/entry/vdso, which isn't linked with KMSAN runtime;
- three files in arch/x86/kernel - boot problems;
- arch/x86/mm/cpu_entry_area.c - recursion;
- EFI stub - build failures;
- kcov, stackdepot - recursion.

To: Alexander Potapenko <[email protected]>
Cc: Ard Biesheuvel <[email protected]>
Cc: Thomas Gleixner <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]
---

Change-Id: I90961eabf2dcb9ae992aed259088953bad5e4d6d

---
 arch/x86/boot/Makefile                | 2 ++
 arch/x86/boot/compressed/Makefile     | 2 ++
 arch/x86/entry/vdso/Makefile          | 3 +++
 arch/x86/kernel/Makefile              | 4 ++++
 arch/x86/kernel/cpu/Makefile          | 1 +
 arch/x86/mm/Makefile                  | 2 ++
 arch/x86/realmode/rm/Makefile         | 2 ++
 drivers/firmware/efi/libstub/Makefile | 1 +
 kernel/Makefile                       | 1 +
 lib/Makefile                          | 1 +
 10 files changed, 19 insertions(+)
arch/x86/boot/Makefile +2 -0
index e2839b5c246c..c039abd4c81f 100644
@@ -10,6 +10,8 @@
 #
 
 KASAN_SANITIZE			:= n
+# KMSAN doesn't work for i386
+KMSAN_SANITIZE			:= n
 OBJECT_FILES_NON_STANDARD	:= y
 
 # Kernel does not boot with kcov instrumentation here.
arch/x86/boot/compressed/Makefile +2 -0
index 6b84afdd7538..9efe2d9fca4c 100644
@@ -18,6 +18,8 @@
 #	compressed vmlinux.bin.all + u32 size of vmlinux.bin.all
 
 KASAN_SANITIZE			:= n
+# KMSAN doesn't work for i386
+KMSAN_SANITIZE			:= n
 OBJECT_FILES_NON_STANDARD	:= y
 
 # Prevents link failures: __sanitizer_cov_trace_pc() is not linked in.
arch/x86/entry/vdso/Makefile +3 -0
index 0f2154106d01..000467a1a4f2 100644
@@ -11,6 +11,9 @@ include $(srctree)/lib/vdso/Makefile
 
 KBUILD_CFLAGS += $(DISABLE_LTO)
 KASAN_SANITIZE			:= n
+# Undefined references to KMSAN hooks.
+KMSAN_SANITIZE_vclock_gettime.o := n
+KMSAN_SANITIZE_vgetcpu.o	:= n
 UBSAN_SANITIZE			:= n
 OBJECT_FILES_NON_STANDARD	:= y
 
arch/x86/kernel/Makefile +4 -0
index 3578ad248bc9..ce39972a7edf 100644
@@ -28,6 +28,10 @@ KASAN_SANITIZE_dumpstack_$(BITS).o			:= n
 KASAN_SANITIZE_stacktrace.o				:= n
 KASAN_SANITIZE_paravirt.o				:= n
 
+# Work around reboot loop.
+KMSAN_SANITIZE_head$(BITS).o				:= n
+KMSAN_SANITIZE_nmi.o					:= n
+
 OBJECT_FILES_NON_STANDARD_relocate_kernel_$(BITS).o	:= y
 OBJECT_FILES_NON_STANDARD_test_nx.o			:= y
 OBJECT_FILES_NON_STANDARD_paravirt_patch.o		:= y
arch/x86/kernel/cpu/Makefile +1 -0
index d7a1e5a9331c..41f4f8f2f2f0 100644
@@ -12,6 +12,7 @@ endif
 # If these files are instrumented, boot hangs during the first second.
 KCOV_INSTRUMENT_common.o := n
 KCOV_INSTRUMENT_perf_event.o := n
+KMSAN_SANITIZE_common.o := n
 
 # Make sure load_percpu_segment has no stackprotector
 nostackp := $(call cc-option, -fno-stack-protector)
arch/x86/mm/Makefile +2 -0
index 84373dc9b341..42cb3a6409b0 100644
@@ -7,6 +7,8 @@ KCOV_INSTRUMENT_mem_encrypt_identity.o	:= n
 KASAN_SANITIZE_mem_encrypt.o		:= n
 KASAN_SANITIZE_mem_encrypt_identity.o	:= n
 
+KMSAN_SANITIZE_cpu_entry_area.o := n
+
 ifdef CONFIG_FUNCTION_TRACER
 CFLAGS_REMOVE_mem_encrypt.o		= -pg
 CFLAGS_REMOVE_mem_encrypt_identity.o	= -pg
arch/x86/realmode/rm/Makefile +2 -0
index f60501a384f9..27e7bc0bbdde 100644
@@ -7,6 +7,8 @@
 #
 #
 KASAN_SANITIZE			:= n
+# KMSAN doesn't work for i386
+KMSAN_SANITIZE			:= n
 OBJECT_FILES_NON_STANDARD	:= y
 
 # Prevents link failures: __sanitizer_cov_trace_pc() is not linked in.
drivers/firmware/efi/libstub/Makefile +1 -0
index 0460c7581220..11869c17a64c 100644
@@ -32,6 +32,7 @@ KBUILD_CFLAGS			:= $(cflags-y) -DDISABLE_BRANCH_PROFILING \
 
 GCOV_PROFILE			:= n
 KASAN_SANITIZE			:= n
+KMSAN_SANITIZE			:= n
 UBSAN_SANITIZE			:= n
 OBJECT_FILES_NON_STANDARD	:= y
 
kernel/Makefile +1 -0
index daad787fb795..5fd6fbca2592 100644
@@ -30,6 +30,7 @@ KCOV_INSTRUMENT_extable.o := n
 # Don't self-instrument.
 KCOV_INSTRUMENT_kcov.o := n
 KASAN_SANITIZE_kcov.o := n
+KMSAN_SANITIZE_kcov.o := n
 CFLAGS_kcov.o := $(call cc-option, -fno-conserve-stack -fno-stack-protector)
 
 # cond_syscall is currently not LTO compatible
lib/Makefile +1 -0
index cb44262c38ee..a96d1d6e6a34 100644
@@ -218,6 +218,7 @@ obj-$(CONFIG_IRQ_POLL) += irq_poll.o
 
 obj-$(CONFIG_STACKDEPOT) += stackdepot.o
 KASAN_SANITIZE_stackdepot.o := n
+KMSAN_SANITIZE_stackdepot.o := n
 KCOV_INSTRUMENT_stackdepot.o := n
 
 libfdt_files = fdt.o fdt_ro.o fdt_wip.o fdt_rw.o fdt_sw.o fdt_strerror.o \
Signature · 1 line
2.23.0.866.gb869b98d4c-goog
author maintainer
#26 lore raw

RFC PATCH 18/26 kmsan: mm: call KMSAN hooks from SLUB code

In reply to [email protected]

In order to report uninitialized memory coming from heap allocations KMSAN has to poison them unless they're created with __GFP_ZERO.

It's handy that we need KMSAN hooks in the places where init_on_alloc/init_on_free initialization is performed.

To: Alexander Potapenko <[email protected]>
Cc: Andrew Morton <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]
---

Change-Id: I51103b7981d3aabed747d0c85cbdc85568665871

---
 mm/slub.c | 37 +++++++++++++++++++++++++++++++------
 1 file changed, 31 insertions(+), 6 deletions(-)
mm/slub.c +31 -6
index 3d63ae320d31..3d6d4c63446e 100644
@@ -21,6 +21,8 @@
 #include <linux/proc_fs.h>
 #include <linux/seq_file.h>
 #include <linux/kasan.h>
+#include <linux/kmsan.h>
+#include <linux/kmsan-checks.h> /* KMSAN_INIT_VALUE */
 #include <linux/cpu.h>
 #include <linux/cpuset.h>
 #include <linux/mempolicy.h>
@@ -285,17 +287,27 @@ static void prefetch_freepointer(const struct kmem_cache *s, void *object)
 	prefetch(object + s->offset);
 }
 
+/*
+ * When running under KMSAN, get_freepointer_safe() may return an uninitialized
+ * pointer value in the case the current thread loses the race for the next
+ * memory chunk in the freelist. In that case this_cpu_cmpxchg_double() in
+ * slab_alloc_node() will fail, so the uninitialized value won't be used, but
+ * KMSAN will still check all arguments of cmpxchg because of imperfect
+ * handling of inline assembly.
+ * To work around this problem, use KMSAN_INIT_VALUE() to force initialize the
+ * return value of get_freepointer_safe().
+ */
 static inline void *get_freepointer_safe(struct kmem_cache *s, void *object)
 {
 	unsigned long freepointer_addr;
 	void *p;
 
 	if (!debug_pagealloc_enabled())
-		return get_freepointer(s, object);
+		return KMSAN_INIT_VALUE(get_freepointer(s, object));
 
 	freepointer_addr = (unsigned long)object + s->offset;
 	probe_kernel_read(&p, (void **)freepointer_addr, sizeof(p));
-	return freelist_ptr(s, p, freepointer_addr);
+	return KMSAN_INIT_VALUE(freelist_ptr(s, p, freepointer_addr));
 }
 
 static inline void set_freepointer(struct kmem_cache *s, void *object, void *fp)
@@ -1390,6 +1402,7 @@ static inline void *kmalloc_large_node_hook(void *ptr, size_t size, gfp_t flags)
 	ptr = kasan_kmalloc_large(ptr, size, flags);
 	/* As ptr might get tagged, call kmemleak hook after KASAN. */
 	kmemleak_alloc(ptr, size, 1, flags);
+	kmsan_kmalloc_large(ptr, size, flags);
 	return ptr;
 }
 
@@ -1397,6 +1410,7 @@ static __always_inline void kfree_hook(void *x)
 {
 	kmemleak_free(x);
 	kasan_kfree_large(x, _RET_IP_);
+	kmsan_kfree_large(x);
 }
 
 static __always_inline bool slab_free_hook(struct kmem_cache *s, void *x)
@@ -1453,6 +1467,12 @@ static inline bool slab_free_freelist_hook(struct kmem_cache *s,
 		} while (object != old_tail);
 	}
 
+	do {
+		object = next;
+		next = get_freepointer(s, object);
+		kmsan_slab_free(s, object);
+	} while (object != old_tail);
+
 /*
  * Compiler cannot detect this function can be removed if slab_free_hook()
  * evaluates to nothing.  Thus, catch all relevant config debug options here.
@@ -2769,6 +2789,7 @@ static __always_inline void *slab_alloc_node(struct kmem_cache *s,
 	if (unlikely(slab_want_init_on_alloc(gfpflags, s)) && object)
 		memset(object, 0, s->object_size);
 
+	kmsan_slab_alloc(s, object, gfpflags);
 	slab_post_alloc_hook(s, gfpflags, 1, &object);
 
 	return object;
@@ -2797,6 +2818,7 @@ void *kmem_cache_alloc_trace(struct kmem_cache *s, gfp_t gfpflags, size_t size)
 	void *ret = slab_alloc(s, gfpflags, _RET_IP_);
 	trace_kmalloc(_RET_IP_, ret, size, s->size, gfpflags);
 	ret = kasan_kmalloc(s, ret, size, gfpflags);
+
 	return ret;
 }
 EXPORT_SYMBOL(kmem_cache_alloc_trace);
@@ -2809,7 +2831,6 @@ void *kmem_cache_alloc_node(struct kmem_cache *s, gfp_t gfpflags, int node)
 
 	trace_kmem_cache_alloc_node(_RET_IP_, ret,
 				    s->object_size, s->size, gfpflags, node);
-
 	return ret;
 }
 EXPORT_SYMBOL(kmem_cache_alloc_node);
@@ -2825,6 +2846,7 @@ void *kmem_cache_alloc_node_trace(struct kmem_cache *s,
 			   size, s->size, gfpflags, node);
 
 	ret = kasan_kmalloc(s, ret, size, gfpflags);
+
 	return ret;
 }
 EXPORT_SYMBOL(kmem_cache_alloc_node_trace);
@@ -3150,7 +3172,7 @@ int kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t size,
 			  void **p)
 {
 	struct kmem_cache_cpu *c;
-	int i;
+	int i, j;
 
 	/* memcg and kmem_cache debug support */
 	s = slab_pre_alloc_hook(s, flags);
@@ -3188,11 +3210,11 @@ int kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t size,
 
 	/* Clear memory outside IRQ disabled fastpath loop */
 	if (unlikely(slab_want_init_on_alloc(flags, s))) {
-		int j;
-
 		for (j = 0; j < i; j++)
 			memset(p[j], 0, s->object_size);
 	}
+	for (j = 0; j < i; j++)
+		kmsan_slab_alloc(s, p[j], flags);
 
 	/* memcg and kmem_cache debug support */
 	slab_post_alloc_hook(s, flags, size, p);
@@ -3793,6 +3815,7 @@ static int __init setup_slub_min_objects(char *str)
 
 __setup("slub_min_objects=", setup_slub_min_objects);
 
+__no_sanitize_memory
 void *__kmalloc(size_t size, gfp_t flags)
 {
 	struct kmem_cache *s;
@@ -5698,6 +5721,7 @@ static char *create_unique_id(struct kmem_cache *s)
 	p += sprintf(p, "%07u", s->size);
 
 	BUG_ON(p > name + ID_STR_LENGTH - 1);
+	kmsan_unpoison_shadow(name, p - name);
 	return name;
 }
 
@@ -5847,6 +5871,7 @@ static int sysfs_slab_alias(struct kmem_cache *s, const char *name)
 	al->name = name;
 	al->next = alias_list;
 	alias_list = al;
+	kmsan_unpoison_shadow(al, sizeof(struct saved_alias));
 	return 0;
 }
 
Signature · 1 line
2.23.0.866.gb869b98d4c-goog
#27 lore raw

Re: [PATCH RFC v1 18/26] kmsan: mm: call KMSAN hooks from SLUB code

Quoted 5 lines · On Fri, 2019-10-18 at 11:42 +0200, [email protected] wrote:

In order to report uninitialized memory coming from heap allocations KMSAN has to poison them unless they're created with __GFP_ZERO.

It's handy that we need KMSAN hooks in the places where init_on_alloc/init_on_free initialization is performed.

Well, there is SLUB debug which has red zoning and poisoning checks. What's value of this patch?

Quoted 165 lines

To: Alexander Potapenko <[email protected]>
Cc: Andrew Morton <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]
---

Change-Id: I51103b7981d3aabed747d0c85cbdc85568665871

---
 mm/slub.c | 37 +++++++++++++++++++++++++++++++------
 1 file changed, 31 insertions(+), 6 deletions(-)
mm/slub.c +31 -6
index 3d63ae320d31..3d6d4c63446e 100644
@@ -21,6 +21,8 @@
 #include <linux/proc_fs.h>
 #include <linux/seq_file.h>
 #include <linux/kasan.h>
+#include <linux/kmsan.h>
+#include <linux/kmsan-checks.h> /* KMSAN_INIT_VALUE */
 #include <linux/cpu.h>
 #include <linux/cpuset.h>
 #include <linux/mempolicy.h>
@@ -285,17 +287,27 @@ static void prefetch_freepointer(const struct kmem_cache *s, void *object)
 	prefetch(object + s->offset);
 }
 
+/*
+ * When running under KMSAN, get_freepointer_safe() may return an uninitialized
+ * pointer value in the case the current thread loses the race for the next
+ * memory chunk in the freelist. In that case this_cpu_cmpxchg_double() in
+ * slab_alloc_node() will fail, so the uninitialized value won't be used, but
+ * KMSAN will still check all arguments of cmpxchg because of imperfect
+ * handling of inline assembly.
+ * To work around this problem, use KMSAN_INIT_VALUE() to force initialize the
+ * return value of get_freepointer_safe().
+ */
 static inline void *get_freepointer_safe(struct kmem_cache *s, void *object)
 {
 	unsigned long freepointer_addr;
 	void *p;
 
 	if (!debug_pagealloc_enabled())
-		return get_freepointer(s, object);
+		return KMSAN_INIT_VALUE(get_freepointer(s, object));
 
 	freepointer_addr = (unsigned long)object + s->offset;
 	probe_kernel_read(&p, (void **)freepointer_addr, sizeof(p));
-	return freelist_ptr(s, p, freepointer_addr);
+	return KMSAN_INIT_VALUE(freelist_ptr(s, p, freepointer_addr));
 }
 
 static inline void set_freepointer(struct kmem_cache *s, void *object, void *fp)
@@ -1390,6 +1402,7 @@ static inline void *kmalloc_large_node_hook(void *ptr, size_t size, gfp_t flags)
 	ptr = kasan_kmalloc_large(ptr, size, flags);
 	/* As ptr might get tagged, call kmemleak hook after KASAN. */
 	kmemleak_alloc(ptr, size, 1, flags);
+	kmsan_kmalloc_large(ptr, size, flags);
 	return ptr;
 }
 
@@ -1397,6 +1410,7 @@ static __always_inline void kfree_hook(void *x)
 {
 	kmemleak_free(x);
 	kasan_kfree_large(x, _RET_IP_);
+	kmsan_kfree_large(x);
 }
 
 static __always_inline bool slab_free_hook(struct kmem_cache *s, void *x)
@@ -1453,6 +1467,12 @@ static inline bool slab_free_freelist_hook(struct kmem_cache *s,
 		} while (object != old_tail);
 	}
 
+	do {
+		object = next;
+		next = get_freepointer(s, object);
+		kmsan_slab_free(s, object);
+	} while (object != old_tail);
+
 /*
  * Compiler cannot detect this function can be removed if slab_free_hook()
  * evaluates to nothing.  Thus, catch all relevant config debug options here.
@@ -2769,6 +2789,7 @@ static __always_inline void *slab_alloc_node(struct kmem_cache *s,
 	if (unlikely(slab_want_init_on_alloc(gfpflags, s)) && object)
 		memset(object, 0, s->object_size);
 
+	kmsan_slab_alloc(s, object, gfpflags);
 	slab_post_alloc_hook(s, gfpflags, 1, &object);
 
 	return object;
@@ -2797,6 +2818,7 @@ void *kmem_cache_alloc_trace(struct kmem_cache *s, gfp_t gfpflags, size_t size)
 	void *ret = slab_alloc(s, gfpflags, _RET_IP_);
 	trace_kmalloc(_RET_IP_, ret, size, s->size, gfpflags);
 	ret = kasan_kmalloc(s, ret, size, gfpflags);
+
 	return ret;
 }
 EXPORT_SYMBOL(kmem_cache_alloc_trace);
@@ -2809,7 +2831,6 @@ void *kmem_cache_alloc_node(struct kmem_cache *s, gfp_t gfpflags, int node)
 
 	trace_kmem_cache_alloc_node(_RET_IP_, ret,
 				    s->object_size, s->size, gfpflags, node);
-
 	return ret;
 }
 EXPORT_SYMBOL(kmem_cache_alloc_node);
@@ -2825,6 +2846,7 @@ void *kmem_cache_alloc_node_trace(struct kmem_cache *s,
 			   size, s->size, gfpflags, node);
 
 	ret = kasan_kmalloc(s, ret, size, gfpflags);
+
 	return ret;
 }
 EXPORT_SYMBOL(kmem_cache_alloc_node_trace);
@@ -3150,7 +3172,7 @@ int kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t size,
 			  void **p)
 {
 	struct kmem_cache_cpu *c;
-	int i;
+	int i, j;
 
 	/* memcg and kmem_cache debug support */
 	s = slab_pre_alloc_hook(s, flags);
@@ -3188,11 +3210,11 @@ int kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t size,
 
 	/* Clear memory outside IRQ disabled fastpath loop */
 	if (unlikely(slab_want_init_on_alloc(flags, s))) {
-		int j;
-
 		for (j = 0; j < i; j++)
 			memset(p[j], 0, s->object_size);
 	}
+	for (j = 0; j < i; j++)
+		kmsan_slab_alloc(s, p[j], flags);
 
 	/* memcg and kmem_cache debug support */
 	slab_post_alloc_hook(s, flags, size, p);
@@ -3793,6 +3815,7 @@ static int __init setup_slub_min_objects(char *str)
 
 __setup("slub_min_objects=", setup_slub_min_objects);
 
+__no_sanitize_memory
 void *__kmalloc(size_t size, gfp_t flags)
 {
 	struct kmem_cache *s;
@@ -5698,6 +5721,7 @@ static char *create_unique_id(struct kmem_cache *s)
 	p += sprintf(p, "%07u", s->size);
 
 	BUG_ON(p > name + ID_STR_LENGTH - 1);
+	kmsan_unpoison_shadow(name, p - name);
 	return name;
 }
 
@@ -5847,6 +5871,7 @@ static int sysfs_slab_alias(struct kmem_cache *s, const char *name)
 	al->name = name;
 	al->next = alias_list;
 	alias_list = al;
+	kmsan_unpoison_shadow(al, sizeof(struct saved_alias));
 	return 0;
 }
 
author maintainer
#28 lore raw

Re: [PATCH RFC v1 18/26] kmsan: mm: call KMSAN hooks from SLUB code

Quoted 10 lines · Qian Cai
Quoted 5 lines · On Fri, 2019-10-18 at 11:42 +0200, [email protected] wrote:

In order to report uninitialized memory coming from heap allocations KMSAN has to poison them unless they're created with __GFP_ZERO.

It's handy that we need KMSAN hooks in the places where init_on_alloc/init_on_free initialization is performed.

Well, there is SLUB debug which has red zoning and poisoning checks. What's value of this patch?

Sorry, are you talking about the whole patchset or just this patch? Note that SLUB debug is unable to detect uninitialized values with bit-to-bit precision, neither have I heard of anyone using it for detecting uses of uninitialized memory in the kernel at all. The purpose of SLUB debug is to detect corruptions of freed memory.

Quoted 165 lines
Quoted 165 lines

To: Alexander Potapenko <[email protected]>
Cc: Andrew Morton <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]
---

Change-Id: I51103b7981d3aabed747d0c85cbdc85568665871

---
 mm/slub.c | 37 +++++++++++++++++++++++++++++++------
 1 file changed, 31 insertions(+), 6 deletions(-)
mm/slub.c +31 -6
index 3d63ae320d31..3d6d4c63446e 100644
@@ -21,6 +21,8 @@
 #include <linux/proc_fs.h>
 #include <linux/seq_file.h>
 #include <linux/kasan.h>
+#include <linux/kmsan.h>
+#include <linux/kmsan-checks.h> /* KMSAN_INIT_VALUE */
 #include <linux/cpu.h>
 #include <linux/cpuset.h>
 #include <linux/mempolicy.h>
@@ -285,17 +287,27 @@ static void prefetch_freepointer(const struct kmem_cache *s, void *object)
      prefetch(object + s->offset);
 }
 
+/*
+ * When running under KMSAN, get_freepointer_safe() may return an uninitialized
+ * pointer value in the case the current thread loses the race for the next
+ * memory chunk in the freelist. In that case this_cpu_cmpxchg_double() in
+ * slab_alloc_node() will fail, so the uninitialized value won't be used, but
+ * KMSAN will still check all arguments of cmpxchg because of imperfect
+ * handling of inline assembly.
+ * To work around this problem, use KMSAN_INIT_VALUE() to force initialize the
+ * return value of get_freepointer_safe().
+ */
 static inline void *get_freepointer_safe(struct kmem_cache *s, void *object)
 {
      unsigned long freepointer_addr;
      void *p;
 
      if (!debug_pagealloc_enabled())
-             return get_freepointer(s, object);
+             return KMSAN_INIT_VALUE(get_freepointer(s, object));
 
      freepointer_addr = (unsigned long)object + s->offset;
      probe_kernel_read(&p, (void **)freepointer_addr, sizeof(p));
-     return freelist_ptr(s, p, freepointer_addr);
+     return KMSAN_INIT_VALUE(freelist_ptr(s, p, freepointer_addr));
 }
 
 static inline void set_freepointer(struct kmem_cache *s, void *object, void *fp)
@@ -1390,6 +1402,7 @@ static inline void *kmalloc_large_node_hook(void *ptr, size_t size, gfp_t flags)
      ptr = kasan_kmalloc_large(ptr, size, flags);
      /* As ptr might get tagged, call kmemleak hook after KASAN. */
      kmemleak_alloc(ptr, size, 1, flags);
+     kmsan_kmalloc_large(ptr, size, flags);
      return ptr;
 }
 
@@ -1397,6 +1410,7 @@ static __always_inline void kfree_hook(void *x)
 {
      kmemleak_free(x);
      kasan_kfree_large(x, _RET_IP_);
+     kmsan_kfree_large(x);
 }
 
 static __always_inline bool slab_free_hook(struct kmem_cache *s, void *x)
@@ -1453,6 +1467,12 @@ static inline bool slab_free_freelist_hook(struct kmem_cache *s,
              } while (object != old_tail);
      }
 
+     do {
+             object = next;
+             next = get_freepointer(s, object);
+             kmsan_slab_free(s, object);
+     } while (object != old_tail);
+
 /*
  * Compiler cannot detect this function can be removed if slab_free_hook()
  * evaluates to nothing.  Thus, catch all relevant config debug options here.
@@ -2769,6 +2789,7 @@ static __always_inline void *slab_alloc_node(struct kmem_cache *s,
      if (unlikely(slab_want_init_on_alloc(gfpflags, s)) && object)
              memset(object, 0, s->object_size);
 
+     kmsan_slab_alloc(s, object, gfpflags);
      slab_post_alloc_hook(s, gfpflags, 1, &object);
 
      return object;
@@ -2797,6 +2818,7 @@ void *kmem_cache_alloc_trace(struct kmem_cache *s, gfp_t gfpflags, size_t size)
      void *ret = slab_alloc(s, gfpflags, _RET_IP_);
      trace_kmalloc(_RET_IP_, ret, size, s->size, gfpflags);
      ret = kasan_kmalloc(s, ret, size, gfpflags);
+
      return ret;
 }
 EXPORT_SYMBOL(kmem_cache_alloc_trace);
@@ -2809,7 +2831,6 @@ void *kmem_cache_alloc_node(struct kmem_cache *s, gfp_t gfpflags, int node)
 
      trace_kmem_cache_alloc_node(_RET_IP_, ret,
                                  s->object_size, s->size, gfpflags, node);
-
      return ret;
 }
 EXPORT_SYMBOL(kmem_cache_alloc_node);
@@ -2825,6 +2846,7 @@ void *kmem_cache_alloc_node_trace(struct kmem_cache *s,
                         size, s->size, gfpflags, node);
 
      ret = kasan_kmalloc(s, ret, size, gfpflags);
+
      return ret;
 }
 EXPORT_SYMBOL(kmem_cache_alloc_node_trace);
@@ -3150,7 +3172,7 @@ int kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t size,
                        void **p)
 {
      struct kmem_cache_cpu *c;
-     int i;
+     int i, j;
 
      /* memcg and kmem_cache debug support */
      s = slab_pre_alloc_hook(s, flags);
@@ -3188,11 +3210,11 @@ int kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t size,
 
      /* Clear memory outside IRQ disabled fastpath loop */
      if (unlikely(slab_want_init_on_alloc(flags, s))) {
-             int j;
-
              for (j = 0; j < i; j++)
                      memset(p[j], 0, s->object_size);
      }
+     for (j = 0; j < i; j++)
+             kmsan_slab_alloc(s, p[j], flags);
 
      /* memcg and kmem_cache debug support */
      slab_post_alloc_hook(s, flags, size, p);
@@ -3793,6 +3815,7 @@ static int __init setup_slub_min_objects(char *str)
 
 __setup("slub_min_objects=", setup_slub_min_objects);
 
+__no_sanitize_memory
 void *__kmalloc(size_t size, gfp_t flags)
 {
      struct kmem_cache *s;
@@ -5698,6 +5721,7 @@ static char *create_unique_id(struct kmem_cache *s)
      p += sprintf(p, "%07u", s->size);
 
      BUG_ON(p > name + ID_STR_LENGTH - 1);
+     kmsan_unpoison_shadow(name, p - name);
      return name;
 }
 
@@ -5847,6 +5871,7 @@ static int sysfs_slab_alias(struct kmem_cache *s, const char *name)
      al->name = name;
      al->next = alias_list;
      alias_list = al;
+     kmsan_unpoison_shadow(al, sizeof(struct saved_alias));
      return 0;
 }
 
Signature · 10 lines
Alexander Potapenko
Software Engineer

Google Germany GmbH
Erika-Mann-Straße, 33
80636 München

Geschäftsführer: Paul Manicle, Halimah DeLaine Prado
Registergericht und -nummer: Hamburg, HRB 86891
Sitz der Gesellschaft: Hamburg
#29 lore raw

Re: [PATCH RFC v1 18/26] kmsan: mm: call KMSAN hooks from SLUB code

Quoted 6 lines · Alexander Potapenko
Quoted 5 lines · Qian Cai
Quoted 3 lines · On Fri, 2019-10-18 at 11:42 +0200, [email protected] wrote:

In order to report uninitialized memory coming from heap allocations KMSAN has to poison them unless they're created with __GFP_ZERO.

Quoted 7 lines
Quoted 5 lines
Quoted 2 lines

It's handy that we need KMSAN hooks in the places where init_on_alloc/init_on_free initialization is performed.

Well, there is SLUB debug which has red zoning and poisoning checks. What's value of this patch?

Sorry, are you talking about the whole patchset or just this patch?

Just this patch.

Quoted 4 lines

Note that SLUB debug is unable to detect uninitialized values with bit-to-bit precision, neither have I heard of anyone using it for detecting uses of uninitialized memory in the kernel at all. The purpose of SLUB debug is to detect corruptions of freed memory.

The point is if developers have SLUB debug enabled, all the free objects will be poisoned, so what's the point of checking uninitialized memory there?

Quoted 168 lines
Quoted 165 lines
Quoted 165 lines

To: Alexander Potapenko <[email protected]>
Cc: Andrew Morton <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]
---

Change-Id: I51103b7981d3aabed747d0c85cbdc85568665871

---
 mm/slub.c | 37 +++++++++++++++++++++++++++++++------
 1 file changed, 31 insertions(+), 6 deletions(-)
mm/slub.c +31 -6
index 3d63ae320d31..3d6d4c63446e 100644
@@ -21,6 +21,8 @@
 #include <linux/proc_fs.h>
 #include <linux/seq_file.h>
 #include <linux/kasan.h>
+#include <linux/kmsan.h>
+#include <linux/kmsan-checks.h> /* KMSAN_INIT_VALUE */
 #include <linux/cpu.h>
 #include <linux/cpuset.h>
 #include <linux/mempolicy.h>
@@ -285,17 +287,27 @@ static void prefetch_freepointer(const struct kmem_cache *s, void *object)
      prefetch(object + s->offset);
 }
 
+/*
+ * When running under KMSAN, get_freepointer_safe() may return an uninitialized
+ * pointer value in the case the current thread loses the race for the next
+ * memory chunk in the freelist. In that case this_cpu_cmpxchg_double() in
+ * slab_alloc_node() will fail, so the uninitialized value won't be used, but
+ * KMSAN will still check all arguments of cmpxchg because of imperfect
+ * handling of inline assembly.
+ * To work around this problem, use KMSAN_INIT_VALUE() to force initialize the
+ * return value of get_freepointer_safe().
+ */
 static inline void *get_freepointer_safe(struct kmem_cache *s, void *object)
 {
      unsigned long freepointer_addr;
      void *p;
 
      if (!debug_pagealloc_enabled())
-             return get_freepointer(s, object);
+             return KMSAN_INIT_VALUE(get_freepointer(s, object));
 
      freepointer_addr = (unsigned long)object + s->offset;
      probe_kernel_read(&p, (void **)freepointer_addr, sizeof(p));
-     return freelist_ptr(s, p, freepointer_addr);
+     return KMSAN_INIT_VALUE(freelist_ptr(s, p, freepointer_addr));
 }
 
 static inline void set_freepointer(struct kmem_cache *s, void *object, void *fp)
@@ -1390,6 +1402,7 @@ static inline void *kmalloc_large_node_hook(void *ptr, size_t size, gfp_t flags)
      ptr = kasan_kmalloc_large(ptr, size, flags);
      /* As ptr might get tagged, call kmemleak hook after KASAN. */
      kmemleak_alloc(ptr, size, 1, flags);
+     kmsan_kmalloc_large(ptr, size, flags);
      return ptr;
 }
 
@@ -1397,6 +1410,7 @@ static __always_inline void kfree_hook(void *x)
 {
      kmemleak_free(x);
      kasan_kfree_large(x, _RET_IP_);
+     kmsan_kfree_large(x);
 }
 
 static __always_inline bool slab_free_hook(struct kmem_cache *s, void *x)
@@ -1453,6 +1467,12 @@ static inline bool slab_free_freelist_hook(struct kmem_cache *s,
              } while (object != old_tail);
      }
 
+     do {
+             object = next;
+             next = get_freepointer(s, object);
+             kmsan_slab_free(s, object);
+     } while (object != old_tail);
+
 /*
  * Compiler cannot detect this function can be removed if slab_free_hook()
  * evaluates to nothing.  Thus, catch all relevant config debug options here.
@@ -2769,6 +2789,7 @@ static __always_inline void *slab_alloc_node(struct kmem_cache *s,
      if (unlikely(slab_want_init_on_alloc(gfpflags, s)) && object)
              memset(object, 0, s->object_size);
 
+     kmsan_slab_alloc(s, object, gfpflags);
      slab_post_alloc_hook(s, gfpflags, 1, &object);
 
      return object;
@@ -2797,6 +2818,7 @@ void *kmem_cache_alloc_trace(struct kmem_cache *s, gfp_t gfpflags, size_t size)
      void *ret = slab_alloc(s, gfpflags, _RET_IP_);
      trace_kmalloc(_RET_IP_, ret, size, s->size, gfpflags);
      ret = kasan_kmalloc(s, ret, size, gfpflags);
+
      return ret;
 }
 EXPORT_SYMBOL(kmem_cache_alloc_trace);
@@ -2809,7 +2831,6 @@ void *kmem_cache_alloc_node(struct kmem_cache *s, gfp_t gfpflags, int node)
 
      trace_kmem_cache_alloc_node(_RET_IP_, ret,
                                  s->object_size, s->size, gfpflags, node);
-
      return ret;
 }
 EXPORT_SYMBOL(kmem_cache_alloc_node);
@@ -2825,6 +2846,7 @@ void *kmem_cache_alloc_node_trace(struct kmem_cache *s,
                         size, s->size, gfpflags, node);
 
      ret = kasan_kmalloc(s, ret, size, gfpflags);
+
      return ret;
 }
 EXPORT_SYMBOL(kmem_cache_alloc_node_trace);
@@ -3150,7 +3172,7 @@ int kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t size,
                        void **p)
 {
      struct kmem_cache_cpu *c;
-     int i;
+     int i, j;
 
      /* memcg and kmem_cache debug support */
      s = slab_pre_alloc_hook(s, flags);
@@ -3188,11 +3210,11 @@ int kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t size,
 
      /* Clear memory outside IRQ disabled fastpath loop */
      if (unlikely(slab_want_init_on_alloc(flags, s))) {
-             int j;
-
              for (j = 0; j < i; j++)
                      memset(p[j], 0, s->object_size);
      }
+     for (j = 0; j < i; j++)
+             kmsan_slab_alloc(s, p[j], flags);
 
      /* memcg and kmem_cache debug support */
      slab_post_alloc_hook(s, flags, size, p);
@@ -3793,6 +3815,7 @@ static int __init setup_slub_min_objects(char *str)
 
 __setup("slub_min_objects=", setup_slub_min_objects);
 
+__no_sanitize_memory
 void *__kmalloc(size_t size, gfp_t flags)
 {
      struct kmem_cache *s;
@@ -5698,6 +5721,7 @@ static char *create_unique_id(struct kmem_cache *s)
      p += sprintf(p, "%07u", s->size);
 
      BUG_ON(p > name + ID_STR_LENGTH - 1);
+     kmsan_unpoison_shadow(name, p - name);
      return name;
 }
 
@@ -5847,6 +5871,7 @@ static int sysfs_slab_alias(struct kmem_cache *s, const char *name)
      al->name = name;
      al->next = alias_list;
      alias_list = al;
+     kmsan_unpoison_shadow(al, sizeof(struct saved_alias));
      return 0;
 }
 
author maintainer
#30 lore raw

Re: [PATCH RFC v1 18/26] kmsan: mm: call KMSAN hooks from SLUB code

Quoted 18 lines · Qian Cai
Quoted 6 lines · Alexander Potapenko
Quoted 5 lines · Qian Cai
Quoted 3 lines · On Fri, 2019-10-18 at 11:42 +0200, [email protected] wrote:

In order to report uninitialized memory coming from heap allocations KMSAN has to poison them unless they're created with __GFP_ZERO.

Quoted 7 lines
Quoted 5 lines
Quoted 2 lines

It's handy that we need KMSAN hooks in the places where init_on_alloc/init_on_free initialization is performed.

Well, there is SLUB debug which has red zoning and poisoning checks. What's value of this patch?

Sorry, are you talking about the whole patchset or just this patch?

Just this patch.

Quoted 7 lines
Quoted 4 lines

Note that SLUB debug is unable to detect uninitialized values with bit-to-bit precision, neither have I heard of anyone using it for detecting uses of uninitialized memory in the kernel at all. The purpose of SLUB debug is to detect corruptions of freed memory.

The point is if developers have SLUB debug enabled, all the free objects will be poisoned, so what's the point of checking uninitialized memory there?

You are right, SLUB debug has to be handled separately. If I'm understanding correctly, right now KMSAN poisons freed memory before SLUB debug wipes it, therefore the memory will count as initialized. On the other hand, newly allocated memory is still marked as uninitialized, so a lot of bugs still remain detectable. TBH, I haven't tested KMSAN with SLUB debug good enough. Note that it's anyway a separate build that requires Clang, so right now it doesn't make much sense to combine KMSAN and SLUB debug together.

Quoted 168 lines
Quoted 168 lines
Quoted 165 lines
Quoted 165 lines

To: Alexander Potapenko <[email protected]>
Cc: Andrew Morton <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]
---

Change-Id: I51103b7981d3aabed747d0c85cbdc85568665871

---
 mm/slub.c | 37 +++++++++++++++++++++++++++++++------
 1 file changed, 31 insertions(+), 6 deletions(-)
mm/slub.c +31 -6
index 3d63ae320d31..3d6d4c63446e 100644
@@ -21,6 +21,8 @@
 #include <linux/proc_fs.h>
 #include <linux/seq_file.h>
 #include <linux/kasan.h>
+#include <linux/kmsan.h>
+#include <linux/kmsan-checks.h> /* KMSAN_INIT_VALUE */
 #include <linux/cpu.h>
 #include <linux/cpuset.h>
 #include <linux/mempolicy.h>
@@ -285,17 +287,27 @@ static void prefetch_freepointer(const struct kmem_cache *s, void *object)
      prefetch(object + s->offset);
 }
 
+/*
+ * When running under KMSAN, get_freepointer_safe() may return an uninitialized
+ * pointer value in the case the current thread loses the race for the next
+ * memory chunk in the freelist. In that case this_cpu_cmpxchg_double() in
+ * slab_alloc_node() will fail, so the uninitialized value won't be used, but
+ * KMSAN will still check all arguments of cmpxchg because of imperfect
+ * handling of inline assembly.
+ * To work around this problem, use KMSAN_INIT_VALUE() to force initialize the
+ * return value of get_freepointer_safe().
+ */
 static inline void *get_freepointer_safe(struct kmem_cache *s, void *object)
 {
      unsigned long freepointer_addr;
      void *p;
 
      if (!debug_pagealloc_enabled())
-             return get_freepointer(s, object);
+             return KMSAN_INIT_VALUE(get_freepointer(s, object));
 
      freepointer_addr = (unsigned long)object + s->offset;
      probe_kernel_read(&p, (void **)freepointer_addr, sizeof(p));
-     return freelist_ptr(s, p, freepointer_addr);
+     return KMSAN_INIT_VALUE(freelist_ptr(s, p, freepointer_addr));
 }
 
 static inline void set_freepointer(struct kmem_cache *s, void *object, void *fp)
@@ -1390,6 +1402,7 @@ static inline void *kmalloc_large_node_hook(void *ptr, size_t size, gfp_t flags)
      ptr = kasan_kmalloc_large(ptr, size, flags);
      /* As ptr might get tagged, call kmemleak hook after KASAN. */
      kmemleak_alloc(ptr, size, 1, flags);
+     kmsan_kmalloc_large(ptr, size, flags);
      return ptr;
 }
 
@@ -1397,6 +1410,7 @@ static __always_inline void kfree_hook(void *x)
 {
      kmemleak_free(x);
      kasan_kfree_large(x, _RET_IP_);
+     kmsan_kfree_large(x);
 }
 
 static __always_inline bool slab_free_hook(struct kmem_cache *s, void *x)
@@ -1453,6 +1467,12 @@ static inline bool slab_free_freelist_hook(struct kmem_cache *s,
              } while (object != old_tail);
      }
 
+     do {
+             object = next;
+             next = get_freepointer(s, object);
+             kmsan_slab_free(s, object);
+     } while (object != old_tail);
+
 /*
  * Compiler cannot detect this function can be removed if slab_free_hook()
  * evaluates to nothing.  Thus, catch all relevant config debug options here.
@@ -2769,6 +2789,7 @@ static __always_inline void *slab_alloc_node(struct kmem_cache *s,
      if (unlikely(slab_want_init_on_alloc(gfpflags, s)) && object)
              memset(object, 0, s->object_size);
 
+     kmsan_slab_alloc(s, object, gfpflags);
      slab_post_alloc_hook(s, gfpflags, 1, &object);
 
      return object;
@@ -2797,6 +2818,7 @@ void *kmem_cache_alloc_trace(struct kmem_cache *s, gfp_t gfpflags, size_t size)
      void *ret = slab_alloc(s, gfpflags, _RET_IP_);
      trace_kmalloc(_RET_IP_, ret, size, s->size, gfpflags);
      ret = kasan_kmalloc(s, ret, size, gfpflags);
+
      return ret;
 }
 EXPORT_SYMBOL(kmem_cache_alloc_trace);
@@ -2809,7 +2831,6 @@ void *kmem_cache_alloc_node(struct kmem_cache *s, gfp_t gfpflags, int node)
 
      trace_kmem_cache_alloc_node(_RET_IP_, ret,
                                  s->object_size, s->size, gfpflags, node);
-
      return ret;
 }
 EXPORT_SYMBOL(kmem_cache_alloc_node);
@@ -2825,6 +2846,7 @@ void *kmem_cache_alloc_node_trace(struct kmem_cache *s,
                         size, s->size, gfpflags, node);
 
      ret = kasan_kmalloc(s, ret, size, gfpflags);
+
      return ret;
 }
 EXPORT_SYMBOL(kmem_cache_alloc_node_trace);
@@ -3150,7 +3172,7 @@ int kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t size,
                        void **p)
 {
      struct kmem_cache_cpu *c;
-     int i;
+     int i, j;
 
      /* memcg and kmem_cache debug support */
      s = slab_pre_alloc_hook(s, flags);
@@ -3188,11 +3210,11 @@ int kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t size,
 
      /* Clear memory outside IRQ disabled fastpath loop */
      if (unlikely(slab_want_init_on_alloc(flags, s))) {
-             int j;
-
              for (j = 0; j < i; j++)
                      memset(p[j], 0, s->object_size);
      }
+     for (j = 0; j < i; j++)
+             kmsan_slab_alloc(s, p[j], flags);
 
      /* memcg and kmem_cache debug support */
      slab_post_alloc_hook(s, flags, size, p);
@@ -3793,6 +3815,7 @@ static int __init setup_slub_min_objects(char *str)
 
 __setup("slub_min_objects=", setup_slub_min_objects);
 
+__no_sanitize_memory
 void *__kmalloc(size_t size, gfp_t flags)
 {
      struct kmem_cache *s;
@@ -5698,6 +5721,7 @@ static char *create_unique_id(struct kmem_cache *s)
      p += sprintf(p, "%07u", s->size);
 
      BUG_ON(p > name + ID_STR_LENGTH - 1);
+     kmsan_unpoison_shadow(name, p - name);
      return name;
 }
 
@@ -5847,6 +5871,7 @@ static int sysfs_slab_alias(struct kmem_cache *s, const char *name)
      al->name = name;
      al->next = alias_list;
      alias_list = al;
+     kmsan_unpoison_shadow(al, sizeof(struct saved_alias));
      return 0;
 }
 
Signature · 10 lines
Alexander Potapenko
Software Engineer

Google Germany GmbH
Erika-Mann-Straße, 33
80636 München

Geschäftsführer: Paul Manicle, Halimah DeLaine Prado
Registergericht und -nummer: Hamburg, HRB 86891
Sitz der Gesellschaft: Hamburg
#31 lore raw

Re: [PATCH RFC v1 18/26] kmsan: mm: call KMSAN hooks from SLUB code

Quoted 24 lines · Alexander Potapenko
Quoted 23 lines · Qian Cai
Quoted 6 lines · Alexander Potapenko
Quoted 5 lines · Qian Cai
Quoted 3 lines · On Fri, 2019-10-18 at 11:42 +0200, [email protected] wrote:

In order to report uninitialized memory coming from heap allocations KMSAN has to poison them unless they're created with __GFP_ZERO.

Quoted 7 lines
Quoted 5 lines
Quoted 2 lines

It's handy that we need KMSAN hooks in the places where init_on_alloc/init_on_free initialization is performed.

Well, there is SLUB debug which has red zoning and poisoning checks. What's value of this patch?

Sorry, are you talking about the whole patchset or just this patch?

Just this patch.

Quoted 4 lines

Note that SLUB debug is unable to detect uninitialized values with bit-to-bit precision, neither have I heard of anyone using it for detecting uses of uninitialized memory in the kernel at all. The purpose of SLUB debug is to detect corruptions of freed memory.

Quoted 8 lines
Quoted 2 lines

The point is if developers have SLUB debug enabled, all the free objects will be poisoned, so what's the point of checking uninitialized memory there?

You are right, SLUB debug has to be handled separately. If I'm understanding correctly, right now KMSAN poisons freed memory before SLUB debug wipes it, therefore the memory will count as initialized. On the other hand, newly allocated memory is still marked as uninitialized, so a lot of bugs still remain detectable.

Yes, but newly allocated slub objects will be poisoned as well.

Quoted 3 lines

TBH, I haven't tested KMSAN with SLUB debug good enough. Note that it's anyway a separate build that requires Clang, so right now it doesn't make much sense to combine KMSAN and SLUB debug together.

Can't you just build a debug kernel with SLUB debug enabled but dropping this patch? If there is an uninitialized memory here leading to data corruption, SLUB debug should be detected as well as this patch. If not, it needs to understand why.

Quoted 171 lines
Quoted 168 lines
Quoted 168 lines
Quoted 165 lines
Quoted 165 lines

To: Alexander Potapenko <[email protected]>
Cc: Andrew Morton <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]
---

Change-Id: I51103b7981d3aabed747d0c85cbdc85568665871

---
 mm/slub.c | 37 +++++++++++++++++++++++++++++++------
 1 file changed, 31 insertions(+), 6 deletions(-)
mm/slub.c +31 -6
index 3d63ae320d31..3d6d4c63446e 100644
@@ -21,6 +21,8 @@
 #include <linux/proc_fs.h>
 #include <linux/seq_file.h>
 #include <linux/kasan.h>
+#include <linux/kmsan.h>
+#include <linux/kmsan-checks.h> /* KMSAN_INIT_VALUE */
 #include <linux/cpu.h>
 #include <linux/cpuset.h>
 #include <linux/mempolicy.h>
@@ -285,17 +287,27 @@ static void prefetch_freepointer(const struct kmem_cache *s, void *object)
      prefetch(object + s->offset);
 }
 
+/*
+ * When running under KMSAN, get_freepointer_safe() may return an uninitialized
+ * pointer value in the case the current thread loses the race for the next
+ * memory chunk in the freelist. In that case this_cpu_cmpxchg_double() in
+ * slab_alloc_node() will fail, so the uninitialized value won't be used, but
+ * KMSAN will still check all arguments of cmpxchg because of imperfect
+ * handling of inline assembly.
+ * To work around this problem, use KMSAN_INIT_VALUE() to force initialize the
+ * return value of get_freepointer_safe().
+ */
 static inline void *get_freepointer_safe(struct kmem_cache *s, void *object)
 {
      unsigned long freepointer_addr;
      void *p;
 
      if (!debug_pagealloc_enabled())
-             return get_freepointer(s, object);
+             return KMSAN_INIT_VALUE(get_freepointer(s, object));
 
      freepointer_addr = (unsigned long)object + s->offset;
      probe_kernel_read(&p, (void **)freepointer_addr, sizeof(p));
-     return freelist_ptr(s, p, freepointer_addr);
+     return KMSAN_INIT_VALUE(freelist_ptr(s, p, freepointer_addr));
 }
 
 static inline void set_freepointer(struct kmem_cache *s, void *object, void *fp)
@@ -1390,6 +1402,7 @@ static inline void *kmalloc_large_node_hook(void *ptr, size_t size, gfp_t flags)
      ptr = kasan_kmalloc_large(ptr, size, flags);
      /* As ptr might get tagged, call kmemleak hook after KASAN. */
      kmemleak_alloc(ptr, size, 1, flags);
+     kmsan_kmalloc_large(ptr, size, flags);
      return ptr;
 }
 
@@ -1397,6 +1410,7 @@ static __always_inline void kfree_hook(void *x)
 {
      kmemleak_free(x);
      kasan_kfree_large(x, _RET_IP_);
+     kmsan_kfree_large(x);
 }
 
 static __always_inline bool slab_free_hook(struct kmem_cache *s, void *x)
@@ -1453,6 +1467,12 @@ static inline bool slab_free_freelist_hook(struct kmem_cache *s,
              } while (object != old_tail);
      }
 
+     do {
+             object = next;
+             next = get_freepointer(s, object);
+             kmsan_slab_free(s, object);
+     } while (object != old_tail);
+
 /*
  * Compiler cannot detect this function can be removed if slab_free_hook()
  * evaluates to nothing.  Thus, catch all relevant config debug options here.
@@ -2769,6 +2789,7 @@ static __always_inline void *slab_alloc_node(struct kmem_cache *s,
      if (unlikely(slab_want_init_on_alloc(gfpflags, s)) && object)
              memset(object, 0, s->object_size);
 
+     kmsan_slab_alloc(s, object, gfpflags);
      slab_post_alloc_hook(s, gfpflags, 1, &object);
 
      return object;
@@ -2797,6 +2818,7 @@ void *kmem_cache_alloc_trace(struct kmem_cache *s, gfp_t gfpflags, size_t size)
      void *ret = slab_alloc(s, gfpflags, _RET_IP_);
      trace_kmalloc(_RET_IP_, ret, size, s->size, gfpflags);
      ret = kasan_kmalloc(s, ret, size, gfpflags);
+
      return ret;
 }
 EXPORT_SYMBOL(kmem_cache_alloc_trace);
@@ -2809,7 +2831,6 @@ void *kmem_cache_alloc_node(struct kmem_cache *s, gfp_t gfpflags, int node)
 
      trace_kmem_cache_alloc_node(_RET_IP_, ret,
                                  s->object_size, s->size, gfpflags, node);
-
      return ret;
 }
 EXPORT_SYMBOL(kmem_cache_alloc_node);
@@ -2825,6 +2846,7 @@ void *kmem_cache_alloc_node_trace(struct kmem_cache *s,
                         size, s->size, gfpflags, node);
 
      ret = kasan_kmalloc(s, ret, size, gfpflags);
+
      return ret;
 }
 EXPORT_SYMBOL(kmem_cache_alloc_node_trace);
@@ -3150,7 +3172,7 @@ int kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t size,
                        void **p)
 {
      struct kmem_cache_cpu *c;
-     int i;
+     int i, j;
 
      /* memcg and kmem_cache debug support */
      s = slab_pre_alloc_hook(s, flags);
@@ -3188,11 +3210,11 @@ int kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t size,
 
      /* Clear memory outside IRQ disabled fastpath loop */
      if (unlikely(slab_want_init_on_alloc(flags, s))) {
-             int j;
-
              for (j = 0; j < i; j++)
                      memset(p[j], 0, s->object_size);
      }
+     for (j = 0; j < i; j++)
+             kmsan_slab_alloc(s, p[j], flags);
 
      /* memcg and kmem_cache debug support */
      slab_post_alloc_hook(s, flags, size, p);
@@ -3793,6 +3815,7 @@ static int __init setup_slub_min_objects(char *str)
 
 __setup("slub_min_objects=", setup_slub_min_objects);
 
+__no_sanitize_memory
 void *__kmalloc(size_t size, gfp_t flags)
 {
      struct kmem_cache *s;
@@ -5698,6 +5721,7 @@ static char *create_unique_id(struct kmem_cache *s)
      p += sprintf(p, "%07u", s->size);
 
      BUG_ON(p > name + ID_STR_LENGTH - 1);
+     kmsan_unpoison_shadow(name, p - name);
      return name;
 }
 
@@ -5847,6 +5871,7 @@ static int sysfs_slab_alias(struct kmem_cache *s, const char *name)
      al->name = name;
      al->next = alias_list;
      alias_list = al;
+     kmsan_unpoison_shadow(al, sizeof(struct saved_alias));
      return 0;
 }
 
author maintainer
#32 lore raw

Re: [PATCH RFC v1 18/26] kmsan: mm: call KMSAN hooks from SLUB code

Quoted 29 lines · Qian Cai
Quoted 27 lines · Alexander Potapenko
Quoted 25 lines · Qian Cai
Quoted 6 lines · Alexander Potapenko
Quoted 5 lines · Qian Cai
Quoted 3 lines · On Fri, 2019-10-18 at 11:42 +0200, [email protected] wrote:

In order to report uninitialized memory coming from heap allocations KMSAN has to poison them unless they're created with __GFP_ZERO.

Quoted 7 lines
Quoted 5 lines
Quoted 2 lines

It's handy that we need KMSAN hooks in the places where init_on_alloc/init_on_free initialization is performed.

Well, there is SLUB debug which has red zoning and poisoning checks. What's value of this patch?

Sorry, are you talking about the whole patchset or just this patch?

Just this patch.

Quoted 4 lines

Note that SLUB debug is unable to detect uninitialized values with bit-to-bit precision, neither have I heard of anyone using it for detecting uses of uninitialized memory in the kernel at all. The purpose of SLUB debug is to detect corruptions of freed memory.

The point is if developers have SLUB debug enabled, all the free objects will be poisoned, so what's the point of checking uninitialized memory there?

Quoted 7 lines
Quoted 5 lines

You are right, SLUB debug has to be handled separately. If I'm understanding correctly, right now KMSAN poisons freed memory before SLUB debug wipes it, therefore the memory will count as initialized. On the other hand, newly allocated memory is still marked as uninitialized, so a lot of bugs still remain detectable.

Yes, but newly allocated slub objects will be poisoned as well.

As far as I can tell, KMSAN hook marking newly allocated objects as uninitialized is called after slub poisoning.
Therefore these allocations will be treated by KMSAN as uninitialized.

Quoted 8 lines
Quoted 3 lines

TBH, I haven't tested KMSAN with SLUB debug good enough. Note that it's anyway a separate build that requires Clang, so right now it doesn't make much sense to combine KMSAN and SLUB debug together.

Can't you just build a debug kernel with SLUB debug enabled but dropping this patch? If there is an uninitialized memory here leading to data corruption, SLUB debug should be detected as well as this patch. If not, it needs to understand why.

Sorry, there might be some misunderstanding here.
KMSAN keeps the state of heap objects separately by keeping exactly the same amount of initialized/uninitialized bits for every allocation.
A call to kmsan_slab_alloc()/kmsan_slab_free() will mark an allocation as uninitialized for KMSAN. Not doing so will result in false reports. A call to memset(object, POISON_FREE, object_size) performed by SLAB debug will actually mark this allocation as initialized from KMSAN point of view, because we're memsetting a range with initialized data. Note that use of uninitialized data doesn't necessarily lead to immediate data corruption, so there might be nothing to detect for SLUB debug.

Quoted 172 lines
Quoted 172 lines
Quoted 169 lines
Quoted 169 lines
Quoted 166 lines
Quoted 166 lines

To: Alexander Potapenko <[email protected]>
Cc: Andrew Morton <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]
---

Change-Id: I51103b7981d3aabed747d0c85cbdc85568665871

---
 mm/slub.c | 37 +++++++++++++++++++++++++++++++------
 1 file changed, 31 insertions(+), 6 deletions(-)
mm/slub.c +31 -6
index 3d63ae320d31..3d6d4c63446e 100644
@@ -21,6 +21,8 @@
 #include <linux/proc_fs.h>
 #include <linux/seq_file.h>
 #include <linux/kasan.h>
+#include <linux/kmsan.h>
+#include <linux/kmsan-checks.h> /* KMSAN_INIT_VALUE */
 #include <linux/cpu.h>
 #include <linux/cpuset.h>
 #include <linux/mempolicy.h>
@@ -285,17 +287,27 @@ static void prefetch_freepointer(const struct kmem_cache *s, void *object)
      prefetch(object + s->offset);
 }
 
+/*
+ * When running under KMSAN, get_freepointer_safe() may return an uninitialized
+ * pointer value in the case the current thread loses the race for the next
+ * memory chunk in the freelist. In that case this_cpu_cmpxchg_double() in
+ * slab_alloc_node() will fail, so the uninitialized value won't be used, but
+ * KMSAN will still check all arguments of cmpxchg because of imperfect
+ * handling of inline assembly.
+ * To work around this problem, use KMSAN_INIT_VALUE() to force initialize the
+ * return value of get_freepointer_safe().
+ */
 static inline void *get_freepointer_safe(struct kmem_cache *s, void *object)
 {
      unsigned long freepointer_addr;
      void *p;
 
      if (!debug_pagealloc_enabled())
-             return get_freepointer(s, object);
+             return KMSAN_INIT_VALUE(get_freepointer(s, object));
 
      freepointer_addr = (unsigned long)object + s->offset;
      probe_kernel_read(&p, (void **)freepointer_addr, sizeof(p));
-     return freelist_ptr(s, p, freepointer_addr);
+     return KMSAN_INIT_VALUE(freelist_ptr(s, p, freepointer_addr));
 }
 
 static inline void set_freepointer(struct kmem_cache *s, void *object, void *fp)
@@ -1390,6 +1402,7 @@ static inline void *kmalloc_large_node_hook(void *ptr, size_t size, gfp_t flags)
      ptr = kasan_kmalloc_large(ptr, size, flags);
      /* As ptr might get tagged, call kmemleak hook after KASAN. */
      kmemleak_alloc(ptr, size, 1, flags);
+     kmsan_kmalloc_large(ptr, size, flags);
      return ptr;
 }
 
@@ -1397,6 +1410,7 @@ static __always_inline void kfree_hook(void *x)
 {
      kmemleak_free(x);
      kasan_kfree_large(x, _RET_IP_);
+     kmsan_kfree_large(x);
 }
 
 static __always_inline bool slab_free_hook(struct kmem_cache *s, void *x)
@@ -1453,6 +1467,12 @@ static inline bool slab_free_freelist_hook(struct kmem_cache *s,
              } while (object != old_tail);
      }
 
+     do {
+             object = next;
+             next = get_freepointer(s, object);
+             kmsan_slab_free(s, object);
+     } while (object != old_tail);
+
 /*
  * Compiler cannot detect this function can be removed if slab_free_hook()
  * evaluates to nothing.  Thus, catch all relevant config debug options here.
@@ -2769,6 +2789,7 @@ static __always_inline void *slab_alloc_node(struct kmem_cache *s,
      if (unlikely(slab_want_init_on_alloc(gfpflags, s)) && object)
              memset(object, 0, s->object_size);
 
+     kmsan_slab_alloc(s, object, gfpflags);
      slab_post_alloc_hook(s, gfpflags, 1, &object);
 
      return object;
@@ -2797,6 +2818,7 @@ void *kmem_cache_alloc_trace(struct kmem_cache *s, gfp_t gfpflags, size_t size)
      void *ret = slab_alloc(s, gfpflags, _RET_IP_);
      trace_kmalloc(_RET_IP_, ret, size, s->size, gfpflags);
      ret = kasan_kmalloc(s, ret, size, gfpflags);
+
      return ret;
 }
 EXPORT_SYMBOL(kmem_cache_alloc_trace);
@@ -2809,7 +2831,6 @@ void *kmem_cache_alloc_node(struct kmem_cache *s, gfp_t gfpflags, int node)
 
      trace_kmem_cache_alloc_node(_RET_IP_, ret,
                                  s->object_size, s->size, gfpflags, node);
-
      return ret;
 }
 EXPORT_SYMBOL(kmem_cache_alloc_node);
@@ -2825,6 +2846,7 @@ void *kmem_cache_alloc_node_trace(struct kmem_cache *s,
                         size, s->size, gfpflags, node);
 
      ret = kasan_kmalloc(s, ret, size, gfpflags);
+
      return ret;
 }
 EXPORT_SYMBOL(kmem_cache_alloc_node_trace);
@@ -3150,7 +3172,7 @@ int kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t size,
                        void **p)
 {
      struct kmem_cache_cpu *c;
-     int i;
+     int i, j;
 
      /* memcg and kmem_cache debug support */
      s = slab_pre_alloc_hook(s, flags);
@@ -3188,11 +3210,11 @@ int kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t size,
 
      /* Clear memory outside IRQ disabled fastpath loop */
      if (unlikely(slab_want_init_on_alloc(flags, s))) {
-             int j;
-
              for (j = 0; j < i; j++)
                      memset(p[j], 0, s->object_size);
      }
+     for (j = 0; j < i; j++)
+             kmsan_slab_alloc(s, p[j], flags);
 
      /* memcg and kmem_cache debug support */
      slab_post_alloc_hook(s, flags, size, p);
@@ -3793,6 +3815,7 @@ static int __init setup_slub_min_objects(char *str)
 
 __setup("slub_min_objects=", setup_slub_min_objects);
 
+__no_sanitize_memory
 void *__kmalloc(size_t size, gfp_t flags)
 {
      struct kmem_cache *s;
@@ -5698,6 +5721,7 @@ static char *create_unique_id(struct kmem_cache *s)
      p += sprintf(p, "%07u", s->size);
 
      BUG_ON(p > name + ID_STR_LENGTH - 1);
+     kmsan_unpoison_shadow(name, p - name);
      return name;
 }
 
@@ -5847,6 +5871,7 @@ static int sysfs_slab_alias(struct kmem_cache *s, const char *name)
      al->name = name;
      al->next = alias_list;
      alias_list = al;
+     kmsan_unpoison_shadow(al, sizeof(struct saved_alias));
      return 0;
 }
 
Signature · 10 lines
Alexander Potapenko
Software Engineer

Google Germany GmbH
Erika-Mann-Straße, 33
80636 München

Geschäftsführer: Paul Manicle, Halimah DeLaine Prado
Registergericht und -nummer: Hamburg, HRB 86891
Sitz der Gesellschaft: Hamburg
#33 lore raw

Re: [PATCH RFC v1 18/26] kmsan: mm: call KMSAN hooks from SLUB code

Quoted 50 lines · Alexander Potapenko
Quoted 29 lines · Qian Cai
Quoted 27 lines · Alexander Potapenko
Quoted 25 lines · Qian Cai
Quoted 6 lines · Alexander Potapenko
Quoted 5 lines · Qian Cai
Quoted 3 lines · On Fri, 2019-10-18 at 11:42 +0200, [email protected] wrote:

In order to report uninitialized memory coming from heap allocations KMSAN has to poison them unless they're created with __GFP_ZERO.

Quoted 7 lines
Quoted 5 lines
Quoted 2 lines

It's handy that we need KMSAN hooks in the places where init_on_alloc/init_on_free initialization is performed.

Well, there is SLUB debug which has red zoning and poisoning checks. What's value of this patch?

Sorry, are you talking about the whole patchset or just this patch?

Just this patch.

Quoted 4 lines

Note that SLUB debug is unable to detect uninitialized values with bit-to-bit precision, neither have I heard of anyone using it for detecting uses of uninitialized memory in the kernel at all. The purpose of SLUB debug is to detect corruptions of freed memory.

The point is if developers have SLUB debug enabled, all the free objects will be poisoned, so what's the point of checking uninitialized memory there?

Quoted 7 lines
Quoted 5 lines

You are right, SLUB debug has to be handled separately. If I'm understanding correctly, right now KMSAN poisons freed memory before SLUB debug wipes it, therefore the memory will count as initialized. On the other hand, newly allocated memory is still marked as uninitialized, so a lot of bugs still remain detectable.

Yes, but newly allocated slub objects will be poisoned as well.

As far as I can tell, KMSAN hook marking newly allocated objects as uninitialized is called after slub poisoning.
Therefore these allocations will be treated by KMSAN as uninitialized.

Quoted 8 lines
Quoted 3 lines

TBH, I haven't tested KMSAN with SLUB debug good enough. Note that it's anyway a separate build that requires Clang, so right now it doesn't make much sense to combine KMSAN and SLUB debug together.

Can't you just build a debug kernel with SLUB debug enabled but dropping this patch? If there is an uninitialized memory here leading to data corruption, SLUB debug should be detected as well as this patch. If not, it needs to understand why.

Quoted 12 lines

Sorry, there might be some misunderstanding here.
KMSAN keeps the state of heap objects separately by keeping exactly the same amount of initialized/uninitialized bits for every allocation.
A call to kmsan_slab_alloc()/kmsan_slab_free() will mark an allocation as uninitialized for KMSAN. Not doing so will result in false reports. A call to memset(object, POISON_FREE, object_size) performed by SLAB debug will actually mark this allocation as initialized from KMSAN point of view, because we're memsetting a range with initialized data. Note that use of uninitialized data doesn't necessarily lead to immediate data corruption, so there might be nothing to detect for SLUB debug.

Well, SLUB debug would mark any access of uninitialized memory as data corruption with the help of poisoning and red zoning.

It is true that KMSAN might be doing a bit more than SLUB debug here, but the question is if it is worth the maintenance burden? Do you have any existing bugs to show that KMSAN would find by this patch but SLUB debug can't?

Quoted 175 lines
Quoted 172 lines
Quoted 172 lines
Quoted 169 lines
Quoted 169 lines
Quoted 166 lines
Quoted 166 lines

To: Alexander Potapenko <[email protected]>
Cc: Andrew Morton <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]
---

Change-Id: I51103b7981d3aabed747d0c85cbdc85568665871

---
 mm/slub.c | 37 +++++++++++++++++++++++++++++++------
 1 file changed, 31 insertions(+), 6 deletions(-)
mm/slub.c +31 -6
index 3d63ae320d31..3d6d4c63446e 100644
@@ -21,6 +21,8 @@
 #include <linux/proc_fs.h>
 #include <linux/seq_file.h>
 #include <linux/kasan.h>
+#include <linux/kmsan.h>
+#include <linux/kmsan-checks.h> /* KMSAN_INIT_VALUE */
 #include <linux/cpu.h>
 #include <linux/cpuset.h>
 #include <linux/mempolicy.h>
@@ -285,17 +287,27 @@ static void prefetch_freepointer(const struct kmem_cache *s, void *object)
      prefetch(object + s->offset);
 }
 
+/*
+ * When running under KMSAN, get_freepointer_safe() may return an uninitialized
+ * pointer value in the case the current thread loses the race for the next
+ * memory chunk in the freelist. In that case this_cpu_cmpxchg_double() in
+ * slab_alloc_node() will fail, so the uninitialized value won't be used, but
+ * KMSAN will still check all arguments of cmpxchg because of imperfect
+ * handling of inline assembly.
+ * To work around this problem, use KMSAN_INIT_VALUE() to force initialize the
+ * return value of get_freepointer_safe().
+ */
 static inline void *get_freepointer_safe(struct kmem_cache *s, void *object)
 {
      unsigned long freepointer_addr;
      void *p;
 
      if (!debug_pagealloc_enabled())
-             return get_freepointer(s, object);
+             return KMSAN_INIT_VALUE(get_freepointer(s, object));
 
      freepointer_addr = (unsigned long)object + s->offset;
      probe_kernel_read(&p, (void **)freepointer_addr, sizeof(p));
-     return freelist_ptr(s, p, freepointer_addr);
+     return KMSAN_INIT_VALUE(freelist_ptr(s, p, freepointer_addr));
 }
 
 static inline void set_freepointer(struct kmem_cache *s, void *object, void *fp)
@@ -1390,6 +1402,7 @@ static inline void *kmalloc_large_node_hook(void *ptr, size_t size, gfp_t flags)
      ptr = kasan_kmalloc_large(ptr, size, flags);
      /* As ptr might get tagged, call kmemleak hook after KASAN. */
      kmemleak_alloc(ptr, size, 1, flags);
+     kmsan_kmalloc_large(ptr, size, flags);
      return ptr;
 }
 
@@ -1397,6 +1410,7 @@ static __always_inline void kfree_hook(void *x)
 {
      kmemleak_free(x);
      kasan_kfree_large(x, _RET_IP_);
+     kmsan_kfree_large(x);
 }
 
 static __always_inline bool slab_free_hook(struct kmem_cache *s, void *x)
@@ -1453,6 +1467,12 @@ static inline bool slab_free_freelist_hook(struct kmem_cache *s,
              } while (object != old_tail);
      }
 
+     do {
+             object = next;
+             next = get_freepointer(s, object);
+             kmsan_slab_free(s, object);
+     } while (object != old_tail);
+
 /*
  * Compiler cannot detect this function can be removed if slab_free_hook()
  * evaluates to nothing.  Thus, catch all relevant config debug options here.
@@ -2769,6 +2789,7 @@ static __always_inline void *slab_alloc_node(struct kmem_cache *s,
      if (unlikely(slab_want_init_on_alloc(gfpflags, s)) && object)
              memset(object, 0, s->object_size);
 
+     kmsan_slab_alloc(s, object, gfpflags);
      slab_post_alloc_hook(s, gfpflags, 1, &object);
 
      return object;
@@ -2797,6 +2818,7 @@ void *kmem_cache_alloc_trace(struct kmem_cache *s, gfp_t gfpflags, size_t size)
      void *ret = slab_alloc(s, gfpflags, _RET_IP_);
      trace_kmalloc(_RET_IP_, ret, size, s->size, gfpflags);
      ret = kasan_kmalloc(s, ret, size, gfpflags);
+
      return ret;
 }
 EXPORT_SYMBOL(kmem_cache_alloc_trace);
@@ -2809,7 +2831,6 @@ void *kmem_cache_alloc_node(struct kmem_cache *s, gfp_t gfpflags, int node)
 
      trace_kmem_cache_alloc_node(_RET_IP_, ret,
                                  s->object_size, s->size, gfpflags, node);
-
      return ret;
 }
 EXPORT_SYMBOL(kmem_cache_alloc_node);
@@ -2825,6 +2846,7 @@ void *kmem_cache_alloc_node_trace(struct kmem_cache *s,
                         size, s->size, gfpflags, node);
 
      ret = kasan_kmalloc(s, ret, size, gfpflags);
+
      return ret;
 }
 EXPORT_SYMBOL(kmem_cache_alloc_node_trace);
@@ -3150,7 +3172,7 @@ int kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t size,
                        void **p)
 {
      struct kmem_cache_cpu *c;
-     int i;
+     int i, j;
 
      /* memcg and kmem_cache debug support */
      s = slab_pre_alloc_hook(s, flags);
@@ -3188,11 +3210,11 @@ int kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t size,
 
      /* Clear memory outside IRQ disabled fastpath loop */
      if (unlikely(slab_want_init_on_alloc(flags, s))) {
-             int j;
-
              for (j = 0; j < i; j++)
                      memset(p[j], 0, s->object_size);
      }
+     for (j = 0; j < i; j++)
+             kmsan_slab_alloc(s, p[j], flags);
 
      /* memcg and kmem_cache debug support */
      slab_post_alloc_hook(s, flags, size, p);
@@ -3793,6 +3815,7 @@ static int __init setup_slub_min_objects(char *str)
 
 __setup("slub_min_objects=", setup_slub_min_objects);
 
+__no_sanitize_memory
 void *__kmalloc(size_t size, gfp_t flags)
 {
      struct kmem_cache *s;
@@ -5698,6 +5721,7 @@ static char *create_unique_id(struct kmem_cache *s)
      p += sprintf(p, "%07u", s->size);
 
      BUG_ON(p > name + ID_STR_LENGTH - 1);
+     kmsan_unpoison_shadow(name, p - name);
      return name;
 }
 
@@ -5847,6 +5871,7 @@ static int sysfs_slab_alias(struct kmem_cache *s, const char *name)
      al->name = name;
      al->next = alias_list;
      alias_list = al;
+     kmsan_unpoison_shadow(al, sizeof(struct saved_alias));
      return 0;
 }
 
author maintainer
#34 lore raw

Re: [PATCH RFC v1 18/26] kmsan: mm: call KMSAN hooks from SLUB code

Quoted 65 lines · Qian Cai
Quoted 50 lines · Alexander Potapenko
Quoted 29 lines · Qian Cai
Quoted 27 lines · Alexander Potapenko
Quoted 25 lines · Qian Cai
Quoted 6 lines · Alexander Potapenko
Quoted 5 lines · Qian Cai
Quoted 3 lines · On Fri, 2019-10-18 at 11:42 +0200, [email protected] wrote:

In order to report uninitialized memory coming from heap allocations KMSAN has to poison them unless they're created with __GFP_ZERO.

Quoted 7 lines
Quoted 5 lines
Quoted 2 lines

It's handy that we need KMSAN hooks in the places where init_on_alloc/init_on_free initialization is performed.

Well, there is SLUB debug which has red zoning and poisoning checks. What's value of this patch?

Sorry, are you talking about the whole patchset or just this patch?

Just this patch.

Quoted 4 lines

Note that SLUB debug is unable to detect uninitialized values with bit-to-bit precision, neither have I heard of anyone using it for detecting uses of uninitialized memory in the kernel at all. The purpose of SLUB debug is to detect corruptions of freed memory.

The point is if developers have SLUB debug enabled, all the free objects will be poisoned, so what's the point of checking uninitialized memory there?

Quoted 7 lines
Quoted 5 lines

You are right, SLUB debug has to be handled separately. If I'm understanding correctly, right now KMSAN poisons freed memory before SLUB debug wipes it, therefore the memory will count as initialized. On the other hand, newly allocated memory is still marked as uninitialized, so a lot of bugs still remain detectable.

Yes, but newly allocated slub objects will be poisoned as well.

As far as I can tell, KMSAN hook marking newly allocated objects as uninitialized is called after slub poisoning.
Therefore these allocations will be treated by KMSAN as uninitialized.

Quoted 8 lines
Quoted 3 lines

TBH, I haven't tested KMSAN with SLUB debug good enough. Note that it's anyway a separate build that requires Clang, so right now it doesn't make much sense to combine KMSAN and SLUB debug together.

Can't you just build a debug kernel with SLUB debug enabled but dropping this patch? If there is an uninitialized memory here leading to data corruption, SLUB debug should be detected as well as this patch. If not, it needs to understand why.

Quoted 12 lines

Sorry, there might be some misunderstanding here.
KMSAN keeps the state of heap objects separately by keeping exactly the same amount of initialized/uninitialized bits for every allocation.
A call to kmsan_slab_alloc()/kmsan_slab_free() will mark an allocation as uninitialized for KMSAN. Not doing so will result in false reports. A call to memset(object, POISON_FREE, object_size) performed by SLAB debug will actually mark this allocation as initialized from KMSAN point of view, because we're memsetting a range with initialized data. Note that use of uninitialized data doesn't necessarily lead to immediate data corruption, so there might be nothing to detect for SLUB debug.

Quoted 6 lines

Well, SLUB debug would mark any access of uninitialized memory as data corruption with the help of poisoning and red zoning.

It is true that KMSAN might be doing a bit more than SLUB debug here, but the question is if it is worth the maintenance burden? Do you have any existing bugs to show that KMSAN would find by this patch but SLUB debug can't?

Consider the following case:

  int *a = kmalloc(sizeof(int), GFP_KERNEL);
  if (*a) {
    // do something, e.g. become root
  }

IIUC SLUB poisoning will result in the branch being taken, but as long as there are no invalid accesses the kernel won't crash and no errors will be reported.
KMSAN however will report an error, because the contents of the buffer pointed to by |a| are poisoned by kmsan_slab_alloc().
The compiler instrumentation will retrieve the shadow value for *a, compare it to zero and report a use of uninitialized memory.

Another example would be copying the contents of newly allocated buffer to the userspace.

Quoted 175 lines
Quoted 175 lines
Quoted 172 lines
Quoted 172 lines
Quoted 169 lines
Quoted 169 lines
Quoted 166 lines
Quoted 166 lines

To: Alexander Potapenko <[email protected]>
Cc: Andrew Morton <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]
---

Change-Id: I51103b7981d3aabed747d0c85cbdc85568665871

---
 mm/slub.c | 37 +++++++++++++++++++++++++++++++------
 1 file changed, 31 insertions(+), 6 deletions(-)
mm/slub.c +31 -6
index 3d63ae320d31..3d6d4c63446e 100644
@@ -21,6 +21,8 @@
 #include <linux/proc_fs.h>
 #include <linux/seq_file.h>
 #include <linux/kasan.h>
+#include <linux/kmsan.h>
+#include <linux/kmsan-checks.h> /* KMSAN_INIT_VALUE */
 #include <linux/cpu.h>
 #include <linux/cpuset.h>
 #include <linux/mempolicy.h>
@@ -285,17 +287,27 @@ static void prefetch_freepointer(const struct kmem_cache *s, void *object)
      prefetch(object + s->offset);
 }
 
+/*
+ * When running under KMSAN, get_freepointer_safe() may return an uninitialized
+ * pointer value in the case the current thread loses the race for the next
+ * memory chunk in the freelist. In that case this_cpu_cmpxchg_double() in
+ * slab_alloc_node() will fail, so the uninitialized value won't be used, but
+ * KMSAN will still check all arguments of cmpxchg because of imperfect
+ * handling of inline assembly.
+ * To work around this problem, use KMSAN_INIT_VALUE() to force initialize the
+ * return value of get_freepointer_safe().
+ */
 static inline void *get_freepointer_safe(struct kmem_cache *s, void *object)
 {
      unsigned long freepointer_addr;
      void *p;
 
      if (!debug_pagealloc_enabled())
-             return get_freepointer(s, object);
+             return KMSAN_INIT_VALUE(get_freepointer(s, object));
 
      freepointer_addr = (unsigned long)object + s->offset;
      probe_kernel_read(&p, (void **)freepointer_addr, sizeof(p));
-     return freelist_ptr(s, p, freepointer_addr);
+     return KMSAN_INIT_VALUE(freelist_ptr(s, p, freepointer_addr));
 }
 
 static inline void set_freepointer(struct kmem_cache *s, void *object, void *fp)
@@ -1390,6 +1402,7 @@ static inline void *kmalloc_large_node_hook(void *ptr, size_t size, gfp_t flags)
      ptr = kasan_kmalloc_large(ptr, size, flags);
      /* As ptr might get tagged, call kmemleak hook after KASAN. */
      kmemleak_alloc(ptr, size, 1, flags);
+     kmsan_kmalloc_large(ptr, size, flags);
      return ptr;
 }
 
@@ -1397,6 +1410,7 @@ static __always_inline void kfree_hook(void *x)
 {
      kmemleak_free(x);
      kasan_kfree_large(x, _RET_IP_);
+     kmsan_kfree_large(x);
 }
 
 static __always_inline bool slab_free_hook(struct kmem_cache *s, void *x)
@@ -1453,6 +1467,12 @@ static inline bool slab_free_freelist_hook(struct kmem_cache *s,
              } while (object != old_tail);
      }
 
+     do {
+             object = next;
+             next = get_freepointer(s, object);
+             kmsan_slab_free(s, object);
+     } while (object != old_tail);
+
 /*
  * Compiler cannot detect this function can be removed if slab_free_hook()
  * evaluates to nothing.  Thus, catch all relevant config debug options here.
@@ -2769,6 +2789,7 @@ static __always_inline void *slab_alloc_node(struct kmem_cache *s,
      if (unlikely(slab_want_init_on_alloc(gfpflags, s)) && object)
              memset(object, 0, s->object_size);
 
+     kmsan_slab_alloc(s, object, gfpflags);
      slab_post_alloc_hook(s, gfpflags, 1, &object);
 
      return object;
@@ -2797,6 +2818,7 @@ void *kmem_cache_alloc_trace(struct kmem_cache *s, gfp_t gfpflags, size_t size)
      void *ret = slab_alloc(s, gfpflags, _RET_IP_);
      trace_kmalloc(_RET_IP_, ret, size, s->size, gfpflags);
      ret = kasan_kmalloc(s, ret, size, gfpflags);
+
      return ret;
 }
 EXPORT_SYMBOL(kmem_cache_alloc_trace);
@@ -2809,7 +2831,6 @@ void *kmem_cache_alloc_node(struct kmem_cache *s, gfp_t gfpflags, int node)
 
      trace_kmem_cache_alloc_node(_RET_IP_, ret,
                                  s->object_size, s->size, gfpflags, node);
-
      return ret;
 }
 EXPORT_SYMBOL(kmem_cache_alloc_node);
@@ -2825,6 +2846,7 @@ void *kmem_cache_alloc_node_trace(struct kmem_cache *s,
                         size, s->size, gfpflags, node);
 
      ret = kasan_kmalloc(s, ret, size, gfpflags);
+
      return ret;
 }
 EXPORT_SYMBOL(kmem_cache_alloc_node_trace);
@@ -3150,7 +3172,7 @@ int kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t size,
                        void **p)
 {
      struct kmem_cache_cpu *c;
-     int i;
+     int i, j;
 
      /* memcg and kmem_cache debug support */
      s = slab_pre_alloc_hook(s, flags);
@@ -3188,11 +3210,11 @@ int kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t size,
 
      /* Clear memory outside IRQ disabled fastpath loop */
      if (unlikely(slab_want_init_on_alloc(flags, s))) {
-             int j;
-
              for (j = 0; j < i; j++)
                      memset(p[j], 0, s->object_size);
      }
+     for (j = 0; j < i; j++)
+             kmsan_slab_alloc(s, p[j], flags);
 
      /* memcg and kmem_cache debug support */
      slab_post_alloc_hook(s, flags, size, p);
@@ -3793,6 +3815,7 @@ static int __init setup_slub_min_objects(char *str)
 
 __setup("slub_min_objects=", setup_slub_min_objects);
 
+__no_sanitize_memory
 void *__kmalloc(size_t size, gfp_t flags)
 {
      struct kmem_cache *s;
@@ -5698,6 +5721,7 @@ static char *create_unique_id(struct kmem_cache *s)
      p += sprintf(p, "%07u", s->size);
 
      BUG_ON(p > name + ID_STR_LENGTH - 1);
+     kmsan_unpoison_shadow(name, p - name);
      return name;
 }
 
@@ -5847,6 +5871,7 @@ static int sysfs_slab_alias(struct kmem_cache *s, const char *name)
      al->name = name;
      al->next = alias_list;
      alias_list = al;
+     kmsan_unpoison_shadow(al, sizeof(struct saved_alias));
      return 0;
 }
 
Signature · 10 lines
Alexander Potapenko
Software Engineer

Google Germany GmbH
Erika-Mann-Straße, 33
80636 München

Geschäftsführer: Paul Manicle, Halimah DeLaine Prado
Registergericht und -nummer: Hamburg, HRB 86891
Sitz der Gesellschaft: Hamburg
#35 lore raw

Re: [PATCH RFC v1 18/26] kmsan: mm: call KMSAN hooks from SLUB code

Quoted 88 lines · Alexander Potapenko
Quoted 65 lines · Qian Cai
Quoted 50 lines · Alexander Potapenko
Quoted 29 lines · Qian Cai
Quoted 27 lines · Alexander Potapenko
Quoted 25 lines · Qian Cai
Quoted 6 lines · Alexander Potapenko
Quoted 5 lines · Qian Cai
Quoted 3 lines · On Fri, 2019-10-18 at 11:42 +0200, [email protected] wrote:

In order to report uninitialized memory coming from heap allocations KMSAN has to poison them unless they're created with __GFP_ZERO.

Quoted 7 lines
Quoted 5 lines
Quoted 2 lines

It's handy that we need KMSAN hooks in the places where init_on_alloc/init_on_free initialization is performed.

Well, there is SLUB debug which has red zoning and poisoning checks. What's value of this patch?

Sorry, are you talking about the whole patchset or just this patch?

Just this patch.

Quoted 4 lines

Note that SLUB debug is unable to detect uninitialized values with bit-to-bit precision, neither have I heard of anyone using it for detecting uses of uninitialized memory in the kernel at all. The purpose of SLUB debug is to detect corruptions of freed memory.

The point is if developers have SLUB debug enabled, all the free objects will be poisoned, so what's the point of checking uninitialized memory there?

Quoted 7 lines
Quoted 5 lines

You are right, SLUB debug has to be handled separately. If I'm understanding correctly, right now KMSAN poisons freed memory before SLUB debug wipes it, therefore the memory will count as initialized. On the other hand, newly allocated memory is still marked as uninitialized, so a lot of bugs still remain detectable.

Yes, but newly allocated slub objects will be poisoned as well.

As far as I can tell, KMSAN hook marking newly allocated objects as uninitialized is called after slub poisoning.
Therefore these allocations will be treated by KMSAN as uninitialized.

Quoted 8 lines
Quoted 3 lines

TBH, I haven't tested KMSAN with SLUB debug good enough. Note that it's anyway a separate build that requires Clang, so right now it doesn't make much sense to combine KMSAN and SLUB debug together.

Can't you just build a debug kernel with SLUB debug enabled but dropping this patch? If there is an uninitialized memory here leading to data corruption, SLUB debug should be detected as well as this patch. If not, it needs to understand why.

Quoted 12 lines

Sorry, there might be some misunderstanding here.
KMSAN keeps the state of heap objects separately by keeping exactly the same amount of initialized/uninitialized bits for every allocation.
A call to kmsan_slab_alloc()/kmsan_slab_free() will mark an allocation as uninitialized for KMSAN. Not doing so will result in false reports. A call to memset(object, POISON_FREE, object_size) performed by SLAB debug will actually mark this allocation as initialized from KMSAN point of view, because we're memsetting a range with initialized data. Note that use of uninitialized data doesn't necessarily lead to immediate data corruption, so there might be nothing to detect for SLUB debug.

Quoted 6 lines

Well, SLUB debug would mark any access of uninitialized memory as data corruption with the help of poisoning and red zoning.

It is true that KMSAN might be doing a bit more than SLUB debug here, but the question is if it is worth the maintenance burden? Do you have any existing bugs to show that KMSAN would find by this patch but SLUB debug can't?

Consider the following case:

  int *a = kmalloc(sizeof(int), GFP_KERNEL);
  if (*a) {
    // do something, e.g. become root
  }

IIUC SLUB poisoning will result in the branch being taken, but as long as there are no invalid accesses the kernel won't crash and no errors will be reported.
KMSAN however will report an error, because the contents of the buffer pointed to by |a| are poisoned by kmsan_slab_alloc().
The compiler instrumentation will retrieve the shadow value for *a, compare it to zero and report a use of uninitialized memory.

Quoted 2 lines

Another example would be copying the contents of newly allocated buffer to the userspace.

Make sense. BTW, it might worth testing this patchset with this MM debug config based on linux-next to edge out some corner cases if you could,

https://raw.githubusercontent.com/cailca/linux-mm/master/x86.config

Quoted 179 lines
Quoted 176 lines
Quoted 176 lines
Quoted 173 lines
Quoted 173 lines
Quoted 170 lines
Quoted 170 lines
Quoted 167 lines
Quoted 167 lines

To: Alexander Potapenko <[email protected]>
Cc: Andrew Morton <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]
---

Change-Id: I51103b7981d3aabed747d0c85cbdc85568665871

---
 mm/slub.c | 37 +++++++++++++++++++++++++++++++------
 1 file changed, 31 insertions(+), 6 deletions(-)
mm/slub.c +31 -6
index 3d63ae320d31..3d6d4c63446e 100644
@@ -21,6 +21,8 @@
 #include <linux/proc_fs.h>
 #include <linux/seq_file.h>
 #include <linux/kasan.h>
+#include <linux/kmsan.h>
+#include <linux/kmsan-checks.h> /* KMSAN_INIT_VALUE */
 #include <linux/cpu.h>
 #include <linux/cpuset.h>
 #include <linux/mempolicy.h>
@@ -285,17 +287,27 @@ static void prefetch_freepointer(const struct kmem_cache *s, void *object)
      prefetch(object + s->offset);
 }
 
+/*
+ * When running under KMSAN, get_freepointer_safe() may return an uninitialized
+ * pointer value in the case the current thread loses the race for the next
+ * memory chunk in the freelist. In that case this_cpu_cmpxchg_double() in
+ * slab_alloc_node() will fail, so the uninitialized value won't be used, but
+ * KMSAN will still check all arguments of cmpxchg because of imperfect
+ * handling of inline assembly.
+ * To work around this problem, use KMSAN_INIT_VALUE() to force initialize the
+ * return value of get_freepointer_safe().
+ */
 static inline void *get_freepointer_safe(struct kmem_cache *s, void *object)
 {
      unsigned long freepointer_addr;
      void *p;
 
      if (!debug_pagealloc_enabled())
-             return get_freepointer(s, object);
+             return KMSAN_INIT_VALUE(get_freepointer(s, object));
 
      freepointer_addr = (unsigned long)object + s->offset;
      probe_kernel_read(&p, (void **)freepointer_addr, sizeof(p));
-     return freelist_ptr(s, p, freepointer_addr);
+     return KMSAN_INIT_VALUE(freelist_ptr(s, p, freepointer_addr));
 }
 
 static inline void set_freepointer(struct kmem_cache *s, void *object, void *fp)
@@ -1390,6 +1402,7 @@ static inline void *kmalloc_large_node_hook(void *ptr, size_t size, gfp_t flags)
      ptr = kasan_kmalloc_large(ptr, size, flags);
      /* As ptr might get tagged, call kmemleak hook after KASAN. */
      kmemleak_alloc(ptr, size, 1, flags);
+     kmsan_kmalloc_large(ptr, size, flags);
      return ptr;
 }
 
@@ -1397,6 +1410,7 @@ static __always_inline void kfree_hook(void *x)
 {
      kmemleak_free(x);
      kasan_kfree_large(x, _RET_IP_);
+     kmsan_kfree_large(x);
 }
 
 static __always_inline bool slab_free_hook(struct kmem_cache *s, void *x)
@@ -1453,6 +1467,12 @@ static inline bool slab_free_freelist_hook(struct kmem_cache *s,
              } while (object != old_tail);
      }
 
+     do {
+             object = next;
+             next = get_freepointer(s, object);
+             kmsan_slab_free(s, object);
+     } while (object != old_tail);
+
 /*
  * Compiler cannot detect this function can be removed if slab_free_hook()
  * evaluates to nothing.  Thus, catch all relevant config debug options here.
@@ -2769,6 +2789,7 @@ static __always_inline void *slab_alloc_node(struct kmem_cache *s,
      if (unlikely(slab_want_init_on_alloc(gfpflags, s)) && object)
              memset(object, 0, s->object_size);
 
+     kmsan_slab_alloc(s, object, gfpflags);
      slab_post_alloc_hook(s, gfpflags, 1, &object);
 
      return object;
@@ -2797,6 +2818,7 @@ void *kmem_cache_alloc_trace(struct kmem_cache *s, gfp_t gfpflags, size_t size)
      void *ret = slab_alloc(s, gfpflags, _RET_IP_);
      trace_kmalloc(_RET_IP_, ret, size, s->size, gfpflags);
      ret = kasan_kmalloc(s, ret, size, gfpflags);
+
      return ret;
 }
 EXPORT_SYMBOL(kmem_cache_alloc_trace);
@@ -2809,7 +2831,6 @@ void *kmem_cache_alloc_node(struct kmem_cache *s, gfp_t gfpflags, int node)
 
      trace_kmem_cache_alloc_node(_RET_IP_, ret,
                                  s->object_size, s->size, gfpflags, node);
-
      return ret;
 }
 EXPORT_SYMBOL(kmem_cache_alloc_node);
@@ -2825,6 +2846,7 @@ void *kmem_cache_alloc_node_trace(struct kmem_cache *s,
                         size, s->size, gfpflags, node);
 
      ret = kasan_kmalloc(s, ret, size, gfpflags);
+
      return ret;
 }
 EXPORT_SYMBOL(kmem_cache_alloc_node_trace);
@@ -3150,7 +3172,7 @@ int kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t size,
                        void **p)
 {
      struct kmem_cache_cpu *c;
-     int i;
+     int i, j;
 
      /* memcg and kmem_cache debug support */
      s = slab_pre_alloc_hook(s, flags);
@@ -3188,11 +3210,11 @@ int kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t size,
 
      /* Clear memory outside IRQ disabled fastpath loop */
      if (unlikely(slab_want_init_on_alloc(flags, s))) {
-             int j;
-
              for (j = 0; j < i; j++)
                      memset(p[j], 0, s->object_size);
      }
+     for (j = 0; j < i; j++)
+             kmsan_slab_alloc(s, p[j], flags);
 
      /* memcg and kmem_cache debug support */
      slab_post_alloc_hook(s, flags, size, p);
@@ -3793,6 +3815,7 @@ static int __init setup_slub_min_objects(char *str)
 
 __setup("slub_min_objects=", setup_slub_min_objects);
 
+__no_sanitize_memory
 void *__kmalloc(size_t size, gfp_t flags)
 {
      struct kmem_cache *s;
@@ -5698,6 +5721,7 @@ static char *create_unique_id(struct kmem_cache *s)
      p += sprintf(p, "%07u", s->size);
 
      BUG_ON(p > name + ID_STR_LENGTH - 1);
+     kmsan_unpoison_shadow(name, p - name);
      return name;
 }
 
@@ -5847,6 +5871,7 @@ static int sysfs_slab_alias(struct kmem_cache *s, const char *name)
      al->name = name;
      al->next = alias_list;
      alias_list = al;
+     kmsan_unpoison_shadow(al, sizeof(struct saved_alias));
      return 0;
 }
 
author maintainer
#36 lore raw

RFC PATCH 19/26 kmsan: call KMSAN hooks where needed

In reply to [email protected]

Insert KMSAN hooks that check for potential memory errors and/or make necessary bookkeeping changes:
- allocate/split/deallocate metadata pages in alloc_pages()/split_page()/free_page();
- clear page shadow and origins in clear_page(), copy_user_highpage();
- copy page metadata in copy_highpage(), wp_page_copy();
- handle vmap()/vunmap()/iounmap();
- handle task creation and deletion;
- handle vprintk();
- call softirq entry/exit hooks in kernel/softirq.c;
- check/initialize memory sent to/read from USB, I2C, and network

To: Alexander Potapenko <[email protected]>
Cc: Andrew Morton <[email protected]>
Cc: Greg Kroah-Hartman <[email protected]>
Cc: Eric Dumazet <[email protected]>
Cc: Wolfram Sang <[email protected]>
Cc: Petr Mladek <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]
---

Change-Id: I1250a928d9263bf71fdaa067a070bdee686ef47b

---
 arch/x86/include/asm/page_64.h | 13 +++++++++++++
 arch/x86/mm/ioremap.c          |  3 +++
 drivers/i2c/i2c-core-base.c    |  2 ++
 drivers/usb/core/urb.c         |  2 ++
 include/linux/highmem.h        |  4 ++++
 kernel/exit.c                  |  2 ++
 kernel/fork.c                  |  2 ++
 kernel/kthread.c               |  2 ++
 kernel/printk/printk.c         |  8 +++++++-
 kernel/softirq.c               |  5 +++++
 lib/ioremap.c                  |  5 +++++
 mm/compaction.c                |  9 +++++++++
 mm/gup.c                       |  3 +++
 mm/memory.c                    |  2 ++
 mm/page_alloc.c                | 16 ++++++++++++++++
 mm/vmalloc.c                   | 23 +++++++++++++++++++++--
 net/sched/sch_generic.c        |  2 ++
 17 files changed, 100 insertions(+), 3 deletions(-)
arch/x86/include/asm/page_64.h +13 -0
index 939b1cff4a7b..0ba43d93414f 100644
@@ -44,14 +44,27 @@ void clear_page_orig(void *page);
 void clear_page_rep(void *page);
 void clear_page_erms(void *page);
 
+/* This is an assembly header, avoid including too much of kmsan.h */
+#ifdef CONFIG_KMSAN
+void kmsan_clear_page(void *page_addr);
+#endif
+__no_sanitize_memory
 static inline void clear_page(void *page)
 {
+#ifdef CONFIG_KMSAN
+	/* alternative_call_2() changes |page|. */
+	void *page_copy = page;
+#endif
 	alternative_call_2(clear_page_orig,
 			   clear_page_rep, X86_FEATURE_REP_GOOD,
 			   clear_page_erms, X86_FEATURE_ERMS,
 			   "=D" (page),
 			   "0" (page)
 			   : "cc", "memory", "rax", "rcx");
+#ifdef CONFIG_KMSAN
+	/* Clear KMSAN shadow for the pages that have it. */
+	kmsan_clear_page(page_copy);
+#endif
 }
 
 void copy_page(void *to, void *from);
arch/x86/mm/ioremap.c +3 -0
index a39dcdb5ae34..fdb2abc11a82 100644
@@ -7,6 +7,7 @@
  * (C) Copyright 1995 1996 Linus Torvalds
  */
 
+#include <linux/kmsan.h>
 #include <linux/memblock.h>
 #include <linux/init.h>
 #include <linux/io.h>
@@ -451,6 +452,8 @@ void iounmap(volatile void __iomem *addr)
 		return;
 	}
 
+	kmsan_iounmap_page_range((unsigned long)addr,
+		(unsigned long)addr + get_vm_area_size(p));
 	free_memtype(p->phys_addr, p->phys_addr + get_vm_area_size(p));
 
 	/* Finally remove it */
drivers/i2c/i2c-core-base.c +2 -0
index 5f6a4985f2bc..9685d3399c79 100644
@@ -27,6 +27,7 @@
 #include <linux/irqflags.h>
 #include <linux/jump_label.h>
 #include <linux/kernel.h>
+#include <linux/kmsan.h>
 #include <linux/module.h>
 #include <linux/mutex.h>
 #include <linux/of_device.h>
@@ -1975,6 +1976,7 @@ int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
 				trace_i2c_write(adap, &msgs[i], i);
 	}
 
+	kmsan_handle_i2c_transfer(msgs, num);
 	/* Retry automatically on arbitration loss */
 	orig_jiffies = jiffies;
 	for (ret = 0, try = 0; try <= adap->retries; try++) {
drivers/usb/core/urb.c +2 -0
index 0eab79f82ce4..5bdb54d71c2e 100644
@@ -8,6 +8,7 @@
 #include <linux/bitops.h>
 #include <linux/slab.h>
 #include <linux/log2.h>
+#include <linux/kmsan-checks.h>
 #include <linux/usb.h>
 #include <linux/wait.h>
 #include <linux/usb/hcd.h>
@@ -401,6 +402,7 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
 			URB_SETUP_MAP_SINGLE | URB_SETUP_MAP_LOCAL |
 			URB_DMA_SG_COMBINED);
 	urb->transfer_flags |= (is_out ? URB_DIR_OUT : URB_DIR_IN);
+	kmsan_handle_urb(urb, is_out);
 
 	if (xfertype != USB_ENDPOINT_XFER_CONTROL &&
 			dev->state < USB_STATE_CONFIGURED)
include/linux/highmem.h +4 -0
index ea5cdbd8c2c3..623b56f48685 100644
@@ -5,6 +5,7 @@
 #include <linux/fs.h>
 #include <linux/kernel.h>
 #include <linux/bug.h>
+#include <linux/kmsan.h>
 #include <linux/mm.h>
 #include <linux/uaccess.h>
 #include <linux/hardirq.h>
@@ -255,6 +256,8 @@ static inline void copy_user_highpage(struct page *to, struct page *from,
 	vfrom = kmap_atomic(from);
 	vto = kmap_atomic(to);
 	copy_user_page(vto, vfrom, vaddr, to);
+	/* User pages don't have shadow, just clear the destination. */
+	kmsan_clear_page(page_address(to));
 	kunmap_atomic(vto);
 	kunmap_atomic(vfrom);
 }
@@ -270,6 +273,7 @@ static inline void copy_highpage(struct page *to, struct page *from)
 	vfrom = kmap_atomic(from);
 	vto = kmap_atomic(to);
 	copy_page(vto, vfrom);
+	kmsan_copy_page_meta(to, from);
 	kunmap_atomic(vto);
 	kunmap_atomic(vfrom);
 }
kernel/exit.c +2 -0
index a46a50d67002..9e3ce929110b 100644
@@ -60,6 +60,7 @@
 #include <linux/writeback.h>
 #include <linux/shm.h>
 #include <linux/kcov.h>
+#include <linux/kmsan.h>
 #include <linux/random.h>
 #include <linux/rcuwait.h>
 #include <linux/compat.h>
@@ -719,6 +720,7 @@ void __noreturn do_exit(long code)
 
 	profile_task_exit(tsk);
 	kcov_task_exit(tsk);
+	kmsan_task_exit(tsk);
 
 	WARN_ON(blk_needs_flush_plug(tsk));
 
kernel/fork.c +2 -0
index bcdf53125210..0f08952a42dc 100644
@@ -37,6 +37,7 @@
 #include <linux/fdtable.h>
 #include <linux/iocontext.h>
 #include <linux/key.h>
+#include <linux/kmsan.h>
 #include <linux/binfmts.h>
 #include <linux/mman.h>
 #include <linux/mmu_notifier.h>
@@ -931,6 +932,7 @@ static struct task_struct *dup_task_struct(struct task_struct *orig, int node)
 	account_kernel_stack(tsk, 1);
 
 	kcov_task_init(tsk);
+	kmsan_task_create(tsk);
 
 #ifdef CONFIG_FAULT_INJECTION
 	tsk->fail_nth = 0;
kernel/kthread.c +2 -0
index 621467c33fef..9b948559a34a 100644
@@ -17,6 +17,7 @@
 #include <linux/unistd.h>
 #include <linux/file.h>
 #include <linux/export.h>
+#include <linux/kmsan.h>
 #include <linux/mutex.h>
 #include <linux/slab.h>
 #include <linux/freezer.h>
@@ -350,6 +351,7 @@ struct task_struct *__kthread_create_on_node(int (*threadfn)(void *data),
 		set_cpus_allowed_ptr(task, cpu_all_mask);
 	}
 	kfree(create);
+	kmsan_task_create(task);
 	return task;
 }
 
kernel/printk/printk.c +7 -1
index ca65327a6de8..f77fdcb5f861 100644
@@ -1914,7 +1914,12 @@ int vprintk_store(int facility, int level,
 	 * The printf needs to come first; we need the syslog
 	 * prefix which might be passed-in as a parameter.
 	 */
-	text_len = vscnprintf(text, sizeof(textbuf), fmt, args);
+	/*
+	 * We've checked the printk arguments in vprintk_emit() already.
+	 * Initialize |text_len| to prevent the errors from spreading.
+	 */
+	text_len = KMSAN_INIT_VALUE(vscnprintf(text, sizeof(textbuf), fmt,
+					       args));
 
 	/* mark and strip a trailing newline */
 	if (text_len && text[text_len-1] == '\n') {
@@ -1972,6 +1977,7 @@ asmlinkage int vprintk_emit(int facility, int level,
 	boot_delay_msec(level);
 	printk_delay();
 
+	kmsan_handle_vprintk(&fmt, args);
 	/* This stops the holder of console_sem just where we want him */
 	logbuf_lock_irqsave(flags);
 	curr_log_seq = log_next_seq;
kernel/softirq.c +5 -0
index 0427a86743a4..6d566dd68b35 100644
@@ -11,6 +11,7 @@
 
 #include <linux/export.h>
 #include <linux/kernel_stat.h>
+#include <linux/kmsan.h>
 #include <linux/interrupt.h>
 #include <linux/init.h>
 #include <linux/mm.h>
@@ -370,7 +371,9 @@ static inline void invoke_softirq(void)
 		 * it is the irq stack, because it should be near empty
 		 * at this stage.
 		 */
+		kmsan_softirq_enter();
 		__do_softirq();
+		kmsan_softirq_exit();
 #else
 		/*
 		 * Otherwise, irq_exit() is called on the task stack that can
@@ -600,7 +603,9 @@ static void run_ksoftirqd(unsigned int cpu)
 		 * We can safely run softirq on inline stack, as we are not deep
 		 * in the task stack here.
 		 */
+		kmsan_softirq_enter();
 		__do_softirq();
+		kmsan_softirq_exit();
 		local_irq_enable();
 		cond_resched();
 		return;
lib/ioremap.c +5 -0
index 0a2ffadc6d71..5f830cee5bfc 100644
@@ -6,6 +6,7 @@
  *
  * (C) Copyright 1995 1996 Linus Torvalds
  */
+#include <linux/kmsan.h>
 #include <linux/vmalloc.h>
 #include <linux/mm.h>
 #include <linux/sched.h>
@@ -214,6 +215,8 @@ int ioremap_page_range(unsigned long addr,
 	unsigned long start;
 	unsigned long next;
 	int err;
+	unsigned long old_addr = addr;
+	phys_addr_t old_phys_addr = phys_addr;
 
 	might_sleep();
 	BUG_ON(addr >= end);
@@ -228,6 +231,8 @@ int ioremap_page_range(unsigned long addr,
 	} while (pgd++, phys_addr += (next - addr), addr = next, addr != end);
 
 	flush_cache_vmap(start, end);
+	if (!err)
+		kmsan_ioremap_page_range(old_addr, end, old_phys_addr, prot);
 
 	return err;
 }
mm/compaction.c +9 -0
index ce08b39d85d4..e9a5898e5fe0 100644
@@ -84,6 +84,15 @@ static void split_map_pages(struct list_head *list)
 
 		for (i = 0; i < nr_pages; i++) {
 			list_add(&page->lru, &tmp_list);
+#ifdef CONFIG_KMSAN
+			/*
+			 * TODO(glider): we may lose the metadata when copying
+			 * something to these pages. Need to allocate shadow
+			 * and origin pages here instead.
+			 */
+			page->shadow = NULL;
+			page->origin = NULL;
+#endif
 			page++;
 		}
 	}
mm/gup.c +3 -0
index 23a9f9c9d377..c16e4288997d 100644
@@ -4,6 +4,7 @@
 #include <linux/err.h>
 #include <linux/spinlock.h>
 
+#include <linux/kmsan.h>
 #include <linux/mm.h>
 #include <linux/memremap.h>
 #include <linux/pagemap.h>
@@ -2347,6 +2348,7 @@ int __get_user_pages_fast(unsigned long start, int nr_pages, int write,
 	    gup_fast_permitted(start, end)) {
 		local_irq_save(flags);
 		gup_pgd_range(start, end, write ? FOLL_WRITE : 0, pages, &nr);
+		kmsan_gup_pgd_range(pages, nr);
 		local_irq_restore(flags);
 	}
 
@@ -2416,6 +2418,7 @@ int get_user_pages_fast(unsigned long start, int nr_pages,
 	    gup_fast_permitted(start, end)) {
 		local_irq_disable();
 		gup_pgd_range(addr, end, gup_flags, pages, &nr);
+		kmsan_gup_pgd_range(pages, nr);
 		local_irq_enable();
 		ret = nr;
 	}
mm/memory.c +2 -0
index b1ca51a079f2..48ceacc06e2d 100644
@@ -51,6 +51,7 @@
 #include <linux/highmem.h>
 #include <linux/pagemap.h>
 #include <linux/memremap.h>
+#include <linux/kmsan.h>
 #include <linux/ksm.h>
 #include <linux/rmap.h>
 #include <linux/export.h>
@@ -2328,6 +2329,7 @@ static vm_fault_t wp_page_copy(struct vm_fault *vmf)
 		if (!new_page)
 			goto oom;
 		cow_user_page(new_page, old_page, vmf->address, vma);
+		kmsan_copy_page_meta(new_page, old_page);
 	}
 
 	if (mem_cgroup_try_charge_delay(new_page, mm, GFP_KERNEL, &memcg, false))
mm/page_alloc.c +16 -0
index c0b2e0306720..f52494e0dcd9 100644
@@ -26,6 +26,8 @@
 #include <linux/compiler.h>
 #include <linux/kernel.h>
 #include <linux/kasan.h>
+#include <linux/kmsan.h>
+#include <linux/kmsan-checks.h>
 #include <linux/module.h>
 #include <linux/suspend.h>
 #include <linux/pagevec.h>
@@ -1133,6 +1135,7 @@ static __always_inline bool free_pages_prepare(struct page *page,
 	VM_BUG_ON_PAGE(PageTail(page), page);
 
 	trace_mm_page_free(page, order);
+	kmsan_free_page(page, order);
 
 	/*
 	 * Check tail pages before head page information is cleared to
@@ -3121,6 +3124,7 @@ void split_page(struct page *page, unsigned int order)
 	VM_BUG_ON_PAGE(PageCompound(page), page);
 	VM_BUG_ON_PAGE(!page_count(page), page);
 
+	kmsan_split_page(page, order);
 	for (i = 1; i < (1 << order); i++)
 		set_page_refcounted(page + i);
 	split_page_owner(page, order);
@@ -3253,6 +3257,13 @@ static struct page *rmqueue_pcplist(struct zone *preferred_zone,
 /*
  * Allocate a page from the given zone. Use pcplists for order-0 allocations.
  */
+/*
+ * TODO(glider): rmqueue() may call __msan_poison_alloca() through a call to
+ * set_pfnblock_flags_mask(). If __msan_poison_alloca() attempts to allocate
+ * pages for the stack depot, it may call rmqueue() again, which will result
+ * in a deadlock.
+ */
+__no_sanitize_memory
 static inline
 struct page *rmqueue(struct zone *preferred_zone,
 			struct zone *zone, unsigned int order,
@@ -4779,6 +4790,11 @@ __alloc_pages_nodemask(gfp_t gfp_mask, unsigned int order, int preferred_nid,
 
 	trace_mm_page_alloc(page, order, alloc_mask, ac.migratetype);
 
+	if (page)
+		if (kmsan_alloc_page(page, order, gfp_mask)) {
+			__free_pages(page, order);
+			page = NULL;
+		}
 	return page;
 }
 EXPORT_SYMBOL(__alloc_pages_nodemask);
mm/vmalloc.c +21 -2
index a3c70e275f4e..bdf66ffcf02c 100644
@@ -29,6 +29,7 @@
 #include <linux/rcupdate.h>
 #include <linux/pfn.h>
 #include <linux/kmemleak.h>
+#include <linux/kmsan.h>
 #include <linux/atomic.h>
 #include <linux/compiler.h>
 #include <linux/llist.h>
@@ -119,7 +120,8 @@ static void vunmap_p4d_range(pgd_t *pgd, unsigned long addr, unsigned long end)
 	} while (p4d++, addr = next, addr != end);
 }
 
-static void vunmap_page_range(unsigned long addr, unsigned long end)
+/* Exported for KMSAN, visible in mm/kmsan/kmsan.h only. */
+void __vunmap_page_range(unsigned long addr, unsigned long end)
 {
 	pgd_t *pgd;
 	unsigned long next;
@@ -133,6 +135,12 @@ static void vunmap_page_range(unsigned long addr, unsigned long end)
 		vunmap_p4d_range(pgd, addr, next);
 	} while (pgd++, addr = next, addr != end);
 }
+EXPORT_SYMBOL(__vunmap_page_range);
+static void vunmap_page_range(unsigned long addr, unsigned long end)
+{
+	kmsan_vunmap_page_range(addr, end);
+	__vunmap_page_range(addr, end);
+}
 
 static int vmap_pte_range(pmd_t *pmd, unsigned long addr,
 		unsigned long end, pgprot_t prot, struct page **pages, int *nr)
@@ -216,8 +224,11 @@ static int vmap_p4d_range(pgd_t *pgd, unsigned long addr,
  * will have pfns corresponding to the "pages" array.
  *
  * Ie. pte at addr+N*PAGE_SIZE shall point to pfn corresponding to pages[N]
+ *
+ * This function is exported for use in KMSAN, but is only declared in KMSAN
+ * headers.
  */
-static int vmap_page_range_noflush(unsigned long start, unsigned long end,
+int __vmap_page_range_noflush(unsigned long start, unsigned long end,
 				   pgprot_t prot, struct page **pages)
 {
 	pgd_t *pgd;
@@ -237,6 +248,14 @@ static int vmap_page_range_noflush(unsigned long start, unsigned long end,
 
 	return nr;
 }
+EXPORT_SYMBOL(__vmap_page_range_noflush);
+
+static int vmap_page_range_noflush(unsigned long start, unsigned long end,
+				   pgprot_t prot, struct page **pages)
+{
+	kmsan_vmap_page_range_noflush(start, end, prot, pages);
+	return __vmap_page_range_noflush(start, end, prot, pages);
+}
 
 static int vmap_page_range(unsigned long start, unsigned long end,
 			   pgprot_t prot, struct page **pages)
net/sched/sch_generic.c +2 -0
index 17bd8f539bc7..fd22c4a4ba42 100644
@@ -11,6 +11,7 @@
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
+#include <linux/kmsan-checks.h>
 #include <linux/sched.h>
 #include <linux/string.h>
 #include <linux/errno.h>
@@ -659,6 +660,7 @@ static struct sk_buff *pfifo_fast_dequeue(struct Qdisc *qdisc)
 	} else {
 		qdisc->empty = true;
 	}
+	kmsan_check_skb(skb);
 
 	return skb;
 }
Signature · 1 line
2.23.0.866.gb869b98d4c-goog
#37 lore raw

Re: [PATCH RFC v1 19/26] kmsan: call KMSAN hooks where needed

Quoted 4 lines · On Fri, 2019-10-18 at 11:42 +0200, [email protected] wrote:

Insert KMSAN hooks that check for potential memory errors and/or make necessary bookkeeping changes:
- allocate/split/deallocate metadata pages in alloc_pages()/split_page()/free_page();

This also seems unnecessary where there are options like page_poison and debug_pagealloc should be able detect uninitialized memory access in the page allocator as well. Even KASAN has some of the functionality.

Quoted 517 lines

- clear page shadow and origins in clear_page(), copy_user_highpage();
- copy page metadata in copy_highpage(), wp_page_copy();
- handle vmap()/vunmap()/iounmap();
- handle task creation and deletion;
- handle vprintk();
- call softirq entry/exit hooks in kernel/softirq.c;
- check/initialize memory sent to/read from USB, I2C, and network

To: Alexander Potapenko <[email protected]>
Cc: Andrew Morton <[email protected]>
Cc: Greg Kroah-Hartman <[email protected]>
Cc: Eric Dumazet <[email protected]>
Cc: Wolfram Sang <[email protected]>
Cc: Petr Mladek <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]
---

Change-Id: I1250a928d9263bf71fdaa067a070bdee686ef47b

---
 arch/x86/include/asm/page_64.h | 13 +++++++++++++
 arch/x86/mm/ioremap.c          |  3 +++
 drivers/i2c/i2c-core-base.c    |  2 ++
 drivers/usb/core/urb.c         |  2 ++
 include/linux/highmem.h        |  4 ++++
 kernel/exit.c                  |  2 ++
 kernel/fork.c                  |  2 ++
 kernel/kthread.c               |  2 ++
 kernel/printk/printk.c         |  8 +++++++-
 kernel/softirq.c               |  5 +++++
 lib/ioremap.c                  |  5 +++++
 mm/compaction.c                |  9 +++++++++
 mm/gup.c                       |  3 +++
 mm/memory.c                    |  2 ++
 mm/page_alloc.c                | 16 ++++++++++++++++
 mm/vmalloc.c                   | 23 +++++++++++++++++++++--
 net/sched/sch_generic.c        |  2 ++
 17 files changed, 100 insertions(+), 3 deletions(-)
arch/x86/include/asm/page_64.h +13 -0
index 939b1cff4a7b..0ba43d93414f 100644
@@ -44,14 +44,27 @@ void clear_page_orig(void *page);
 void clear_page_rep(void *page);
 void clear_page_erms(void *page);
 
+/* This is an assembly header, avoid including too much of kmsan.h */
+#ifdef CONFIG_KMSAN
+void kmsan_clear_page(void *page_addr);
+#endif
+__no_sanitize_memory
 static inline void clear_page(void *page)
 {
+#ifdef CONFIG_KMSAN
+	/* alternative_call_2() changes |page|. */
+	void *page_copy = page;
+#endif
 	alternative_call_2(clear_page_orig,
 			   clear_page_rep, X86_FEATURE_REP_GOOD,
 			   clear_page_erms, X86_FEATURE_ERMS,
 			   "=D" (page),
 			   "0" (page)
 			   : "cc", "memory", "rax", "rcx");
+#ifdef CONFIG_KMSAN
+	/* Clear KMSAN shadow for the pages that have it. */
+	kmsan_clear_page(page_copy);
+#endif
 }
 
 void copy_page(void *to, void *from);
arch/x86/mm/ioremap.c +3 -0
index a39dcdb5ae34..fdb2abc11a82 100644
@@ -7,6 +7,7 @@
  * (C) Copyright 1995 1996 Linus Torvalds
  */
 
+#include <linux/kmsan.h>
 #include <linux/memblock.h>
 #include <linux/init.h>
 #include <linux/io.h>
@@ -451,6 +452,8 @@ void iounmap(volatile void __iomem *addr)
 		return;
 	}
 
+	kmsan_iounmap_page_range((unsigned long)addr,
+		(unsigned long)addr + get_vm_area_size(p));
 	free_memtype(p->phys_addr, p->phys_addr + get_vm_area_size(p));
 
 	/* Finally remove it */
drivers/i2c/i2c-core-base.c +2 -0
index 5f6a4985f2bc..9685d3399c79 100644
@@ -27,6 +27,7 @@
 #include <linux/irqflags.h>
 #include <linux/jump_label.h>
 #include <linux/kernel.h>
+#include <linux/kmsan.h>
 #include <linux/module.h>
 #include <linux/mutex.h>
 #include <linux/of_device.h>
@@ -1975,6 +1976,7 @@ int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
 				trace_i2c_write(adap, &msgs[i], i);
 	}
 
+	kmsan_handle_i2c_transfer(msgs, num);
 	/* Retry automatically on arbitration loss */
 	orig_jiffies = jiffies;
 	for (ret = 0, try = 0; try <= adap->retries; try++) {
drivers/usb/core/urb.c +2 -0
index 0eab79f82ce4..5bdb54d71c2e 100644
@@ -8,6 +8,7 @@
 #include <linux/bitops.h>
 #include <linux/slab.h>
 #include <linux/log2.h>
+#include <linux/kmsan-checks.h>
 #include <linux/usb.h>
 #include <linux/wait.h>
 #include <linux/usb/hcd.h>
@@ -401,6 +402,7 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
 			URB_SETUP_MAP_SINGLE | URB_SETUP_MAP_LOCAL |
 			URB_DMA_SG_COMBINED);
 	urb->transfer_flags |= (is_out ? URB_DIR_OUT : URB_DIR_IN);
+	kmsan_handle_urb(urb, is_out);
 
 	if (xfertype != USB_ENDPOINT_XFER_CONTROL &&
 			dev->state < USB_STATE_CONFIGURED)
include/linux/highmem.h +4 -0
index ea5cdbd8c2c3..623b56f48685 100644
@@ -5,6 +5,7 @@
 #include <linux/fs.h>
 #include <linux/kernel.h>
 #include <linux/bug.h>
+#include <linux/kmsan.h>
 #include <linux/mm.h>
 #include <linux/uaccess.h>
 #include <linux/hardirq.h>
@@ -255,6 +256,8 @@ static inline void copy_user_highpage(struct page *to, struct page *from,
 	vfrom = kmap_atomic(from);
 	vto = kmap_atomic(to);
 	copy_user_page(vto, vfrom, vaddr, to);
+	/* User pages don't have shadow, just clear the destination. */
+	kmsan_clear_page(page_address(to));
 	kunmap_atomic(vto);
 	kunmap_atomic(vfrom);
 }
@@ -270,6 +273,7 @@ static inline void copy_highpage(struct page *to, struct page *from)
 	vfrom = kmap_atomic(from);
 	vto = kmap_atomic(to);
 	copy_page(vto, vfrom);
+	kmsan_copy_page_meta(to, from);
 	kunmap_atomic(vto);
 	kunmap_atomic(vfrom);
 }
kernel/exit.c +2 -0
index a46a50d67002..9e3ce929110b 100644
@@ -60,6 +60,7 @@
 #include <linux/writeback.h>
 #include <linux/shm.h>
 #include <linux/kcov.h>
+#include <linux/kmsan.h>
 #include <linux/random.h>
 #include <linux/rcuwait.h>
 #include <linux/compat.h>
@@ -719,6 +720,7 @@ void __noreturn do_exit(long code)
 
 	profile_task_exit(tsk);
 	kcov_task_exit(tsk);
+	kmsan_task_exit(tsk);
 
 	WARN_ON(blk_needs_flush_plug(tsk));
 
kernel/fork.c +2 -0
index bcdf53125210..0f08952a42dc 100644
@@ -37,6 +37,7 @@
 #include <linux/fdtable.h>
 #include <linux/iocontext.h>
 #include <linux/key.h>
+#include <linux/kmsan.h>
 #include <linux/binfmts.h>
 #include <linux/mman.h>
 #include <linux/mmu_notifier.h>
@@ -931,6 +932,7 @@ static struct task_struct *dup_task_struct(struct task_struct *orig, int node)
 	account_kernel_stack(tsk, 1);
 
 	kcov_task_init(tsk);
+	kmsan_task_create(tsk);
 
 #ifdef CONFIG_FAULT_INJECTION
 	tsk->fail_nth = 0;
kernel/kthread.c +2 -0
index 621467c33fef..9b948559a34a 100644
@@ -17,6 +17,7 @@
 #include <linux/unistd.h>
 #include <linux/file.h>
 #include <linux/export.h>
+#include <linux/kmsan.h>
 #include <linux/mutex.h>
 #include <linux/slab.h>
 #include <linux/freezer.h>
@@ -350,6 +351,7 @@ struct task_struct *__kthread_create_on_node(int (*threadfn)(void *data),
 		set_cpus_allowed_ptr(task, cpu_all_mask);
 	}
 	kfree(create);
+	kmsan_task_create(task);
 	return task;
 }
 
kernel/printk/printk.c +7 -1
index ca65327a6de8..f77fdcb5f861 100644
@@ -1914,7 +1914,12 @@ int vprintk_store(int facility, int level,
 	 * The printf needs to come first; we need the syslog
 	 * prefix which might be passed-in as a parameter.
 	 */
-	text_len = vscnprintf(text, sizeof(textbuf), fmt, args);
+	/*
+	 * We've checked the printk arguments in vprintk_emit() already.
+	 * Initialize |text_len| to prevent the errors from spreading.
+	 */
+	text_len = KMSAN_INIT_VALUE(vscnprintf(text, sizeof(textbuf), fmt,
+					       args));
 
 	/* mark and strip a trailing newline */
 	if (text_len && text[text_len-1] == '\n') {
@@ -1972,6 +1977,7 @@ asmlinkage int vprintk_emit(int facility, int level,
 	boot_delay_msec(level);
 	printk_delay();
 
+	kmsan_handle_vprintk(&fmt, args);
 	/* This stops the holder of console_sem just where we want him */
 	logbuf_lock_irqsave(flags);
 	curr_log_seq = log_next_seq;
kernel/softirq.c +5 -0
index 0427a86743a4..6d566dd68b35 100644
@@ -11,6 +11,7 @@
 
 #include <linux/export.h>
 #include <linux/kernel_stat.h>
+#include <linux/kmsan.h>
 #include <linux/interrupt.h>
 #include <linux/init.h>
 #include <linux/mm.h>
@@ -370,7 +371,9 @@ static inline void invoke_softirq(void)
 		 * it is the irq stack, because it should be near empty
 		 * at this stage.
 		 */
+		kmsan_softirq_enter();
 		__do_softirq();
+		kmsan_softirq_exit();
 #else
 		/*
 		 * Otherwise, irq_exit() is called on the task stack that can
@@ -600,7 +603,9 @@ static void run_ksoftirqd(unsigned int cpu)
 		 * We can safely run softirq on inline stack, as we are not deep
 		 * in the task stack here.
 		 */
+		kmsan_softirq_enter();
 		__do_softirq();
+		kmsan_softirq_exit();
 		local_irq_enable();
 		cond_resched();
 		return;
lib/ioremap.c +5 -0
index 0a2ffadc6d71..5f830cee5bfc 100644
@@ -6,6 +6,7 @@
  *
  * (C) Copyright 1995 1996 Linus Torvalds
  */
+#include <linux/kmsan.h>
 #include <linux/vmalloc.h>
 #include <linux/mm.h>
 #include <linux/sched.h>
@@ -214,6 +215,8 @@ int ioremap_page_range(unsigned long addr,
 	unsigned long start;
 	unsigned long next;
 	int err;
+	unsigned long old_addr = addr;
+	phys_addr_t old_phys_addr = phys_addr;
 
 	might_sleep();
 	BUG_ON(addr >= end);
@@ -228,6 +231,8 @@ int ioremap_page_range(unsigned long addr,
 	} while (pgd++, phys_addr += (next - addr), addr = next, addr != end);
 
 	flush_cache_vmap(start, end);
+	if (!err)
+		kmsan_ioremap_page_range(old_addr, end, old_phys_addr, prot);
 
 	return err;
 }
mm/compaction.c +9 -0
index ce08b39d85d4..e9a5898e5fe0 100644
@@ -84,6 +84,15 @@ static void split_map_pages(struct list_head *list)
 
 		for (i = 0; i < nr_pages; i++) {
 			list_add(&page->lru, &tmp_list);
+#ifdef CONFIG_KMSAN
+			/*
+			 * TODO(glider): we may lose the metadata when copying
+			 * something to these pages. Need to allocate shadow
+			 * and origin pages here instead.
+			 */
+			page->shadow = NULL;
+			page->origin = NULL;
+#endif
 			page++;
 		}
 	}
mm/gup.c +3 -0
index 23a9f9c9d377..c16e4288997d 100644
@@ -4,6 +4,7 @@
 #include <linux/err.h>
 #include <linux/spinlock.h>
 
+#include <linux/kmsan.h>
 #include <linux/mm.h>
 #include <linux/memremap.h>
 #include <linux/pagemap.h>
@@ -2347,6 +2348,7 @@ int __get_user_pages_fast(unsigned long start, int nr_pages, int write,
 	    gup_fast_permitted(start, end)) {
 		local_irq_save(flags);
 		gup_pgd_range(start, end, write ? FOLL_WRITE : 0, pages, &nr);
+		kmsan_gup_pgd_range(pages, nr);
 		local_irq_restore(flags);
 	}
 
@@ -2416,6 +2418,7 @@ int get_user_pages_fast(unsigned long start, int nr_pages,
 	    gup_fast_permitted(start, end)) {
 		local_irq_disable();
 		gup_pgd_range(addr, end, gup_flags, pages, &nr);
+		kmsan_gup_pgd_range(pages, nr);
 		local_irq_enable();
 		ret = nr;
 	}
mm/memory.c +2 -0
index b1ca51a079f2..48ceacc06e2d 100644
@@ -51,6 +51,7 @@
 #include <linux/highmem.h>
 #include <linux/pagemap.h>
 #include <linux/memremap.h>
+#include <linux/kmsan.h>
 #include <linux/ksm.h>
 #include <linux/rmap.h>
 #include <linux/export.h>
@@ -2328,6 +2329,7 @@ static vm_fault_t wp_page_copy(struct vm_fault *vmf)
 		if (!new_page)
 			goto oom;
 		cow_user_page(new_page, old_page, vmf->address, vma);
+		kmsan_copy_page_meta(new_page, old_page);
 	}
 
 	if (mem_cgroup_try_charge_delay(new_page, mm, GFP_KERNEL, &memcg, false))
mm/page_alloc.c +16 -0
index c0b2e0306720..f52494e0dcd9 100644
@@ -26,6 +26,8 @@
 #include <linux/compiler.h>
 #include <linux/kernel.h>
 #include <linux/kasan.h>
+#include <linux/kmsan.h>
+#include <linux/kmsan-checks.h>
 #include <linux/module.h>
 #include <linux/suspend.h>
 #include <linux/pagevec.h>
@@ -1133,6 +1135,7 @@ static __always_inline bool free_pages_prepare(struct page *page,
 	VM_BUG_ON_PAGE(PageTail(page), page);
 
 	trace_mm_page_free(page, order);
+	kmsan_free_page(page, order);
 
 	/*
 	 * Check tail pages before head page information is cleared to
@@ -3121,6 +3124,7 @@ void split_page(struct page *page, unsigned int order)
 	VM_BUG_ON_PAGE(PageCompound(page), page);
 	VM_BUG_ON_PAGE(!page_count(page), page);
 
+	kmsan_split_page(page, order);
 	for (i = 1; i < (1 << order); i++)
 		set_page_refcounted(page + i);
 	split_page_owner(page, order);
@@ -3253,6 +3257,13 @@ static struct page *rmqueue_pcplist(struct zone *preferred_zone,
 /*
  * Allocate a page from the given zone. Use pcplists for order-0 allocations.
  */
+/*
+ * TODO(glider): rmqueue() may call __msan_poison_alloca() through a call to
+ * set_pfnblock_flags_mask(). If __msan_poison_alloca() attempts to allocate
+ * pages for the stack depot, it may call rmqueue() again, which will result
+ * in a deadlock.
+ */
+__no_sanitize_memory
 static inline
 struct page *rmqueue(struct zone *preferred_zone,
 			struct zone *zone, unsigned int order,
@@ -4779,6 +4790,11 @@ __alloc_pages_nodemask(gfp_t gfp_mask, unsigned int order, int preferred_nid,
 
 	trace_mm_page_alloc(page, order, alloc_mask, ac.migratetype);
 
+	if (page)
+		if (kmsan_alloc_page(page, order, gfp_mask)) {
+			__free_pages(page, order);
+			page = NULL;
+		}
 	return page;
 }
 EXPORT_SYMBOL(__alloc_pages_nodemask);
mm/vmalloc.c +21 -2
index a3c70e275f4e..bdf66ffcf02c 100644
@@ -29,6 +29,7 @@
 #include <linux/rcupdate.h>
 #include <linux/pfn.h>
 #include <linux/kmemleak.h>
+#include <linux/kmsan.h>
 #include <linux/atomic.h>
 #include <linux/compiler.h>
 #include <linux/llist.h>
@@ -119,7 +120,8 @@ static void vunmap_p4d_range(pgd_t *pgd, unsigned long addr, unsigned long end)
 	} while (p4d++, addr = next, addr != end);
 }
 
-static void vunmap_page_range(unsigned long addr, unsigned long end)
+/* Exported for KMSAN, visible in mm/kmsan/kmsan.h only. */
+void __vunmap_page_range(unsigned long addr, unsigned long end)
 {
 	pgd_t *pgd;
 	unsigned long next;
@@ -133,6 +135,12 @@ static void vunmap_page_range(unsigned long addr, unsigned long end)
 		vunmap_p4d_range(pgd, addr, next);
 	} while (pgd++, addr = next, addr != end);
 }
+EXPORT_SYMBOL(__vunmap_page_range);
+static void vunmap_page_range(unsigned long addr, unsigned long end)
+{
+	kmsan_vunmap_page_range(addr, end);
+	__vunmap_page_range(addr, end);
+}
 
 static int vmap_pte_range(pmd_t *pmd, unsigned long addr,
 		unsigned long end, pgprot_t prot, struct page **pages, int *nr)
@@ -216,8 +224,11 @@ static int vmap_p4d_range(pgd_t *pgd, unsigned long addr,
  * will have pfns corresponding to the "pages" array.
  *
  * Ie. pte at addr+N*PAGE_SIZE shall point to pfn corresponding to pages[N]
+ *
+ * This function is exported for use in KMSAN, but is only declared in KMSAN
+ * headers.
  */
-static int vmap_page_range_noflush(unsigned long start, unsigned long end,
+int __vmap_page_range_noflush(unsigned long start, unsigned long end,
 				   pgprot_t prot, struct page **pages)
 {
 	pgd_t *pgd;
@@ -237,6 +248,14 @@ static int vmap_page_range_noflush(unsigned long start, unsigned long end,
 
 	return nr;
 }
+EXPORT_SYMBOL(__vmap_page_range_noflush);
+
+static int vmap_page_range_noflush(unsigned long start, unsigned long end,
+				   pgprot_t prot, struct page **pages)
+{
+	kmsan_vmap_page_range_noflush(start, end, prot, pages);
+	return __vmap_page_range_noflush(start, end, prot, pages);
+}
 
 static int vmap_page_range(unsigned long start, unsigned long end,
 			   pgprot_t prot, struct page **pages)
net/sched/sch_generic.c +2 -0
index 17bd8f539bc7..fd22c4a4ba42 100644
@@ -11,6 +11,7 @@
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
+#include <linux/kmsan-checks.h>
 #include <linux/sched.h>
 #include <linux/string.h>
 #include <linux/errno.h>
@@ -659,6 +660,7 @@ static struct sk_buff *pfifo_fast_dequeue(struct Qdisc *qdisc)
 	} else {
 		qdisc->empty = true;
 	}
+	kmsan_check_skb(skb);
 
 	return skb;
 }
author maintainer
#38 lore raw

Re: [PATCH RFC v1 19/26] kmsan: call KMSAN hooks where needed

Quoted 10 lines · Qian Cai
Quoted 4 lines · On Fri, 2019-10-18 at 11:42 +0200, [email protected] wrote:

Insert KMSAN hooks that check for potential memory errors and/or make necessary bookkeeping changes:
- allocate/split/deallocate metadata pages in alloc_pages()/split_page()/free_page();

This also seems unnecessary where there are options like page_poison and debug_pagealloc should be able detect uninitialized memory access in the page allocator as well. Even KASAN has some of the functionality.

I still believe there's some misunderstanding between us because I didn't CC you on KMSAN runtime or the documentation.
I'll do this and will be happy to answer questions if you still have them. Without these hooks, KMSAN will be virtually unusable, because it won't know about heap allocations.

When a buffer is allocated on heap, KMSAN updates the metadata for that region of memory, writing 0xFF (meaning uninitialized) to every shadow byte and a stack ID to every 4 region bytes.
Note that shadow and origin bytes are stored separately and are generally incompatible with what debug pagealloc/page poison do.

Quoted 517 lines
Quoted 517 lines

- clear page shadow and origins in clear_page(), copy_user_highpage();
- copy page metadata in copy_highpage(), wp_page_copy();
- handle vmap()/vunmap()/iounmap();
- handle task creation and deletion;
- handle vprintk();
- call softirq entry/exit hooks in kernel/softirq.c;
- check/initialize memory sent to/read from USB, I2C, and network

To: Alexander Potapenko <[email protected]>
Cc: Andrew Morton <[email protected]>
Cc: Greg Kroah-Hartman <[email protected]>
Cc: Eric Dumazet <[email protected]>
Cc: Wolfram Sang <[email protected]>
Cc: Petr Mladek <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]
---

Change-Id: I1250a928d9263bf71fdaa067a070bdee686ef47b

---
 arch/x86/include/asm/page_64.h | 13 +++++++++++++
 arch/x86/mm/ioremap.c          |  3 +++
 drivers/i2c/i2c-core-base.c    |  2 ++
 drivers/usb/core/urb.c         |  2 ++
 include/linux/highmem.h        |  4 ++++
 kernel/exit.c                  |  2 ++
 kernel/fork.c                  |  2 ++
 kernel/kthread.c               |  2 ++
 kernel/printk/printk.c         |  8 +++++++-
 kernel/softirq.c               |  5 +++++
 lib/ioremap.c                  |  5 +++++
 mm/compaction.c                |  9 +++++++++
 mm/gup.c                       |  3 +++
 mm/memory.c                    |  2 ++
 mm/page_alloc.c                | 16 ++++++++++++++++
 mm/vmalloc.c                   | 23 +++++++++++++++++++++--
 net/sched/sch_generic.c        |  2 ++
 17 files changed, 100 insertions(+), 3 deletions(-)
arch/x86/include/asm/page_64.h +13 -0
index 939b1cff4a7b..0ba43d93414f 100644
@@ -44,14 +44,27 @@ void clear_page_orig(void *page);
 void clear_page_rep(void *page);
 void clear_page_erms(void *page);
 
+/* This is an assembly header, avoid including too much of kmsan.h */
+#ifdef CONFIG_KMSAN
+void kmsan_clear_page(void *page_addr);
+#endif
+__no_sanitize_memory
 static inline void clear_page(void *page)
 {
+#ifdef CONFIG_KMSAN
+     /* alternative_call_2() changes |page|. */
+     void *page_copy = page;
+#endif
      alternative_call_2(clear_page_orig,
                         clear_page_rep, X86_FEATURE_REP_GOOD,
                         clear_page_erms, X86_FEATURE_ERMS,
                         "=D" (page),
                         "0" (page)
                         : "cc", "memory", "rax", "rcx");
+#ifdef CONFIG_KMSAN
+     /* Clear KMSAN shadow for the pages that have it. */
+     kmsan_clear_page(page_copy);
+#endif
 }
 
 void copy_page(void *to, void *from);
arch/x86/mm/ioremap.c +3 -0
index a39dcdb5ae34..fdb2abc11a82 100644
@@ -7,6 +7,7 @@
  * (C) Copyright 1995 1996 Linus Torvalds
  */
 
+#include <linux/kmsan.h>
 #include <linux/memblock.h>
 #include <linux/init.h>
 #include <linux/io.h>
@@ -451,6 +452,8 @@ void iounmap(volatile void __iomem *addr)
              return;
      }
 
+     kmsan_iounmap_page_range((unsigned long)addr,
+             (unsigned long)addr + get_vm_area_size(p));
      free_memtype(p->phys_addr, p->phys_addr + get_vm_area_size(p));
 
      /* Finally remove it */
drivers/i2c/i2c-core-base.c +2 -0
index 5f6a4985f2bc..9685d3399c79 100644
@@ -27,6 +27,7 @@
 #include <linux/irqflags.h>
 #include <linux/jump_label.h>
 #include <linux/kernel.h>
+#include <linux/kmsan.h>
 #include <linux/module.h>
 #include <linux/mutex.h>
 #include <linux/of_device.h>
@@ -1975,6 +1976,7 @@ int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
                              trace_i2c_write(adap, &msgs[i], i);
      }
 
+     kmsan_handle_i2c_transfer(msgs, num);
      /* Retry automatically on arbitration loss */
      orig_jiffies = jiffies;
      for (ret = 0, try = 0; try <= adap->retries; try++) {
drivers/usb/core/urb.c +2 -0
index 0eab79f82ce4..5bdb54d71c2e 100644
@@ -8,6 +8,7 @@
 #include <linux/bitops.h>
 #include <linux/slab.h>
 #include <linux/log2.h>
+#include <linux/kmsan-checks.h>
 #include <linux/usb.h>
 #include <linux/wait.h>
 #include <linux/usb/hcd.h>
@@ -401,6 +402,7 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
                      URB_SETUP_MAP_SINGLE | URB_SETUP_MAP_LOCAL |
                      URB_DMA_SG_COMBINED);
      urb->transfer_flags |= (is_out ? URB_DIR_OUT : URB_DIR_IN);
+     kmsan_handle_urb(urb, is_out);
 
      if (xfertype != USB_ENDPOINT_XFER_CONTROL &&
                      dev->state < USB_STATE_CONFIGURED)
include/linux/highmem.h +4 -0
index ea5cdbd8c2c3..623b56f48685 100644
@@ -5,6 +5,7 @@
 #include <linux/fs.h>
 #include <linux/kernel.h>
 #include <linux/bug.h>
+#include <linux/kmsan.h>
 #include <linux/mm.h>
 #include <linux/uaccess.h>
 #include <linux/hardirq.h>
@@ -255,6 +256,8 @@ static inline void copy_user_highpage(struct page *to, struct page *from,
      vfrom = kmap_atomic(from);
      vto = kmap_atomic(to);
      copy_user_page(vto, vfrom, vaddr, to);
+     /* User pages don't have shadow, just clear the destination. */
+     kmsan_clear_page(page_address(to));
      kunmap_atomic(vto);
      kunmap_atomic(vfrom);
 }
@@ -270,6 +273,7 @@ static inline void copy_highpage(struct page *to, struct page *from)
      vfrom = kmap_atomic(from);
      vto = kmap_atomic(to);
      copy_page(vto, vfrom);
+     kmsan_copy_page_meta(to, from);
      kunmap_atomic(vto);
      kunmap_atomic(vfrom);
 }
kernel/exit.c +2 -0
index a46a50d67002..9e3ce929110b 100644
@@ -60,6 +60,7 @@
 #include <linux/writeback.h>
 #include <linux/shm.h>
 #include <linux/kcov.h>
+#include <linux/kmsan.h>
 #include <linux/random.h>
 #include <linux/rcuwait.h>
 #include <linux/compat.h>
@@ -719,6 +720,7 @@ void __noreturn do_exit(long code)
 
      profile_task_exit(tsk);
      kcov_task_exit(tsk);
+     kmsan_task_exit(tsk);
 
      WARN_ON(blk_needs_flush_plug(tsk));
 
kernel/fork.c +2 -0
index bcdf53125210..0f08952a42dc 100644
@@ -37,6 +37,7 @@
 #include <linux/fdtable.h>
 #include <linux/iocontext.h>
 #include <linux/key.h>
+#include <linux/kmsan.h>
 #include <linux/binfmts.h>
 #include <linux/mman.h>
 #include <linux/mmu_notifier.h>
@@ -931,6 +932,7 @@ static struct task_struct *dup_task_struct(struct task_struct *orig, int node)
      account_kernel_stack(tsk, 1);
 
      kcov_task_init(tsk);
+     kmsan_task_create(tsk);
 
 #ifdef CONFIG_FAULT_INJECTION
      tsk->fail_nth = 0;
kernel/kthread.c +2 -0
index 621467c33fef..9b948559a34a 100644
@@ -17,6 +17,7 @@
 #include <linux/unistd.h>
 #include <linux/file.h>
 #include <linux/export.h>
+#include <linux/kmsan.h>
 #include <linux/mutex.h>
 #include <linux/slab.h>
 #include <linux/freezer.h>
@@ -350,6 +351,7 @@ struct task_struct *__kthread_create_on_node(int (*threadfn)(void *data),
              set_cpus_allowed_ptr(task, cpu_all_mask);
      }
      kfree(create);
+     kmsan_task_create(task);
      return task;
 }
 
kernel/printk/printk.c +7 -1
index ca65327a6de8..f77fdcb5f861 100644
@@ -1914,7 +1914,12 @@ int vprintk_store(int facility, int level,
       * The printf needs to come first; we need the syslog
       * prefix which might be passed-in as a parameter.
       */
-     text_len = vscnprintf(text, sizeof(textbuf), fmt, args);
+     /*
+      * We've checked the printk arguments in vprintk_emit() already.
+      * Initialize |text_len| to prevent the errors from spreading.
+      */
+     text_len = KMSAN_INIT_VALUE(vscnprintf(text, sizeof(textbuf), fmt,
+                                            args));
 
      /* mark and strip a trailing newline */
      if (text_len && text[text_len-1] == '\n') {
@@ -1972,6 +1977,7 @@ asmlinkage int vprintk_emit(int facility, int level,
      boot_delay_msec(level);
      printk_delay();
 
+     kmsan_handle_vprintk(&fmt, args);
      /* This stops the holder of console_sem just where we want him */
      logbuf_lock_irqsave(flags);
      curr_log_seq = log_next_seq;
kernel/softirq.c +5 -0
index 0427a86743a4..6d566dd68b35 100644
@@ -11,6 +11,7 @@
 
 #include <linux/export.h>
 #include <linux/kernel_stat.h>
+#include <linux/kmsan.h>
 #include <linux/interrupt.h>
 #include <linux/init.h>
 #include <linux/mm.h>
@@ -370,7 +371,9 @@ static inline void invoke_softirq(void)
               * it is the irq stack, because it should be near empty
               * at this stage.
               */
+             kmsan_softirq_enter();
              __do_softirq();
+             kmsan_softirq_exit();
 #else
              /*
               * Otherwise, irq_exit() is called on the task stack that can
@@ -600,7 +603,9 @@ static void run_ksoftirqd(unsigned int cpu)
               * We can safely run softirq on inline stack, as we are not deep
               * in the task stack here.
               */
+             kmsan_softirq_enter();
              __do_softirq();
+             kmsan_softirq_exit();
              local_irq_enable();
              cond_resched();
              return;
lib/ioremap.c +5 -0
index 0a2ffadc6d71..5f830cee5bfc 100644
@@ -6,6 +6,7 @@
  *
  * (C) Copyright 1995 1996 Linus Torvalds
  */
+#include <linux/kmsan.h>
 #include <linux/vmalloc.h>
 #include <linux/mm.h>
 #include <linux/sched.h>
@@ -214,6 +215,8 @@ int ioremap_page_range(unsigned long addr,
      unsigned long start;
      unsigned long next;
      int err;
+     unsigned long old_addr = addr;
+     phys_addr_t old_phys_addr = phys_addr;
 
      might_sleep();
      BUG_ON(addr >= end);
@@ -228,6 +231,8 @@ int ioremap_page_range(unsigned long addr,
      } while (pgd++, phys_addr += (next - addr), addr = next, addr != end);
 
      flush_cache_vmap(start, end);
+     if (!err)
+             kmsan_ioremap_page_range(old_addr, end, old_phys_addr, prot);
 
      return err;
 }
mm/compaction.c +9 -0
index ce08b39d85d4..e9a5898e5fe0 100644
@@ -84,6 +84,15 @@ static void split_map_pages(struct list_head *list)
 
              for (i = 0; i < nr_pages; i++) {
                      list_add(&page->lru, &tmp_list);
+#ifdef CONFIG_KMSAN
+                     /*
+                      * TODO(glider): we may lose the metadata when copying
+                      * something to these pages. Need to allocate shadow
+                      * and origin pages here instead.
+                      */
+                     page->shadow = NULL;
+                     page->origin = NULL;
+#endif
                      page++;
              }
      }
mm/gup.c +3 -0
index 23a9f9c9d377..c16e4288997d 100644
@@ -4,6 +4,7 @@
 #include <linux/err.h>
 #include <linux/spinlock.h>
 
+#include <linux/kmsan.h>
 #include <linux/mm.h>
 #include <linux/memremap.h>
 #include <linux/pagemap.h>
@@ -2347,6 +2348,7 @@ int __get_user_pages_fast(unsigned long start, int nr_pages, int write,
          gup_fast_permitted(start, end)) {
              local_irq_save(flags);
              gup_pgd_range(start, end, write ? FOLL_WRITE : 0, pages, &nr);
+             kmsan_gup_pgd_range(pages, nr);
              local_irq_restore(flags);
      }
 
@@ -2416,6 +2418,7 @@ int get_user_pages_fast(unsigned long start, int nr_pages,
          gup_fast_permitted(start, end)) {
              local_irq_disable();
              gup_pgd_range(addr, end, gup_flags, pages, &nr);
+             kmsan_gup_pgd_range(pages, nr);
              local_irq_enable();
              ret = nr;
      }
mm/memory.c +2 -0
index b1ca51a079f2..48ceacc06e2d 100644
@@ -51,6 +51,7 @@
 #include <linux/highmem.h>
 #include <linux/pagemap.h>
 #include <linux/memremap.h>
+#include <linux/kmsan.h>
 #include <linux/ksm.h>
 #include <linux/rmap.h>
 #include <linux/export.h>
@@ -2328,6 +2329,7 @@ static vm_fault_t wp_page_copy(struct vm_fault *vmf)
              if (!new_page)
                      goto oom;
              cow_user_page(new_page, old_page, vmf->address, vma);
+             kmsan_copy_page_meta(new_page, old_page);
      }
 
      if (mem_cgroup_try_charge_delay(new_page, mm, GFP_KERNEL, &memcg, false))
mm/page_alloc.c +16 -0
index c0b2e0306720..f52494e0dcd9 100644
@@ -26,6 +26,8 @@
 #include <linux/compiler.h>
 #include <linux/kernel.h>
 #include <linux/kasan.h>
+#include <linux/kmsan.h>
+#include <linux/kmsan-checks.h>
 #include <linux/module.h>
 #include <linux/suspend.h>
 #include <linux/pagevec.h>
@@ -1133,6 +1135,7 @@ static __always_inline bool free_pages_prepare(struct page *page,
      VM_BUG_ON_PAGE(PageTail(page), page);
 
      trace_mm_page_free(page, order);
+     kmsan_free_page(page, order);
 
      /*
       * Check tail pages before head page information is cleared to
@@ -3121,6 +3124,7 @@ void split_page(struct page *page, unsigned int order)
      VM_BUG_ON_PAGE(PageCompound(page), page);
      VM_BUG_ON_PAGE(!page_count(page), page);
 
+     kmsan_split_page(page, order);
      for (i = 1; i < (1 << order); i++)
              set_page_refcounted(page + i);
      split_page_owner(page, order);
@@ -3253,6 +3257,13 @@ static struct page *rmqueue_pcplist(struct zone *preferred_zone,
 /*
  * Allocate a page from the given zone. Use pcplists for order-0 allocations.
  */
+/*
+ * TODO(glider): rmqueue() may call __msan_poison_alloca() through a call to
+ * set_pfnblock_flags_mask(). If __msan_poison_alloca() attempts to allocate
+ * pages for the stack depot, it may call rmqueue() again, which will result
+ * in a deadlock.
+ */
+__no_sanitize_memory
 static inline
 struct page *rmqueue(struct zone *preferred_zone,
                      struct zone *zone, unsigned int order,
@@ -4779,6 +4790,11 @@ __alloc_pages_nodemask(gfp_t gfp_mask, unsigned int order, int preferred_nid,
 
      trace_mm_page_alloc(page, order, alloc_mask, ac.migratetype);
 
+     if (page)
+             if (kmsan_alloc_page(page, order, gfp_mask)) {
+                     __free_pages(page, order);
+                     page = NULL;
+             }
      return page;
 }
 EXPORT_SYMBOL(__alloc_pages_nodemask);
mm/vmalloc.c +21 -2
index a3c70e275f4e..bdf66ffcf02c 100644
@@ -29,6 +29,7 @@
 #include <linux/rcupdate.h>
 #include <linux/pfn.h>
 #include <linux/kmemleak.h>
+#include <linux/kmsan.h>
 #include <linux/atomic.h>
 #include <linux/compiler.h>
 #include <linux/llist.h>
@@ -119,7 +120,8 @@ static void vunmap_p4d_range(pgd_t *pgd, unsigned long addr, unsigned long end)
      } while (p4d++, addr = next, addr != end);
 }
 
-static void vunmap_page_range(unsigned long addr, unsigned long end)
+/* Exported for KMSAN, visible in mm/kmsan/kmsan.h only. */
+void __vunmap_page_range(unsigned long addr, unsigned long end)
 {
      pgd_t *pgd;
      unsigned long next;
@@ -133,6 +135,12 @@ static void vunmap_page_range(unsigned long addr, unsigned long end)
              vunmap_p4d_range(pgd, addr, next);
      } while (pgd++, addr = next, addr != end);
 }
+EXPORT_SYMBOL(__vunmap_page_range);
+static void vunmap_page_range(unsigned long addr, unsigned long end)
+{
+     kmsan_vunmap_page_range(addr, end);
+     __vunmap_page_range(addr, end);
+}
 
 static int vmap_pte_range(pmd_t *pmd, unsigned long addr,
              unsigned long end, pgprot_t prot, struct page **pages, int *nr)
@@ -216,8 +224,11 @@ static int vmap_p4d_range(pgd_t *pgd, unsigned long addr,
  * will have pfns corresponding to the "pages" array.
  *
  * Ie. pte at addr+N*PAGE_SIZE shall point to pfn corresponding to pages[N]
+ *
+ * This function is exported for use in KMSAN, but is only declared in KMSAN
+ * headers.
  */
-static int vmap_page_range_noflush(unsigned long start, unsigned long end,
+int __vmap_page_range_noflush(unsigned long start, unsigned long end,
                                 pgprot_t prot, struct page **pages)
 {
      pgd_t *pgd;
@@ -237,6 +248,14 @@ static int vmap_page_range_noflush(unsigned long start, unsigned long end,
 
      return nr;
 }
+EXPORT_SYMBOL(__vmap_page_range_noflush);
+
+static int vmap_page_range_noflush(unsigned long start, unsigned long end,
+                                pgprot_t prot, struct page **pages)
+{
+     kmsan_vmap_page_range_noflush(start, end, prot, pages);
+     return __vmap_page_range_noflush(start, end, prot, pages);
+}
 
 static int vmap_page_range(unsigned long start, unsigned long end,
                         pgprot_t prot, struct page **pages)
net/sched/sch_generic.c +2 -0
index 17bd8f539bc7..fd22c4a4ba42 100644
@@ -11,6 +11,7 @@
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
+#include <linux/kmsan-checks.h>
 #include <linux/sched.h>
 #include <linux/string.h>
 #include <linux/errno.h>
@@ -659,6 +660,7 @@ static struct sk_buff *pfifo_fast_dequeue(struct Qdisc *qdisc)
      } else {
              qdisc->empty = true;
      }
+     kmsan_check_skb(skb);
 
      return skb;
 }
Signature · 10 lines
Alexander Potapenko
Software Engineer

Google Germany GmbH
Erika-Mann-Straße, 33
80636 München

Geschäftsführer: Paul Manicle, Halimah DeLaine Prado
Registergericht und -nummer: Hamburg, HRB 86891
Sitz der Gesellschaft: Hamburg
#39 lore raw

Re: [PATCH RFC v1 19/26] kmsan: call KMSAN hooks where needed

Quoted 18 lines · Alexander Potapenko
Quoted 10 lines · Qian Cai
Quoted 4 lines · On Fri, 2019-10-18 at 11:42 +0200, [email protected] wrote:

Insert KMSAN hooks that check for potential memory errors and/or make necessary bookkeeping changes:
- allocate/split/deallocate metadata pages in alloc_pages()/split_page()/free_page();

This also seems unnecessary where there are options like page_poison and debug_pagealloc should be able detect uninitialized memory access in the page allocator as well. Even KASAN has some of the functionality.

I still believe there's some misunderstanding between us because I didn't CC you on KMSAN runtime or the documentation.
I'll do this and will be happy to answer questions if you still have them. Without these hooks, KMSAN will be virtually unusable, because it won't know about heap allocations.

Quoted 5 lines

When a buffer is allocated on heap, KMSAN updates the metadata for that region of memory, writing 0xFF (meaning uninitialized) to every shadow byte and a stack ID to every 4 region bytes.
Note that shadow and origin bytes are stored separately and are generally incompatible with what debug pagealloc/page poison do.

That makes more sense to me now. I was a bit worry about some of those options could stub one's toe with KMSAN, but I had shared with you the MM debug config in another email before, so you could try it out, so I don't need to spam you later once this hit the linux-next.

maintainer
#40 lore raw

Re: [PATCH RFC v1 19/26] kmsan: call KMSAN hooks where needed

In reply to [email protected]
Quoted 9 lines · On Fri 2019-10-18 11:42:57, [email protected] wrote:

Insert KMSAN hooks that check for potential memory errors and/or make necessary bookkeeping changes:
- allocate/split/deallocate metadata pages in alloc_pages()/split_page()/free_page();
- clear page shadow and origins in clear_page(), copy_user_highpage();
- copy page metadata in copy_highpage(), wp_page_copy();
- handle vmap()/vunmap()/iounmap();
- handle task creation and deletion;
- handle vprintk();

I looked only at the printk part.

Quoted 4 lines

- call softirq entry/exit hooks in kernel/softirq.c;
- check/initialize memory sent to/read from USB, I2C, and network

Quoted 9 lines

To: Alexander Potapenko <[email protected]>
Cc: Andrew Morton <[email protected]>
Cc: Greg Kroah-Hartman <[email protected]>
Cc: Eric Dumazet <[email protected]>
Cc: Wolfram Sang <[email protected]>
Cc: Petr Mladek <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]

Could you please add into CC also the other printk co-maitainers?

+ Sergey Senozhatsky <[email protected]>
+ Steven Rostedt <[email protected]>

Quoted 5 lines
kernel/printk/printk.c +0 -0
index ca65327a6de8..f77fdcb5f861 100644

kernel/printk/printk.c int vprintk_store(int facility, int level, · line 1922

Quoted 10 lines
kernel/printk/printk.c +6 -1
@@ -1914,7 +1914,12 @@ int vprintk_store(int facility, int level,
 	 * The printf needs to come first; we need the syslog
 	 * prefix which might be passed-in as a parameter.
 	 */
-	text_len = vscnprintf(text, sizeof(textbuf), fmt, args);
+	/*
+	 * We've checked the printk arguments in vprintk_emit() already.
+	 * Initialize |text_len| to prevent the errors from spreading.
+	 */
+	text_len = KMSAN_INIT_VALUE(vscnprintf(text, sizeof(textbuf), fmt,
+					       args));

I am a bit confused by the comment.

What is the exact meaning of KMSAN_INIT_VALUE(), please? Does it prevent checking fmt again?
Does make the text_len variable special? In which way?

kernel/printk/printk.c asmlinkage int vprintk_emit(int facility, int level, · line 1980

Quoted 7 lines
kernel/printk/printk.c +1 -0
 	/* mark and strip a trailing newline */
 	if (text_len && text[text_len-1] == '\n') {
@@ -1972,6 +1977,7 @@ asmlinkage int vprintk_emit(int facility, int level,
 	boot_delay_msec(level);
 	printk_delay();
 
+	kmsan_handle_vprintk(&fmt, args);

What does this function, please?
Could I find more details in another patch?

kernel/printk/printk.c asmlinkage int vprintk_emit(int facility, int level, · line 1983

Quoted 3 lines
kernel/printk/printk.c +0 -0
 	/* This stops the holder of console_sem just where we want him */
 	logbuf_lock_irqsave(flags);
 	curr_log_seq = log_next_seq;

Best Regards,
Petr

author maintainer
#41 lore raw

Re: [PATCH RFC v1 19/26] kmsan: call KMSAN hooks where needed

Quoted 28 lines · Petr Mladek
Quoted 9 lines · On Fri 2019-10-18 11:42:57, [email protected] wrote:

Insert KMSAN hooks that check for potential memory errors and/or make necessary bookkeeping changes:
- allocate/split/deallocate metadata pages in alloc_pages()/split_page()/free_page();
- clear page shadow and origins in clear_page(), copy_user_highpage();
- copy page metadata in copy_highpage(), wp_page_copy();
- handle vmap()/vunmap()/iounmap();
- handle task creation and deletion;
- handle vprintk();

I looked only at the printk part.

Quoted 4 lines

- call softirq entry/exit hooks in kernel/softirq.c;
- check/initialize memory sent to/read from USB, I2C, and network

Quoted 9 lines

To: Alexander Potapenko <[email protected]>
Cc: Andrew Morton <[email protected]>
Cc: Greg Kroah-Hartman <[email protected]>
Cc: Eric Dumazet <[email protected]>
Cc: Wolfram Sang <[email protected]>
Cc: Petr Mladek <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]

Quoted 1 line

Could you please add into CC also the other printk co-maitainers?

Will do in the next version of this patch.

Quoted 13 lines

+ Sergey Senozhatsky <[email protected]>
+ Steven Rostedt <[email protected]>

Quoted 9 lines
kernel/printk/printk.c +0 -1
index ca65327a6de8..f77fdcb5f861 100644
@@ -1914,7 +1914,12 @@ int vprintk_store(int facility, int level,
       * The printf needs to come first; we need the syslog
       * prefix which might be passed-in as a parameter.
       */
-     text_len = vscnprintf(text, sizeof(textbuf), fmt, args);
Quoted 8 lines

kernel/printk/printk.c int vprintk_store(int facility, int level, · line 1922

Quoted 6 lines
kernel/printk/printk.c +6 -0
+     /*
+      * We've checked the printk arguments in vprintk_emit() already.
+      * Initialize |text_len| to prevent the errors from spreading.
+      */
+     text_len = KMSAN_INIT_VALUE(vscnprintf(text, sizeof(textbuf), fmt,
+                                            args));

I am a bit confused by the comment.

Fixed in v2.

Quoted 1 line

What is the exact meaning of KMSAN_INIT_VALUE(), please?

Hope it'll be clear from the "kmsan: add KMSAN runtime" patch. In fact KMSAN_INIT_VALUE is a wrapper that turns an uninitialized value into an initialized one:
https://github.com/google/kmsan/blob/3207d604ad68f7e5defdbbd6e63cb97750f380c1/include/linux/kmsan-checks.h#L58
It passes the value into an identity function that always returns unpoisoned values.
As a result, from now on text_len is considered initialized, regardless of what vscnprintf() returned.
Without KMSAN_INIT_VALUE the tool will report a huge number of errors when text_len is used in vprintk_store() and log_output().

Quoted 21 lines

Does it prevent checking fmt again?
Does make the text_len variable special? In which way?

kernel/printk/printk.c asmlinkage int vprintk_emit(int facility, int level, · line 1980

Quoted 7 lines
kernel/printk/printk.c +1 -0
      /* mark and strip a trailing newline */
      if (text_len && text[text_len-1] == '\n') {
@@ -1972,6 +1977,7 @@ asmlinkage int vprintk_emit(int facility, int level,
      boot_delay_msec(level);
      printk_delay();
 
+     kmsan_handle_vprintk(&fmt, args);

What does this function, please?
Could I find more details in another patch?

kernel/printk/printk.c asmlinkage int vprintk_emit(int facility, int level, · line 1983

Quoted 3 lines
kernel/printk/printk.c +0 -0
      /* This stops the holder of console_sem just where we want him */
      logbuf_lock_irqsave(flags);
      curr_log_seq = log_next_seq;

Best Regards,
Petr

Signature · 10 lines
Alexander Potapenko
Software Engineer

Google Germany GmbH
Erika-Mann-Straße, 33
80636 München

Geschäftsführer: Paul Manicle, Halimah DeLaine Prado
Registergericht und -nummer: Hamburg, HRB 86891
Sitz der Gesellschaft: Hamburg
author maintainer
#42 lore raw

RFC PATCH 20/26 kmsan: disable instrumentation of certain functions

In reply to [email protected]

Some functions are called from handwritten assembly, and therefore don't have their arguments' metadata fully set up by the instrumentation code. Mark them with __no_sanitize_memory to avoid false positives from spreading further.
Certain functions perform task switching, so that the value of |current| is different as they proceed. Because KMSAN state pointer is only read once at the beginning of the function, touching it after |current| has changed may be dangerous.

To: Alexander Potapenko <[email protected]>
Cc: Thomas Gleixner <[email protected]>
Cc: Andrew Morton <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]
---

Change-Id: I684d23dac5a22eb0a4cea71993cb934302b17cea

---
 arch/x86/entry/common.c                |  1 +
 arch/x86/include/asm/irq_regs.h        |  1 +
 arch/x86/include/asm/syscall_wrapper.h |  1 +
 arch/x86/kernel/apic/apic.c            |  2 ++
 arch/x86/kernel/dumpstack_64.c         |  1 +
 arch/x86/kernel/process_64.c           |  5 +++++
 arch/x86/kernel/traps.c                | 12 ++++++++++--
 arch/x86/kernel/uprobes.c              |  7 ++++++-
 kernel/profile.c                       |  1 +
 kernel/sched/core.c                    | 11 +++++++++++
 10 files changed, 39 insertions(+), 3 deletions(-)
arch/x86/entry/common.c +1 -0
index 3f8e22615812..0dd5b2acb355 100644
@@ -275,6 +275,7 @@ __visible inline void syscall_return_slowpath(struct pt_regs *regs)
 }
 
 #ifdef CONFIG_X86_64
+__no_sanitize_memory
 __visible void do_syscall_64(unsigned long nr, struct pt_regs *regs)
 {
 	struct thread_info *ti;
arch/x86/include/asm/irq_regs.h +1 -0
index 187ce59aea28..d65a00bd6f02 100644
@@ -14,6 +14,7 @@
 
 DECLARE_PER_CPU(struct pt_regs *, irq_regs);
 
+__no_sanitize_memory
 static inline struct pt_regs *get_irq_regs(void)
 {
 	return __this_cpu_read(irq_regs);
arch/x86/include/asm/syscall_wrapper.h +1 -0
index e046a405743d..43910ce1b53b 100644
@@ -159,6 +159,7 @@
 	ALLOW_ERROR_INJECTION(__x64_sys##name, ERRNO);			\
 	static long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__));	\
 	static inline long __do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__));\
+	__no_sanitize_memory						\
 	asmlinkage long __x64_sys##name(const struct pt_regs *regs)	\
 	{								\
 		return __se_sys##name(SC_X86_64_REGS_TO_ARGS(x,__VA_ARGS__));\
arch/x86/kernel/apic/apic.c +2 -0
index 9e2dd2b296cd..43a1edd919ea 100644
@@ -1118,6 +1118,8 @@ static void local_apic_timer_interrupt(void)
  * [ if a single-CPU system runs an SMP kernel then we call the local
  *   interrupt as well. Thus we cannot inline the local irq ... ]
  */
+/* TODO(glider): |regs| is uninitialized, so is |*regs|. */
+__no_sanitize_memory
 __visible void __irq_entry smp_apic_timer_interrupt(struct pt_regs *regs)
 {
 	struct pt_regs *old_regs = set_irq_regs(regs);
arch/x86/kernel/dumpstack_64.c +1 -0
index 753b8cfe8b8a..ba883d282a43 100644
@@ -143,6 +143,7 @@ static bool in_irq_stack(unsigned long *stack, struct stack_info *info)
 	return true;
 }
 
+__no_sanitize_memory
 int get_stack_info(unsigned long *stack, struct task_struct *task,
 		   struct stack_info *info, unsigned long *visit_mask)
 {
arch/x86/kernel/process_64.c +5 -0
index af64519b2695..27649ee3dbf8 100644
@@ -500,6 +500,11 @@ void compat_start_thread(struct pt_regs *regs, u32 new_ip, u32 new_sp)
  * Kprobes not supported here. Set the probe on schedule instead.
  * Function graph tracer not supported too.
  */
+/*
+ * TODO(glider): __switch_to() does weird things with tasks, don't report
+ * anything here (also avoid touching the KMSAN state).
+ */
+__no_sanitize_memory
 __visible __notrace_funcgraph struct task_struct *
 __switch_to(struct task_struct *prev_p, struct task_struct *next_p)
 {
arch/x86/kernel/traps.c +10 -2
index 4bb0f8447112..a94282d1f60b 100644
@@ -618,7 +618,10 @@ NOKPROBE_SYMBOL(do_int3);
  * Help handler running on a per-cpu (IST or entry trampoline) stack
  * to switch to the normal thread stack if the interrupted code was in
  * user mode. The actual stack switch is done in entry_64.S
+ *
+ * This function switches the registers - don't instrument it with KMSAN!
  */
+__no_sanitize_memory
 asmlinkage __visible notrace struct pt_regs *sync_regs(struct pt_regs *eregs)
 {
 	struct pt_regs *regs = (struct pt_regs *)this_cpu_read(cpu_current_top_of_stack) - 1;
@@ -634,6 +637,11 @@ struct bad_iret_stack {
 };
 
 asmlinkage __visible notrace
+/*
+ * Dark magic happening here, let's not instrument this function.
+ * Also avoid copying any metadata by using raw __memmove().
+ */
+__no_sanitize_memory
 struct bad_iret_stack *fixup_bad_iret(struct bad_iret_stack *s)
 {
 	/*
@@ -648,10 +656,10 @@ struct bad_iret_stack *fixup_bad_iret(struct bad_iret_stack *s)
 		(struct bad_iret_stack *)this_cpu_read(cpu_tss_rw.x86_tss.sp0) - 1;
 
 	/* Copy the IRET target to the new stack. */
-	memmove(&new_stack->regs.ip, (void *)s->regs.sp, 5*8);
+	__memmove(&new_stack->regs.ip, (void *)s->regs.sp, 5*8);
 
 	/* Copy the remainder of the stack from the current stack. */
-	memmove(new_stack, s, offsetof(struct bad_iret_stack, regs.ip));
+	__memmove(new_stack, s, offsetof(struct bad_iret_stack, regs.ip));
 
 	BUG_ON(!user_mode(&new_stack->regs));
 	return new_stack;
arch/x86/kernel/uprobes.c +6 -1
index 8cd745ef8c7b..bcd4bf5a909f 100644
@@ -8,6 +8,7 @@
  *	Jim Keniston
  */
 #include <linux/kernel.h>
+#include <linux/kmsan-checks.h>
 #include <linux/sched.h>
 #include <linux/ptrace.h>
 #include <linux/uprobes.h>
@@ -997,9 +998,13 @@ int arch_uprobe_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
 int arch_uprobe_exception_notify(struct notifier_block *self, unsigned long val, void *data)
 {
 	struct die_args *args = data;
-	struct pt_regs *regs = args->regs;
+	struct pt_regs *regs;
 	int ret = NOTIFY_DONE;
 
+	kmsan_unpoison_shadow(args, sizeof(*args));
+	regs = args->regs;
+	if (regs)
+		kmsan_unpoison_shadow(regs, sizeof(*regs));
 	/* We are only interested in userspace traps */
 	if (regs && !user_mode(regs))
 		return NOTIFY_DONE;
kernel/profile.c +1 -0
index af7c94bf5fa1..835a5b66d1a4 100644
@@ -399,6 +399,7 @@ void profile_hits(int type, void *__pc, unsigned int nr_hits)
 }
 EXPORT_SYMBOL_GPL(profile_hits);
 
+__no_sanitize_memory
 void profile_tick(int type)
 {
 	struct pt_regs *regs = get_irq_regs();
kernel/sched/core.c +11 -0
index dd05a378631a..951d19d217d7 100644
@@ -475,6 +475,8 @@ void wake_q_add_safe(struct wake_q_head *head, struct task_struct *task)
 		put_task_struct(task);
 }
 
+/* TODO(glider): context switching here. */
+__no_sanitize_memory
 void wake_up_q(struct wake_q_head *head)
 {
 	struct wake_q_node *node = head->first;
@@ -3180,6 +3182,8 @@ prepare_task_switch(struct rq *rq, struct task_struct *prev,
  * past. prev == current is still correct but we need to recalculate this_rq
  * because prev may have moved to another CPU.
  */
+/* TODO(glider): |current| changes here. */
+__no_sanitize_memory
 static struct rq *finish_task_switch(struct task_struct *prev)
 	__releases(rq->lock)
 {
@@ -3986,6 +3990,8 @@ pick_next_task(struct rq *rq, struct task_struct *prev, struct rq_flags *rf)
  *
  * WARNING: must be called with preemption disabled!
  */
+/* TODO(glider): |current| changes here. */
+__no_sanitize_memory
 static void __sched notrace __schedule(bool preempt)
 {
 	struct task_struct *prev, *next;
@@ -4605,6 +4611,8 @@ int task_prio(const struct task_struct *p)
  *
  * Return: 1 if the CPU is currently idle. 0 otherwise.
  */
+/* TODO(glider): nothing to report here. */
+__no_sanitize_memory
 int idle_cpu(int cpu)
 {
 	struct rq *rq = cpu_rq(cpu);
@@ -6544,6 +6552,7 @@ static struct kmem_cache *task_group_cache __read_mostly;
 DECLARE_PER_CPU(cpumask_var_t, load_balance_mask);
 DECLARE_PER_CPU(cpumask_var_t, select_idle_mask);
 
+__no_sanitize_memory
 void __init sched_init(void)
 {
 	unsigned long ptr = 0;
@@ -6716,6 +6725,8 @@ static inline int preempt_count_equals(int preempt_offset)
 	return (nested == preempt_offset);
 }
 
+/* TODO(glider): the args are most certainly initialized. */
+__no_sanitize_memory
 void __might_sleep(const char *file, int line, int preempt_offset)
 {
 	/*
Signature · 1 line
2.23.0.866.gb869b98d4c-goog
author maintainer
#43 lore raw

RFC PATCH 21/26 kmsan: unpoison |tlb| in arch_tlb_gather_mmu()

In reply to [email protected]

This is a hack to reduce stackdepot pressure.

To: Alexander Potapenko <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]

---

Change-Id: I22a201e7e4f67ed74f8129072f12e5351b26103a

---
 mm/mmu_gather.c | 10 ++++++++++
 1 file changed, 10 insertions(+)
mm/mmu_gather.c +10 -0
index 7d70e5c78f97..8c5ea2d2e7d5 100644
@@ -1,6 +1,7 @@
 #include <linux/gfp.h>
 #include <linux/highmem.h>
 #include <linux/kernel.h>
+#include <linux/kmsan-checks.h>
 #include <linux/mmdebug.h>
 #include <linux/mm_types.h>
 #include <linux/pagemap.h>
@@ -206,6 +207,15 @@ void tlb_flush_mmu(struct mmu_gather *tlb)
 void tlb_gather_mmu(struct mmu_gather *tlb, struct mm_struct *mm,
 			unsigned long start, unsigned long end)
 {
+	/*
+	 * TODO(glider): struct mmu_gather contains 7 1-bit fields packed into a
+	 * 32-bit unsigned int value. The remaining 25 bits remain uninitialized
+	 * and are never used, but KMSAN updates the origin for them in
+	 * zap_pXX_range() in mm/memory.c, thus creating very long origin
+	 * chains. This is technically correct, but consumes too much memory.
+	 * Unpoisoning the whole structure will prevent creating such chains.
+	 */
+	kmsan_unpoison_shadow(tlb, sizeof(*tlb));
 	tlb->mm = mm;
 
 	/* Is it from 0 to ~0? */
Signature · 1 line
2.23.0.866.gb869b98d4c-goog
author maintainer
#44 lore raw

RFC PATCH 22/26 kmsan: use __msan_memcpy() where possible.

In reply to [email protected]

Unless stated otherwise (by explicitly calling __memcpy()) we want all memcpy() calls to call __msan_memcpy() so that shadow and origin values are updated accordingly.

To: Alexander Potapenko <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]
---

Change-Id: Ib2512ce5aa8d457453dd38caa12f58f002166813

---
 arch/x86/include/asm/string_64.h | 9 ++++++++-
 include/linux/compiler.h         | 9 ++++++++-
 include/linux/string.h           | 2 ++
 3 files changed, 18 insertions(+), 2 deletions(-)
arch/x86/include/asm/string_64.h +8 -1
index 75314c3dbe47..d3c76d910c23 100644
@@ -11,7 +11,13 @@
    function. */
 
 #define __HAVE_ARCH_MEMCPY 1
+#if defined(CONFIG_KMSAN)
+#undef memcpy
+/* __msan_memcpy() is defined in compiler.h */
+#define memcpy(dst, src, len) __msan_memcpy(dst, src, len)
+#else
 extern void *memcpy(void *to, const void *from, size_t len);
+#endif
 extern void *__memcpy(void *to, const void *from, size_t len);
 
 #define __HAVE_ARCH_MEMSET
@@ -64,7 +70,8 @@ char *strcpy(char *dest, const char *src);
 char *strcat(char *dest, const char *src);
 int strcmp(const char *cs, const char *ct);
 
-#if defined(CONFIG_KASAN) && !defined(__SANITIZE_ADDRESS__)
+#if (defined(CONFIG_KASAN) && !defined(__SANITIZE_ADDRESS__)) || \
+	(defined(CONFIG_KMSAN) && !defined(__SANITIZE_MEMORY__))
 
 /*
  * For files that not instrumented (e.g. mm/slub.c) we
include/linux/compiler.h +8 -1
index 4a594bd1bc47..40b1ab13fec7 100644
@@ -179,6 +179,13 @@ void ftrace_likely_update(struct ftrace_likely_data *f, int val,
 
 #include <uapi/linux/types.h>
 
+#ifdef CONFIG_KMSAN
+void *__msan_memcpy(void *dst, const void *src, u64 size);
+#define __DO_MEMCPY(res, p, size) __msan_memcpy(res, p, size)
+#else
+#define __DO_MEMCPY(res, p, size) __builtin_memcpy(res, p, size)
+#endif
+
 #define __READ_ONCE_SIZE						\
 ({									\
 	switch (size) {							\
@@ -188,7 +195,7 @@ void ftrace_likely_update(struct ftrace_likely_data *f, int val,
 	case 8: *(__u64 *)res = *(volatile __u64 *)p; break;		\
 	default:							\
 		barrier();						\
-		__builtin_memcpy((void *)res, (const void *)p, size);	\
+		__DO_MEMCPY((void *)res, (const void *)p, size);	\
 		barrier();						\
 	}								\
 })
include/linux/string.h +2 -0
index b6ccdc2c7f02..5d8ce09cba2e 100644
@@ -363,6 +363,7 @@ __FORTIFY_INLINE void *memset(void *p, int c, __kernel_size_t size)
 	return __builtin_memset(p, c, size);
 }
 
+#ifndef CONFIG_KMSAN
 __FORTIFY_INLINE void *memcpy(void *p, const void *q, __kernel_size_t size)
 {
 	size_t p_size = __builtin_object_size(p, 0);
@@ -377,6 +378,7 @@ __FORTIFY_INLINE void *memcpy(void *p, const void *q, __kernel_size_t size)
 		fortify_panic(__func__);
 	return __builtin_memcpy(p, q, size);
 }
+#endif
 
 __FORTIFY_INLINE void *memmove(void *p, const void *q, __kernel_size_t size)
 {
Signature · 1 line
2.23.0.866.gb869b98d4c-goog
author maintainer
#45 lore raw

RFC PATCH 23/26 kmsan: unpoisoning buffers from devices etc.

In reply to [email protected]

When data is copied to memory from a device KMSAN should treat it as initialized. In most cases it's enough to just unpoison the buffer that is known to come from a device.
In the case with __do_page_cache_readahead() and bio_copy_user_iov() we have to mark the whole pages as ignored by KMSAN, as it's not obvious where these pages are read again.

To: Alexander Potapenko <[email protected]>
Cc: Andrew Morton <[email protected]>
Cc: Jens Axboe <[email protected]>
Cc: "Theodore Ts'o" <[email protected]>
Cc: Dmitry Torokhov <[email protected]>
Cc: Martin K. Petersen <[email protected]>
Cc: "Michael S. Tsirkin" <[email protected]>
Cc: Christoph Hellwig <[email protected]>
Cc: Eric Dumazet <[email protected]>
Cc: Eric Van Hensbergen <[email protected]>
Cc: Takashi Iwai <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]

---
Suggestions on how to avoid scattering too many annotations over the code are welcome. Perhaps annotating DMA reads/writes is enough to get rid of most of these annotations.

Change-Id: Id460e7a86ce564f1357469f53d0c7410ca08f0e9

---
 block/bio.c                  | 20 ++++++++++++++++++++
 block/partition-generic.c    |  9 ++++++++-
 drivers/char/random.c        |  2 ++
 drivers/input/serio/libps2.c |  6 +++++-
 drivers/scsi/scsi_lib.c      |  4 ++++
 drivers/usb/core/message.c   |  6 +++++-
 drivers/virtio/virtio_ring.c | 14 ++++++++++++++
 fs/buffer.c                  |  7 ++++++-
 include/linux/dma-mapping.h  |  2 ++
 include/linux/skbuff.h       |  5 ++++-
 mm/filemap.c                 |  3 +++
 mm/readahead.c               |  6 ++++++
 net/9p/protocol.c            |  2 ++
 sound/core/oss/pcm_oss.c     |  7 +++++++
 14 files changed, 88 insertions(+), 5 deletions(-)
block/bio.c +20 -0
index 8f0ed6228fc5..40773cf4b50c 100644
@@ -11,6 +11,7 @@
 #include <linux/slab.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
+#include <linux/kmsan-checks.h>
 #include <linux/export.h>
 #include <linux/mempool.h>
 #include <linux/workqueue.h>
@@ -900,6 +901,13 @@ static int __bio_iov_iter_get_pages(struct bio *bio, struct iov_iter *iter)
 			__bio_add_page(bio, page, len, offset);
 		}
 		offset = 0;
+		/*
+		 * TODO(glider): these pages will be soon passed to block
+		 * device for reading, so consider them initialized.
+		 */
+		if (iov_iter_rw(iter) == READ)
+			kmsan_unpoison_shadow(page_address(page),
+						PAGE_SIZE);
 	}
 
 	iov_iter_advance(iter, size);
@@ -1274,11 +1282,16 @@ struct bio *bio_copy_user_iov(struct request_queue *q,
 
 			i++;
 		} else {
+			/*
+			 * TODO(glider): KMSAN doesn't track the pages
+			 * allocated for bio here.
+			 */
 			page = alloc_page(q->bounce_gfp | gfp_mask);
 			if (!page) {
 				ret = -ENOMEM;
 				break;
 			}
+			kmsan_ignore_page(page, /*order*/0);
 		}
 
 		if (bio_add_pc_page(q, bio, page, bytes, offset) < bytes) {
@@ -1574,6 +1587,13 @@ struct bio *bio_copy_kern(struct request_queue *q, void *data, unsigned int len,
 		if (!page)
 			goto cleanup;
 
+		/*
+		 * TODO(glider): if we're about to read data from a SCSI device,
+		 * assume the page allocated for that is already initialized.
+		 */
+		if (reading)
+			kmsan_unpoison_shadow(page_address(page), PAGE_SIZE);
+
 		if (!reading)
 			memcpy(page_address(page), p, bytes);
 
block/partition-generic.c +8 -1
index aee643ce13d1..87b3ae9e7727 100644
@@ -663,13 +663,20 @@ unsigned char *read_dev_sector(struct block_device *bdev, sector_t n, Sector *p)
 {
 	struct address_space *mapping = bdev->bd_inode->i_mapping;
 	struct page *page;
+	unsigned char *retval;
 
 	page = read_mapping_page(mapping, (pgoff_t)(n >> (PAGE_SHIFT-9)), NULL);
 	if (!IS_ERR(page)) {
 		if (PageError(page))
 			goto fail;
 		p->v = page;
-		return (unsigned char *)page_address(page) +  ((n & ((1 << (PAGE_SHIFT - 9)) - 1)) << 9);
+		retval = (unsigned char *)page_address(page) +
+			 ((n & ((1 << (PAGE_SHIFT - 9)) - 1)) << 9);
+		/*
+		 * Unpoison sector-sized chunk of memory coming from the device.
+		 */
+		kmsan_unpoison_shadow(retval, SECTOR_SIZE);
+		return retval;
 fail:
 		put_page(page);
 	}
drivers/char/random.c +2 -0
index de434feb873a..cc4afc0f1039 100644
@@ -320,6 +320,7 @@
 #include <linux/fs.h>
 #include <linux/genhd.h>
 #include <linux/interrupt.h>
+#include <linux/kmsan-checks.h>
 #include <linux/mm.h>
 #include <linux/nodemask.h>
 #include <linux/spinlock.h>
@@ -1061,6 +1062,7 @@ static void _extract_crng(struct crng_state *crng,
 	spin_lock_irqsave(&crng->lock, flags);
 	if (arch_get_random_long(&v))
 		crng->state[14] ^= v;
+	kmsan_unpoison_shadow(crng->state, sizeof(crng->state));
 	chacha20_block(&crng->state[0], out);
 	if (crng->state[12] == 0)
 		crng->state[13]++;
drivers/input/serio/libps2.c +5 -1
index a8c94a940a79..80a9e0a9d3c3 100644
@@ -8,6 +8,7 @@
 
 
 #include <linux/delay.h>
+#include <linux/kmsan-checks.h>
 #include <linux/module.h>
 #include <linux/sched.h>
 #include <linux/interrupt.h>
@@ -30,6 +31,7 @@ static int ps2_do_sendbyte(struct ps2dev *ps2dev, u8 byte,
 	int error;
 
 	lockdep_assert_held(&ps2dev->serio->lock);
+	kmsan_check_memory(&byte, 1);
 
 	do {
 		ps2dev->nak = 1;
@@ -294,9 +296,11 @@ int __ps2_command(struct ps2dev *ps2dev, u8 *param, unsigned int command)
 
 	serio_pause_rx(ps2dev->serio);
 
-	if (param)
+	if (param) {
 		for (i = 0; i < receive; i++)
 			param[i] = ps2dev->cmdbuf[(receive - 1) - i];
+		kmsan_unpoison_shadow(param, receive);
+	}
 
 	if (ps2dev->cmdcnt &&
 	    (command != PS2_CMD_RESET_BAT || ps2dev->cmdcnt != 1)) {
drivers/scsi/scsi_lib.c +4 -0
index dc210b9d4896..4235b317ac5f 100644
@@ -14,6 +14,7 @@
 #include <linux/blkdev.h>
 #include <linux/completion.h>
 #include <linux/kernel.h>
+#include <linux/kmsan-checks.h>
 #include <linux/export.h>
 #include <linux/init.h>
 #include <linux/pci.h>
@@ -296,6 +297,9 @@ int __scsi_execute(struct scsi_device *sdev, const unsigned char *cmd,
 	ret = rq->result;
  out:
 	blk_put_request(req);
+	/* TODO(glider): this is a bit rough. */
+	if (data_direction == DMA_FROM_DEVICE)
+		kmsan_unpoison_shadow(buffer, bufflen);
 
 	return ret;
 }
drivers/usb/core/message.c +5 -1
index 5adf489428aa..f1c3f8b3cfae 100644
@@ -9,6 +9,7 @@
 #include <linux/usb.h>
 #include <linux/module.h>
 #include <linux/slab.h>
+#include <linux/kmsan-checks.h>
 #include <linux/mm.h>
 #include <linux/timer.h>
 #include <linux/ctype.h>
@@ -101,8 +102,11 @@ static int usb_internal_control_msg(struct usb_device *usb_dev,
 	retv = usb_start_wait_urb(urb, timeout, &length);
 	if (retv < 0)
 		return retv;
-	else
+	else {
+		/* TODO(glider): USB initializes |length| bytes? */
+		kmsan_unpoison_shadow(data, length);
 		return length;
+	}
 }
 
 /**
drivers/virtio/virtio_ring.c +14 -0
index bdc08244a648..8b7ddc43618f 100644
@@ -11,6 +11,7 @@
 #include <linux/module.h>
 #include <linux/hrtimer.h>
 #include <linux/dma-mapping.h>
+#include <linux/kmsan-checks.h>
 #include <xen/xen.h>
 
 #ifdef DEBUG
@@ -387,6 +388,12 @@ static void vring_unmap_one_split(const struct vring_virtqueue *vq,
 			       (flags & VRING_DESC_F_WRITE) ?
 			       DMA_FROM_DEVICE : DMA_TO_DEVICE);
 	}
+	/*
+	 * Unmapping DMA memory after a transfer from device requires this
+	 * memory to be unpoisoned.
+	 */
+	if (flags & VRING_DESC_F_WRITE)
+		kmsan_unpoison_shadow((const void *)desc->addr, desc->len);
 }
 
 static struct vring_desc *alloc_indirect_split(struct virtqueue *_vq,
@@ -500,6 +507,13 @@ static inline int virtqueue_add_split(struct virtqueue *_vq,
 			desc[i].flags = cpu_to_virtio16(_vq->vdev, VRING_DESC_F_NEXT | VRING_DESC_F_WRITE);
 			desc[i].addr = cpu_to_virtio64(_vq->vdev, addr);
 			desc[i].len = cpu_to_virtio32(_vq->vdev, sg->length);
+			/*
+			 * It's hard to figure out the buffer's address upon
+			 * receive. Instead we unpoison it once, when exposing
+			 * it to the device, and hope nobody else will write to
+			 * it.
+			 */
+			kmsan_unpoison_shadow(sg_virt(sg), sg->length);
 			prev = i;
 			i = virtio16_to_cpu(_vq->vdev, desc[i].next);
 		}
fs/buffer.c +6 -1
index 86a38b979323..c08fb1a77fa1 100644
@@ -1170,14 +1170,17 @@ static struct buffer_head *__bread_slow(struct buffer_head *bh)
 	lock_buffer(bh);
 	if (buffer_uptodate(bh)) {
 		unlock_buffer(bh);
+		kmsan_unpoison_shadow(bh->b_data, bh->b_size);
 		return bh;
 	} else {
 		get_bh(bh);
 		bh->b_end_io = end_buffer_read_sync;
 		submit_bh(REQ_OP_READ, 0, bh);
 		wait_on_buffer(bh);
-		if (buffer_uptodate(bh))
+		if (buffer_uptodate(bh)) {
+			kmsan_unpoison_shadow(bh->b_data, bh->b_size);
 			return bh;
+		}
 	}
 	brelse(bh);
 	return NULL;
@@ -1320,6 +1323,8 @@ __getblk_gfp(struct block_device *bdev, sector_t block,
 	might_sleep();
 	if (bh == NULL)
 		bh = __getblk_slow(bdev, block, size, gfp);
+	if (bh)
+		kmsan_unpoison_shadow(bh->b_data, bh->b_size);
 	return bh;
 }
 EXPORT_SYMBOL(__getblk_gfp);
include/linux/dma-mapping.h +2 -0
index 4a1c4fca475a..cc7b05eb024f 100644
@@ -8,6 +8,7 @@
 #include <linux/err.h>
 #include <linux/dma-debug.h>
 #include <linux/dma-direction.h>
+#include <linux/kmsan-checks.h>
 #include <linux/scatterlist.h>
 #include <linux/bug.h>
 #include <linux/mem_encrypt.h>
@@ -281,6 +282,7 @@ static inline dma_addr_t dma_map_page_attrs(struct device *dev,
 	const struct dma_map_ops *ops = get_dma_ops(dev);
 	dma_addr_t addr;
 
+	kmsan_unpoison_shadow(page_address(page), size);
 	BUG_ON(!valid_dma_direction(dir));
 	if (dma_is_direct(ops))
 		addr = dma_direct_map_page(dev, page, offset, size, dir, attrs);
include/linux/skbuff.h +4 -1
index 4351577b14d7..b8e9cd697248 100644
@@ -11,6 +11,7 @@
 #define _LINUX_SKBUFF_H
 
 #include <linux/kernel.h>
+#include <linux/kmsan-checks.h>
 #include <linux/compiler.h>
 #include <linux/time.h>
 #include <linux/bug.h>
@@ -2214,6 +2215,8 @@ static inline void *skb_put_data(struct sk_buff *skb, const void *data,
 {
 	void *tmp = skb_put(skb, len);
 
+	/* Unpoison the data received from the network device. */
+	kmsan_unpoison_shadow(data, len);
 	memcpy(tmp, data, len);
 
 	return tmp;
@@ -2221,7 +2224,7 @@ static inline void *skb_put_data(struct sk_buff *skb, const void *data,
 
 static inline void skb_put_u8(struct sk_buff *skb, u8 val)
 {
-	*(u8 *)skb_put(skb, 1) = val;
+	*(u8 *)skb_put(skb, 1) = KMSAN_INIT_VALUE(val);
 }
 
 void *skb_push(struct sk_buff *skb, unsigned int len);
mm/filemap.c +3 -0
index 1146fcfa3215..8a17f15806cc 100644
@@ -18,6 +18,7 @@
 #include <linux/uaccess.h>
 #include <linux/capability.h>
 #include <linux/kernel_stat.h>
+#include <linux/kmsan-checks.h>
 #include <linux/gfp.h>
 #include <linux/mm.h>
 #include <linux/swap.h>
@@ -2810,6 +2811,8 @@ static struct page *do_read_cache_page(struct address_space *mapping,
 		page = wait_on_page_read(page);
 		if (IS_ERR(page))
 			return page;
+		/* Assume all pages in page cache are initialized. */
+		kmsan_unpoison_shadow(page_address(page), PAGE_SIZE);
 		goto out;
 	}
 	if (PageUptodate(page))
mm/readahead.c +6 -0
index 2fe72cd29b47..1fe61128095b 100644
@@ -193,7 +193,13 @@ unsigned int __do_page_cache_readahead(struct address_space *mapping,
 			continue;
 		}
 
+		/*
+		 * The easiest way to handle these pages is to mark them
+		 * untracked by KMSAN, assuming they're never used by anything
+		 * else.
+		 */
 		page = __page_cache_alloc(gfp_mask);
+		kmsan_ignore_page(page, /*order*/0);
 		if (!page)
 			break;
 		page->index = page_offset;
net/9p/protocol.c +2 -0
index 03593eb240d8..5d88ed181422 100644
@@ -13,6 +13,7 @@
 #include <linux/module.h>
 #include <linux/errno.h>
 #include <linux/kernel.h>
+#include <linux/kmsan-checks.h>
 #include <linux/uaccess.h>
 #include <linux/slab.h>
 #include <linux/sched.h>
@@ -46,6 +47,7 @@ EXPORT_SYMBOL(p9stat_free);
 size_t pdu_read(struct p9_fcall *pdu, void *data, size_t size)
 {
 	size_t len = min(pdu->size - pdu->offset, size);
+	kmsan_unpoison_shadow(&pdu->sdata[pdu->offset], len);
 	memcpy(data, &pdu->sdata[pdu->offset], len);
 	pdu->offset += len;
 	return size - len;
sound/core/oss/pcm_oss.c +7 -0
index f57c610d7523..c3795e495196 100644
@@ -12,6 +12,7 @@
 #endif
 
 #include <linux/init.h>
+#include <linux/kmsan-checks.h>	/* for kmsan_unpoison_shadow() */
 #include <linux/slab.h>
 #include <linux/sched/signal.h>
 #include <linux/time.h>
@@ -1054,6 +1055,12 @@ static int snd_pcm_oss_change_params_locked(struct snd_pcm_substream *substream)
 		err = -ENOMEM;
 		goto failure;
 	}
+	/*
+	 * Unpoison the freshly created buffer to prevent KMSAN from reporting
+	 * uninit errors.
+	 * TODO(glider): unpoison it only when it's actually initialized.
+	 */
+	kmsan_unpoison_shadow(runtime->oss.buffer, runtime->oss.period_bytes);
 
 	runtime->oss.params = 0;
 	runtime->oss.prepare = 1;
Signature · 1 line
2.23.0.866.gb869b98d4c-goog
#46 lore raw

Re: [PATCH RFC v1 23/26] kmsan: unpoisoning buffers from devices etc.

Quoted 6 lines · On Fri, Oct 18, 2019 at 11:43:01AM +0200, [email protected] wrote:

When data is copied to memory from a device KMSAN should treat it as initialized. In most cases it's enough to just unpoison the buffer that is known to come from a device.
In the case with __do_page_cache_readahead() and bio_copy_user_iov() we have to mark the whole pages as ignored by KMSAN, as it's not obvious where these pages are read again.

I'm not sure how your annotations work because you only Cced me on this one patch. Don't do that.

maintainer
#47 lore raw

Re: [PATCH RFC v1 23/26] kmsan: unpoisoning buffers from devices etc.

In reply to [email protected]
Quoted 6 lines · On Fri, Oct 18, 2019 at 11:43:01AM +0200, [email protected] wrote:

When data is copied to memory from a device KMSAN should treat it as initialized. In most cases it's enough to just unpoison the buffer that is known to come from a device.
In the case with __do_page_cache_readahead() and bio_copy_user_iov() we have to mark the whole pages as ignored by KMSAN, as it's not obvious where these pages are read again.

...

Quoted 10 lines

+++ b/mm/filemap.c

mm/filemap.c +1 -0
@@ -18,6 +18,7 @@
 #include <linux/uaccess.h>
 #include <linux/capability.h>
 #include <linux/kernel_stat.h>
+#include <linux/kmsan-checks.h>
 #include <linux/gfp.h>
 #include <linux/mm.h>
 #include <linux/swap.h>

mm/filemap.c static struct page *do_read_cache_page(struct address_space *mapping, · line 2815

Quoted 5 lines
mm/filemap.c +2 -0
@@ -2810,6 +2811,8 @@ static struct page *do_read_cache_page(struct address_space *mapping,
 		page = wait_on_page_read(page);
 		if (IS_ERR(page))
 			return page;
+		/* Assume all pages in page cache are initialized. */
+		kmsan_unpoison_shadow(page_address(page), PAGE_SIZE);

Why would you do that? The page cache already keeps track of which pages are initialised -- the PageUptodate flag is set on them. Indeed, just adding a kmsan call to SetPageUptodate and __SetPageUptodate would probably be a very straightforward way of handling things, and probably means you can get rid of a lot of these other calls.

author maintainer
#48 lore raw

Re: [PATCH RFC v1 23/26] kmsan: unpoisoning buffers from devices etc.

Quoted 24 lines · Matthew Wilcox
Quoted 6 lines · On Fri, Oct 18, 2019 at 11:43:01AM +0200, [email protected] wrote:

When data is copied to memory from a device KMSAN should treat it as initialized. In most cases it's enough to just unpoison the buffer that is known to come from a device.
In the case with __do_page_cache_readahead() and bio_copy_user_iov() we have to mark the whole pages as ignored by KMSAN, as it's not obvious where these pages are read again.

...

Quoted 13 lines

+++ b/mm/filemap.c

mm/filemap.c +1 -0
@@ -18,6 +18,7 @@
 #include <linux/uaccess.h>
 #include <linux/capability.h>
 #include <linux/kernel_stat.h>
+#include <linux/kmsan-checks.h>
 #include <linux/gfp.h>
 #include <linux/mm.h>
 #include <linux/swap.h>
@@ -2810,6 +2811,8 @@ static struct page *do_read_cache_page(struct address_space *mapping,
              page = wait_on_page_read(page);
              if (IS_ERR(page))
                      return page;
Quoted 8 lines

mm/filemap.c static struct page *do_read_cache_page(struct address_space *mapping, · line 2815

Quoted 2 lines
mm/filemap.c +2 -0
+             /* Assume all pages in page cache are initialized. */
+             kmsan_unpoison_shadow(page_address(page), PAGE_SIZE);

Why would you do that? The page cache already keeps track of which pages are initialised -- the PageUptodate flag is set on them. Indeed, just adding a kmsan call to SetPageUptodate and __SetPageUptodate would probably be a very straightforward way of handling things, and probably means you can get rid of a lot of these other calls.

This seems like a very good thing to do, I'll definitely try that. I however noticed that __SetPageUptodate is used when copying pages, not during disk I/O. Is that really so?

We basically need the following behavior: if a device writes to a page, the contents of that page are considered initialized. However when the kernel copies one page to another, we must explicitly copy the source shadow page to the destination.

On a related note, there seems to be a PG_dirty bit that indicates the page is to be flushed to disk.
What's the best place to check such pages for being initialized, so that we can also report writes of uninitialized data to the disk?

Signature · 10 lines
Alexander Potapenko
Software Engineer

Google Germany GmbH
Erika-Mann-Straße, 33
80636 München

Geschäftsführer: Paul Manicle, Halimah DeLaine Prado
Registergericht und -nummer: Hamburg, HRB 86891
Sitz der Gesellschaft: Hamburg
author maintainer
#49 lore raw

Re: [PATCH RFC v1 23/26] kmsan: unpoisoning buffers from devices etc.

Quoted 34 lines · Alexander Potapenko
Quoted 24 lines · Matthew Wilcox
Quoted 6 lines · On Fri, Oct 18, 2019 at 11:43:01AM +0200, [email protected] wrote:

When data is copied to memory from a device KMSAN should treat it as initialized. In most cases it's enough to just unpoison the buffer that is known to come from a device.
In the case with __do_page_cache_readahead() and bio_copy_user_iov() we have to mark the whole pages as ignored by KMSAN, as it's not obvious where these pages are read again.

...

Quoted 13 lines

+++ b/mm/filemap.c

mm/filemap.c +1 -0
@@ -18,6 +18,7 @@
 #include <linux/uaccess.h>
 #include <linux/capability.h>
 #include <linux/kernel_stat.h>
+#include <linux/kmsan-checks.h>
 #include <linux/gfp.h>
 #include <linux/mm.h>
 #include <linux/swap.h>
@@ -2810,6 +2811,8 @@ static struct page *do_read_cache_page(struct address_space *mapping,
              page = wait_on_page_read(page);
              if (IS_ERR(page))
                      return page;
Quoted 8 lines

mm/filemap.c static struct page *do_read_cache_page(struct address_space *mapping, · line 2815

Quoted 2 lines
mm/filemap.c +2 -0
+             /* Assume all pages in page cache are initialized. */
+             kmsan_unpoison_shadow(page_address(page), PAGE_SIZE);

Why would you do that? The page cache already keeps track of which pages are initialised -- the PageUptodate flag is set on them. Indeed, just adding a kmsan call to SetPageUptodate and __SetPageUptodate would probably be a very straightforward way of handling things, and probably means you can get rid of a lot of these other calls.

Quoted 1 line

This seems like a very good thing to do, I'll definitely try that.

Hm, turns out there's no need for this particular call to kmsan_unpoison_memory(),
because the errors that it was suppressing are better addressed by making READ_ONCE_NOCHECK() return initialized memory.
On the other hand, unpoisoning memory in SetPageUptodate doesn't seem to address the same errors.

=====================================================

 BUG: KMSAN: uninit-value in[<      none      >] get_wchan+0x2c6/0x410
arch/x86/kernel/process.c:851
 CPU: 0 PID: 5159 Comm: ps Not tainted 5.4.0-rc5+ #3220
 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.12.0-1 04/01/2014
 Call Trace:
 [<     inline     >] __dump_stack lib/dump_stack.c:77
 [<      none      >] dump_stack+0x196/0x1f0 lib/dump_stack.c:113
 [<      none      >] kmsan_report+0x127/0x220 mm/kmsan/kmsan_report.c:108
 [<      none      >] __msan_warning+0x73/0xe0 mm/kmsan/kmsan_instr.c:245
 [<      none      >] get_wchan+0x2c6/0x410 arch/x86/kernel/process.c:851
 [<      none      >] do_task_stat+0x16f3/0x30a0 fs/proc/array.c:522
 [<      none      >] proc_tgid_stat+0xbe/0xf0 fs/proc/array.c:632
 [<      none      >] proc_single_show+0x1a8/0x2b0 fs/proc/base.c:756
 [<      none      >] seq_read+0xad1/0x1da0 fs/seq_file.c:229
 [<      none      >] __vfs_read+0x1a9/0xc90 fs/read_write.c:425
 [<      none      >] vfs_read+0x333/0x6a0 fs/read_write.c:461
 [<      none      >] ksys_read+0x281/0x460 fs/read_write.c:587
 [<     inline     >] __do_sys_read fs/read_write.c:597
 [<      none      >] __se_sys_read+0x92/0xb0 fs/read_write.c:595
 [<      none      >] __x64_sys_read+0x4a/0x70 fs/read_write.c:595
 [<      none      >] do_syscall_64+0xb6/0x160 arch/x86/entry/common.c:291
 [<      none      >] entry_SYSCALL_64_after_hwframe+0x63/0xe7
arch/x86/entry/entry_64.S:177
 Local variable description: ----stat.i@__se_sys_newstat
 Variable was created at:
 [<     inline     >] __do_sys_newstat fs/stat.c:337
 [<      none      >] __se_sys_newstat+0x71/0xac0 fs/stat.c:337
 [<     inline     >] __do_sys_newstat fs/stat.c:337
 [<      none      >] __se_sys_newstat+0x71/0xac0 fs/stat.c:337
 =====================================================
Quoted 18 lines

I however noticed that __SetPageUptodate is used when copying pages, not during disk I/O. Is that really so?

We basically need the following behavior: if a device writes to a page, the contents of that page are considered initialized. However when the kernel copies one page to another, we must explicitly copy the source shadow page to the destination.

On a related note, there seems to be a PG_dirty bit that indicates the page is to be flushed to disk.
What's the best place to check such pages for being initialized, so that we can also report writes of uninitialized data to the disk?

--
Alexander Potapenko
Software Engineer

Quoted 7 lines

Google Germany GmbH
Erika-Mann-Straße, 33
80636 München

Geschäftsführer: Paul Manicle, Halimah DeLaine Prado Registergericht und -nummer: Hamburg, HRB 86891 Sitz der Gesellschaft: Hamburg

--
Alexander Potapenko
Software Engineer

Google Germany GmbH
Erika-Mann-Straße, 33
80636 München

Geschäftsführer: Paul Manicle, Halimah DeLaine Prado Registergericht und -nummer: Hamburg, HRB 86891 Sitz der Gesellschaft: Hamburg

author maintainer
#50 lore raw

RFC PATCH 24/26 kmsan: hooks for copy_to_user() and friends

In reply to [email protected]

Memory that is copied from userspace must be unpoisoned. Before copying memory to userspace, check it and report an error if it contains uninitialized bits.

To: Alexander Potapenko <[email protected]>
Cc: Alexander Viro <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]
---

Change-Id: I38428b9c7d1909b8441dcec1749b080494a7af99

---
 arch/x86/include/asm/uaccess.h   | 12 ++++++++++++
 include/asm-generic/cacheflush.h |  7 ++++++-
 include/asm-generic/uaccess.h    | 12 ++++++++++--
 include/linux/uaccess.h          | 32 +++++++++++++++++++++++++++-----
 lib/iov_iter.c                   |  6 ++++++
 lib/usercopy.c                   |  6 +++++-
 6 files changed, 66 insertions(+), 9 deletions(-)
arch/x86/include/asm/uaccess.h +12 -0
index 61d93f062a36..ac4b26583f7c 100644
@@ -6,6 +6,7 @@
  */
 #include <linux/compiler.h>
 #include <linux/kasan-checks.h>
+#include <linux/kmsan-checks.h>
 #include <linux/string.h>
 #include <asm/asm.h>
 #include <asm/page.h>
@@ -174,6 +175,7 @@ __typeof__(__builtin_choose_expr(sizeof(x) > sizeof(0UL), 0ULL, 0UL))
 			ASM_CALL_CONSTRAINT				\
 		     : "0" (ptr), "i" (sizeof(*(ptr))));		\
 	(x) = (__force __typeof__(*(ptr))) __val_gu;			\
+	kmsan_unpoison_shadow(&(x), sizeof(*(ptr)));			\
 	__builtin_expect(__ret_gu, 0);					\
 })
 
@@ -248,6 +250,7 @@ extern void __put_user_8(void);
 	__chk_user_ptr(ptr);					\
 	might_fault();						\
 	__pu_val = x;						\
+	kmsan_check_memory(&(__pu_val), sizeof(*(ptr)));	\
 	switch (sizeof(*(ptr))) {				\
 	case 1:							\
 		__put_user_x(1, __pu_val, ptr, __ret_pu);	\
@@ -270,7 +273,9 @@ extern void __put_user_8(void);
 
 #define __put_user_size(x, ptr, size, label)				\
 do {									\
+	__typeof__(*(ptr)) __pus_val = x;				\
 	__chk_user_ptr(ptr);						\
+	kmsan_check_memory(&(__pus_val), size);				\
 	switch (size) {							\
 	case 1:								\
 		__put_user_goto(x, ptr, "b", "b", "iq", label);	\
@@ -295,7 +300,10 @@ do {									\
  */
 #define __put_user_size_ex(x, ptr, size)				\
 do {									\
+	__typeof__(*(ptr)) __puse_val;					\
 	__chk_user_ptr(ptr);						\
+	__puse_val = x;							\
+	kmsan_check_memory(&(__puse_val), size);			\
 	switch (size) {							\
 	case 1:								\
 		__put_user_asm_ex(x, ptr, "b", "b", "iq");		\
@@ -363,6 +371,7 @@ do {									\
 	default:							\
 		(x) = __get_user_bad();					\
 	}								\
+	kmsan_unpoison_shadow(&(x), size);				\
 } while (0)
 
 #define __get_user_asm(x, addr, err, itype, rtype, ltype, errret)	\
@@ -413,6 +422,7 @@ do {									\
 	default:							\
 		(x) = __get_user_bad();					\
 	}								\
+	kmsan_unpoison_shadow(&(x), size);				\
 } while (0)
 
 #define __get_user_asm_ex(x, addr, itype, rtype, ltype)			\
@@ -428,11 +438,13 @@ do {									\
 #define __put_user_nocheck(x, ptr, size)			\
 ({								\
 	__label__ __pu_label;					\
+	__typeof__(*(ptr)) __pun_val = x;			\
 	int __pu_err = -EFAULT;					\
 	__typeof__(*(ptr)) __pu_val = (x);			\
 	__typeof__(ptr) __pu_ptr = (ptr);			\
 	__typeof__(size) __pu_size = (size);			\
 	__uaccess_begin();					\
+	kmsan_check_memory(&(__pun_val), size);			\
 	__put_user_size(__pu_val, __pu_ptr, __pu_size, __pu_label);	\
 	__pu_err = 0;						\
 __pu_label:							\
include/asm-generic/cacheflush.h +6 -1
index a950a22c4890..707531dccf5e 100644
@@ -4,6 +4,7 @@
 
 /* Keep includes the same across arches.  */
 #include <linux/mm.h>
+#include <linux/kmsan-checks.h>
 
 #define ARCH_IMPLEMENTS_FLUSH_DCACHE_PAGE 0
 
@@ -72,10 +73,14 @@ static inline void flush_cache_vunmap(unsigned long start, unsigned long end)
 
 #define copy_to_user_page(vma, page, vaddr, dst, src, len) \
 	do { \
+		kmsan_check_memory(src, len); \
 		memcpy(dst, src, len); \
 		flush_icache_user_range(vma, page, vaddr, len); \
 	} while (0)
 #define copy_from_user_page(vma, page, vaddr, dst, src, len) \
-	memcpy(dst, src, len)
+	do { \
+		memcpy(dst, src, len); \
+		kmsan_unpoison_shadow(dst, len); \
+	} while (0)
 
 #endif /* __ASM_CACHEFLUSH_H */
include/asm-generic/uaccess.h +10 -2
index e935318804f8..18a50333ffc0 100644
@@ -142,7 +142,11 @@ static inline int __access_ok(unsigned long addr, unsigned long size)
 
 static inline int __put_user_fn(size_t size, void __user *ptr, void *x)
 {
-	return unlikely(raw_copy_to_user(ptr, x, size)) ? -EFAULT : 0;
+	int n;
+
+	n = raw_copy_to_user(ptr, x, size);
+	kmsan_copy_to_user(ptr, x, size, n);
+	return unlikely(n) ? -EFAULT : 0;
 }
 
 #define __put_user_fn(sz, u, k)	__put_user_fn(sz, u, k)
@@ -203,7 +207,11 @@ extern int __put_user_bad(void) __attribute__((noreturn));
 #ifndef __get_user_fn
 static inline int __get_user_fn(size_t size, const void __user *ptr, void *x)
 {
-	return unlikely(raw_copy_from_user(x, ptr, size)) ? -EFAULT : 0;
+	int copied, to_copy = size;
+
+	copied = raw_copy_from_user(x, ptr, size);
+	kmsan_unpoison_shadow(to, to_copy - res);
+	return unlikely(copied) ? -EFAULT : 0;
 }
 
 #define __get_user_fn(sz, u, k)	__get_user_fn(sz, u, k)
include/linux/uaccess.h +27 -5
index d4ee6e942562..4257b5f80689 100644
@@ -5,6 +5,7 @@
 #include <linux/sched.h>
 #include <linux/thread_info.h>
 #include <linux/kasan-checks.h>
+#include <linux/kmsan-checks.h>
 
 #define uaccess_kernel() segment_eq(get_fs(), KERNEL_DS)
 
@@ -58,18 +59,26 @@
 static __always_inline __must_check unsigned long
 __copy_from_user_inatomic(void *to, const void __user *from, unsigned long n)
 {
+	unsigned long to_copy = n;
+
 	kasan_check_write(to, n);
 	check_object_size(to, n, false);
-	return raw_copy_from_user(to, from, n);
+	n = raw_copy_from_user(to, from, n);
+	kmsan_unpoison_shadow(to, to_copy - n);
+	return n;
 }
 
 static __always_inline __must_check unsigned long
 __copy_from_user(void *to, const void __user *from, unsigned long n)
 {
+	unsigned long to_copy = n;
+
 	might_fault();
 	kasan_check_write(to, n);
 	check_object_size(to, n, false);
-	return raw_copy_from_user(to, from, n);
+	n = raw_copy_from_user(to, from, n);
+	kmsan_unpoison_shadow(to, to_copy - n);
+	return n;
 }
 
 /**
@@ -88,29 +97,39 @@ __copy_from_user(void *to, const void __user *from, unsigned long n)
 static __always_inline __must_check unsigned long
 __copy_to_user_inatomic(void __user *to, const void *from, unsigned long n)
 {
+	unsigned long to_copy = n;
+
 	kasan_check_read(from, n);
 	check_object_size(from, n, true);
-	return raw_copy_to_user(to, from, n);
+	n = raw_copy_to_user(to, from, n);
+	kmsan_copy_to_user(to, from, to_copy, n);
+	return n;
 }
 
 static __always_inline __must_check unsigned long
 __copy_to_user(void __user *to, const void *from, unsigned long n)
 {
+	unsigned long to_copy = n;
+
 	might_fault();
 	kasan_check_read(from, n);
 	check_object_size(from, n, true);
-	return raw_copy_to_user(to, from, n);
+	n = raw_copy_to_user(to, from, n);
+	kmsan_copy_to_user(to, from, to_copy, n);
+	return n;
 }
 
 #ifdef INLINE_COPY_FROM_USER
 static inline __must_check unsigned long
 _copy_from_user(void *to, const void __user *from, unsigned long n)
 {
-	unsigned long res = n;
+	unsigned long res = n, to_copy = n;
+
 	might_fault();
 	if (likely(access_ok(from, n))) {
 		kasan_check_write(to, n);
 		res = raw_copy_from_user(to, from, n);
+		kmsan_unpoison_shadow(to, to_copy - res);
 	}
 	if (unlikely(res))
 		memset(to + (n - res), 0, res);
@@ -125,10 +144,13 @@ _copy_from_user(void *, const void __user *, unsigned long);
 static inline __must_check unsigned long
 _copy_to_user(void __user *to, const void *from, unsigned long n)
 {
+	unsigned long to_copy = n;
+
 	might_fault();
 	if (access_ok(to, n)) {
 		kasan_check_read(from, n);
 		n = raw_copy_to_user(to, from, n);
+		kmsan_copy_to_user(to, from, to_copy, n);
 	}
 	return n;
 }
lib/iov_iter.c +6 -0
index 639d5e7014c1..f038676068b2 100644
@@ -137,18 +137,24 @@
 
 static int copyout(void __user *to, const void *from, size_t n)
 {
+	size_t to_copy = n;
+
 	if (access_ok(to, n)) {
 		kasan_check_read(from, n);
 		n = raw_copy_to_user(to, from, n);
+		kmsan_copy_to_user(to, from, to_copy, n);
 	}
 	return n;
 }
 
 static int copyin(void *to, const void __user *from, size_t n)
 {
+	size_t to_copy = n;
+
 	if (access_ok(from, n)) {
 		kasan_check_write(to, n);
 		n = raw_copy_from_user(to, from, n);
+		kmsan_unpoison_shadow(to, to_copy - n);
 	}
 	return n;
 }
lib/usercopy.c +5 -1
index cbb4d9ec00f2..abfd93edecba 100644
@@ -1,4 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0
+#include <linux/kmsan-checks.h>
 #include <linux/uaccess.h>
 #include <linux/bitops.h>
 
@@ -7,11 +8,12 @@
 #ifndef INLINE_COPY_FROM_USER
 unsigned long _copy_from_user(void *to, const void __user *from, unsigned long n)
 {
-	unsigned long res = n;
+	unsigned long res = n, to_copy = n;
 	might_fault();
 	if (likely(access_ok(from, n))) {
 		kasan_check_write(to, n);
 		res = raw_copy_from_user(to, from, n);
+		kmsan_unpoison_shadow(to, to_copy - res);
 	}
 	if (unlikely(res))
 		memset(to + (n - res), 0, res);
@@ -23,10 +25,12 @@ EXPORT_SYMBOL(_copy_from_user);
 #ifndef INLINE_COPY_TO_USER
 unsigned long _copy_to_user(void __user *to, const void *from, unsigned long n)
 {
+	unsigned long to_copy = n;
 	might_fault();
 	if (likely(access_ok(to, n))) {
 		kasan_check_read(from, n);
 		n = raw_copy_to_user(to, from, n);
+		kmsan_copy_to_user(to, from, to_copy, n);
 	}
 	return n;
 }
Signature · 1 line
2.23.0.866.gb869b98d4c-goog
author maintainer
#51 lore raw

RFC PATCH 25/26 kmsan: disable strscpy() optimization under KMSAN

In reply to [email protected]

Disable the efficient 8-byte reading under KMSAN to avoid false positives.

To: Alexander Potapenko <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]

---

Change-Id: I25d1acf5c3df6eff85894cd94f5ddbe93308271c

---
 lib/string.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)
lib/string.c +4 -1
index 08ec58cc673b..15efdc51bda6 100644
@@ -186,7 +186,10 @@ ssize_t strscpy(char *dest, const char *src, size_t count)
 	if (count == 0 || WARN_ON_ONCE(count > INT_MAX))
 		return -E2BIG;
 
-#ifdef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
+/**
+ * Disable the efficient 8-byte reading under KMSAN to avoid false positives.
+ */
+#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) && !defined(CONFIG_KMSAN)
 	/*
 	 * If src is unaligned, don't cross a page boundary,
 	 * since we don't know if the next page is mapped.
Signature · 1 line
2.23.0.866.gb869b98d4c-goog
author maintainer
#52 lore raw

RFC PATCH 26/26 net: kasan: kmsan: support CONFIG_GENERIC_CSUM on x86, enable it for KASAN/KMSAN

In reply to [email protected]

This is needed to allow memory tools like KASAN and KMSAN see the memory accesses from the checksum code. Without CONFIG_GENERIC_CSUM the tools can't see memory accesses originating from handwritten assembly code.
For KASAN it's a question of detecting more bugs, for KMSAN using the C implementation also helps avoid false positives originating from seemingly uninitialized checksum values.

To: Alexander Potapenko <[email protected]>
Cc: Arnd Bergmann <[email protected]>
Cc: Michal Simek <[email protected]>
Cc: Andrey Ryabinin <[email protected]>
Cc: Vegard Nossum <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: [email protected]
---

Change-Id: I645e2c097253a8d5717ad87e2e2df6f6f67251f3

---
 arch/x86/Kconfig                |  5 +++++
 arch/x86/include/asm/checksum.h | 10 +++++++---
 arch/x86/lib/Makefile           |  2 ++
 3 files changed, 14 insertions(+), 3 deletions(-)
arch/x86/Kconfig +5 -0
index 6d2b0fb3a871..dfec655d1a7a 100644
@@ -272,6 +272,11 @@ config GENERIC_ISA_DMA
 	def_bool y
 	depends on ISA_DMA_API
 
+config GENERIC_CSUM
+	bool
+	default y if KMSAN || KASAN
+	default n
+
 config GENERIC_BUG
 	def_bool y
 	depends on BUG
arch/x86/include/asm/checksum.h +7 -3
index d79d1e622dcf..ab3464cbce26 100644
@@ -1,6 +1,10 @@
 /* SPDX-License-Identifier: GPL-2.0 */
-#ifdef CONFIG_X86_32
-# include <asm/checksum_32.h>
+#ifdef CONFIG_GENERIC_CSUM
+# include <asm-generic/checksum.h>
 #else
-# include <asm/checksum_64.h>
+# ifdef CONFIG_X86_32
+#  include <asm/checksum_32.h>
+# else
+#  include <asm/checksum_64.h>
+# endif
 #endif
arch/x86/lib/Makefile +2 -0
index 5246db42de45..bca9031de9ff 100644
@@ -55,7 +55,9 @@ endif
         lib-$(CONFIG_X86_USE_3DNOW) += mmx_32.o
 else
         obj-y += iomap_copy_64.o
+ifneq ($(CONFIG_GENERIC_CSUM),y)
         lib-y += csum-partial_64.o csum-copy_64.o csum-wrappers_64.o
+endif
         lib-y += clear_page_64.o copy_page_64.o
         lib-y += memmove_64.o memset_64.o
         lib-y += copy_user_64.o
Signature · 1 line
2.23.0.866.gb869b98d4c-goog
reviewer
#53 lore raw

Re: [PATCH RFC v1 26/26] net: kasan: kmsan: support CONFIG_GENERIC_CSUM on x86, enable it for KASAN/KMSAN

Hi,

arch/x86/Kconfig config GENERIC_ISA_DMA · line 278

Quoted 12 lines · On 10/18/19 2:43 AM, [email protected] wrote:
arch/x86/Kconfig +4 -0
index 6d2b0fb3a871..dfec655d1a7a 100644
@@ -272,6 +272,11 @@ config GENERIC_ISA_DMA
 	def_bool y
 	depends on ISA_DMA_API
 
+config GENERIC_CSUM
+	bool
+	default y if KMSAN || KASAN
+	default n

Drop the "default n". It's already the default default and we prefer not to repeat the default default.

arch/x86/Kconfig config GENERIC_ISA_DMA · line 282

Quoted 4 lines
arch/x86/Kconfig +1 -0
+
 config GENERIC_BUG
 	def_bool y
 	depends on BUG

ta.

Signature · 1 line
~Randy