summaryrefslogtreecommitdiff
path: root/src/boot
diff options
context:
space:
mode:
authorYu Watanabe <watanabe.yu+github@gmail.com>2023-03-18 15:07:45 +0900
committerGitHub <noreply@github.com>2023-03-18 15:07:45 +0900
commit0bc2bcf4fe957e64f95d875245a8910724abd808 (patch)
tree252c6c944feb37b7230755df5ed8562247c3cfac /src/boot
parentcafd2c0be404cb8879f91d15e05cc8b695b32629 (diff)
parent1e7ff4ba8855996c8e077e0546acf9491c97663f (diff)
downloadsystemd-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.c12
-rw-r--r--src/boot/efi/log.c59
-rw-r--r--src/boot/efi/log.h17
-rw-r--r--src/boot/efi/meson.build22
-rw-r--r--src/boot/efi/ubsan.c46
-rw-r--r--src/boot/efi/util.h2
-rw-r--r--src/boot/efi/vmm.c1
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 } }