]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[efi] Nullify interfaces and leak memory on uninstallation failure
authorMichael Brown <mcb30@ipxe.org>
Mon, 26 Oct 2020 15:10:18 +0000 (15:10 +0000)
committerMichael Brown <mcb30@ipxe.org>
Mon, 26 Oct 2020 15:24:00 +0000 (15:24 +0000)
The UEFI specification allows uninstallation of a protocol interface
to fail.  There is no sensible way for code to react to this, since
uninstallation is likely to be taking place on a code path that cannot
itself fail (e.g. a code path that is itself a failure path).

Where the protocol structure exists within a dynamically allocated
block of memory, this leads to possible use-after-free bugs.  Work
around this unfortunate design choice by nullifying the protocol
(i.e. overwriting the method pointers with no-ops) and leaking the
memory containing the protocol structure.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
src/include/ipxe/efi/efi_null.h [new file with mode: 0644]
src/include/ipxe/efi/efi_snp.h
src/interface/efi/efi_block.c
src/interface/efi/efi_null.c [new file with mode: 0644]
src/interface/efi/efi_pxe.c
src/interface/efi/efi_snp.c
src/interface/efi/efi_snp_hii.c

diff --git a/src/include/ipxe/efi/efi_null.h b/src/include/ipxe/efi/efi_null.h
new file mode 100644 (file)
index 0000000..cc91e09
--- /dev/null
@@ -0,0 +1,31 @@
+#ifndef _IPXE_EFI_NULL_H
+#define _IPXE_EFI_NULL_H
+
+/** @file
+ *
+ * EFI null interfaces
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <ipxe/efi/efi.h>
+#include <ipxe/efi/Protocol/AppleNetBoot.h>
+#include <ipxe/efi/Protocol/BlockIo.h>
+#include <ipxe/efi/Protocol/ComponentName2.h>
+#include <ipxe/efi/Protocol/HiiConfigAccess.h>
+#include <ipxe/efi/Protocol/LoadFile.h>
+#include <ipxe/efi/Protocol/NetworkInterfaceIdentifier.h>
+#include <ipxe/efi/Protocol/PxeBaseCode.h>
+#include <ipxe/efi/Protocol/SimpleNetwork.h>
+
+extern void efi_nullify_snp ( EFI_SIMPLE_NETWORK_PROTOCOL *snp );
+extern void efi_nullify_nii ( EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL *nii );
+extern void efi_nullify_name2 ( EFI_COMPONENT_NAME2_PROTOCOL *name2 );
+extern void efi_nullify_load_file ( EFI_LOAD_FILE_PROTOCOL *load_file );
+extern void efi_nullify_hii ( EFI_HII_CONFIG_ACCESS_PROTOCOL *hii );
+extern void efi_nullify_block ( EFI_BLOCK_IO_PROTOCOL *block );
+extern void efi_nullify_pxe ( EFI_PXE_BASE_CODE_PROTOCOL *pxe );
+extern void efi_nullify_apple ( EFI_APPLE_NET_BOOT_PROTOCOL *apple );
+
+#endif /* _IPXE_EFI_NULL_H */
index 9076f1d565676926f7a2910a6fb62fab23c178ea..c278b1d4cb05bc26eca8132a162d9ddf8ab15016 100644 (file)
@@ -76,7 +76,7 @@ struct efi_snp_device {
 };
 
 extern int efi_snp_hii_install ( struct efi_snp_device *snpdev );
-extern void efi_snp_hii_uninstall ( struct efi_snp_device *snpdev );
+extern int efi_snp_hii_uninstall ( struct efi_snp_device *snpdev );
 extern struct efi_snp_device * find_snpdev ( EFI_HANDLE handle );
 extern struct efi_snp_device * last_opened_snpdev ( void );
 extern void efi_snp_add_claim ( int delta );
index c974ca065da464eb8f00b8294b34d6041bac25f9..19f669fd2a5d056da6ef6ea62e26be7fcc2ab751 100644 (file)
@@ -55,6 +55,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
 #include <ipxe/efi/efi_strings.h>
 #include <ipxe/efi/efi_snp.h>
 #include <ipxe/efi/efi_path.h>
+#include <ipxe/efi/efi_null.h>
 #include <ipxe/efi/efi_block.h>
 
 /** ACPI table protocol protocol */
