diff mbox series

[2/3] efi_loader: avoid pointer access after calling efi_delete_handle

Message ID 20231225044356.626900-3-masahisa.kojima@linaro.org
State New
Headers show
Series fix and refactoring of efi_disk.c | expand

Commit Message

Masahisa Kojima Dec. 25, 2023, 4:43 a.m. UTC
efi_delete_handle() calls efi_purge_handle(), then it finally
frees the efi handle.
Both diskobj and handle variables in efi_disk_remove() have
the same pointer, we can not access diskobj->dp after calling
efi_delete_handle().

This commit saves the struct efi_device_path pointer before
calling efi_delete_handle(). This commit also fixes the
missing free for volume member in struct efi_disk_obj.

Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
---
 lib/efi_loader/efi_disk.c | 12 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)

Comments

Heinrich Schuchardt Dec. 25, 2023, 10:05 a.m. UTC | #1
On 12/25/23 05:43, Masahisa Kojima wrote:
> efi_delete_handle() calls efi_purge_handle(), then it finally
> frees the efi handle.
> Both diskobj and handle variables in efi_disk_remove() have
> the same pointer, we can not access diskobj->dp after calling
> efi_delete_handle().
>
> This commit saves the struct efi_device_path pointer before
> calling efi_delete_handle(). This commit also fixes the
> missing free for volume member in struct efi_disk_obj.
>
> Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
> ---
>   lib/efi_loader/efi_disk.c | 12 +++++++++---
>   1 file changed, 9 insertions(+), 3 deletions(-)
>
> diff --git a/lib/efi_loader/efi_disk.c b/lib/efi_loader/efi_disk.c
> index a2f8b531a3..415d8601ba 100644
> --- a/lib/efi_loader/efi_disk.c
> +++ b/lib/efi_loader/efi_disk.c
> @@ -701,7 +701,9 @@ int efi_disk_remove(void *ctx, struct event *event)
>   	struct udevice *dev = event->data.dm.dev;
>   	efi_handle_t handle;
>   	struct blk_desc *desc;
> +	struct efi_device_path *dp = NULL;
>   	struct efi_disk_obj *diskobj = NULL;
> +	struct efi_simple_file_system_protocol *volume = NULL;
>   	efi_status_t ret;
>
>   	if (dev_tag_get_ptr(dev, DM_TAG_EFI, (void **)&handle))
> @@ -722,14 +724,18 @@ int efi_disk_remove(void *ctx, struct event *event)
>   		return 0;
>   	}
>
> +	if (diskobj) {


   diskobj = container_of(handle, struct efi_disk_obj, header);

can be replaced by

   diskobj = handle

We should check the handle immediately after dev_tag_get_ptr() and
return 0 if the handle == NULL.

> +		dp = diskobj->dp;
> +		volume = diskobj->volume;
> +	}
> +
>   	ret = efi_delete_handle(handle);

We must not delete the handle in case of UCLASS_EFI_LOADER.

Instead of calling efi_delete_handle we should uninstall all protocols
that we have installed. If no protocol is left the handle will go away.

To make the protocols to delete tractable they should be opened with
BY_DRIVER.

When a partition is removed we must close the protocol interfaces opened
with EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER (cf. efi_disk_add_dev()).

Best regards

Heinrich

>   	/* Do not delete DM device if there are still EFI drivers attached. */
>   	if (ret != EFI_SUCCESS)
>   		return -1;
>
> -	if (diskobj)
> -		efi_free_pool(diskobj->dp);
> -
> +	efi_free_pool(dp);
> +	free(volume);
>   	dev_tag_del(dev, DM_TAG_EFI);
>
>   	return 0;
Masahisa Kojima Dec. 26, 2023, 1:13 a.m. UTC | #2
Hi Heinrich,

