[PATCH v4 0/6] ubsan: Split out bounds checker

6 views
Skip to first unread message

Kees Cook

unread,
Feb 27, 2020, 1:49:29 PM2/27/20
to Andrew Morton, Kees Cook, Andrey Ryabinin, Elena Petrova, Andrey Konovalov, Alexander Potapenko, Dan Carpenter, Gustavo A. R. Silva, Arnd Bergmann, Ard Biesheuvel, kasa...@googlegroups.com, linu...@kvack.org, linux-...@vger.kernel.org, kernel-h...@lists.openwall.com, syzk...@googlegroups.com
This splits out the bounds checker so it can be individually used. This
is enabled in Android and hopefully for syzbot. Includes LKDTM tests for
behavioral corner-cases (beyond just the bounds checker), and adjusts
ubsan and kasan slightly for correct panic handling.

-Kees

v4:
- use hyphenated bug class names (andreyknvl)
- add Acks
v3: https://lore.kernel.org/lkml/20200116012321....@chromium.org
v2: https://lore.kernel.org/lkml/20191121181519....@chromium.org
v1: https://lore.kernel.org/lkml/20191120010636....@chromium.org


Kees Cook (6):
ubsan: Add trap instrumentation option
ubsan: Split "bounds" checker from other options
lkdtm/bugs: Add arithmetic overflow and array bounds checks
ubsan: Check panic_on_warn
kasan: Unset panic_on_warn before calling panic()
ubsan: Include bug type in report header

drivers/misc/lkdtm/bugs.c | 75 ++++++++++++++++++++++++++++++++++++++
drivers/misc/lkdtm/core.c | 3 ++
drivers/misc/lkdtm/lkdtm.h | 3 ++
lib/Kconfig.ubsan | 49 +++++++++++++++++++++----
lib/Makefile | 2 +
lib/ubsan.c | 47 +++++++++++++-----------
mm/kasan/report.c | 10 ++++-
scripts/Makefile.ubsan | 16 ++++++--
8 files changed, 172 insertions(+), 33 deletions(-)

--
2.20.1

Kees Cook

unread,
Feb 27, 2020, 1:49:29 PM2/27/20
to Andrew Morton, Kees Cook, Andrey Ryabinin, Elena Petrova, Andrey Konovalov, Alexander Potapenko, Dan Carpenter, Gustavo A. R. Silva, Arnd Bergmann, Ard Biesheuvel, kasa...@googlegroups.com, linu...@kvack.org, linux-...@vger.kernel.org, kernel-h...@lists.openwall.com, syzk...@googlegroups.com
Syzkaller expects kernel warnings to panic when the panic_on_warn
sysctl is set. More work is needed here to have UBSan reuse the WARN
infrastructure, but for now, just check the flag manually.

Link: https://lore.kernel.org/lkml/CACT4Y+bsLJ-wFx_TaXqax3JB...@mail.gmail.com
Signed-off-by: Kees Cook <kees...@chromium.org>
---
lib/ubsan.c | 11 +++++++++++
1 file changed, 11 insertions(+)

diff --git a/lib/ubsan.c b/lib/ubsan.c
index 7b9b58aee72c..429663eef6a7 100644
--- a/lib/ubsan.c
+++ b/lib/ubsan.c
@@ -156,6 +156,17 @@ static void ubsan_epilogue(void)
"========================================\n");

current->in_ubsan--;
+
+ if (panic_on_warn) {
+ /*
+ * This thread may hit another WARN() in the panic path.
+ * Resetting this prevents additional WARN() from panicking the
+ * system on this thread. Other threads are blocked by the
+ * panic_mutex in panic().
+ */
+ panic_on_warn = 0;
+ panic("panic_on_warn set ...\n");
+ }
}

static void handle_overflow(struct overflow_data *data, void *lhs,
--
2.20.1

Kees Cook

unread,
Feb 27, 2020, 1:49:30 PM2/27/20
to Andrew Morton, Kees Cook, Dmitry Vyukov, Andrey Ryabinin, Elena Petrova, Andrey Konovalov, Alexander Potapenko, Dan Carpenter, Gustavo A. R. Silva, Arnd Bergmann, Ard Biesheuvel, kasa...@googlegroups.com, linu...@kvack.org, linux-...@vger.kernel.org, kernel-h...@lists.openwall.com, syzk...@googlegroups.com
Adds LKDTM tests for arithmetic overflow (both signed and unsigned),
as well as array bounds checking.

Signed-off-by: Kees Cook <kees...@chromium.org>
Acked-by: Dmitry Vyukov <dvy...@google.com>
---
drivers/misc/lkdtm/bugs.c | 75 ++++++++++++++++++++++++++++++++++++++
drivers/misc/lkdtm/core.c | 3 ++
drivers/misc/lkdtm/lkdtm.h | 3 ++
3 files changed, 81 insertions(+)

diff --git a/drivers/misc/lkdtm/bugs.c b/drivers/misc/lkdtm/bugs.c
index de87693cf557..e4c61ffea35c 100644
--- a/drivers/misc/lkdtm/bugs.c
+++ b/drivers/misc/lkdtm/bugs.c
@@ -11,6 +11,7 @@
#include <linux/sched/signal.h>
#include <linux/sched/task_stack.h>
#include <linux/uaccess.h>
+#include <linux/slab.h>

#ifdef CONFIG_X86_32
#include <asm/desc.h>
@@ -175,6 +176,80 @@ void lkdtm_HUNG_TASK(void)
schedule();
}

