Re: Potentially missing CloseProtocol() call


Laszlo Ersek
 

On 02/08/21 16:28, Michael Brown wrote:
On 08/02/2021 14:22, Laszlo Ersek wrote:
gBS->OpenProtocol() calls that pass the EFI_OPEN_PROTOCOL_GET_PROTOCOL
argument for the "Attributes" parameter *need not* be mirrored with
gBS->CloseProtocol() calls.

Please refer to the UEFI spec on the OpenProtocol() boot service,
Attributes=EFI_OPEN_PROTOCOL_GET_PROTOCOL:

     [...] The caller is also not required to close the protocol
     interface with EFI_BOOT_SERVICES.CloseProtocol().

So you *can* call CloseProtocol(), but you don't have to.
This reminds me of a very longstanding question I've had around
OpenProtocol(): is there any defined way for something that is an
ordinary consumer (rather than a driver or child controller) to obtain a
long-lived pointer to a protocol interface?

For example:
ShellPkg/Library/UefiShellDebug1CommandsLib/Edit/MainTextEditor.c seems
to open EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL using just HandleProtocol()
and then continues to use the pointer for the lifetime of the
application.  But, as far as I can tell, there's nothing that guarantees
that this pointer remains valid?
The CloseProtocol() specification has parts as follows (relevant passage
is the last one below):

The caller is responsible for ensuring that there are no references
to a protocol interface that has been removed. In some cases,
outstanding reference information is not available in the protocol,
so the protocol, once added, cannot be removed. Examples include
Console I/O, Block I/O, Disk I/O, and (in general) handles to device
protocols.

[...]

EFI 1.10 Extension

The extension to this service directly addresses the limitations
described in the section above. There may be some drivers that are
currently consuming the protocol interface that needs to be
uninstalled, so it may be dangerous to just blindly remove a
protocol interface from the system. Since the usage of protocol
interfaces is now being tracked for components that use the
EFI_BOOT_SERVICES.OpenProtocol() and
EFI_BOOT_SERVICES.CloseProtocol() boot services, a safe version of
this function can be implemented.

[...]

Lastly, all of the agents that have the protocol interface open with
an attribute of EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL,
EFI_OPEN_PROTOCOL_GET_PROTOCOL, or EFI_OPEN_PROTOCOL_TEST_PROTOCOL
are closed. If there are any agents remaining that still have the
protocol interface open, the protocol interface is not removed from
the handle and EFI_ACCESS_DENIED is returned. In addition, all of
the drivers that were disconnected with the boot service
DisconnectController() earlier, are reconnected with the boot
service EFI_BOOT_SERVICES.ConnectController(). If there are no
agents remaining that are consuming the protocol interface, then the
protocol interface is removed from the handle as described above.

My comments:

(1) I don't really understand what the last quoted passage means by
"closing an agent".

(2) I've never tested this, nor attempted to verify it from the edk2
source, myself. It does suggest however that GET_PROTOCOL opens are
tracked too, and thus, *not* closing a GET_PROTOCOL open is technically
still a leak.

Thanks
Laszlo

Join discuss@edk2.groups.io to automatically receive all group messages.