On Mon, 25 Dec 2023 at 19:05, Heinrich Schuchardt <xypron.glpk@gmx.de> wrote:
>
> On 12/25/23 05:43, Masahisa Kojima wrote:
> > efi_delete_handle() calls efi_purge_handle(), then it finally
> > frees the efi handle.
> > Both diskobj and handle variables in efi_disk_remove() have
> > the same pointer, we can not access diskobj->dp after calling
> > efi_delete_handle().
> >
> > This commit saves the struct efi_device_path pointer before
> > calling efi_delete_handle(). This commit also fixes the
> > missing free for volume member in struct efi_disk_obj.
> >
> > Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
> > ---
> >   lib/efi_loader/efi_disk.c | 12 +++++++++---
> >   1 file changed, 9 insertions(+), 3 deletions(-)
> >
> > diff --git a/lib/efi_loader/efi_disk.c b/lib/efi_loader/efi_disk.c
> > index a2f8b531a3..415d8601ba 100644
> > --- a/lib/efi_loader/efi_disk.c
> > +++ b/lib/efi_loader/efi_disk.c
> > @@ -701,7 +701,9 @@ int efi_disk_remove(void *ctx, struct event *event)
> >       struct udevice *dev = event->data.dm.dev;
> >       efi_handle_t handle;
> >       struct blk_desc *desc;
> > +     struct efi_device_path *dp = NULL;
> >       struct efi_disk_obj *diskobj = NULL;
> > +     struct efi_simple_file_system_protocol *volume = NULL;
> >       efi_status_t ret;
> >
> >       if (dev_tag_get_ptr(dev, DM_TAG_EFI, (void **)&handle))
> > @@ -722,14 +724,18 @@ int efi_disk_remove(void *ctx, struct event *event)
> >               return 0;
> >       }
> >
> > +     if (diskobj) {
>
>
>    diskobj = container_of(handle, struct efi_disk_obj, header);
>
> can be replaced by
>
>    diskobj = handle
>
> We should check the handle immediately after dev_tag_get_ptr() and
> return 0 if the handle == NULL.

OK, I will try to improve the efi_disk_remove() function.

>
> > +             dp = diskobj->dp;
> > +             volume = diskobj->volume;
> > +     }
> > +
> >       ret = efi_delete_handle(handle);
>
> We must not delete the handle in case of UCLASS_EFI_LOADER.
>
> Instead of calling efi_delete_handle we should uninstall all protocols
> that we have installed. If no protocol is left the handle will go away.
>
> To make the protocols to delete tractable they should be opened with
> BY_DRIVER.

efi_delete_handle() calls efi_remove_all_protocols(), and frees the efi handle
if no protocol is left on the handle.
So I think efi_delete_handle() does all the required processes mentioned above.

>
> When a partition is removed we must close the protocol interfaces opened
> with EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER (cf. efi_disk_add_dev()).

OK, I will fix this.

Thanks,
Masahisa Kojima

>
> Best regards
>
> Heinrich
>
> >       /* Do not delete DM device if there are still EFI drivers attached. */
> >       if (ret != EFI_SUCCESS)
> >               return -1;
> >
> > -     if (diskobj)
> > -             efi_free_pool(diskobj->dp);
> > -
> > +     efi_free_pool(dp);
> > +     free(volume);
> >       dev_tag_del(dev, DM_TAG_EFI);
> >
> >       return 0;
>
Heinrich Schuchardt Dec. 26, 2023, 1:43 a.m. UTC | #3
Am 26. Dezember 2023 02:13:08 MEZ schrieb Masahisa Kojima <masahisa.kojima@linaro.org>:
>Hi Heinrich,
>
>On Mon, 25 Dec 2023 at 19:05, Heinrich Schuchardt <xypron.glpk@gmx.de> wrote:
>>
>> On 12/25/23 05:43, Masahisa Kojima wrote:
>> > efi_delete_handle() calls efi_purge_handle(), then it finally
>> > frees the efi handle.
>> > Both diskobj and handle variables in efi_disk_remove() have
>> > the same pointer, we can not access diskobj->dp after calling
>> > efi_delete_handle().
>> >
>> > This commit saves the struct efi_device_path pointer before
>> > calling efi_delete_handle(). This commit also fixes the
>> > missing free for volume member in struct efi_disk_obj.
>> >
>> > Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
>> > ---
>> >   lib/efi_loader/efi_disk.c | 12 +++++++++---
>> >   1 file changed, 9 insertions(+), 3 deletions(-)
>> >
>> > diff --git a/lib/efi_loader/efi_disk.c b/lib/efi_loader/efi_disk.c
>> > index a2f8b531a3..415d8601ba 100644
>> > --- a/lib/efi_loader/efi_disk.c
>> > +++ b/lib/efi_loader/efi_disk.c
>> > @@ -701,7 +701,9 @@ int efi_disk_remove(void *ctx, struct event *event)
>> >       struct udevice *dev = event->data.dm.dev;
>> >       efi_handle_t handle;
>> >       struct blk_desc *desc;
>> > +     struct efi_device_path *dp = NULL;
>> >       struct efi_disk_obj *diskobj = NULL;
>> > +     struct efi_simple_file_system_protocol *volume = NULL;
>> >       efi_status_t ret;
>> >
>> >       if (dev_tag_get_ptr(dev, DM_TAG_EFI, (void **)&handle))
>> > @@ -722,14 +724,18 @@ int efi_disk_remove(void *ctx, struct event *event)
>> >               return 0;
>> >       }
>> >
>> > +     if (diskobj) {
>>
>>
>>    diskobj = container_of(handle, struct efi_disk_obj, header);
>>
>> can be replaced by
>>
>>    diskobj = handle
>>
>> We should check the handle immediately after dev_tag_get_ptr() and
>> return 0 if the handle == NULL.
>
>OK, I will try to improve the efi_disk_remove() function.
>
>>
>> > +             dp = diskobj->dp;
>> > +             volume = diskobj->volume;
>> > +     }
>> > +
>> >       ret = efi_delete_handle(handle);
>>
>> We must not delete the handle in case of UCLASS_EFI_LOADER.
>>
>> Instead of calling efi_delete_handle we should uninstall all protocols
>> that we have installed. If no protocol is left the handle will go away.
>>
>> To make the protocols to delete tractable they should be opened with
>> BY_DRIVER.
>
>efi_delete_handle() calls efi_remove_all_protocols(), and frees the efi handle
>if no protocol is left on the handle.
>So I think efi_delete_handle() does all the required processes mentioned above.

