diff options
author | Yu Watanabe <watanabe.yu+github@gmail.com> | 2023-03-18 15:07:45 +0900 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-03-18 15:07:45 +0900 |
commit | 0bc2bcf4fe957e64f95d875245a8910724abd808 (patch) | |
tree | 252c6c944feb37b7230755df5ed8562247c3cfac /src/boot | |
parent | cafd2c0be404cb8879f91d15e05cc8b695b32629 (diff) | |
parent | 1e7ff4ba8855996c8e077e0546acf9491c97663f (diff) | |
download | systemd-0bc2bcf4fe957e64f95d875245a8910724abd808.tar.gz |
Merge pull request #26759 from medhefgo/boot-stack-protector
boot: Add support for stack protector/trapping math/ubsan
Diffstat (limited to 'src/boot')
-rw-r--r-- | src/boot/efi/efi-string.c | 12 | ||||
-rw-r--r-- | src/boot/efi/log.c | 59 | ||||
-rw-r--r-- | src/boot/efi/log.h | 17 | ||||
-rw-r--r-- | src/boot/efi/meson.build | 22 | ||||
-rw-r--r-- | src/boot/efi/ubsan.c | 46 | ||||
-rw-r--r-- | src/boot/efi/util.h | 2 | ||||
-rw-r--r-- | src/boot/efi/vmm.c | 1 |
7 files changed, 140 insertions, 19 deletions
diff --git a/src/boot/efi/efi-string.c b/src/boot/efi/efi-string.c index a94e2e4c17..d199410881 100644 --- a/src/boot/efi/efi-string.c +++ b/src/boot/efi/efi-string.c @@ -882,6 +882,10 @@ char16_t *xvasprintf_status(EFI_STATUS status, const char *format, va_list ap) { # undef memcmp # undef memcpy # undef memset +_used_ void *memchr(const void *p, int c, size_t n); +_used_ int memcmp(const void *p1, const void *p2, size_t n); +_used_ void *memcpy(void * restrict dest, const void * restrict src, size_t n); +_used_ void *memset(void *p, int c, size_t n); #else /* And for userspace unit testing we need to give them an efi_ prefix. */ # define memchr efi_memchr @@ -890,7 +894,7 @@ char16_t *xvasprintf_status(EFI_STATUS status, const char *format, va_list ap) { # define memset efi_memset #endif -_used_ void *memchr(const void *p, int c, size_t n) { +void *memchr(const void *p, int c, size_t n) { if (!p || n == 0) return NULL; @@ -902,7 +906,7 @@ _used_ void *memchr(const void *p, int c, size_t n) { return NULL; } -_used_ int memcmp(const void *p1, const void *p2, size_t n) { +int memcmp(const void *p1, const void *p2, size_t n) { const uint8_t *up1 = p1, *up2 = p2; int r; @@ -922,7 +926,7 @@ _used_ int memcmp(const void *p1, const void *p2, size_t n) { return 0; } -_used_ void *memcpy(void * restrict dest, const void * restrict src, size_t n) { +void *memcpy(void * restrict dest, const void * restrict src, size_t n) { if (!dest || !src || n == 0) return dest; @@ -949,7 +953,7 @@ _used_ void *memcpy(void * restrict dest, const void * restrict src, size_t n) { return dest; } -_used_ void *memset(void *p, int c, size_t n) { +void *memset(void *p, int c, size_t n) { if (!p || n == 0) return p; diff --git a/src/boot/efi/log.c b/src/boot/efi/log.c index 9cbbb3a933..6d0edec2cf 100644 --- a/src/boot/efi/log.c +++ b/src/boot/efi/log.c @@ -1,17 +1,33 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "log.h" +#include "proto/rng.h" #include "proto/simple-text-io.h" static unsigned log_count = 0; -_noreturn_ static void freeze(void) { +void freeze(void) { for (;;) BS->Stall(60 * 1000 * 1000); } +_noreturn_ static void panic(const char16_t *message) { + if (ST->ConOut->Mode->CursorColumn > 0) + ST->ConOut->OutputString(ST->ConOut, (char16_t *) u"\r\n"); + ST->ConOut->SetAttribute(ST->ConOut, EFI_TEXT_ATTR(EFI_LIGHTRED, EFI_BLACK)); + ST->ConOut->OutputString(ST->ConOut, (char16_t *) message); + freeze(); +} + void efi_assert(const char *expr, const char *file, unsigned line, const char *function) { - log_error("systemd-boot assertion '%s' failed at %s:%u@%s. Halting.", expr, file, line, function); + static bool asserting = false; + + /* Let's be paranoid. */ + if (asserting) + panic(u"systemd-boot: Nested assertion failure, halting."); + + asserting = true; + log_error("systemd-boot: Assertion '%s' failed at %s:%u@%s, halting.", expr, file, line, function); freeze(); } @@ -44,16 +60,43 @@ void log_wait(void) { log_count = 0; } +_used_ intptr_t __stack_chk_guard = (intptr_t) 0x70f6967de78acae3; + +/* We can only set a random stack canary if this function attribute is available, + * otherwise this may create a stack check fail. */ +#if STACK_PROTECTOR_RANDOM +void __stack_chk_guard_init(void) { + EFI_RNG_PROTOCOL *rng; + if (BS->LocateProtocol(MAKE_GUID_PTR(EFI_RNG_PROTOCOL), NULL, (void **) &rng) == EFI_SUCCESS) + (void) rng->GetRNG(rng, NULL, sizeof(__stack_chk_guard), (void *) &__stack_chk_guard); +} +#endif + +_used_ _noreturn_ void __stack_chk_fail(void); +_used_ _noreturn_ void __stack_chk_fail_local(void); +void __stack_chk_fail(void) { + panic(u"systemd-boot: Stack check failed, halting."); +} +void __stack_chk_fail_local(void) { + __stack_chk_fail(); +} + +/* Called by libgcc for some fatal errors like integer overflow with -ftrapv. */ +_used_ _noreturn_ void abort(void); +void abort(void) { + panic(u"systemd-boot: Unknown error, halting."); +} + #if defined(__ARM_EABI__) /* These override the (weak) div0 handlers from libgcc as they would otherwise call raise() instead. */ +_used_ _noreturn_ int __aeabi_idiv0(int return_value); +_used_ _noreturn_ long long __aeabi_ldiv0(long long return_value); -_used_ _noreturn_ int __aeabi_idiv0(int return_value) { - log_error("Division by zero."); - freeze(); +int __aeabi_idiv0(int return_value) { + panic(u"systemd-boot: Division by zero, halting."); } -_used_ _noreturn_ long long __aeabi_ldiv0(long long return_value) { - log_error("Division by zero."); - freeze(); +long long __aeabi_ldiv0(long long return_value) { + panic(u"systemd-boot: Division by zero, halting."); } #endif diff --git a/src/boot/efi/log.h b/src/boot/efi/log.h index 9bdcfad923..973e13c260 100644 --- a/src/boot/efi/log.h +++ b/src/boot/efi/log.h @@ -3,6 +3,23 @@ #include "efi-string.h" +#if defined __has_attribute +# if __has_attribute(no_stack_protector) +# define HAVE_NO_STACK_PROTECTOR_ATTRIBUTE +# endif +#endif + +#if defined(HAVE_NO_STACK_PROTECTOR_ATTRIBUTE) && \ + (defined(__SSP__) || defined(__SSP_ALL__) || \ + defined(__SSP_STRONG__) || defined(__SSP_EXPLICIT__)) +# define STACK_PROTECTOR_RANDOM 1 +__attribute__((no_stack_protector, noinline)) void __stack_chk_guard_init(void); +#else +# define STACK_PROTECTOR_RANDOM 0 +# define __stack_chk_guard_init() +#endif + +_noreturn_ void freeze(void); void log_wait(void); _gnu_printf_(2, 3) EFI_STATUS log_internal(EFI_STATUS status, const char *format, ...); #define log_error_status(status, ...) log_internal(status, __VA_ARGS__) diff --git a/src/boot/efi/meson.build b/src/boot/efi/meson.build index 7e497f7866..e6e7eed3bc 100644 --- a/src/boot/efi/meson.build +++ b/src/boot/efi/meson.build @@ -139,6 +139,7 @@ efi_c_args += cc.get_supported_arguments( '-fwide-exec-charset=UCS2', # gcc docs says this is required for ms_abi to work correctly. '-maccumulate-outgoing-args', + '-mstack-protector-guard=global', ) # Debug information has little value in release builds as no normal human being knows @@ -178,20 +179,23 @@ efi_disabled_c_args = cc.get_supported_arguments( '-fcf-protection=none', '-fno-asynchronous-unwind-tables', '-fno-exceptions', - '-fno-trapv', - '-fno-sanitize=all', - '-fno-stack-clash-protection', - '-fno-stack-protector', '-fno-unwind-tables', ) -efi_c_args += efi_disabled_c_args -efi_c_ld_args += efi_disabled_c_args efi_override_options = [ 'b_coverage=false', 'b_pgo=off', - 'b_sanitize=none', ] +if get_option('b_sanitize') == 'undefined' + efi_disabled_c_args += cc.get_supported_arguments('-fno-sanitize-link-runtime') +else + efi_disabled_c_args += cc.get_supported_arguments('-fno-sanitize=all') + efi_override_options += 'b_sanitize=none' +endif + +efi_c_args += efi_disabled_c_args +efi_c_ld_args += efi_disabled_c_args + if cc.get_id() == 'clang' # clang is too picky sometimes. efi_c_args += '-Wno-unused-command-line-argument' @@ -254,6 +258,10 @@ stub_sources = files( 'stub.c', ) +if get_option('b_sanitize') == 'undefined' + libefi_sources += files('ubsan.c') +endif + if host_machine.cpu_family() in ['x86', 'x86_64'] stub_sources += files('linux_x86.c') endif diff --git a/src/boot/efi/ubsan.c b/src/boot/efi/ubsan.c new file mode 100644 index 0000000000..951204683e --- /dev/null +++ b/src/boot/efi/ubsan.c @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "log.h" + +typedef struct { + const char *filename; + uint32_t line; + uint32_t column; +} SourceLocation; + +/* Note that all ubsan handlers have a pointer to a type-specific struct passed as first argument. + * Since we do not inspect the extra data in it we can just treat it as a SourceLocation struct + * directly to keep things simple. */ + +#define HANDLER(name, ...) \ + _used_ _noreturn_ void __ubsan_handle_##name(__VA_ARGS__); \ + void __ubsan_handle_##name(__VA_ARGS__) { \ + log_error("systemd-boot: %s in %s@%u:%u", \ + __func__, \ + location->filename, \ + location->line, \ + location->column); \ + freeze(); \ + } + +#define UNARY_HANDLER(name) HANDLER(name, SourceLocation *location, uintptr_t v) +#define BINARY_HANDLER(name) HANDLER(name, SourceLocation *location, uintptr_t v1, uintptr_t v2) + +UNARY_HANDLER(load_invalid_value); +UNARY_HANDLER(negate_overflow); +UNARY_HANDLER(out_of_bounds); +UNARY_HANDLER(type_mismatch_v1); +UNARY_HANDLER(vla_bound_not_positive); + +BINARY_HANDLER(add_overflow); +BINARY_HANDLER(divrem_overflow); +BINARY_HANDLER(implicit_conversion); +BINARY_HANDLER(mul_overflow); +BINARY_HANDLER(pointer_overflow); +BINARY_HANDLER(shift_out_of_bounds); +BINARY_HANDLER(sub_overflow); + +HANDLER(builtin_unreachable, SourceLocation *location); +HANDLER(invalid_builtin, SourceLocation *location); +HANDLER(nonnull_arg, SourceLocation *location); +HANDLER(nonnull_return_v1, SourceLocation *attr_location, SourceLocation *location); diff --git a/src/boot/efi/util.h b/src/boot/efi/util.h index 5e1085c788..c321062996 100644 --- a/src/boot/efi/util.h +++ b/src/boot/efi/util.h @@ -172,10 +172,12 @@ void hexdump(const char16_t *prefix, const void *data, size_t size); EFI_SYSTEM_TABLE *ST; \ EFI_BOOT_SERVICES *BS; \ EFI_RUNTIME_SERVICES *RT; \ + EFIAPI EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *system_table); \ EFIAPI EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *system_table) { \ ST = system_table; \ BS = system_table->BootServices; \ RT = system_table->RuntimeServices; \ + __stack_chk_guard_init(); \ notify_debugger((identity), (wait_for_debugger)); \ EFI_STATUS err = func(image); \ log_wait(); \ diff --git a/src/boot/efi/vmm.c b/src/boot/efi/vmm.c index 60e8a97c43..951b4e3766 100644 --- a/src/boot/efi/vmm.c +++ b/src/boot/efi/vmm.c @@ -10,6 +10,7 @@ #include "proto/device-path.h" #include "string-util-fundamental.h" #include "util.h" +#include "vmm.h" #define QEMU_KERNEL_LOADER_FS_MEDIA_GUID \ { 0x1428f772, 0xb64a, 0x441e, { 0xb8, 0xc3, 0x9e, 0xbd, 0xd7, 0xf8, 0x93, 0xc7 } } |