+volatile unsigned int huge = INT_MAX - 2;
+volatile unsigned int ignored;
+
+void lkdtm_OVERFLOW_SIGNED(void)
+{
+ int value;
+
+ value = huge;
+ pr_info("Normal signed addition ...\n");
+ value += 1;
+ ignored = value;
+
+ pr_info("Overflowing signed addition ...\n");
+ value += 4;
+ ignored = value;
+}
+
+
+void lkdtm_OVERFLOW_UNSIGNED(void)
+{
+ unsigned int value;
+
+ value = huge;
+ pr_info("Normal unsigned addition ...\n");
+ value += 1;
+ ignored = value;
+
+ pr_info("Overflowing unsigned addition ...\n");
+ value += 4;
+ ignored = value;
+}
+
+/* Intentially using old-style flex array definition of 1 byte. */
+struct array_bounds_flex_array {
+ int one;
+ int two;
+ char data[1];
+};
+
+struct array_bounds {
+ int one;
+ int two;
+ char data[8];
+ int three;
+};
+
+void lkdtm_ARRAY_BOUNDS(void)
+{
+ struct array_bounds_flex_array *not_checked;
+ struct array_bounds *checked;
+ volatile int i;
+
+ not_checked = kmalloc(sizeof(*not_checked) * 2, GFP_KERNEL);
+ checked = kmalloc(sizeof(*checked) * 2, GFP_KERNEL);
+
+ pr_info("Array access within bounds ...\n");
+ /* For both, touch all bytes in the actual member size. */
+ for (i = 0; i < sizeof(checked->data); i++)
+ checked->data[i] = 'A';
+ /*
+ * For the uninstrumented flex array member, also touch 1 byte
+ * beyond to verify it is correctly uninstrumented.
+ */
+ for (i = 0; i < sizeof(not_checked->data) + 1; i++)
+ not_checked->data[i] = 'A';
+
+ pr_info("Array access beyond bounds ...\n");
+ for (i = 0; i < sizeof(checked->data) + 1; i++)
+ checked->data[i] = 'B';
+
+ kfree(not_checked);
+ kfree(checked);
+}
+
void lkdtm_CORRUPT_LIST_ADD(void)
{
/*
diff --git a/drivers/misc/lkdtm/core.c b/drivers/misc/lkdtm/core.c
index ee0d6e721441..2e04719b503c 100644
--- a/drivers/misc/lkdtm/core.c
+++ b/drivers/misc/lkdtm/core.c
@@ -129,6 +129,9 @@ static const struct crashtype crashtypes[] = {
CRASHTYPE(HARDLOCKUP),
CRASHTYPE(SPINLOCKUP),
CRASHTYPE(HUNG_TASK),
+ CRASHTYPE(OVERFLOW_SIGNED),
+ CRASHTYPE(OVERFLOW_UNSIGNED),
+ CRASHTYPE(ARRAY_BOUNDS),
CRASHTYPE(EXEC_DATA),
CRASHTYPE(EXEC_STACK),
CRASHTYPE(EXEC_KMALLOC),
diff --git a/drivers/misc/lkdtm/lkdtm.h b/drivers/misc/lkdtm/lkdtm.h
index c56d23e37643..8391081c6f13 100644
--- a/drivers/misc/lkdtm/lkdtm.h
+++ b/drivers/misc/lkdtm/lkdtm.h
@@ -22,6 +22,9 @@ void lkdtm_SOFTLOCKUP(void);
void lkdtm_HARDLOCKUP(void);
void lkdtm_SPINLOCKUP(void);
void lkdtm_HUNG_TASK(void);
+void lkdtm_OVERFLOW_SIGNED(void);
+void lkdtm_OVERFLOW_UNSIGNED(void);
+void lkdtm_ARRAY_BOUNDS(void);
void lkdtm_CORRUPT_LIST_ADD(void);
void lkdtm_CORRUPT_LIST_DEL(void);
void lkdtm_CORRUPT_USER_DS(void);
--
2.20.1

Kees Cook

unread,
Feb 27, 2020, 1:49:30 PM2/27/20
to Andrew Morton, Kees Cook, Elena Petrova, Dmitry Vyukov, Andrey Ryabinin, Andrey Konovalov, Alexander Potapenko, Dan Carpenter, Gustavo A. R. Silva, Arnd Bergmann, Ard Biesheuvel, kasa...@googlegroups.com, linu...@kvack.org, linux-...@vger.kernel.org, kernel-h...@lists.openwall.com, syzk...@googlegroups.com
The Undefined Behavior Sanitizer can operate in two modes: warning
reporting mode via lib/ubsan.c handler calls, or trap mode, which uses
__builtin_trap() as the handler. Using lib/ubsan.c means the kernel
image is about 5% larger (due to all the debugging text and reporting
structures to capture details about the warning conditions). Using the
trap mode, the image size changes are much smaller, though at the loss
of the "warning only" mode.

In order to give greater flexibility to system builders that want
minimal changes to image size and are prepared to deal with kernel code
being aborted and potentially destabilizing the system, this introduces
CONFIG_UBSAN_TRAP. The resulting image sizes comparison:

text data bss dec hex filename
19533663 6183037 18554956 44271656 2a38828 vmlinux.stock
19991849 7618513 18874448 46484810 2c54d4a vmlinux.ubsan
19712181 6284181 18366540 44362902 2a4ec96 vmlinux.ubsan-trap

CONFIG_UBSAN=y: image +4.8% (text +2.3%, data +18.9%)
CONFIG_UBSAN_TRAP=y: image +0.2% (text +0.9%, data +1.6%)

Additionally adjusts the CONFIG_UBSAN Kconfig help for clarity and
removes the mention of non-existing boot param "ubsan_handle".

Suggested-by: Elena Petrova <len...@google.com>
Signed-off-by: Kees Cook <kees...@chromium.org>
Acked-by: Dmitry Vyukov <dvy...@google.com>
---
lib/Kconfig.ubsan | 22 ++++++++++++++++++----
lib/Makefile | 2 ++
scripts/Makefile.ubsan | 9 +++++++--
3 files changed, 27 insertions(+), 6 deletions(-)

diff --git a/lib/Kconfig.ubsan b/lib/Kconfig.ubsan
index 0e04fcb3ab3d..9deb655838b0 100644
--- a/lib/Kconfig.ubsan
+++ b/lib/Kconfig.ubsan
@@ -5,11 +5,25 @@ config ARCH_HAS_UBSAN_SANITIZE_ALL
config UBSAN
bool "Undefined behaviour sanity checker"
help
- This option enables undefined behaviour sanity checker
+ This option enables the Undefined Behaviour sanity checker.
Compile-time instrumentation is used to detect various undefined
- behaviours in runtime. Various types of checks may be enabled
- via boot parameter ubsan_handle
- (see: Documentation/dev-tools/ubsan.rst).
+ behaviours at runtime. For more details, see:
+ Documentation/dev-tools/ubsan.rst
+
+config UBSAN_TRAP
+ bool "On Sanitizer warnings, abort the running kernel code"
+ depends on UBSAN
+ depends on $(cc-option, -fsanitize-undefined-trap-on-error)
+ help
+ Building kernels with Sanitizer features enabled tends to grow
+ the kernel size by around 5%, due to adding all the debugging
+ text on failure paths. To avoid this, Sanitizer instrumentation
+ can just issue a trap. This reduces the kernel size overhead but
+ turns all warnings (including potentially harmless conditions)
+ into full exceptions that abort the running kernel code
+ (regardless of context, locks held, etc), which may destabilize
+ the system. For some system builders this is an acceptable
+ trade-off.

config UBSAN_SANITIZE_ALL
bool "Enable instrumentation for the entire kernel"
diff --git a/lib/Makefile b/lib/Makefile
index 611872c06926..55cc8d73cd43 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -279,7 +279,9 @@ quiet_cmd_build_OID_registry = GEN $@
clean-files += oid_registry_data.c

obj-$(CONFIG_UCS2_STRING) += ucs2_string.o
+ifneq ($(CONFIG_UBSAN_TRAP),y)
obj-$(CONFIG_UBSAN) += ubsan.o
+endif

UBSAN_SANITIZE_ubsan.o := n
KASAN_SANITIZE_ubsan.o := n
diff --git a/scripts/Makefile.ubsan b/scripts/Makefile.ubsan
index 019771b845c5..668a91510bfe 100644
--- a/scripts/Makefile.ubsan
+++ b/scripts/Makefile.ubsan
@@ -1,5 +1,10 @@
# SPDX-License-Identifier: GPL-2.0
ifdef CONFIG_UBSAN
+
+ifdef CONFIG_UBSAN_ALIGNMENT
+ CFLAGS_UBSAN += $(call cc-option, -fsanitize=alignment)
+endif
+
CFLAGS_UBSAN += $(call cc-option, -fsanitize=shift)
CFLAGS_UBSAN += $(call cc-option, -fsanitize=integer-divide-by-zero)
CFLAGS_UBSAN += $(call cc-option, -fsanitize=unreachable)
@@ -9,8 +14,8 @@ ifdef CONFIG_UBSAN
CFLAGS_UBSAN += $(call cc-option, -fsanitize=bool)
CFLAGS_UBSAN += $(call cc-option, -fsanitize=enum)

-ifdef CONFIG_UBSAN_ALIGNMENT
- CFLAGS_UBSAN += $(call cc-option, -fsanitize=alignment)
+ifdef CONFIG_UBSAN_TRAP
+ CFLAGS_UBSAN += $(call cc-option, -fsanitize-undefined-trap-on-error)
endif

# -fsanitize=* options makes GCC less smart than usual and
--
2.20.1

Kees Cook

unread,
Feb 27, 2020, 1:49:33 PM2/27/20
to Andrew Morton, Kees Cook, Dmitry Vyukov, Andrey Ryabinin, Elena Petrova, Andrey Konovalov, Alexander Potapenko, Dan Carpenter, Gustavo A. R. Silva, Arnd Bergmann, Ard Biesheuvel, kasa...@googlegroups.com, linu...@kvack.org, linux-...@vger.kernel.org, kernel-h...@lists.openwall.com, syzk...@googlegroups.com
When syzbot tries to figure out how to deduplicate bug reports, it
prefers seeing a hint about a specific bug type (we can do better than
just "UBSAN"). This lifts the handler reason into the UBSAN report line
that includes the file path that tripped a check. Unfortunately, UBSAN
does not provide function names.

Suggested-by: Dmitry Vyukov <dvy...@google.com>
lib/ubsan.c | 36 +++++++++++++++---------------------
1 file changed, 15 insertions(+), 21 deletions(-)

diff --git a/lib/ubsan.c b/lib/ubsan.c
index 429663eef6a7..057d5375bfc6 100644
--- a/lib/ubsan.c
+++ b/lib/ubsan.c
@@ -45,13 +45,6 @@ static bool was_reported(struct source_location *location)
return test_and_set_bit(REPORTED_BIT, &location->reported);
}

-static void print_source_location(const char *prefix,
- struct source_location *loc)
-{
- pr_err("%s %s:%d:%d\n", prefix, loc->file_name,
- loc->line & LINE_MASK, loc->column & COLUMN_MASK);
-}
-
static bool suppress_report(struct source_location *loc)
{
return current->in_ubsan || was_reported(loc);
@@ -140,13 +133,14 @@ static void val_to_string(char *str, size_t size, struct type_descriptor *type,
}
}

-static void ubsan_prologue(struct source_location *location)
+static void ubsan_prologue(struct source_location *loc, const char *reason)
{
current->in_ubsan++;

pr_err("========================================"
"========================================\n");
- print_source_location("UBSAN: Undefined behaviour in", location);
+ pr_err("UBSAN: %s in %s:%d:%d\n", reason, loc->file_name,
+ loc->line & LINE_MASK, loc->column & COLUMN_MASK);
}

static void ubsan_epilogue(void)
@@ -180,12 +174,12 @@ static void handle_overflow(struct overflow_data *data, void *lhs,
if (suppress_report(&data->location))
return;

- ubsan_prologue(&data->location);
+ ubsan_prologue(&data->location, type_is_signed(type) ?
+ "signed integer overflow" :
+ "unsigned integer overflow");

val_to_string(lhs_val_str, sizeof(lhs_val_str), type, lhs);
val_to_string(rhs_val_str, sizeof(rhs_val_str), type, rhs);
- pr_err("%s integer overflow:\n",
- type_is_signed(type) ? "signed" : "unsigned");
pr_err("%s %c %s cannot be represented in type %s\n",
lhs_val_str,
op,
@@ -225,7 +219,7 @@ void __ubsan_handle_negate_overflow(struct overflow_data *data,
if (suppress_report(&data->location))
return;

- ubsan_prologue(&data->location);
+ ubsan_prologue(&data->location, "negation overflow");

val_to_string(old_val_str, sizeof(old_val_str), data->type, old_val);

@@ -245,7 +239,7 @@ void __ubsan_handle_divrem_overflow(struct overflow_data *data,
if (suppress_report(&data->location))
return;

- ubsan_prologue(&data->location);
+ ubsan_prologue(&data->location, "division overflow");

val_to_string(rhs_val_str, sizeof(rhs_val_str), data->type, rhs);

@@ -264,7 +258,7 @@ static void handle_null_ptr_deref(struct type_mismatch_data_common *data)
if (suppress_report(data->location))
return;

- ubsan_prologue(data->location);
+ ubsan_prologue(data->location, "NULL pointer dereference");

pr_err("%s null pointer of type %s\n",
type_check_kinds[data->type_check_kind],
@@ -279,7 +273,7 @@ static void handle_misaligned_access(struct type_mismatch_data_common *data,
if (suppress_report(data->location))
return;

- ubsan_prologue(data->location);
+ ubsan_prologue(data->location, "misaligned access");

pr_err("%s misaligned address %p for type %s\n",
type_check_kinds[data->type_check_kind],
@@ -295,7 +289,7 @@ static void handle_object_size_mismatch(struct type_mismatch_data_common *data,
if (suppress_report(data->location))
return;

- ubsan_prologue(data->location);
+ ubsan_prologue(data->location, "object size mismatch");
pr_err("%s address %p with insufficient space\n",
type_check_kinds[data->type_check_kind],
(void *) ptr);
@@ -354,7 +348,7 @@ void __ubsan_handle_out_of_bounds(struct out_of_bounds_data *data, void *index)
if (suppress_report(&data->location))
return;

- ubsan_prologue(&data->location);
+ ubsan_prologue(&data->location, "array index out of bounds");

val_to_string(index_str, sizeof(index_str), data->index_type, index);
pr_err("index %s is out of range for type %s\n", index_str,
@@ -375,7 +369,7 @@ void __ubsan_handle_shift_out_of_bounds(struct shift_out_of_bounds_data *data,
if (suppress_report(&data->location))
goto out;

- ubsan_prologue(&data->location);
+ ubsan_prologue(&data->location, "shift out of bounds");

val_to_string(rhs_str, sizeof(rhs_str), rhs_type, rhs);
val_to_string(lhs_str, sizeof(lhs_str), lhs_type, lhs);
@@ -407,7 +401,7 @@ EXPORT_SYMBOL(__ubsan_handle_shift_out_of_bounds);

void __ubsan_handle_builtin_unreachable(struct unreachable_data *data)
{
- ubsan_prologue(&data->location);
+ ubsan_prologue(&data->location, "unreachable");
pr_err("calling __builtin_unreachable()\n");
ubsan_epilogue();
panic("can't return from __builtin_unreachable()");
@@ -422,7 +416,7 @@ void __ubsan_handle_load_invalid_value(struct invalid_value_data *data,
if (suppress_report(&data->location))
return;

- ubsan_prologue(&data->location);
+ ubsan_prologue(&data->location, "invalid load");

val_to_string(val_str, sizeof(val_str), data->type, val);

--
2.20.1

Kees Cook

unread,
Feb 27, 2020, 1:49:33 PM2/27/20
to Andrew Morton, Kees Cook, Dmitry Vyukov, Andrey Ryabinin, Elena Petrova, Andrey Konovalov, Alexander Potapenko, Dan Carpenter, Gustavo A. R. Silva, Arnd Bergmann, Ard Biesheuvel, kasa...@googlegroups.com, linu...@kvack.org, linux-...@vger.kernel.org, kernel-h...@lists.openwall.com, syzk...@googlegroups.com
As done in the full WARN() handler, panic_on_warn needs to be cleared
before calling panic() to avoid recursive panics.

Signed-off-by: Kees Cook <kees...@chromium.org>
Acked-by: Dmitry Vyukov <dvy...@google.com>
---
mm/kasan/report.c | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/mm/kasan/report.c b/mm/kasan/report.c
index 5ef9f24f566b..54bd98a1fc7b 100644
--- a/mm/kasan/report.c
+++ b/mm/kasan/report.c
@@ -92,8 +92,16 @@ static void end_report(unsigned long *flags)
pr_err("==================================================================\n");
add_taint(TAINT_BAD_PAGE, LOCKDEP_NOW_UNRELIABLE);
spin_unlock_irqrestore(&report_lock, *flags);
- if (panic_on_warn)
+ if (panic_on_warn) {
+ /*
+ * This thread may hit another WARN() in the panic path.
+ * Resetting this prevents additional WARN() from panicking the
+ * system on this thread. Other threads are blocked by the
+ * panic_mutex in panic().
+ */
+ panic_on_warn = 0;
panic("panic_on_warn set ...\n");
+ }
kasan_enable_current();
}

