Skip to content

Commit ed2b548

Browse files
committed
ubsan/overflow: Rework integer overflow sanitizer option to turn on everything
Since we're going to approach integer overflow mitigation a type at a time, we need to enable all of the associated sanitizers, and then opt into types one at a time. Rename the existing "signed wrap" sanitizer to just the entire topic area: "integer wrap". Enable the implicit integer truncation sanitizers, with required callbacks and tests. Notably, this requires features (currently) only available in Clang, so we can depend on the cc-option tests to determine availability instead of doing version tests. Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Kees Cook <[email protected]>
1 parent d2cf8cc commit ed2b548

File tree

8 files changed

+69
-24
lines changed

8 files changed

+69
-24
lines changed

include/linux/compiler_types.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -360,7 +360,7 @@ struct ftrace_likely_data {
360360
#endif
361361

362362
/* Do not trap wrapping arithmetic within an annotated function. */
363-
#ifdef CONFIG_UBSAN_SIGNED_WRAP
363+
#ifdef CONFIG_UBSAN_INTEGER_WRAP
364364
# define __signed_wrap __attribute__((no_sanitize("signed-integer-overflow")))
365365
#else
366366
# define __signed_wrap

kernel/configs/hardening.config

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ CONFIG_UBSAN_BOUNDS=y
4646
# CONFIG_UBSAN_SHIFT is not set
4747
# CONFIG_UBSAN_DIV_ZERO is not set
4848
# CONFIG_UBSAN_UNREACHABLE is not set
49-
# CONFIG_UBSAN_SIGNED_WRAP is not set
49+
# CONFIG_UBSAN_INTEGER_WRAP is not set
5050
# CONFIG_UBSAN_BOOL is not set
5151
# CONFIG_UBSAN_ENUM is not set
5252
# CONFIG_UBSAN_ALIGNMENT is not set

lib/Kconfig.ubsan

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -116,21 +116,20 @@ config UBSAN_UNREACHABLE
116116
This option enables -fsanitize=unreachable which checks for control
117117
flow reaching an expected-to-be-unreachable position.
118118

119-
config UBSAN_SIGNED_WRAP
120-
bool "Perform checking for signed arithmetic wrap-around"
119+
config UBSAN_INTEGER_WRAP
120+
bool "Perform checking for integer arithmetic wrap-around"
121121
default UBSAN
122122
depends on !COMPILE_TEST
123-
# The no_sanitize attribute was introduced in GCC with version 8.
124-
depends on !CC_IS_GCC || GCC_VERSION >= 80000
125123
depends on $(cc-option,-fsanitize=signed-integer-overflow)
126-
help
127-
This option enables -fsanitize=signed-integer-overflow which checks
128-
for wrap-around of any arithmetic operations with signed integers.
129-
This currently performs nearly no instrumentation due to the
130-
kernel's use of -fno-strict-overflow which converts all would-be
131-
arithmetic undefined behavior into wrap-around arithmetic. Future
132-
sanitizer versions will allow for wrap-around checking (rather than
133-
exclusively undefined behavior).
124+
depends on $(cc-option,-fsanitize=unsigned-integer-overflow)
125+
depends on $(cc-option,-fsanitize=implicit-signed-integer-truncation)
126+
depends on $(cc-option,-fsanitize=implicit-unsigned-integer-truncation)
127+
help
128+
This option enables all of the sanitizers involved in integer overflow
129+
(wrap-around) mitigation: signed-integer-overflow, unsigned-integer-overflow,
130+
implicit-signed-integer-truncation, and implicit-unsigned-integer-truncation.
131+
This is currently limited only to the size_t type while testing and
132+
compiler development continues.
134133

135134
config UBSAN_BOOL
136135
bool "Perform checking for non-boolean values used as boolean"

lib/test_ubsan.c

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ static void test_ubsan_add_overflow(void)
1515
{
1616
volatile int val = INT_MAX;
1717

18-
UBSAN_TEST(CONFIG_UBSAN_SIGNED_WRAP);
18+
UBSAN_TEST(CONFIG_UBSAN_INTEGER_WRAP);
1919
val += 2;
2020
}
2121

@@ -24,23 +24,23 @@ static void test_ubsan_sub_overflow(void)
2424
volatile int val = INT_MIN;
2525
volatile int val2 = 2;
2626

27-
UBSAN_TEST(CONFIG_UBSAN_SIGNED_WRAP);
27+
UBSAN_TEST(CONFIG_UBSAN_INTEGER_WRAP);
2828
val -= val2;
2929
}
3030

