Skip to content

Commit f80be45

Browse files
ramosian-gliderakpm00
authored andcommitted
kmsan: add KMSAN runtime core
For each memory location KernelMemorySanitizer maintains two types of metadata: 1. The so-called shadow of that location - а byte:byte mapping describing whether or not individual bits of memory are initialized (shadow is 0) or not (shadow is 1). 2. The origins of that location - а 4-byte:4-byte mapping containing 4-byte IDs of the stack traces where uninitialized values were created. Each struct page now contains pointers to two struct pages holding KMSAN metadata (shadow and origins) for the original struct page. Utility routines in mm/kmsan/core.c and mm/kmsan/shadow.c handle the metadata creation, addressing, copying and checking. mm/kmsan/report.c performs error reporting in the cases an uninitialized value is used in a way that leads to undefined behavior. KMSAN compiler instrumentation is responsible for tracking the metadata along with the kernel memory. mm/kmsan/instrumentation.c provides the implementation for instrumentation hooks that are called from files compiled with -fsanitize=kernel-memory. To aid parameter passing (also done at instrumentation level), each task_struct now contains a struct kmsan_task_state used to track the metadata of function parameters and return values for that task. Finally, this patch provides CONFIG_KMSAN that enables KMSAN, and declares CFLAGS_KMSAN, which are applied to files compiled with KMSAN. The KMSAN_SANITIZE:=n Makefile directive can be used to completely disable KMSAN instrumentation for certain files. Similarly, KMSAN_ENABLE_CHECKS:=n disables KMSAN checks and makes newly created stack memory initialized. Users can also use functions from include/linux/kmsan-checks.h to mark certain memory regions as uninitialized or initialized (this is called "poisoning" and "unpoisoning") or check that a particular region is initialized. Link: https://lkml.kernel.org/r/20220915150417.722975-12-glider@google.com Signed-off-by: Alexander Potapenko <glider@google.com> Acked-by: Marco Elver <elver@google.com> Cc: Alexander Viro <viro@zeniv.linux.org.uk> Cc: Alexei Starovoitov <ast@kernel.org> Cc: Andrey Konovalov <andreyknvl@gmail.com> Cc: Andrey Konovalov <andreyknvl@google.com> Cc: Andy Lutomirski <luto@kernel.org> Cc: Arnd Bergmann <arnd@arndb.de> Cc: Borislav Petkov <bp@alien8.de> Cc: Christoph Hellwig <hch@lst.de> Cc: Christoph Lameter <cl@linux.com> Cc: David Rientjes <rientjes@google.com> Cc: Dmitry Vyukov <dvyukov@google.com> Cc: Eric Biggers <ebiggers@google.com> Cc: Eric Biggers <ebiggers@kernel.org> Cc: Eric Dumazet <edumazet@google.com> Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Cc: Herbert Xu <herbert@gondor.apana.org.au> Cc: Ilya Leoshkevich <iii@linux.ibm.com> Cc: Ingo Molnar <mingo@redhat.com> Cc: Jens Axboe <axboe@kernel.dk> Cc: Joonsoo Kim <iamjoonsoo.kim@lge.com> Cc: Kees Cook <keescook@chromium.org> Cc: Mark Rutland <mark.rutland@arm.com> Cc: Matthew Wilcox <willy@infradead.org> Cc: Michael S. Tsirkin <mst@redhat.com> Cc: Pekka Enberg <penberg@kernel.org> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Petr Mladek <pmladek@suse.com> Cc: Stephen Rothwell <sfr@canb.auug.org.au> Cc: Steven Rostedt <rostedt@goodmis.org> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Vasily Gorbik <gor@linux.ibm.com> Cc: Vegard Nossum <vegard.nossum@oracle.com> Cc: Vlastimil Babka <vbabka@suse.cz> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
1 parent 6e9f05d commit f80be45

File tree

17 files changed

+1592
-0
lines changed

17 files changed

+1592
-0
lines changed

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1015,6 +1015,7 @@ include-y := scripts/Makefile.extrawarn
10151015
include-$(CONFIG_DEBUG_INFO) += scripts/Makefile.debug
10161016
include-$(CONFIG_KASAN) += scripts/Makefile.kasan
10171017
include-$(CONFIG_KCSAN) += scripts/Makefile.kcsan
1018+
include-$(CONFIG_KMSAN) += scripts/Makefile.kmsan
10181019
include-$(CONFIG_UBSAN) += scripts/Makefile.ubsan
10191020
include-$(CONFIG_KCOV) += scripts/Makefile.kcov
10201021
include-$(CONFIG_RANDSTRUCT) += scripts/Makefile.randstruct

