diff options
Diffstat (limited to 'src/interface')
| -rw-r--r-- | src/interface/efi/efi_block.c | 3 | ||||
| -rw-r--r-- | src/interface/efi/efi_download.c | 5 | ||||
| -rw-r--r-- | src/interface/efi/efi_driver.c | 27 | ||||
| -rw-r--r-- | src/interface/efi/efi_entropy.c | 12 | ||||
| -rw-r--r-- | src/interface/efi/efi_init.c | 121 | ||||
| -rw-r--r-- | src/interface/efi/efi_local.c | 15 | ||||
| -rw-r--r-- | src/interface/efi/efi_snp_hii.c | 9 | ||||
| -rw-r--r-- | src/interface/efi/efi_usb.c | 2 | ||||
| -rw-r--r-- | src/interface/efi/efidrvprefix.c | 12 | ||||
| -rw-r--r-- | src/interface/efi/efiprefix.c | 3 |
10 files changed, 199 insertions, 10 deletions
diff --git a/src/interface/efi/efi_block.c b/src/interface/efi/efi_block.c index 91f830a11..64d1e1980 100644 --- a/src/interface/efi/efi_block.c +++ b/src/interface/efi/efi_block.c @@ -594,11 +594,14 @@ static int efi_block_boot_image ( struct san_device *sandev, EFI_HANDLE handle, sandev->drive, efi_devpath_text ( boot_path ) ); /* Try loading boot image from this device */ + *image = NULL; if ( ( efirc = bs->LoadImage ( FALSE, efi_image_handle, boot_path, NULL, 0, image ) ) != 0 ) { rc = -EEFI ( efirc ); DBGC ( sandev, "EFIBLK %#02x could not load image: %s\n", sandev->drive, strerror ( rc ) ); + if ( efirc == EFI_SECURITY_VIOLATION ) + bs->UnloadImage ( *image ); goto err_load_image; } diff --git a/src/interface/efi/efi_download.c b/src/interface/efi/efi_download.c index 1218852e2..8d12bd57c 100644 --- a/src/interface/efi/efi_download.c +++ b/src/interface/efi/efi_download.c @@ -138,8 +138,11 @@ efi_download_start ( IPXE_DOWNLOAD_PROTOCOL *This __unused, struct efi_download_file *file; int rc; + efi_snp_claim(); + file = malloc ( sizeof ( struct efi_download_file ) ); if ( file == NULL ) { + efi_snp_release(); return EFI_OUT_OF_RESOURCES; } @@ -147,10 +150,10 @@ efi_download_start ( IPXE_DOWNLOAD_PROTOCOL *This __unused, rc = xfer_open ( &file->xfer, LOCATION_URI_STRING, Url ); if ( rc ) { free ( file ); + efi_snp_release(); return EFIRC ( rc ); } - efi_snp_claim(); file->pos = 0; file->data_callback = DataCallback; file->finish_callback = FinishCallback; diff --git a/src/interface/efi/efi_driver.c b/src/interface/efi/efi_driver.c index 7be2e585d..760ee41a8 100644 --- a/src/interface/efi/efi_driver.c +++ b/src/interface/efi/efi_driver.c @@ -39,6 +39,20 @@ FILE_LICENCE ( GPL2_OR_LATER ); * */ +/* Disambiguate the various error causes */ +#define EINFO_EEFI_CONNECT \ + __einfo_uniqify ( EINFO_EPLATFORM, 0x01, \ + "Could not connect controllers" ) +#define EINFO_EEFI_CONNECT_PROHIBITED \ + __einfo_platformify ( EINFO_EEFI_CONNECT, \ + EFI_SECURITY_VIOLATION, \ + "Connecting controllers prohibited by " \ + "security policy" ) +#define EEFI_CONNECT_PROHIBITED \ + __einfo_error ( EINFO_EEFI_CONNECT_PROHIBITED ) +#define EEFI_CONNECT( efirc ) EPLATFORM ( EINFO_EEFI_CONNECT, efirc, \ + EEFI_CONNECT_PROHIBITED ) + static EFI_DRIVER_BINDING_PROTOCOL efi_driver_binding; /** List of controlled EFI devices */ @@ -457,10 +471,19 @@ static int efi_driver_connect ( EFI_HANDLE device ) { efi_handle_name ( device ) ); if ( ( efirc = bs->ConnectController ( device, drivers, NULL, FALSE ) ) != 0 ) { - rc = -EEFI ( efirc ); + rc = -EEFI_CONNECT ( efirc ); DBGC ( device, "EFIDRV %s could not connect new drivers: " "%s\n", efi_handle_name ( device ), strerror ( rc ) ); - return rc; + DBGC ( device, "EFIDRV %s connecting driver directly\n", + efi_handle_name ( device ) ); + if ( ( efirc = efi_driver_start ( &efi_driver_binding, device, + NULL ) ) != 0 ) { + rc = -EEFI_CONNECT ( efirc ); + DBGC ( device, "EFIDRV %s could not connect driver " + "directly: %s\n", efi_handle_name ( device ), + strerror ( rc ) ); + return rc; + } } DBGC2 ( device, "EFIDRV %s after connecting:\n", efi_handle_name ( device ) ); diff --git a/src/interface/efi/efi_entropy.c b/src/interface/efi/efi_entropy.c index a865bad59..f6e82e2c0 100644 --- a/src/interface/efi/efi_entropy.c +++ b/src/interface/efi/efi_entropy.c @@ -222,6 +222,7 @@ static int efi_get_noise_ticks ( noise_sample_t *noise ) { * @ret rc Return status code */ static int efi_get_noise_rng ( noise_sample_t *noise ) { + static uint8_t prev[EFI_ENTROPY_RNG_LEN]; uint8_t buf[EFI_ENTROPY_RNG_LEN]; EFI_STATUS efirc; int rc; @@ -239,6 +240,17 @@ static int efi_get_noise_rng ( noise_sample_t *noise ) { return rc; } + /* Fail (and permanently disable the EFI RNG) if we get + * consecutive identical results. + */ + if ( memcmp ( buf, prev, sizeof ( buf ) ) == 0 ) { + DBGC ( &tick, "ENTROPY detected broken EFI RNG:\n" ); + DBGC_HDA ( &tick, 0, buf, sizeof ( buf ) ); + efirng = NULL; + return -EIO; + } + memcpy ( prev, buf, sizeof ( prev ) ); + /* Reduce random bytes to a single noise sample. This seems * like overkill, but we have no way of knowing how much * entropy is actually present in the bytes returned by the diff --git a/src/interface/efi/efi_init.c b/src/interface/efi/efi_init.c index ed9707f2c..70212b184 100644 --- a/src/interface/efi/efi_init.c +++ b/src/interface/efi/efi_init.c @@ -21,9 +21,12 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include <string.h> #include <errno.h> +#include <endian.h> #include <ipxe/init.h> +#include <ipxe/rotate.h> #include <ipxe/efi/efi.h> #include <ipxe/efi/efi_driver.h> +#include <ipxe/efi/efi_utils.h> #include <ipxe/efi/Protocol/LoadedImage.h> /** Image handle passed to entry point */ @@ -32,6 +35,9 @@ EFI_HANDLE efi_image_handle; /** Loaded image protocol for this image */ EFI_LOADED_IMAGE_PROTOCOL *efi_loaded_image; +/** Device path for the loaded image's device handle */ +EFI_DEVICE_PATH_PROTOCOL *efi_loaded_image_path; + /** System table passed to entry point * * We construct the symbol name efi_systab via the PLATFORM macro. @@ -47,6 +53,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 +104,46 @@ 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 ) { + unsigned long cookie = 0; + unsigned int rotation = ( 8 * sizeof ( cookie ) / 4 ); + + /* 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. + */ + cookie ^= ( ( unsigned long ) handle ); + cookie = roll ( cookie, rotation ); + cookie ^= ( ( unsigned long ) &handle ); + cookie = roll ( cookie, rotation ); + cookie ^= profile_timestamp(); + cookie = roll ( cookie, rotation ); + cookie ^= build_id; + + /* 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 + * lower-order bits. + */ + cookie <<= 8; + + /* Ensure that the NUL byte is placed at the bottom of the + * stack cookie, to avoid potential disclosure via an + * unterminated string. + */ + if ( __BYTE_ORDER == __BIG_ENDIAN ) + cookie >>= 8; + + return cookie; +} + +/** * Initialise EFI environment * * @v image_handle Image handle @@ -100,6 +156,9 @@ EFI_STATUS efi_init ( EFI_HANDLE image_handle, struct efi_protocol *prot; struct efi_config_table *tab; void *loaded_image; + void *device_path; + void *device_path_copy; + size_t device_path_len; EFI_STATUS efirc; int rc; @@ -130,6 +189,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, @@ -175,6 +237,33 @@ EFI_STATUS efi_init ( EFI_HANDLE image_handle, DBGC ( systab, "EFI image base address %p\n", efi_loaded_image->ImageBase ); + /* Get loaded image's device handle's device path */ + if ( ( efirc = bs->OpenProtocol ( efi_loaded_image->DeviceHandle, + &efi_device_path_protocol_guid, + &device_path, image_handle, NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( systab, "EFI could not get loaded image's device path: " + "%s", strerror ( rc ) ); + goto err_no_device_path; + } + + /* Make a copy of the loaded image's device handle's device + * path, since the device handle itself may become invalidated + * when we load our own drivers. + */ + device_path_len = ( efi_devpath_len ( device_path ) + + sizeof ( EFI_DEVICE_PATH_PROTOCOL ) ); + if ( ( efirc = bs->AllocatePool ( EfiBootServicesData, device_path_len, + &device_path_copy ) ) != 0 ) { + rc = -EEFI ( efirc ); + goto err_alloc_device_path; + } + memcpy ( device_path_copy, device_path, device_path_len ); + efi_loaded_image_path = device_path_copy; + DBGC ( systab, "EFI image device path %s\n", + efi_devpath_text ( efi_loaded_image_path ) ); + /* EFI is perfectly capable of gracefully shutting down any * loaded devices if it decides to fall back to a legacy boot. * For no particularly comprehensible reason, it doesn't @@ -206,6 +295,9 @@ EFI_STATUS efi_init ( EFI_HANDLE image_handle, err_driver_install: bs->CloseEvent ( efi_shutdown_event ); err_create_event: + bs->FreePool ( efi_loaded_image_path ); + err_alloc_device_path: + err_no_device_path: err_no_loaded_image: err_missing_table: err_missing_protocol: @@ -236,7 +328,36 @@ static EFI_STATUS EFIAPI efi_unload ( EFI_HANDLE image_handle __unused ) { /* Uninstall exit boot services event */ bs->CloseEvent ( efi_shutdown_event ); + /* Free copy of loaded image's device handle's device path */ + bs->FreePool ( efi_loaded_image_path ); + DBGC ( systab, "EFI image unloaded\n" ); 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/efi_local.c b/src/interface/efi/efi_local.c index bd010ad2e..79ea822f3 100644 --- a/src/interface/efi/efi_local.c +++ b/src/interface/efi/efi_local.c @@ -307,6 +307,7 @@ static int efi_local_open_volume ( struct efi_local *local, EFI_GUID *protocol = &efi_simple_file_system_protocol_guid; int ( * check ) ( struct efi_local *local, EFI_HANDLE device, EFI_FILE_PROTOCOL *root, const char *volume ); + EFI_DEVICE_PATH_PROTOCOL *path; EFI_FILE_PROTOCOL *root; EFI_HANDLE *handles; EFI_HANDLE device; @@ -328,8 +329,18 @@ static int efi_local_open_volume ( struct efi_local *local, } check = efi_local_check_volume_name; } else { - /* Use our loaded image's device handle */ - handles = &efi_loaded_image->DeviceHandle; + /* Locate filesystem from which we were loaded */ + path = efi_loaded_image_path; + if ( ( efirc = bs->LocateDevicePath ( protocol, &path, + &device ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( local, "LOCAL %p could not locate file system " + "on %s: %s\n", local, + efi_devpath_text ( efi_loaded_image_path ), + strerror ( rc ) ); + return rc; + } + handles = &device; num_handles = 1; check = NULL; } diff --git a/src/interface/efi/efi_snp_hii.c b/src/interface/efi/efi_snp_hii.c index 651bef040..1e681a429 100644 --- a/src/interface/efi/efi_snp_hii.c +++ b/src/interface/efi/efi_snp_hii.c @@ -247,16 +247,17 @@ static int efi_snp_hii_append ( struct efi_snp_device *snpdev __unused, const char *key, const char *value, wchar_t **results ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_STATUS efirc; size_t len; void *new; /* Allocate new string */ len = ( ( *results ? ( wcslen ( *results ) + 1 /* "&" */ ) : 0 ) + strlen ( key ) + 1 /* "=" */ + strlen ( value ) + 1 /* NUL */ ); - bs->AllocatePool ( EfiBootServicesData, ( len * sizeof ( wchar_t ) ), - &new ); - if ( ! new ) - return -ENOMEM; + if ( ( efirc = bs->AllocatePool ( EfiBootServicesData, + ( len * sizeof ( wchar_t ) ), + &new ) ) != 0 ) + return -EEFI ( efirc ); /* Populate string */ efi_snprintf ( new, len, "%ls%s%s=%s", ( *results ? *results : L"" ), diff --git a/src/interface/efi/efi_usb.c b/src/interface/efi/efi_usb.c index 48274f1d6..6f49b369e 100644 --- a/src/interface/efi/efi_usb.c +++ b/src/interface/efi/efi_usb.c @@ -1100,7 +1100,7 @@ static int efi_usb_install ( struct efi_usb_device *usbdev, usbpath->Header.Type = MESSAGING_DEVICE_PATH; usbpath->Header.SubType = MSG_USB_DP; usbpath->Header.Length[0] = sizeof ( *usbpath ); - usbpath->ParentPortNumber = usb->port->address; + usbpath->ParentPortNumber = ( usb->port->address - 1 ); } /* Add to list of interfaces */ diff --git a/src/interface/efi/efidrvprefix.c b/src/interface/efi/efidrvprefix.c index 4fbb19ff7..ac7d94374 100644 --- a/src/interface/efi/efidrvprefix.c +++ b/src/interface/efi/efidrvprefix.c @@ -34,16 +34,28 @@ FILE_LICENCE ( GPL2_OR_LATER ); */ EFI_STATUS EFIAPI _efidrv_start ( EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *systab ) { + EFI_BOOT_SERVICES *bs; + EFI_TPL saved_tpl; 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; + /* Raise TPL */ + bs = efi_systab->BootServices; + saved_tpl = bs->RaiseTPL ( TPL_CALLBACK ); + /* Initialise iPXE environment */ initialise(); startup(); + /* Restore TPL */ + bs->RestoreTPL ( saved_tpl ); + return 0; } diff --git a/src/interface/efi/efiprefix.c b/src/interface/efi/efiprefix.c index de3572c75..2c5a5b31d 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; |
