efi_loader: avoid crash in OpenProtocol()
authorHeinrich Schuchardt <xypron.glpk@gmx.de>
Thu, 30 May 2019 12:16:31 +0000 (14:16 +0200)
committerHeinrich Schuchardt <xypron.glpk@gmx.de>
Fri, 31 May 2019 21:27:12 +0000 (23:27 +0200)
When trying to open a protocol exclusively attached drivers have to be
removed. This removes entries in the open protocol information linked list
over which we are looping. As additionally child controllers may have been
removed the only safe thing to do is to restart the loop over the linked
list when a driver is removed.

By observing the return code of DisconnectController() we can eliminate a
loop.

Signed-off-by: Heinrich Schuchardt <xypron.glpk@gmx.de>
lib/efi_loader/efi_boottime.c

index 610eea463ebf37ba84e2258c480e675668afc895..f71268440b50dd2f5155ec1f1e1943eae5de8f4a 100644 (file)
@@ -2658,21 +2658,29 @@ static efi_status_t efi_protocol_open(
        /* Prepare exclusive opening */
        if (attributes & EFI_OPEN_PROTOCOL_EXCLUSIVE) {
                /* Try to disconnect controllers */
+disconnect_next:
+               opened_by_driver = false;
                list_for_each_entry(item, &handler->open_infos, link) {
+                       efi_status_t ret;
+
                        if (item->info.attributes ==
-                                       EFI_OPEN_PROTOCOL_BY_DRIVER)
-                               EFI_CALL(efi_disconnect_controller(
+                                       EFI_OPEN_PROTOCOL_BY_DRIVER) {
+                               ret = EFI_CALL(efi_disconnect_controller(
                                                item->info.controller_handle,
                                                item->info.agent_handle,
                                                NULL));
+                               if (ret == EFI_SUCCESS)
+                                       /*
+                                        * Child controllers may have been
+                                        * removed from the open_infos list. So
+                                        * let's restart the loop.
+                                        */
+                                       goto disconnect_next;
+                               else
+                                       opened_by_driver = true;
+                       }
                }
-               opened_by_driver = false;
-               /* Check if all controllers are disconnected */
-               list_for_each_entry(item, &handler->open_infos, link) {
-                       if (item->info.attributes & EFI_OPEN_PROTOCOL_BY_DRIVER)
-                               opened_by_driver = true;
-               }
-               /* Only one controller can be connected */
+               /* Only one driver can be connected */
                if (opened_by_driver)
                        return EFI_ACCESS_DENIED;
        }