include/linux/kmsan-checks.h

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
/*
3+
* KMSAN checks to be used for one-off annotations in subsystems.
4+
*
5+
* Copyright (C) 2017-2022 Google LLC
6+
* Author: Alexander Potapenko <glider@google.com>
7+
*
8+
*/
9+
10+
#ifndef _LINUX_KMSAN_CHECKS_H
11+
#define _LINUX_KMSAN_CHECKS_H
12+
13+
#include <linux/types.h>
14+
15+
#ifdef CONFIG_KMSAN
16+
17+
/**
18+
* kmsan_poison_memory() - Mark the memory range as uninitialized.
19+
* @address: address to start with.
20+
* @size: size of buffer to poison.
21+
* @flags: GFP flags for allocations done by this function.
22+
*
23+
* Until other data is written to this range, KMSAN will treat it as
24+
* uninitialized. Error reports for this memory will reference the call site of
25+
* kmsan_poison_memory() as origin.
26+
*/
27+
void kmsan_poison_memory(const void *address, size_t size, gfp_t flags);
28+
29+
/**
30+
* kmsan_unpoison_memory() - Mark the memory range as initialized.
31+
* @address: address to start with.
32+
* @size: size of buffer to unpoison.
33+
*
34+
* Until other data is written to this range, KMSAN will treat it as
35+
* initialized.
36+
*/
37+
void kmsan_unpoison_memory(const void *address, size_t size);
38+
39+
/**
40+
* kmsan_check_memory() - Check the memory range for being initialized.
41+
* @address: address to start with.
42+
* @size: size of buffer to check.
43+
*
44+
* If any piece of the given range is marked as uninitialized, KMSAN will report
45+
* an error.
46+
*/
47+
void kmsan_check_memory(const void *address, size_t size);
48+
49+
#else
50+
51+
static inline void kmsan_poison_memory(const void *address, size_t size,
52+
gfp_t flags)
53+
{
54+
}
55+
static inline void kmsan_unpoison_memory(const void *address, size_t size)
56+
{
57+
}
58+
static inline void kmsan_check_memory(const void *address, size_t size)
59+
{
60+
}
61+
62+
#endif
63+
64+
#endif /* _LINUX_KMSAN_CHECKS_H */

include/linux/kmsan_types.h

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
/*
3+
* A minimal header declaring types added by KMSAN to existing kernel structs.
4+
*
5+
* Copyright (C) 2017-2022 Google LLC
6+
* Author: Alexander Potapenko <glider@google.com>
7+
*
8+
*/
9+
#ifndef _LINUX_KMSAN_TYPES_H
10+
#define _LINUX_KMSAN_TYPES_H
11+
12+
/* These constants are defined in the MSan LLVM instrumentation pass. */
13+
#define KMSAN_RETVAL_SIZE 800
14+
#define KMSAN_PARAM_SIZE 800
15+
16+
struct kmsan_context_state {
17+
char param_tls[KMSAN_PARAM_SIZE];
18+
char retval_tls[KMSAN_RETVAL_SIZE];
19+
char va_arg_tls[KMSAN_PARAM_SIZE];
20+
char va_arg_origin_tls[KMSAN_PARAM_SIZE];
21+
u64 va_arg_overflow_size_tls;
22+
char param_origin_tls[KMSAN_PARAM_SIZE];
23+
u32 retval_origin_tls;
24+
};
25+
26+
#undef KMSAN_PARAM_SIZE
27+
#undef KMSAN_RETVAL_SIZE
28+
29+
struct kmsan_ctx {
30+
struct kmsan_context_state cstate;
31+
int kmsan_in_runtime;
32+
bool allow_reporting;
33+
};
34+
35+
#endif /* _LINUX_KMSAN_TYPES_H */

include/linux/mm_types.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,18 @@ struct page {
224224
not kmapped, ie. highmem) */
225225
#endif /* WANT_PAGE_VIRTUAL */
226226

227+
#ifdef CONFIG_KMSAN
228+
/*
229+
* KMSAN metadata for this page:
230+
* - shadow page: every bit indicates whether the corresponding
231+
* bit of the original page is initialized (0) or not (1);
232+
* - origin page: every 4 bytes contain an id of the stack trace
233+
* where the uninitialized value was created.
234+
*/
235+
struct page *kmsan_shadow;
236+
struct page *kmsan_origin;
237+
#endif
238+
227239
#ifdef LAST_CPUPID_NOT_IN_PAGE_FLAGS
228240
int _last_cpupid;
229241
#endif