--
2.20.1

Kees Cook

unread,
Feb 27, 2020, 2:35:23 PM2/27/20
to Andrew Morton, Kees Cook, Andrey Ryabinin, Elena Petrova, Andrey Konovalov, Alexander Potapenko, Dan Carpenter, Gustavo A. R. Silva, Arnd Bergmann, Ard Biesheuvel, kasa...@googlegroups.com, linu...@kvack.org, linux-...@vger.kernel.org, kernel-h...@lists.openwall.com, syzk...@googlegroups.com
Syzkaller expects kernel warnings to panic when the panic_on_warn
sysctl is set. More work is needed here to have UBSan reuse the WARN
infrastructure, but for now, just check the flag manually.

lib/ubsan.c | 11 +++++++++++
1 file changed, 11 insertions(+)

diff --git a/lib/ubsan.c b/lib/ubsan.c
index 7b9b58aee72c..429663eef6a7 100644
--- a/lib/ubsan.c
+++ b/lib/ubsan.c
@@ -156,6 +156,17 @@ static void ubsan_epilogue(void)
"========================================\n");

current->in_ubsan--;
+
+ if (panic_on_warn) {
+ /*
+ * This thread may hit another WARN() in the panic path.
+ * Resetting this prevents additional WARN() from panicking the
+ * system on this thread. Other threads are blocked by the
+ * panic_mutex in panic().
+ */
+ panic_on_warn = 0;
+ panic("panic_on_warn set ...\n");
+ }
}

