# -*- 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
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.
#
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))
#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 {
#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 );
/** 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 );
return NULL;
}
+/**
+ * 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
*
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,
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 ) {}
+
+}
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;
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;