include/linux/sched.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include <linux/pid.h>
1515
#include <linux/sem.h>
1616
#include <linux/shm.h>
17+
#include <linux/kmsan_types.h>
1718
#include <linux/mutex.h>
1819
#include <linux/plist.h>
1920
#include <linux/hrtimer.h>
@@ -1362,6 +1363,10 @@ struct task_struct {
13621363
#endif
13631364
#endif
13641365

1366+
#ifdef CONFIG_KMSAN
1367+
struct kmsan_ctx kmsan_ctx;
1368+
#endif
1369+
13651370
#if IS_ENABLED(CONFIG_KUNIT)
13661371
struct kunit *kunit_test;
13671372
#endif

lib/Kconfig.debug

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -970,6 +970,7 @@ config DEBUG_STACKOVERFLOW
970970

971971
source "lib/Kconfig.kasan"
972972
source "lib/Kconfig.kfence"
973+
source "lib/Kconfig.kmsan"
973974

974975
endmenu # "Memory Debugging"
975976

lib/Kconfig.kmsan

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# SPDX-License-Identifier: GPL-2.0-only
2+
config HAVE_ARCH_KMSAN
3+
bool
4+
5+
config HAVE_KMSAN_COMPILER
6+
# Clang versions <14.0.0 also support -fsanitize=kernel-memory, but not
7+
# all the features necessary to build the kernel with KMSAN.
8+
depends on CC_IS_CLANG && CLANG_VERSION >= 140000
9+
def_bool $(cc-option,-fsanitize=kernel-memory -mllvm -msan-disable-checks=1)
10+
11+
config KMSAN
12+
bool "KMSAN: detector of uninitialized values use"
13+
depends on HAVE_ARCH_KMSAN && HAVE_KMSAN_COMPILER
14+
depends on SLUB && DEBUG_KERNEL && !KASAN && !KCSAN
15+
select STACKDEPOT
16+
select STACKDEPOT_ALWAYS_INIT
17+
help
18+
KernelMemorySanitizer (KMSAN) is a dynamic detector of uses of
19+
uninitialized values in the kernel. It is based on compiler
20+
instrumentation provided by Clang and thus requires Clang to build.
21+
22+
An important note is that KMSAN is not intended for production use,
23+
because it drastically increases kernel memory footprint and slows
24+
the whole system down.
25+
26+
See <file:Documentation/dev-tools/kmsan.rst> for more details.
27+
28+
if KMSAN
29+
30+
config HAVE_KMSAN_PARAM_RETVAL
31+
# -fsanitize-memory-param-retval is supported only by Clang >= 14.
32+
depends on HAVE_KMSAN_COMPILER
33+
def_bool $(cc-option,-fsanitize=kernel-memory -fsanitize-memory-param-retval)
34+
35+
config KMSAN_CHECK_PARAM_RETVAL
36+
bool "Check for uninitialized values passed to and returned from functions"
37+
default y
38+
depends on HAVE_KMSAN_PARAM_RETVAL
39+
help
40+
If the compiler supports -fsanitize-memory-param-retval, KMSAN will
41+
eagerly check every function parameter passed by value and every
42+
function return value.
43+
44+
Disabling KMSAN_CHECK_PARAM_RETVAL will result in tracking shadow for
45+
function parameters and return values across function borders. This
46+
is a more relaxed mode, but it generates more instrumentation code and
47+
may potentially report errors in corner cases when non-instrumented
48+
functions call instrumented ones.
49+
50+
endif

mm/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ obj-$(CONFIG_SLAB) += slab.o
8989
obj-$(CONFIG_SLUB) += slub.o
9090
obj-$(CONFIG_KASAN) += kasan/
9191
obj-$(CONFIG_KFENCE) += kfence/
92+
obj-$(CONFIG_KMSAN) += kmsan/
9293
obj-$(CONFIG_FAILSLAB) += failslab.o
9394
obj-$(CONFIG_MEMTEST) += memtest.o
9495
obj-$(CONFIG_MIGRATION) += migrate.o

mm/kmsan/Makefile

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# SPDX-License-Identifier: GPL-2.0
2+
#
3+
# Makefile for KernelMemorySanitizer (KMSAN).
4+
#
5+
#
6+
obj-y := core.o instrumentation.o hooks.o report.o shadow.o
7+
8+
KMSAN_SANITIZE := n
9+
KCOV_INSTRUMENT := n
10+
UBSAN_SANITIZE := n
11+
12+
# Disable instrumentation of KMSAN runtime with other tools.
13+
CC_FLAGS_KMSAN_RUNTIME := -fno-stack-protector
14+
CC_FLAGS_KMSAN_RUNTIME += $(call cc-option,-fno-conserve-stack)
15+
CC_FLAGS_KMSAN_RUNTIME += -DDISABLE_BRANCH_PROFILING
16+
17+
CFLAGS_REMOVE.o = $(CC_FLAGS_FTRACE)
18+
19+
CFLAGS_core.o := $(CC_FLAGS_KMSAN_RUNTIME)
20+
CFLAGS_hooks.o := $(CC_FLAGS_KMSAN_RUNTIME)
21+
CFLAGS_instrumentation.o := $(CC_FLAGS_KMSAN_RUNTIME)
22+
CFLAGS_report.o := $(CC_FLAGS_KMSAN_RUNTIME)
23+
CFLAGS_shadow.o := $(CC_FLAGS_KMSAN_RUNTIME)

0 commit comments

Comments
 (0)