static void handle_overflow(struct overflow_data *data, void *lhs,
--
2.20.1

Kees Cook

unread,
Feb 27, 2020, 2:35:24 PM2/27/20
to Andrew Morton, Kees Cook, Elena Petrova, Dmitry Vyukov, Andrey Ryabinin, Andrey Konovalov, Alexander Potapenko, Dan Carpenter, Gustavo A. R. Silva, Arnd Bergmann, Ard Biesheuvel, kasa...@googlegroups.com, linu...@kvack.org, linux-...@vger.kernel.org, kernel-h...@lists.openwall.com, syzk...@googlegroups.com
The Undefined Behavior Sanitizer can operate in two modes: warning
reporting mode via lib/ubsan.c handler calls, or trap mode, which uses
__builtin_trap() as the handler. Using lib/ubsan.c means the kernel
image is about 5% larger (due to all the debugging text and reporting
structures to capture details about the warning conditions). Using the
trap mode, the image size changes are much smaller, though at the loss
of the "warning only" mode.

In order to give greater flexibility to system builders that want
minimal changes to image size and are prepared to deal with kernel code
being aborted and potentially destabilizing the system, this introduces
CONFIG_UBSAN_TRAP. The resulting image sizes comparison:

text data bss dec hex filename
19533663 6183037 18554956 44271656 2a38828 vmlinux.stock
19991849 7618513 18874448 46484810 2c54d4a vmlinux.ubsan
19712181 6284181 18366540 44362902 2a4ec96 vmlinux.ubsan-trap

CONFIG_UBSAN=y: image +4.8% (text +2.3%, data +18.9%)
CONFIG_UBSAN_TRAP=y: image +0.2% (text +0.9%, data +1.6%)

Additionally adjusts the CONFIG_UBSAN Kconfig help for clarity and
removes the mention of non-existing boot param "ubsan_handle".

Suggested-by: Elena Petrova <len...@google.com>
Signed-off-by: Kees Cook <kees...@chromium.org>
Acked-by: Dmitry Vyukov <dvy...@google.com>
---

Kees Cook

unread,
Feb 27, 2020, 2:35:25 PM2/27/20
to Andrew Morton, Kees Cook, Andrey Ryabinin, Elena Petrova, Andrey Konovalov, Alexander Potapenko, Dan Carpenter, Gustavo A. R. Silva, Arnd Bergmann, Ard Biesheuvel, kasa...@googlegroups.com, linu...@kvack.org, linux-...@vger.kernel.org, kernel-h...@lists.openwall.com, syzk...@googlegroups.com
Argh, v4 missed uncommitted changes. v5 brown paper bag release! :)