Given an EFI application like iPXE that provides a handle with the block IO protocol implemented by the application: Why should U-Boot remove the block IO protocol or the device path protocol when the application calls DisconnectController()?

Best regards

Heinrich

>
>>
>> When a partition is removed we must close the protocol interfaces opened
>> with EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER (cf. efi_disk_add_dev()).
>
>OK, I will fix this.
>
>Thanks,
>Masahisa Kojima
>
>>
>> Best regards
>>
>> Heinrich
>>
>> >       /* Do not delete DM device if there are still EFI drivers attached. */
>> >       if (ret != EFI_SUCCESS)
>> >               return -1;
>> >
>> > -     if (diskobj)
>> > -             efi_free_pool(diskobj->dp);
>> > -
>> > +     efi_free_pool(dp);
>> > +     free(volume);
>> >       dev_tag_del(dev, DM_TAG_EFI);
>> >
>> >       return 0;
>>
Masahisa Kojima Jan. 15, 2024, 8:03 a.m. UTC | #4
Hi Heinrich,

On Tue, 26 Dec 2023 at 10:43, Heinrich Schuchardt <xypron.glpk@gmx.de> wrote:
>
>
>
> Am 26. Dezember 2023 02:13:08 MEZ schrieb Masahisa Kojima <masahisa.kojima@linaro.org>:
> >Hi Heinrich,
> >
> >On Mon, 25 Dec 2023 at 19:05, Heinrich Schuchardt <xypron.glpk@gmx.de> wrote:
> >>
> >> On 12/25/23 05:43, Masahisa Kojima wrote:
> >> > efi_delete_handle() calls efi_purge_handle(), then it finally
> >> > frees the efi handle.
> >> > Both diskobj and handle variables in efi_disk_remove() have
> >> > the same pointer, we can not access diskobj->dp after calling
> >> > efi_delete_handle().
> >> >
> >> > This commit saves the struct efi_device_path pointer before
> >> > calling efi_delete_handle(). This commit also fixes the
> >> > missing free for volume member in struct efi_disk_obj.
> >> >
> >> > Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
> >> > ---
> >> >   lib/efi_loader/efi_disk.c | 12 +++++++++---
> >> >   1 file changed, 9 insertions(+), 3 deletions(-)
> >> >
> >> > diff --git a/lib/efi_loader/efi_disk.c b/lib/efi_loader/efi_disk.c
> >> > index a2f8b531a3..415d8601ba 100644
> >> > --- a/lib/efi_loader/efi_disk.c
> >> > +++ b/lib/efi_loader/efi_disk.c
> >> > @@ -701,7 +701,9 @@ int efi_disk_remove(void *ctx, struct event *event)
> >> >       struct udevice *dev = event->data.dm.dev;
> >> >       efi_handle_t handle;
> >> >       struct blk_desc *desc;
> >> > +     struct efi_device_path *dp = NULL;
> >> >       struct efi_disk_obj *diskobj = NULL;
> >> > +     struct efi_simple_file_system_protocol *volume = NULL;
> >> >       efi_status_t ret;
> >> >
> >> >       if (dev_tag_get_ptr(dev, DM_TAG_EFI, (void **)&handle))
> >> > @@ -722,14 +724,18 @@ int efi_disk_remove(void *ctx, struct event *event)
> >> >               return 0;
> >> >       }
> >> >
> >> > +     if (diskobj) {
> >>
> >>
> >>    diskobj = container_of(handle, struct efi_disk_obj, header);
> >>
> >> can be replaced by
> >>
> >>    diskobj = handle
> >>
> >> We should check the handle immediately after dev_tag_get_ptr() and
> >> return 0 if the handle == NULL.
> >
> >OK, I will try to improve the efi_disk_remove() function.
> >
> >>
> >> > +             dp = diskobj->dp;
> >> > +             volume = diskobj->volume;
> >> > +     }
> >> > +
> >> >       ret = efi_delete_handle(handle);
> >>
> >> We must not delete the handle in case of UCLASS_EFI_LOADER.
> >>
> >> Instead of calling efi_delete_handle we should uninstall all protocols
> >> that we have installed. If no protocol is left the handle will go away.
> >>
> >> To make the protocols to delete tractable they should be opened with
> >> BY_DRIVER.
> >
> >efi_delete_handle() calls efi_remove_all_protocols(), and frees the efi handle
> >if no protocol is left on the handle.
> >So I think efi_delete_handle() does all the required processes mentioned above.
>
> Given an EFI application like iPXE that provides a handle with the block IO protocol implemented by the application: Why should U-Boot remove the block IO protocol or the device path protocol when the application calls DisconnectController()?