3131
static void test_ubsan_mul_overflow(void)
3232
{
3333
volatile int val = INT_MAX / 2;
3434

35-
UBSAN_TEST(CONFIG_UBSAN_SIGNED_WRAP);
35+
UBSAN_TEST(CONFIG_UBSAN_INTEGER_WRAP);
3636
val *= 3;
3737
}
3838

3939
static void test_ubsan_negate_overflow(void)
4040
{
4141
volatile int val = INT_MIN;
4242

43-
UBSAN_TEST(CONFIG_UBSAN_SIGNED_WRAP);
43+
UBSAN_TEST(CONFIG_UBSAN_INTEGER_WRAP);
4444
val = -val;
4545
}
4646

@@ -53,6 +53,15 @@ static void test_ubsan_divrem_overflow(void)
5353
val /= val2;
5454
}
5555

56+
static void test_ubsan_truncate_signed(void)
57+
{
58+
volatile long val = LONG_MAX;
59+
volatile int val2 = 0;
60+
61+
UBSAN_TEST(CONFIG_UBSAN_INTEGER_WRAP);
62+
val2 = val;
63+
}
64+
5665
static void test_ubsan_shift_out_of_bounds(void)
5766
{
5867
volatile int neg = -1, wrap = 4;
@@ -127,6 +136,7 @@ static const test_ubsan_fp test_ubsan_array[] = {
127136
test_ubsan_sub_overflow,
128137
test_ubsan_mul_overflow,
129138
test_ubsan_negate_overflow,
139+
test_ubsan_truncate_signed,
130140
test_ubsan_shift_out_of_bounds,
131141
test_ubsan_out_of_bounds,
132142
test_ubsan_load_invalid_value,

lib/ubsan.c

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ const char *report_ubsan_failure(struct pt_regs *regs, u32 check_type)
4444
case ubsan_shift_out_of_bounds:
4545
return "UBSAN: shift out of bounds";
4646
#endif
47-
#if defined(CONFIG_UBSAN_DIV_ZERO) || defined(CONFIG_UBSAN_SIGNED_WRAP)
47+
#if defined(CONFIG_UBSAN_DIV_ZERO) || defined(CONFIG_UBSAN_INTEGER_WRAP)
4848
/*
4949
* SanitizerKind::IntegerDivideByZero and
5050
* SanitizerKind::SignedIntegerOverflow emit
@@ -79,7 +79,7 @@ const char *report_ubsan_failure(struct pt_regs *regs, u32 check_type)
7979
case ubsan_type_mismatch:
8080
return "UBSAN: type mismatch";
8181
#endif
82-
#ifdef CONFIG_UBSAN_SIGNED_WRAP
82+
#ifdef CONFIG_UBSAN_INTEGER_WRAP
8383
/*
8484
* SanitizerKind::SignedIntegerOverflow emits
8585
* SanitizerHandler::AddOverflow, SanitizerHandler::SubOverflow,
@@ -303,6 +303,30 @@ void __ubsan_handle_negate_overflow(void *_data, void *old_val)
303303
}
304304
EXPORT_SYMBOL(__ubsan_handle_negate_overflow);
305305

306+
void __ubsan_handle_implicit_conversion(void *_data, void *from_val, void *to_val)
307+
{
308+
struct implicit_conversion_data *data = _data;
309+
char from_val_str[VALUE_LENGTH];
310+
char to_val_str[VALUE_LENGTH];
311+
312+
if (suppress_report(&data->location))
313+
return;
314+
315+
val_to_string(from_val_str, sizeof(from_val_str), data->from_type, from_val);
316+
val_to_string(to_val_str, sizeof(to_val_str), data->to_type, to_val);
317+
318+
ubsan_prologue(&data->location, "implicit-conversion");
319+
320+
pr_err("cannot represent %s value %s during %s %s, truncated to %s\n",
321+
data->from_type->type_name,
322+
from_val_str,
323+
type_check_kinds[data->type_check_kind],
324+
data->to_type->type_name,
325+
to_val_str);
326+
327+
ubsan_epilogue();
328+
}
329+
EXPORT_SYMBOL(__ubsan_handle_implicit_conversion);
306330

307331
void __ubsan_handle_divrem_overflow(void *_data, void *lhs, void *rhs)
308332
{

lib/ubsan.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,13 @@ struct overflow_data {
6262
struct type_descriptor *type;
6363
};
6464

65+
struct implicit_conversion_data {
66+
struct source_location location;
67+
struct type_descriptor *from_type;
68+
struct type_descriptor *to_type;
69+
unsigned char type_check_kind;
70+
};
71+
6572
struct type_mismatch_data {
6673
struct source_location location;
6774
struct type_descriptor *type;
@@ -142,6 +149,7 @@ void ubsan_linkage __ubsan_handle_sub_overflow(void *data, void *lhs, void *rhs)
142149
void ubsan_linkage __ubsan_handle_mul_overflow(void *data, void *lhs, void *rhs);
143150
void ubsan_linkage __ubsan_handle_negate_overflow(void *_data, void *old_val);
144151
void ubsan_linkage __ubsan_handle_divrem_overflow(void *_data, void *lhs, void *rhs);
152+
void ubsan_linkage __ubsan_handle_implicit_conversion(void *_data, void *lhs, void *rhs);
145153
void ubsan_linkage __ubsan_handle_type_mismatch(struct type_mismatch_data *data, void *ptr);
146154
void ubsan_linkage __ubsan_handle_type_mismatch_v1(void *_data, void *ptr);
147155
void ubsan_linkage __ubsan_handle_out_of_bounds(void *_data, void *index);

scripts/Makefile.lib

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,8 +166,8 @@ _c_flags += $(if $(patsubst n%,, \
166166
$(UBSAN_SANITIZE_$(target-stem).o)$(UBSAN_SANITIZE)$(is-kernel-object)), \
167167
$(CFLAGS_UBSAN))
168168
_c_flags += $(if $(patsubst n%,, \
169-
$(UBSAN_SIGNED_WRAP_$(target-stem).o)$(UBSAN_SANITIZE_$(target-stem).o)$(UBSAN_SIGNED_WRAP)$(UBSAN_SANITIZE)$(is-kernel-object)), \
170-
$(CFLAGS_UBSAN_SIGNED_WRAP))
169+
$(UBSAN_INTEGER_WRAP_$(target-stem).o)$(UBSAN_SANITIZE_$(target-stem).o)$(UBSAN_INTEGER_WRAP)$(UBSAN_SANITIZE)$(is-kernel-object)), \
170+
$(CFLAGS_UBSAN_INTEGER_WRAP))
171171
endif
172172

173173
ifeq ($(CONFIG_KCOV),y)

scripts/Makefile.ubsan

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,9 @@ ubsan-cflags-$(CONFIG_UBSAN_TRAP) += $(call cc-option,-fsanitize-trap=undefined
1414

1515
export CFLAGS_UBSAN := $(ubsan-cflags-y)
1616

17-
ubsan-signed-wrap-cflags-$(CONFIG_UBSAN_SIGNED_WRAP) += -fsanitize=signed-integer-overflow
18-
export CFLAGS_UBSAN_SIGNED_WRAP := $(ubsan-signed-wrap-cflags-y)
17+
ubsan-integer-wrap-cflags-$(CONFIG_UBSAN_INTEGER_WRAP) += \
18+
-fsanitize=signed-integer-overflow \
19+
-fsanitize=unsigned-integer-overflow \
20+
-fsanitize=implicit-signed-integer-truncation \
21+
-fsanitize=implicit-unsigned-integer-truncation
22+
export CFLAGS_UBSAN_INTEGER_WRAP := $(ubsan-integer-wrap-cflags-y)

0 commit comments

Comments
 (0)