This splits out the bounds checker so it can be individually used. This
is enabled in Android and hopefully for syzbot. Includes LKDTM tests for
behavioral corner-cases (beyond just the bounds checker), and adjusts
ubsan and kasan slightly for correct panic handling.

-Kees

v5:
- _actually_ use hyphenated bug class names (andreyknvl)
v4: https://lore.kernel.org/lkml/20200227184921....@chromium.org
v3: https://lore.kernel.org/lkml/20200116012321....@chromium.org
v2: https://lore.kernel.org/lkml/20191121181519....@chromium.org
v1: https://lore.kernel.org/lkml/20191120010636....@chromium.org


Kees Cook (6):
ubsan: Add trap instrumentation option
ubsan: Split "bounds" checker from other options
lkdtm/bugs: Add arithmetic overflow and array bounds checks
ubsan: Check panic_on_warn
kasan: Unset panic_on_warn before calling panic()
ubsan: Include bug type in report header

drivers/misc/lkdtm/bugs.c | 75 ++++++++++++++++++++++++++++++++++++++
drivers/misc/lkdtm/core.c | 3 ++
drivers/misc/lkdtm/lkdtm.h | 3 ++

Kees Cook

unread,
Feb 27, 2020, 2:35:26 PM2/27/20
to Andrew Morton, Kees Cook, Dmitry Vyukov, Andrey Ryabinin, Elena Petrova, Andrey Konovalov, Alexander Potapenko, Dan Carpenter, Gustavo A. R. Silva, Arnd Bergmann, Ard Biesheuvel, kasa...@googlegroups.com, linu...@kvack.org, linux-...@vger.kernel.org, kernel-h...@lists.openwall.com, syzk...@googlegroups.com
Adds LKDTM tests for arithmetic overflow (both signed and unsigned),
as well as array bounds checking.

