aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/Makefile.efi8
-rw-r--r--src/Makefile.housekeeping18
-rw-r--r--src/include/ipxe/efi/efi.h32
-rw-r--r--src/interface/efi/efi_init.c62
-rw-r--r--src/interface/efi/efidrvprefix.c3
-rw-r--r--src/interface/efi/efiprefix.c3
6 files changed, 115 insertions, 11 deletions
diff --git a/src/Makefile.efi b/src/Makefile.efi
index 151b331..10f3fe7 100644
--- a/src/Makefile.efi
+++ b/src/Makefile.efi
@@ -1,5 +1,13 @@
# -*- makefile -*- : Force emacs to use Makefile mode
+# Enable stack protection if available
+#
+SPG_TEST = $(CC) -fstack-protector-strong -mstack-protector-guard=global \
+ -x c -c /dev/null -o /dev/null >/dev/null 2>&1
+SPG_FLAGS := $(shell $(SPG_TEST) && $(ECHO) '-fstack-protector-strong ' \
+ '-mstack-protector-guard=global')
+CFLAGS += $(SPG_FLAGS)
+
# The EFI linker script
#
LDSCRIPT = scripts/efi.lds
diff --git a/src/Makefile.housekeeping b/src/Makefile.housekeeping
index 1dd1479..66d6dd4 100644
--- a/src/Makefile.housekeeping
+++ b/src/Makefile.housekeeping
@@ -146,17 +146,6 @@ define NEWLINE
endef
-# Some widespread patched versions of gcc include -fstack-protector by
-# default, even when -ffreestanding is specified. We therefore need
-# to disable -fstack-protector if the compiler supports it.
-#
-ifeq ($(CCTYPE),gcc)
-SP_TEST = $(CC) -fno-stack-protector -x c -c /dev/null \
- -o /dev/null >/dev/null 2>&1
-SP_FLAGS := $(shell $(SP_TEST) && $(ECHO) '-fno-stack-protector')
-WORKAROUND_CFLAGS += $(SP_FLAGS)
-endif
-
# gcc 4.4 generates .eh_frame sections by default, which distort the
# output of "size". Inhibit this.
#
@@ -415,6 +404,13 @@ ifdef BIN
incdirs :
@$(ECHO) $(INCDIRS)
+# Inhibit -fstack-protector (which is implicitly enabled in some
+# patched gcc versions) unless explicitly mentioned in CFLAGS.
+#
+ifeq ($(findstring -fstack-protector,$(CFLAGS)),)
+CFLAGS += -fno-stack-protector
+endif
+
# Common flags
#
CFLAGS += $(foreach INC,$(INCDIRS),-I$(INC))
diff --git a/src/include/ipxe/efi/efi.h b/src/include/ipxe/efi/efi.h
index 669e536..b8777ef 100644
--- a/src/include/ipxe/efi/efi.h
+++ b/src/include/ipxe/efi/efi.h
@@ -65,6 +65,8 @@ typedef struct {} *EFI_HANDLE;
#include <ipxe/tables.h>
#include <ipxe/uuid.h>
+#include <ipxe/version.h>
+#include <ipxe/profile.h>
/** An EFI protocol used by iPXE */
struct efi_protocol {
@@ -272,6 +274,36 @@ extern void dbg_efi_protocols ( EFI_HANDLE handle );
#define DBGCP_EFI_PROTOCOLS( ... ) \
DBGC_EFI_PROTOCOLS_IF ( PROFILE, ##__VA_ARGS__ )
+extern unsigned long __stack_chk_guard;
+extern unsigned long efi_stack_cookie ( EFI_HANDLE handle );
+extern void __stack_chk_fail ( void );
+
+/**
+ * Initialise stack cookie
+ *
+ * @v handle Image handle
+ */
+static inline __attribute__ (( always_inline )) void
+efi_init_stack_guard ( EFI_HANDLE handle ) {
+
+ /* The calling function must not itself use stack protection,
+ * since the change in the stack guard value would trigger a
+ * false positive.
+ *
+ * There is unfortunately no way to annotate a function to
+ * exclude the use of stack protection. We must therefore
+ * rely on correctly anticipating the compiler's decision on
+ * the use of stack protection.
+ *
+ * The calculation of the stack cookie value deliberately
+ * takes the address of a stack variable (to provide an
+ * additional source of entropy). This operation would
+ * trigger the application of stack protection to the calling
+ * function, and so must be externalised.
+ */
+ __stack_chk_guard = efi_stack_cookie ( handle );
+}
+
extern EFI_STATUS efi_init ( EFI_HANDLE image_handle,
EFI_SYSTEM_TABLE *systab );
diff --git a/src/interface/efi/efi_init.c b/src/interface/efi/efi_init.c
index ed9707f..df46bb1 100644
--- a/src/interface/efi/efi_init.c
+++ b/src/interface/efi/efi_init.c
@@ -47,6 +47,16 @@ int efi_shutdown_in_progress;
/** Event used to signal shutdown */
static EFI_EVENT efi_shutdown_event;
+/** Stack cookie */
+unsigned long __stack_chk_guard;
+
+/** Exit function
+ *
+ * Cached to minimise external dependencies when a stack check
+ * failure is triggered.
+ */
+static EFI_EXIT efi_exit;
+
/* Forward declarations */
static EFI_STATUS EFIAPI efi_unload ( EFI_HANDLE image_handle );
@@ -88,6 +98,29 @@ static void * efi_find_table ( EFI_GUID *guid ) {
}
/**
+ * Construct a stack cookie value
+ *
+ * @v handle Image handle
+ * @ret cookie Stack cookie
+ */
+__attribute__ (( noinline )) unsigned long
+efi_stack_cookie ( EFI_HANDLE handle ) {
+
+ /* There is no viable source of entropy available at this
+ * point. Construct a value that is at least likely to vary
+ * between platforms and invocations.
+ *
+ * Ensure that the value contains a NUL byte, to act as a
+ * runaway string terminator. Construct the NUL using a shift
+ * rather than a mask, to avoid losing valuable entropy in the
+ * low-order bits.
+ */
+ return ( ( ( ( unsigned long ) handle ) ^
+ ( ( unsigned long ) &handle ) ^
+ profile_timestamp() ^ build_id ) << 8 );
+}
+
+/**
* Initialise EFI environment
*
* @v image_handle Image handle
@@ -130,6 +163,9 @@ EFI_STATUS efi_init ( EFI_HANDLE image_handle,
DBGC ( systab, "EFI handle %p systab %p\n", image_handle, systab );
bs = systab->BootServices;
+ /* Store abort function pointer */
+ efi_exit = bs->Exit;
+
/* Look up used protocols */
for_each_table_entry ( prot, EFI_PROTOCOLS ) {
if ( ( efirc = bs->LocateProtocol ( &prot->guid, NULL,
@@ -240,3 +276,29 @@ static EFI_STATUS EFIAPI efi_unload ( EFI_HANDLE image_handle __unused ) {
return 0;
}
+
+/**
+ * Abort on stack check failure
+ *
+ */
+__attribute__ (( noreturn )) void __stack_chk_fail ( void ) {
+ EFI_STATUS efirc;
+ int rc;
+
+ /* Report failure (when debugging) */
+ DBGC ( efi_systab, "EFI stack check failed (cookie %#lx); aborting\n",
+ __stack_chk_guard );
+
+ /* Attempt to exit cleanly with an error status */
+ if ( efi_exit ) {
+ efirc = efi_exit ( efi_image_handle, EFI_COMPROMISED_DATA,
+ 0, NULL );
+ rc = -EEFI ( efirc );
+ DBGC ( efi_systab, "EFI stack check exit failed: %s\n",
+ strerror ( rc ) );
+ }
+
+ /* If the exit fails for any reason, lock the system */
+ while ( 1 ) {}
+
+}
diff --git a/src/interface/efi/efidrvprefix.c b/src/interface/efi/efidrvprefix.c
index 4fbb19f..f9bfa4c 100644
--- a/src/interface/efi/efidrvprefix.c
+++ b/src/interface/efi/efidrvprefix.c
@@ -36,6 +36,9 @@ EFI_STATUS EFIAPI _efidrv_start ( EFI_HANDLE image_handle,
EFI_SYSTEM_TABLE *systab ) {
EFI_STATUS efirc;
+ /* Initialise stack cookie */
+ efi_init_stack_guard ( image_handle );
+
/* Initialise EFI environment */
if ( ( efirc = efi_init ( image_handle, systab ) ) != 0 )
return efirc;
diff --git a/src/interface/efi/efiprefix.c b/src/interface/efi/efiprefix.c
index de3572c..2c5a5b3 100644
--- a/src/interface/efi/efiprefix.c
+++ b/src/interface/efi/efiprefix.c
@@ -41,6 +41,9 @@ EFI_STATUS EFIAPI _efi_start ( EFI_HANDLE image_handle,
EFI_STATUS efirc;
int rc;
+ /* Initialise stack cookie */
+ efi_init_stack_guard ( image_handle );
+
/* Initialise EFI environment */
if ( ( efirc = efi_init ( image_handle, systab ) ) != 0 )
goto err_init;