Yes, but the current implementation does the same.
So in the case of UCLASS_EFI_LOADER, we need to immediately return without
calling efi_delete_handle(), is my understanding correct?
I checked lib/efi_selftest/efi_selftest_block_device.c implementation,
the teadown() process uninstalls the all protocol_interface then
the EFI handle goes away if no protocol is left.

Thanks,
Masahisa Kojima

>
> Best regards
>
> Heinrich
>
> >
> >>
> >> When a partition is removed we must close the protocol interfaces opened
> >> with EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER (cf. efi_disk_add_dev()).
> >
> >OK, I will fix this.
> >
> >Thanks,
> >Masahisa Kojima
> >
> >>
> >> Best regards
> >>
> >> Heinrich
> >>
> >> >       /* Do not delete DM device if there are still EFI drivers attached. */
> >> >       if (ret != EFI_SUCCESS)
> >> >               return -1;
> >> >
> >> > -     if (diskobj)
> >> > -             efi_free_pool(diskobj->dp);
> >> > -
> >> > +     efi_free_pool(dp);
> >> > +     free(volume);
> >> >       dev_tag_del(dev, DM_TAG_EFI);
> >> >
> >> >       return 0;
> >>
diff mbox series

Patch

diff --git a/lib/efi_loader/efi_disk.c b/lib/efi_loader/efi_disk.c
index a2f8b531a3..415d8601ba 100644
--- a/lib/efi_loader/efi_disk.c
+++ b/lib/efi_loader/efi_disk.c
@@ -701,7 +701,9 @@  int efi_disk_remove(void *ctx, struct event *event)
 	struct udevice *dev = event->data.dm.dev;
 	efi_handle_t handle;
 	struct blk_desc *desc;
+	struct efi_device_path *dp = NULL;
 	struct efi_disk_obj *diskobj = NULL;
+	struct efi_simple_file_system_protocol *volume = NULL;
 	efi_status_t ret;
 
 	if (dev_tag_get_ptr(dev, DM_TAG_EFI, (void **)&handle))
@@ -722,14 +724,18 @@  int efi_disk_remove(void *ctx, struct event *event)
 		return 0;
 	}
 
+	if (diskobj) {
+		dp = diskobj->dp;
+		volume = diskobj->volume;
+	}
+
 	ret = efi_delete_handle(handle);
 	/* Do not delete DM device if there are still EFI drivers attached. */
 	if (ret != EFI_SUCCESS)
 		return -1;
 
-	if (diskobj)
-		efi_free_pool(diskobj->dp);
-
+	efi_free_pool(dp);
+	free(volume);
 	dev_tag_del(dev, DM_TAG_EFI);
 
 	return 0;