Signed-off-by: Kees Cook <kees...@chromium.org>
Acked-by: Dmitry Vyukov <dvy...@google.com>
---
drivers/misc/lkdtm/bugs.c | 75 ++++++++++++++++++++++++++++++++++++++
drivers/misc/lkdtm/core.c | 3 ++
drivers/misc/lkdtm/lkdtm.h | 3 ++

Kees Cook

unread,
Feb 27, 2020, 2:35:29 PM2/27/20
to Andrew Morton, Kees Cook, Dmitry Vyukov, Andrey Ryabinin, Elena Petrova, Andrey Konovalov, Alexander Potapenko, Dan Carpenter, Gustavo A. R. Silva, Arnd Bergmann, Ard Biesheuvel, kasa...@googlegroups.com, linu...@kvack.org, linux-...@vger.kernel.org, kernel-h...@lists.openwall.com, syzk...@googlegroups.com
As done in the full WARN() handler, panic_on_warn needs to be cleared
before calling panic() to avoid recursive panics.

Signed-off-by: Kees Cook <kees...@chromium.org>
Acked-by: Dmitry Vyukov <dvy...@google.com>
---
mm/kasan/report.c | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/mm/kasan/report.c b/mm/kasan/report.c
index 5ef9f24f566b..54bd98a1fc7b 100644
--- a/mm/kasan/report.c
+++ b/mm/kasan/report.c
@@ -92,8 +92,16 @@ static void end_report(unsigned long *flags)
pr_err("==================================================================\n");
add_taint(TAINT_BAD_PAGE, LOCKDEP_NOW_UNRELIABLE);
spin_unlock_irqrestore(&report_lock, *flags);
- if (panic_on_warn)
+ if (panic_on_warn) {
+ /*
+ * This thread may hit another WARN() in the panic path.
+ * Resetting this prevents additional WARN() from panicking the
+ * system on this thread. Other threads are blocked by the
+ * panic_mutex in panic().
+ */
+ panic_on_warn = 0;
panic("panic_on_warn set ...\n");
+ }
kasan_enable_current();
}