@@ -244,6 +245,7 @@ static int efi_block_hook ( unsigned int drive, struct uri **uris,
        EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
        struct san_device *sandev;
        struct efi_block_data *block;
+       int leak = 0;
        EFI_STATUS efirc;
        int rc;
 
@@ -317,20 +319,33 @@ static int efi_block_hook ( unsigned int drive, struct uri **uris,
 
        return drive;
 
-       bs->UninstallMultipleProtocolInterfaces (
+       if ( ( efirc = bs->UninstallMultipleProtocolInterfaces (
                        block->handle,
                        &efi_block_io_protocol_guid, &block->block_io,
-                       &efi_device_path_protocol_guid, block->path, NULL );
+                       &efi_device_path_protocol_guid, block->path,
+                       NULL ) ) != 0 ) {
+               DBGC ( sandev, "EFIBLK %#02x could not uninstall protocols: "
+                      "%s\n", sandev->drive, strerror ( -EEFI ( efirc ) ) );
+               efi_nullify_block ( &block->block_io );
+               leak = 1;
+       }
  err_install:
-       free ( block->path );
-       block->path = NULL;
+       if ( ! leak )  {
+               free ( block->path );
+               block->path = NULL;
+       }
  err_describe:
  err_active:
        unregister_sandev ( sandev );
  err_register:
-       sandev_put ( sandev );
+       if ( ! leak )
+               sandev_put ( sandev );
  err_alloc:
  err_no_uris:
+       if ( leak ) {
+               DBGC ( sandev, "EFIBLK %#02x nullified and leaked\n",
+                      sandev->drive );
+       }
        return rc;
 }
 
@@ -343,6 +358,8 @@ static void efi_block_unhook ( unsigned int drive ) {
        EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
        struct san_device *sandev;
        struct efi_block_data *block;
+       int leak = 0;
+       EFI_STATUS efirc;
 
        /* Find SAN device */
        sandev = sandev_find ( drive );
@@ -353,20 +370,35 @@ static void efi_block_unhook ( unsigned int drive ) {
        block = sandev->priv;
 
        /* Uninstall protocols */
-       bs->UninstallMultipleProtocolInterfaces (
+       if ( ( efirc = bs->UninstallMultipleProtocolInterfaces (
                        block->handle,
                        &efi_block_io_protocol_guid, &block->block_io,
-                       &efi_device_path_protocol_guid, block->path, NULL );
+                       &efi_device_path_protocol_guid, block->path,
+                       NULL ) ) != 0 ) {
+               DBGC ( sandev, "EFIBLK %#02x could not uninstall protocols: "
+                      "%s\n", sandev->drive, strerror ( -EEFI ( efirc ) ) );
+               efi_nullify_block ( &block->block_io );
+               leak = 1;
+       }
 
        /* Free device path */
-       free ( block->path );
-       block->path = NULL;
+       if ( ! leak ) {
+               free ( block->path );
+               block->path = NULL;
+       }
 
        /* Unregister SAN device */
        unregister_sandev ( sandev );
 
        /* Drop reference to drive */
-       sandev_put ( sandev );
+       if ( ! leak )
+               sandev_put ( sandev );
+
+       /* Report leakage, if applicable */
+       if ( leak ) {
+               DBGC ( sandev, "EFIBLK %#02x nullified and leaked\n",
+                      sandev->drive );
+       }
 }
 
 /** An installed ACPI table */
diff --git a/src/interface/efi/efi_null.c b/src/interface/efi/efi_null.c
new file mode 100644 (file)
index 0000000..0bd6963
--- /dev/null
@@ -0,0 +1,532 @@
+/*
+ * Copyright (C) 2020 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <string.h>
+#include <ipxe/efi/efi.h>
+#include <ipxe/efi/efi_null.h>
+
+/** @file
+ *
+ * EFI null interfaces
+ *
+ */
+
+/******************************************************************************
+ *
+ * Simple Network Protocol
+ *
+ ******************************************************************************
+ */
+
+static EFI_STATUS EFIAPI
+efi_null_snp_start ( EFI_SIMPLE_NETWORK_PROTOCOL *snp __unused ) {
+       return EFI_UNSUPPORTED;
+}
+
+static EFI_STATUS EFIAPI
+efi_null_snp_stop ( EFI_SIMPLE_NETWORK_PROTOCOL *snp __unused ) {
+       return EFI_UNSUPPORTED;
+}
+
+static EFI_STATUS EFIAPI
+efi_null_snp_initialize ( EFI_SIMPLE_NETWORK_PROTOCOL *snp __unused,
+                         UINTN extra_rx_bufsize __unused,
+                         UINTN extra_tx_bufsize __unused ) {
+       return EFI_UNSUPPORTED;
+}
+
+static EFI_STATUS EFIAPI
+efi_null_snp_reset ( EFI_SIMPLE_NETWORK_PROTOCOL *snp __unused,
+                    BOOLEAN ext_verify __unused ) {
+       return EFI_UNSUPPORTED;
+}
+
+static EFI_STATUS EFIAPI
+efi_null_snp_shutdown ( EFI_SIMPLE_NETWORK_PROTOCOL *snp __unused ) {
+       return EFI_UNSUPPORTED;
+}
+
+static EFI_STATUS EFIAPI
+efi_null_snp_receive_filters ( EFI_SIMPLE_NETWORK_PROTOCOL *snp __unused,
+                              UINT32 enable __unused,
+                              UINT32 disable __unused,
+                              BOOLEAN mcast_reset __unused,
+                              UINTN mcast_count __unused,
+                              EFI_MAC_ADDRESS *mcast __unused ) {
+       return EFI_UNSUPPORTED;
+}
+
+static EFI_STATUS EFIAPI
+efi_null_snp_station_address ( EFI_SIMPLE_NETWORK_PROTOCOL *snp __unused,
+                              BOOLEAN reset __unused,
+                              EFI_MAC_ADDRESS *new __unused ) {
+       return EFI_UNSUPPORTED;
+}
+
+static EFI_STATUS EFIAPI
+efi_null_snp_statistics ( EFI_SIMPLE_NETWORK_PROTOCOL *snp __unused,
+                         BOOLEAN reset __unused, UINTN *stats_len __unused,
+                         EFI_NETWORK_STATISTICS *stats __unused ) {
+       return EFI_UNSUPPORTED;
+}
+
+static EFI_STATUS EFIAPI
+efi_null_snp_mcast_ip_to_mac ( EFI_SIMPLE_NETWORK_PROTOCOL *snp __unused,
+                              BOOLEAN ipv6 __unused,
+                              EFI_IP_ADDRESS *ip __unused,
+                              EFI_MAC_ADDRESS *mac __unused ) {
+       return EFI_UNSUPPORTED;
+}
+
+static EFI_STATUS EFIAPI
+efi_null_snp_nvdata ( EFI_SIMPLE_NETWORK_PROTOCOL *snp __unused,
+                     BOOLEAN read __unused, UINTN offset __unused,
+                     UINTN len __unused, VOID *data __unused ) {
+       return EFI_UNSUPPORTED;
+}
+
+static EFI_STATUS EFIAPI
+efi_null_snp_get_status ( EFI_SIMPLE_NETWORK_PROTOCOL *snp __unused,
+                         UINT32 *interrupts __unused, VOID **txbuf __unused ) {
+       return EFI_UNSUPPORTED;
+}
+
+static EFI_STATUS EFIAPI
+efi_null_snp_transmit ( EFI_SIMPLE_NETWORK_PROTOCOL *snp __unused,
+                       UINTN ll_header_len __unused, UINTN len __unused,
+                       VOID *data __unused, EFI_MAC_ADDRESS *ll_src __unused,
+                       EFI_MAC_ADDRESS *ll_dest __unused,
+                       UINT16 *net_proto __unused ) {
+       return EFI_UNSUPPORTED;
+}
+
+static EFI_STATUS EFIAPI
+efi_null_snp_receive ( EFI_SIMPLE_NETWORK_PROTOCOL *snp __unused,
+                      UINTN *ll_header_len __unused, UINTN *len __unused,
+                      VOID *data __unused, EFI_MAC_ADDRESS *ll_src __unused,
+                      EFI_MAC_ADDRESS *ll_dest __unused,
+                      UINT16 *net_proto __unused ) {
+       return EFI_UNSUPPORTED;
+}
+
+static EFI_SIMPLE_NETWORK_PROTOCOL efi_null_snp = {
+       .Revision       = EFI_SIMPLE_NETWORK_PROTOCOL_REVISION,
+       .Start          = efi_null_snp_start,
+       .Stop           = efi_null_snp_stop,
+       .Initialize     = efi_null_snp_initialize,
+       .Reset          = efi_null_snp_reset,
+       .Shutdown       = efi_null_snp_shutdown,
+       .ReceiveFilters = efi_null_snp_receive_filters,
+       .StationAddress = efi_null_snp_station_address,
+       .Statistics     = efi_null_snp_statistics,
+       .MCastIpToMac   = efi_null_snp_mcast_ip_to_mac,
+       .NvData         = efi_null_snp_nvdata,
+       .GetStatus      = efi_null_snp_get_status,
+       .Transmit       = efi_null_snp_transmit,
+       .Receive        = efi_null_snp_receive,
+};
+
+/**
+ * Nullify SNP interface
+ *
+ * @v snp              SNP interface
+ */
+void efi_nullify_snp ( EFI_SIMPLE_NETWORK_PROTOCOL *snp ) {
+
+       memcpy ( snp, &efi_null_snp,
+                offsetof ( typeof ( *snp ), WaitForPacket ) );
+       snp->Mode->State = EfiSimpleNetworkStopped;
+}
+
+/******************************************************************************
+ *
+ * Network Interface Identification protocol
+ *
+ ******************************************************************************
+ */
+
+static EFIAPI VOID efi_null_undi_issue ( UINT64 cdb_phys ) {
+       PXE_CDB *cdb = ( ( void * ) ( intptr_t ) cdb_phys );
+
+       cdb->StatCode = PXE_STATCODE_UNSUPPORTED;
+       cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+}
+
+static PXE_SW_UNDI efi_null_undi __attribute__ (( aligned ( 16 ) )) = {
+       .Signature      = PXE_ROMID_SIGNATURE,
+       .Len            = sizeof ( efi_null_undi ),
+       .Rev            = PXE_ROMID_REV,
+       .MajorVer       = PXE_ROMID_MAJORVER,
+       .MinorVer       = PXE_ROMID_MINORVER,
+       .Implementation = PXE_ROMID_IMP_SW_VIRT_ADDR,
+};
+
+/**
+ * Nullify NII interface
+ *
+ * @v nii              NII interface
+ */
+void efi_nullify_nii ( EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL *nii ) {
+       efi_null_undi.EntryPoint = ( ( intptr_t ) efi_null_undi_issue );
+       nii->Id = ( ( intptr_t ) &efi_null_undi );
+}
+
+/******************************************************************************
+ *
+ * Component name protocol
+ *
+ ******************************************************************************
+ */
+
+static EFI_STATUS EFIAPI
+efi_null_get_driver_name ( EFI_COMPONENT_NAME2_PROTOCOL *name2 __unused,
+                          CHAR8 *language __unused,
+                          CHAR16 **driver_name __unused ) {
+       return EFI_UNSUPPORTED;
+}
+
+static EFI_STATUS EFIAPI
+efi_null_get_controller_name ( EFI_COMPONENT_NAME2_PROTOCOL *name2 __unused,
+                              EFI_HANDLE device __unused,
+                              EFI_HANDLE child __unused,
+                              CHAR8 *language __unused,
+                              CHAR16 **controller_name __unused ) {
+       return EFI_UNSUPPORTED;
+}
+
+static EFI_COMPONENT_NAME2_PROTOCOL efi_null_name2 = {
+       .GetDriverName = efi_null_get_driver_name,
+       .GetControllerName = efi_null_get_controller_name,
+       .SupportedLanguages = "",
+};
+
+/**
+ * Nullify Component Name Protocol interface
+ *
+ * @v name2            Component name protocol
+ */
+void efi_nullify_name2 ( EFI_COMPONENT_NAME2_PROTOCOL *name2 ) {
+
+       memcpy ( name2, &efi_null_name2, sizeof ( name2 ) );
+}
+
+/******************************************************************************
+ *
+ * Load file protocol
+ *
+ ******************************************************************************
+ */
+
+static EFI_STATUS EFIAPI
+efi_null_load_file ( EFI_LOAD_FILE_PROTOCOL *load_file __unused,
+                    EFI_DEVICE_PATH_PROTOCOL *path __unused,
+                    BOOLEAN booting __unused, UINTN *len __unused,
+                    VOID *data __unused ) {
+       return EFI_UNSUPPORTED;
+}
+
+/**
+ * Nullify Load File Protocol interface
+ *
+ * @v load_file                Load file protocol
+ */
+void efi_nullify_load_file ( EFI_LOAD_FILE_PROTOCOL *load_file ) {
+       load_file->LoadFile = efi_null_load_file;
+}
+
+/******************************************************************************
+ *
+ * HII configuration access protocol
+ *
+ ******************************************************************************
+ */
+
+static EFI_STATUS EFIAPI
+efi_null_hii_extract ( const EFI_HII_CONFIG_ACCESS_PROTOCOL *hii __unused,
+                      EFI_STRING request __unused,
+                      EFI_STRING *progress __unused,
+                      EFI_STRING *results __unused ) {
+       return EFI_UNSUPPORTED;
+}
+
+static EFI_STATUS EFIAPI
+efi_null_hii_route ( const EFI_HII_CONFIG_ACCESS_PROTOCOL *hii __unused,
+                    EFI_STRING config __unused,
+                    EFI_STRING *progress __unused ) {
+       return EFI_UNSUPPORTED;
+}
+
+static EFI_STATUS EFIAPI
+efi_null_hii_callback ( const EFI_HII_CONFIG_ACCESS_PROTOCOL *hii __unused,
+                       EFI_BROWSER_ACTION action __unused,
+                       EFI_QUESTION_ID question_id __unused,
+                       UINT8 type __unused, EFI_IFR_TYPE_VALUE *value __unused,
+                       EFI_BROWSER_ACTION_REQUEST *action_request __unused ) {
+       return EFI_UNSUPPORTED;
+}
+
+static EFI_HII_CONFIG_ACCESS_PROTOCOL efi_null_hii = {
+       .ExtractConfig  = efi_null_hii_extract,
+       .RouteConfig    = efi_null_hii_route,
+       .Callback       = efi_null_hii_callback,
+};
+
+/**
+ * Nullify HII configuration access protocol
+ *
+ * @v hii              HII configuration access protocol
+ */
+void efi_nullify_hii ( EFI_HII_CONFIG_ACCESS_PROTOCOL *hii ) {
+
+       memcpy ( hii, &efi_null_hii, sizeof ( *hii ) );
+}
+
+/******************************************************************************
+ *
+ * Block I/O protocol
+ *
+ ******************************************************************************
+ */
+
+static EFI_STATUS EFIAPI
+efi_null_block_reset ( EFI_BLOCK_IO_PROTOCOL *block __unused,
+                      BOOLEAN verify __unused ) {
+       return EFI_UNSUPPORTED;
+}
+
+static EFI_STATUS EFIAPI
+efi_null_block_read ( EFI_BLOCK_IO_PROTOCOL *block __unused,
+                     UINT32 media __unused, EFI_LBA lba __unused,
+                     UINTN len __unused, VOID *data __unused ) {
+       return EFI_UNSUPPORTED;
+}
+
+static EFI_STATUS EFIAPI
+efi_null_block_write ( EFI_BLOCK_IO_PROTOCOL *block __unused,
+                      UINT32 media __unused, EFI_LBA lba __unused,
+                      UINTN len __unused, VOID *data __unused ) {
+       return EFI_UNSUPPORTED;
+}
+
+static EFI_STATUS EFIAPI
+efi_null_block_flush ( EFI_BLOCK_IO_PROTOCOL *block __unused ) {
+       return EFI_UNSUPPORTED;
+}
+
+static EFI_BLOCK_IO_MEDIA efi_null_block_media;
+
+static EFI_BLOCK_IO_PROTOCOL efi_null_block = {
+       .Revision = EFI_BLOCK_IO_INTERFACE_REVISION,
+       .Media = &efi_null_block_media,
+       .Reset = efi_null_block_reset,
+       .ReadBlocks = efi_null_block_read,
+       .WriteBlocks = efi_null_block_write,
+       .FlushBlocks = efi_null_block_flush,
+};
+
+/**
+ * Nullify block I/O protocol
+ *
+ * @v block            Block I/O protocol
+ */
+void efi_nullify_block ( EFI_BLOCK_IO_PROTOCOL *block ) {
+
+       memcpy ( block, &efi_null_block, sizeof ( *block ) );
+}
+
+/******************************************************************************
+ *
+ * PXE base code protocol
+ *
+ ******************************************************************************
+ */
+
+static EFI_STATUS EFIAPI
+efi_null_pxe_start ( EFI_PXE_BASE_CODE_PROTOCOL *pxe __unused,
+                    BOOLEAN use_ipv6 __unused ) {
+       return EFI_UNSUPPORTED;
+}
+
+static EFI_STATUS EFIAPI
+efi_null_pxe_stop ( EFI_PXE_BASE_CODE_PROTOCOL *pxe __unused ) {
+       return EFI_UNSUPPORTED;
+}
+
+static EFI_STATUS EFIAPI
+efi_null_pxe_dhcp ( EFI_PXE_BASE_CODE_PROTOCOL *pxe __unused,
+                   BOOLEAN sort __unused ) {
+       return EFI_UNSUPPORTED;
+}
+
+static EFI_STATUS EFIAPI
+efi_null_pxe_discover ( EFI_PXE_BASE_CODE_PROTOCOL *pxe __unused,
+                       UINT16 type __unused, UINT16 *layer __unused,
+                       BOOLEAN bis __unused,
+                       EFI_PXE_BASE_CODE_DISCOVER_INFO *info __unused ) {
+       return EFI_UNSUPPORTED;
+}
+
+static EFI_STATUS EFIAPI
+efi_null_pxe_mtftp ( EFI_PXE_BASE_CODE_PROTOCOL *pxe __unused,
+                    EFI_PXE_BASE_CODE_TFTP_OPCODE opcode __unused,
+                    VOID *data __unused, BOOLEAN overwrite __unused,
+                    UINT64 *len __unused, UINTN *blksize __unused,
+                    EFI_IP_ADDRESS *ip __unused, UINT8 *filename __unused,
+                    EFI_PXE_BASE_CODE_MTFTP_INFO *info __unused,
+                    BOOLEAN callback __unused ) {
+       return EFI_UNSUPPORTED;
+}
+
+static EFI_STATUS EFIAPI
+efi_null_pxe_udp_write ( EFI_PXE_BASE_CODE_PROTOCOL *pxe __unused,
+                        UINT16 flags __unused,
+                        EFI_IP_ADDRESS *dest_ip __unused,
+                        EFI_PXE_BASE_CODE_UDP_PORT *dest_port __unused,
+                        EFI_IP_ADDRESS *gateway __unused,
+                        EFI_IP_ADDRESS *src_ip __unused,
+                        EFI_PXE_BASE_CODE_UDP_PORT *src_port __unused,
+                        UINTN *hdr_len __unused, VOID *hdr __unused,
+                        UINTN *len __unused, VOID *data __unused ) {
+       return EFI_UNSUPPORTED;
+}
+
+static EFI_STATUS EFIAPI
+efi_null_pxe_udp_read ( EFI_PXE_BASE_CODE_PROTOCOL *pxe __unused,
+                       UINT16 flags __unused,
+                       EFI_IP_ADDRESS *dest_ip __unused,
+                       EFI_PXE_BASE_CODE_UDP_PORT *dest_port __unused,
+                       EFI_IP_ADDRESS *src_ip __unused,
+                       EFI_PXE_BASE_CODE_UDP_PORT *src_port __unused,
+                       UINTN *hdr_len __unused, VOID *hdr __unused,
+                       UINTN *len __unused, VOID *data __unused ) {
+       return EFI_UNSUPPORTED;
+}
+
+static EFI_STATUS EFIAPI
+efi_null_pxe_set_ip_filter ( EFI_PXE_BASE_CODE_PROTOCOL *pxe __unused,
+                            EFI_PXE_BASE_CODE_IP_FILTER *filter __unused ) {
+       return EFI_UNSUPPORTED;
+}
+
+static EFI_STATUS EFIAPI
+efi_null_pxe_arp ( EFI_PXE_BASE_CODE_PROTOCOL *pxe __unused,
+                  EFI_IP_ADDRESS *ip __unused,
+                  EFI_MAC_ADDRESS *mac __unused ) {
+       return EFI_UNSUPPORTED;
+}
+
+static EFI_STATUS EFIAPI
+efi_null_pxe_set_parameters ( EFI_PXE_BASE_CODE_PROTOCOL *pxe __unused,
+                             BOOLEAN *autoarp __unused,
+                             BOOLEAN *sendguid __unused, UINT8 *ttl __unused,
+                             UINT8 *tos __unused,
+                             BOOLEAN *callback __unused ) {
+       return EFI_UNSUPPORTED;
+}
+
+static EFI_STATUS EFIAPI
+efi_null_pxe_set_station_ip ( EFI_PXE_BASE_CODE_PROTOCOL *pxe __unused,
+                             EFI_IP_ADDRESS *ip __unused,
+                             EFI_IP_ADDRESS *netmask __unused ) {
+       return EFI_UNSUPPORTED;
+}
+
+static EFI_STATUS EFIAPI
+efi_null_pxe_set_packets ( EFI_PXE_BASE_CODE_PROTOCOL *pxe __unused,
+                          BOOLEAN *dhcpdisc_ok __unused,
+                          BOOLEAN *dhcpack_ok __unused,
+                          BOOLEAN *proxyoffer_ok __unused,
+                          BOOLEAN *pxebsdisc_ok __unused,
+                          BOOLEAN *pxebsack_ok __unused,
+                          BOOLEAN *pxebsbis_ok __unused,
+                          EFI_PXE_BASE_CODE_PACKET *dhcpdisc __unused,
+                          EFI_PXE_BASE_CODE_PACKET *dhcpack __unused,
+                          EFI_PXE_BASE_CODE_PACKET *proxyoffer __unused,
+                          EFI_PXE_BASE_CODE_PACKET *pxebsdisc __unused,
+                          EFI_PXE_BASE_CODE_PACKET *pxebsack __unused,
+                          EFI_PXE_BASE_CODE_PACKET *pxebsbis __unused ) {
+       return EFI_UNSUPPORTED;
+}
+
+static EFI_PXE_BASE_CODE_PROTOCOL efi_null_pxe = {
+       .Revision       = EFI_PXE_BASE_CODE_PROTOCOL_REVISION,
+       .Start          = efi_null_pxe_start,
+       .Stop           = efi_null_pxe_stop,
+       .Dhcp           = efi_null_pxe_dhcp,
+       .Discover       = efi_null_pxe_discover,
+       .Mtftp          = efi_null_pxe_mtftp,
+       .UdpWrite       = efi_null_pxe_udp_write,
+       .UdpRead        = efi_null_pxe_udp_read,
+       .SetIpFilter    = efi_null_pxe_set_ip_filter,
+       .Arp            = efi_null_pxe_arp,
+       .SetParameters  = efi_null_pxe_set_parameters,
+       .SetStationIp   = efi_null_pxe_set_station_ip,
+       .SetPackets     = efi_null_pxe_set_packets,
+};
+
+/**
+ * Nullify PXE base code protocol
+ *
+ * @v pxe              PXE base code protocol
+ */
+void efi_nullify_pxe ( EFI_PXE_BASE_CODE_PROTOCOL *pxe ) {
+
+       memcpy ( pxe, &efi_null_pxe, offsetof ( typeof ( *pxe ), Mode ) );
+       pxe->Mode->Started = FALSE;
+}
+
+/******************************************************************************
+ *
+ * Apple Net Boot protocol
+ *
+ ******************************************************************************
+ */
+
+static EFI_STATUS EFIAPI
+efi_null_apple_dhcp ( EFI_APPLE_NET_BOOT_PROTOCOL *apple __unused,
+                     UINTN *len __unused, VOID *data __unused ) {
+       return EFI_UNSUPPORTED;
+}
+
+static EFI_STATUS EFIAPI
+efi_null_apple_bsdp ( EFI_APPLE_NET_BOOT_PROTOCOL *apple __unused,
+                     UINTN *len __unused, VOID *data __unused ) {
+       return EFI_UNSUPPORTED;
+}
+
+static EFI_APPLE_NET_BOOT_PROTOCOL efi_null_apple = {
+       .GetDhcpResponse = efi_null_apple_dhcp,
+       .GetBsdpResponse = efi_null_apple_bsdp,
+};
+
+/**
+ * Nullify Apple Net Boot protocol
+ *
+ * @v apple            Apple Net Boot protocol
+ */
+void efi_nullify_apple ( EFI_APPLE_NET_BOOT_PROTOCOL *apple ) {
+
+       memcpy ( apple, &efi_null_apple, sizeof ( *apple ) );
+}
index a1f81df59761c8adb7975f72336c6d482b626400..4422dd28360927abe3bb03bba4049b7796ad6790 100644 (file)
@@ -41,6 +41,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
 #include <ipxe/efi/efi.h>
 #include <ipxe/efi/efi_snp.h>
 #include <ipxe/efi/efi_pxe.h>
+#include <ipxe/efi/efi_null.h>
 #include <ipxe/efi/Protocol/PxeBaseCode.h>
 #include <ipxe/efi/Protocol/AppleNetBoot.h>
 #include <usr/ifmgmt.h>
@@ -1591,6 +1592,7 @@ int efi_pxe_install ( EFI_HANDLE handle, struct net_device *netdev ) {
        struct efi_pxe *pxe;
        struct in_addr ip;
        BOOLEAN use_ipv6;
+       int leak = 0;
        EFI_STATUS efirc;
        int rc;
 
@@ -1643,14 +1645,23 @@ int efi_pxe_install ( EFI_HANDLE handle, struct net_device *netdev ) {
               pxe->name, efi_handle_name ( handle ) );
        return 0;
 
-       bs->UninstallMultipleProtocolInterfaces (
+       if ( ( efirc = bs->UninstallMultipleProtocolInterfaces (
                        handle,
                        &efi_pxe_base_code_protocol_guid, &pxe->base,
                        &efi_apple_net_boot_protocol_guid, &pxe->apple,
-                       NULL );
+                       NULL ) ) != 0 ) {
+               DBGC ( pxe, "PXE %s could not uninstall: %s\n",
+                      pxe->name, strerror ( -EEFI ( efirc ) ) );
+               efi_nullify_pxe ( &pxe->base );
+               efi_nullify_apple ( &pxe->apple );
+               leak = 1;
+       }
  err_install_protocol:
-       ref_put ( &pxe->refcnt );
+       if ( ! leak )
+               ref_put ( &pxe->refcnt );
  err_alloc:
+       if ( leak )
+               DBGC ( pxe, "PXE %s nullified and leaked\n", pxe->name );
        return rc;
 }
 
@@ -1662,6 +1673,8 @@ int efi_pxe_install ( EFI_HANDLE handle, struct net_device *netdev ) {
 void efi_pxe_uninstall ( EFI_HANDLE handle ) {
        EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
        struct efi_pxe *pxe;
+       int leak = 0;
+       EFI_STATUS efirc;
 
        /* Locate PXE base code */
        pxe = efi_pxe_find ( handle );
@@ -1675,13 +1688,24 @@ void efi_pxe_uninstall ( EFI_HANDLE handle ) {
        efi_pxe_stop ( &pxe->base );
 
        /* Uninstall PXE base code protocol */
-       bs->UninstallMultipleProtocolInterfaces (
+       if ( ( efirc = bs->UninstallMultipleProtocolInterfaces (
                        handle,
                        &efi_pxe_base_code_protocol_guid, &pxe->base,
                        &efi_apple_net_boot_protocol_guid, &pxe->apple,
-                       NULL );
+                       NULL ) ) != 0 ) {
+               DBGC ( pxe, "PXE %s could not uninstall: %s\n",
+                      pxe->name, strerror ( -EEFI ( efirc ) ) );
+               efi_nullify_pxe ( &pxe->base );
+               efi_nullify_apple ( &pxe->apple );
+               leak = 1;
+       }
 
        /* Remove from list and drop list's reference */
        list_del ( &pxe->list );
-       ref_put ( &pxe->refcnt );
+       if ( ! leak )
+               ref_put ( &pxe->refcnt );
+
+       /* Report leakage, if applicable */
+       if ( leak )
+               DBGC ( pxe, "PXE %s nullified and leaked\n", pxe->name );
 }
index 91e796a2e8b62b7062a588e0784556c87a538874..5285d322badf7ee9f1f437ce3d4f76c672e10d30 100644 (file)
@@ -36,6 +36,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
 #include <ipxe/efi/efi_path.h>
 #include <ipxe/efi/efi_utils.h>
 #include <ipxe/efi/efi_watchdog.h>
+#include <ipxe/efi/efi_null.h>
 #include <ipxe/efi/efi_snp.h>
 #include <usr/autoboot.h>
 #include <config/general.h>
@@ -1626,6 +1627,7 @@ static int efi_snp_probe ( struct net_device *netdev ) {
        struct efi_snp_device *snpdev;
        unsigned int ifcnt;
        void *interface;
+       int leak = 0;
        EFI_STATUS efirc;
        int rc;
 
@@ -1794,7 +1796,7 @@ static int efi_snp_probe ( struct net_device *netdev ) {
 
        list_del ( &snpdev->list );
        if ( snpdev->package_list )
-               efi_snp_hii_uninstall ( snpdev );
+               leak |= efi_snp_hii_uninstall ( snpdev );
        efi_child_del ( efidev->device, snpdev->handle );
  err_efi_child_add:
        bs->CloseProtocol ( snpdev->handle, &efi_nii31_protocol_guid,
@@ -1803,7 +1805,7 @@ static int efi_snp_probe ( struct net_device *netdev ) {
        bs->CloseProtocol ( snpdev->handle, &efi_nii_protocol_guid,
                            efi_image_handle, snpdev->handle );
  err_open_nii:
-       bs->UninstallMultipleProtocolInterfaces (
+       if ( ( efirc = bs->UninstallMultipleProtocolInterfaces (
                        snpdev->handle,
                        &efi_simple_network_protocol_guid, &snpdev->snp,
                        &efi_device_path_protocol_guid, snpdev->path,
@@ -1811,17 +1813,30 @@ static int efi_snp_probe ( struct net_device *netdev ) {
                        &efi_nii31_protocol_guid, &snpdev->nii,
                        &efi_component_name2_protocol_guid, &snpdev->name2,
                        &efi_load_file_protocol_guid, &snpdev->load_file,
-                       NULL );
+                       NULL ) ) != 0 ) {
+               DBGC ( snpdev, "SNPDEV %p could not uninstall: %s\n",
+                      snpdev, strerror ( -EEFI ( efirc ) ) );
+               efi_nullify_snp ( &snpdev->snp );
+               efi_nullify_nii ( &snpdev->nii );
+               efi_nullify_name2 ( &snpdev->name2 );
+               efi_nullify_load_file ( &snpdev->load_file );
+               leak = 1;
+       }
  err_install_protocol_interface:
-       free ( snpdev->path );
+       if ( ! leak )
+               free ( snpdev->path );
  err_path:
        bs->CloseEvent ( snpdev->snp.WaitForPacket );
  err_create_event:
  err_ll_addr_len:
-       netdev_put ( netdev );
-       free ( snpdev );
+       if ( ! leak ) {
+               netdev_put ( netdev );
+               free ( snpdev );
+       }
  err_alloc_snp:
  err_no_efidev:
+       if ( leak )
+               DBGC ( snpdev, "SNPDEV %p nullified and leaked\n", snpdev );
        return rc;
 }
 
@@ -1858,6 +1873,8 @@ static void efi_snp_notify ( struct net_device *netdev ) {
 static void efi_snp_remove ( struct net_device *netdev ) {
        EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
        struct efi_snp_device *snpdev;
+       int leak = 0;
+       EFI_STATUS efirc;
 
        /* Locate SNP device */
        snpdev = efi_snp_demux ( netdev );
@@ -1869,13 +1886,13 @@ static void efi_snp_remove ( struct net_device *netdev ) {
        /* Uninstall the SNP */
        list_del ( &snpdev->list );
        if ( snpdev->package_list )
-               efi_snp_hii_uninstall ( snpdev );
+               leak |= efi_snp_hii_uninstall ( snpdev );
        efi_child_del ( snpdev->efidev->device, snpdev->handle );
        bs->CloseProtocol ( snpdev->handle, &efi_nii_protocol_guid,
                            efi_image_handle, snpdev->handle );
        bs->CloseProtocol ( snpdev->handle, &efi_nii31_protocol_guid,
                            efi_image_handle, snpdev->handle );
-       bs->UninstallMultipleProtocolInterfaces (
+       if ( ( efirc = bs->UninstallMultipleProtocolInterfaces (
                        snpdev->handle,
                        &efi_simple_network_protocol_guid, &snpdev->snp,
                        &efi_device_path_protocol_guid, snpdev->path,
@@ -1883,11 +1900,26 @@ static void efi_snp_remove ( struct net_device *netdev ) {
                        &efi_nii31_protocol_guid, &snpdev->nii,
                        &efi_component_name2_protocol_guid, &snpdev->name2,
                        &efi_load_file_protocol_guid, &snpdev->load_file,
-                       NULL );
-       free ( snpdev->path );
+                       NULL ) ) != 0 ) {
+               DBGC ( snpdev, "SNPDEV %p could not uninstall: %s\n",
+                      snpdev, strerror ( -EEFI ( efirc ) ) );
+               efi_nullify_snp ( &snpdev->snp );
+               efi_nullify_nii ( &snpdev->nii );
+               efi_nullify_name2 ( &snpdev->name2 );
+               efi_nullify_load_file ( &snpdev->load_file );
+               leak = 1;
+       }
+       if ( ! leak )
+               free ( snpdev->path );
        bs->CloseEvent ( snpdev->snp.WaitForPacket );
-       netdev_put ( snpdev->netdev );
-       free ( snpdev );
+       if ( ! leak ) {
+               netdev_put ( snpdev->netdev );
+               free ( snpdev );
+       }
+
+       /* Report leakage, if applicable */
+       if ( leak )
+               DBGC ( snpdev, "SNPDEV %p nullified and leaked\n", snpdev );
 }
 
 /** SNP driver */
index 05c068a8c5157ffb05a898b44d18400c49590efa..4bb7214ff7c2e2e6cfb3a93c6adce582e110def6 100644 (file)
@@ -65,6 +65,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
 #include <ipxe/efi/efi_strings.h>
 #include <ipxe/efi/efi_path.h>
 #include <ipxe/efi/efi_utils.h>
+#include <ipxe/efi/efi_null.h>
 #include <config/branding.h>
 
 /** EFI platform setup formset GUID */
@@ -659,7 +660,8 @@ int efi_snp_hii_install ( struct efi_snp_device *snpdev ) {
        VENDOR_DEVICE_PATH *vendor_path;
        EFI_DEVICE_PATH_PROTOCOL *path_end;
        size_t path_prefix_len;
-       int efirc;
+       int leak = 0;
+       EFI_STATUS efirc;
        int rc;
 
        /* Do nothing if HII database protocol is not supported */
@@ -751,23 +753,37 @@ int efi_snp_hii_install ( struct efi_snp_device *snpdev ) {
 
        efi_child_del ( snpdev->handle, snpdev->hii_child_handle );
  err_efi_child_add:
-       bs->UninstallMultipleProtocolInterfaces (
+       if ( ( efirc = bs->UninstallMultipleProtocolInterfaces (
                        snpdev->hii_child_handle,
                        &efi_hii_config_access_protocol_guid, &snpdev->hii,
-                       NULL );
+                       NULL ) ) != 0 ) {
+               DBGC ( snpdev, "SNPDEV %p could not uninstall HII protocol: "
+                      "%s\n", snpdev, strerror ( -EEFI ( efirc ) ) );
+               efi_nullify_hii ( &snpdev->hii );
+               leak = 1;
+       }
  err_install_protocol:
-       efihii->RemovePackageList ( efihii, snpdev->hii_handle );
+       if ( ! leak )
+               efihii->RemovePackageList ( efihii, snpdev->hii_handle );
  err_new_package_list:
-       bs->UninstallMultipleProtocolInterfaces (
+       if ( ( efirc = bs->UninstallMultipleProtocolInterfaces (
                        snpdev->hii_child_handle,
                        &efi_device_path_protocol_guid, snpdev->hii_child_path,
-                       NULL );
+                       NULL ) ) != 0 ) {
+               DBGC ( snpdev, "SNPDEV %p could not uninstall HII path: %s\n",
+                      snpdev, strerror ( -EEFI ( efirc ) ) );
+               leak = 1;
+       }
  err_hii_child_handle:
-       free ( snpdev->hii_child_path );
-       snpdev->hii_child_path = NULL;
+       if ( ! leak ) {
+               free ( snpdev->hii_child_path );
+               snpdev->hii_child_path = NULL;
+       }
  err_alloc_child_path:
-       free ( snpdev->package_list );
-       snpdev->package_list = NULL;
+       if ( ! leak ) {
+               free ( snpdev->package_list );
+               snpdev->package_list = NULL;
+       }
  err_build_package_list:
  err_no_hii:
        return rc;
@@ -777,27 +793,47 @@ int efi_snp_hii_install ( struct efi_snp_device *snpdev ) {
  * Uninstall HII protocol and package for SNP device
  *
  * @v snpdev           SNP device
+ * @ret leak           Uninstallation failed: leak memory
  */
-void efi_snp_hii_uninstall ( struct efi_snp_device *snpdev ) {
+int efi_snp_hii_uninstall ( struct efi_snp_device *snpdev ) {
        EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+       int leak = 0;
+       EFI_STATUS efirc;
 
        /* Do nothing if HII database protocol is not supported */
        if ( ! efihii )
-               return;
+               return 0;
 
        /* Uninstall protocols and remove package list */
        efi_child_del ( snpdev->handle, snpdev->hii_child_handle );
-       bs->UninstallMultipleProtocolInterfaces (
+       if ( ( efirc = bs->UninstallMultipleProtocolInterfaces (
                        snpdev->hii_child_handle,
                        &efi_hii_config_access_protocol_guid, &snpdev->hii,
-                       NULL );
-       efihii->RemovePackageList ( efihii, snpdev->hii_handle );
-       bs->UninstallMultipleProtocolInterfaces (
+                       NULL ) ) != 0 ) {
+               DBGC ( snpdev, "SNPDEV %p could not uninstall HII protocol: "
+                      "%s\n", snpdev, strerror ( -EEFI ( efirc ) ) );
+               efi_nullify_hii ( &snpdev->hii );
+               leak = 1;
+       }
+       if ( ! leak )
+               efihii->RemovePackageList ( efihii, snpdev->hii_handle );
+       if ( ( efirc = bs->UninstallMultipleProtocolInterfaces (
                        snpdev->hii_child_handle,
                        &efi_device_path_protocol_guid, snpdev->hii_child_path,
-                       NULL );
-       free ( snpdev->hii_child_path );
-       snpdev->hii_child_path = NULL;
-       free ( snpdev->package_list );
-       snpdev->package_list = NULL;
+                       NULL ) ) != 0 ) {
+               DBGC ( snpdev, "SNPDEV %p could not uninstall HII path: %s\n",
+                      snpdev, strerror ( -EEFI ( efirc ) ) );
+               leak = 1;
+       }
+       if ( ! leak ) {
+               free ( snpdev->hii_child_path );
+               snpdev->hii_child_path = NULL;
+               free ( snpdev->package_list );
+               snpdev->package_list = NULL;
+       }
+
+       /* Report leakage, if applicable */
+       if ( leak )
+               DBGC ( snpdev, "SNPDEV %p HII nullified and leaked\n", snpdev );
+       return leak;
 }