--
2.20.1

Kees Cook

unread,
Feb 27, 2020, 2:35:30 PM2/27/20
to Andrew Morton, Kees Cook, Dmitry Vyukov, Andrey Ryabinin, Elena Petrova, Andrey Konovalov, Alexander Potapenko, Dan Carpenter, Gustavo A. R. Silva, Arnd Bergmann, Ard Biesheuvel, kasa...@googlegroups.com, linu...@kvack.org, linux-...@vger.kernel.org, kernel-h...@lists.openwall.com, syzk...@googlegroups.com
When syzbot tries to figure out how to deduplicate bug reports, it
prefers seeing a hint about a specific bug type (we can do better than
just "UBSAN"). This lifts the handler reason into the UBSAN report line
that includes the file path that tripped a check. Unfortunately, UBSAN
does not provide function names.

Suggested-by: Dmitry Vyukov <dvy...@google.com>
lib/ubsan.c | 36 +++++++++++++++---------------------
1 file changed, 15 insertions(+), 21 deletions(-)

diff --git a/lib/ubsan.c b/lib/ubsan.c
index 429663eef6a7..f8c0ccf35f29 100644
--- a/lib/ubsan.c
+++ b/lib/ubsan.c
+ "signed-integer-overflow" :
+ "unsigned-integer-overflow");

val_to_string(lhs_val_str, sizeof(lhs_val_str), type, lhs);
val_to_string(rhs_val_str, sizeof(rhs_val_str), type, rhs);
- pr_err("%s integer overflow:\n",
- type_is_signed(type) ? "signed" : "unsigned");
pr_err("%s %c %s cannot be represented in type %s\n",
lhs_val_str,
op,
@@ -225,7 +219,7 @@ void __ubsan_handle_negate_overflow(struct overflow_data *data,
if (suppress_report(&data->location))
return;

- ubsan_prologue(&data->location);
+ ubsan_prologue(&data->location, "negation-overflow");

val_to_string(old_val_str, sizeof(old_val_str), data->type, old_val);

@@ -245,7 +239,7 @@ void __ubsan_handle_divrem_overflow(struct overflow_data *data,
if (suppress_report(&data->location))
return;

- ubsan_prologue(&data->location);
+ ubsan_prologue(&data->location, "division-overflow");

val_to_string(rhs_val_str, sizeof(rhs_val_str), data->type, rhs);

@@ -264,7 +258,7 @@ static void handle_null_ptr_deref(struct type_mismatch_data_common *data)
if (suppress_report(data->location))
return;

- ubsan_prologue(data->location);
+ ubsan_prologue(data->location, "null-ptr-deref");

pr_err("%s null pointer of type %s\n",
type_check_kinds[data->type_check_kind],
@@ -279,7 +273,7 @@ static void handle_misaligned_access(struct type_mismatch_data_common *data,
if (suppress_report(data->location))
return;

- ubsan_prologue(data->location);
+ ubsan_prologue(data->location, "misaligned-access");

pr_err("%s misaligned address %p for type %s\n",
type_check_kinds[data->type_check_kind],
@@ -295,7 +289,7 @@ static void handle_object_size_mismatch(struct type_mismatch_data_common *data,
if (suppress_report(data->location))
return;

- ubsan_prologue(data->location);
+ ubsan_prologue(data->location, "object-size-mismatch");
pr_err("%s address %p with insufficient space\n",
type_check_kinds[data->type_check_kind],
(void *) ptr);
@@ -354,7 +348,7 @@ void __ubsan_handle_out_of_bounds(struct out_of_bounds_data *data, void *index)
if (suppress_report(&data->location))
return;

- ubsan_prologue(&data->location);
+ ubsan_prologue(&data->location, "array-index-out-of-bounds");

val_to_string(index_str, sizeof(index_str), data->index_type, index);
pr_err("index %s is out of range for type %s\n", index_str,
@@ -375,7 +369,7 @@ void __ubsan_handle_shift_out_of_bounds(struct shift_out_of_bounds_data *data,
if (suppress_report(&data->location))
goto out;

- ubsan_prologue(&data->location);
+ ubsan_prologue(&data->location, "shift-out-of-bounds");

val_to_string(rhs_str, sizeof(rhs_str), rhs_type, rhs);
val_to_string(lhs_str, sizeof(lhs_str), lhs_type, lhs);
@@ -407,7 +401,7 @@ EXPORT_SYMBOL(__ubsan_handle_shift_out_of_bounds);

void __ubsan_handle_builtin_unreachable(struct unreachable_data *data)
{
- ubsan_prologue(&data->location);
+ ubsan_prologue(&data->location, "unreachable");
pr_err("calling __builtin_unreachable()\n");
ubsan_epilogue();
panic("can't return from __builtin_unreachable()");
@@ -422,7 +416,7 @@ void __ubsan_handle_load_invalid_value(struct invalid_value_data *data,
if (suppress_report(&data->location))
return;

- ubsan_prologue(&data->location);
+ ubsan_prologue(&data->location, "invalid-load");

Andrey Konovalov

unread,
Feb 28, 2020, 7:45:21 AM2/28/20
to Kees Cook, Andrew Morton, Dmitry Vyukov, Andrey Ryabinin, Elena Petrova, Alexander Potapenko, Dan Carpenter, Gustavo A. R. Silva, Arnd Bergmann, Ard Biesheuvel, kasan-dev, Linux Memory Management List, LKML, kernel-h...@lists.openwall.com, syzkaller
> --
> You received this message because you are subscribed to the Google Groups "kasan-dev" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to kasan-dev+...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/kasan-dev/20200227193516.32566-7-keescook%40chromium.org.

Acked-by: Andrey Konovalov <andre...@google.com>

Thanks!
Reply all
Reply to author
Forward
0 new messages