diff mbox series

efi_loader: check update capsule parameters

Message ID 20210611161520.30315-1-vincent.stehle@arm.com
State Changes Requested, archived
Delegated to: Heinrich Schuchardt
Headers show
Series efi_loader: check update capsule parameters | expand

Commit Message

Vincent Stehlé June 11, 2021, 4:15 p.m. UTC
UpdateCapsule() must return EFI_INVALID_PARAMETER in a number of cases,
listed by the UEFI specification and tested by the SCT. Add a common
function to do that.

This fixes SCT UpdateCapsule_Conf failures.

Reviewed-by: Grant Likely <grant.likely@arm.com>
Signed-off-by: Vincent Stehlé <vincent.stehle@arm.com>
Cc: Heinrich Schuchardt <xypron.glpk@gmx.de>
Cc: Alexander Graf <agraf@csgraf.de>
---
 include/efi_loader.h         | 24 ++++++++++++++++++++++++
 lib/efi_loader/efi_capsule.c |  8 ++++----
 lib/efi_loader/efi_runtime.c |  8 ++++++++
 3 files changed, 36 insertions(+), 4 deletions(-)

Comments

Heinrich Schuchardt June 11, 2021, 5:06 p.m. UTC | #1
Cc: Takahiro, Sughosh, Ilias

On 6/11/21 6:15 PM, Vincent Stehlé wrote:
> UpdateCapsule() must return EFI_INVALID_PARAMETER in a number of cases,
> listed by the UEFI specification and tested by the SCT. Add a common
> function to do that.
>
> This fixes SCT UpdateCapsule_Conf failures.
>
> Reviewed-by: Grant Likely <grant.likely@arm.com>
> Signed-off-by: Vincent Stehlé <vincent.stehle@arm.com>
> Cc: Heinrich Schuchardt <xypron.glpk@gmx.de>
> Cc: Alexander Graf <agraf@csgraf.de>
> ---
>   include/efi_loader.h         | 24 ++++++++++++++++++++++++
>   lib/efi_loader/efi_capsule.c |  8 ++++----
>   lib/efi_loader/efi_runtime.c |  8 ++++++++
>   3 files changed, 36 insertions(+), 4 deletions(-)
>
> diff --git a/include/efi_loader.h b/include/efi_loader.h
> index 0a9c82a257e..426d1c72d7d 100644
> --- a/include/efi_loader.h
> +++ b/include/efi_loader.h
> @@ -910,6 +910,30 @@ extern const struct efi_firmware_management_protocol efi_fmp_fit;
>   extern const struct efi_firmware_management_protocol efi_fmp_raw;
>
>   /* Capsule update */
> +static inline efi_status_t
> +efi_valid_update_capsule_params(struct efi_capsule_header
> +						**capsule_header_array,
> +				efi_uintn_t capsule_count,
> +				u64 scatter_gather_list)
> +{
> +	u32 flags;
> +
> +	if (!capsule_count)
> +		return EFI_INVALID_PARAMETER;

If capsule count > 1, don't you have to check all capsules headers?

> +
> +	flags = capsule_header_array[0]->flags;
> +
> +	if (((flags & CAPSULE_FLAGS_PERSIST_ACROSS_RESET) &&
> +	     !scatter_gather_list) ||
> +	    ((flags & CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE) &&
> +	     !(flags & CAPSULE_FLAGS_PERSIST_ACROSS_RESET)) ||
> +	    ((flags & CAPSULE_FLAGS_INITIATE_RESET) &&
> +	     !(flags & CAPSULE_FLAGS_PERSIST_ACROSS_RESET)))
> +		return EFI_INVALID_PARAMETER;

What happens if capsule(0) has CAPSULE_FLAGS_INITIATE_RESET and
capsule(4) has !CAPSULE_FLAGS_PERSIST_ACROSS_RESET?

Best regards

Heinrich

> +
> +	return EFI_SUCCESS;
> +}
> +
>   efi_status_t EFIAPI efi_update_capsule(
>   		struct efi_capsule_header **capsule_header_array,
>   		efi_uintn_t capsule_count,
> diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c
> index 60309d4a07d..380cfd70290 100644
> --- a/lib/efi_loader/efi_capsule.c
> +++ b/lib/efi_loader/efi_capsule.c
> @@ -442,12 +442,12 @@ efi_status_t EFIAPI efi_update_capsule(
>   	EFI_ENTRY("%p, %zu, %llu\n", capsule_header_array, capsule_count,
>   		  scatter_gather_list);
>
> -	if (!capsule_count) {
> -		ret = EFI_INVALID_PARAMETER;
> +	ret = efi_valid_update_capsule_params(capsule_header_array,
> +					      capsule_count,
> +					      scatter_gather_list);
> +	if (ret != EFI_SUCCESS)
>   		goto out;
> -	}
>
> -	ret = EFI_SUCCESS;
>   	for (i = 0, capsule = *capsule_header_array; i < capsule_count;
>   	     i++, capsule = *(++capsule_header_array)) {
>   		/* sanity check */
> diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c
> index 93a695fc27e..449ad8b9f36 100644
> --- a/lib/efi_loader/efi_runtime.c
> +++ b/lib/efi_loader/efi_runtime.c
> @@ -467,6 +467,14 @@ efi_status_t __efi_runtime EFIAPI efi_update_capsule_unsupported(
>   			efi_uintn_t capsule_count,
>   			u64 scatter_gather_list)
>   {
> +	efi_status_t ret;
> +
> +	ret = efi_valid_update_capsule_params(capsule_header_array,
> +					      capsule_count,
> +					      scatter_gather_list);
> +	if (ret != EFI_SUCCESS)
> +		return ret;
> +
>   	return EFI_UNSUPPORTED;
>   }
>
>
AKASHI Takahiro June 12, 2021, 12:13 a.m. UTC | #2
Hi Vincent,

Thank you for the update.

On Fri, Jun 11, 2021 at 07:06:01PM +0200, Heinrich Schuchardt wrote:
> Cc: Takahiro, Sughosh, Ilias
> 
> On 6/11/21 6:15 PM, Vincent Stehlé wrote:
> > UpdateCapsule() must return EFI_INVALID_PARAMETER in a number of cases,
> > listed by the UEFI specification and tested by the SCT. Add a common
> > function to do that.

First of all, I would like to make clear one thing:
My initial implementation of capsule support does *not*
intend to support "UpdateCapsule" as runtime API.
Instead, the sole objective is to provide "delivery of
capsules via file" (or capsule on disk) method.

This is because the initially-expected use case was
that we would adopt capsules as an alternative for
updating UEFI variables without using SetVariable runtime API.
(This idea is no longer upheld, though.)

This is why the API does not handle nor check parameters
in the current code. I even regret to have added
EFI_RUNTIME_UPDATE_CAPSULE option, which makes people confused.

Nevertheless, I'm more than happy if other folks implement
full semantics of UpdateCapsule.

> > This fixes SCT UpdateCapsule_Conf failures.
> > 
> > Reviewed-by: Grant Likely <grant.likely@arm.com>
> > Signed-off-by: Vincent Stehlé <vincent.stehle@arm.com>
> > Cc: Heinrich Schuchardt <xypron.glpk@gmx.de>
> > Cc: Alexander Graf <agraf@csgraf.de>
> > ---
> >   include/efi_loader.h         | 24 ++++++++++++++++++++++++
> >   lib/efi_loader/efi_capsule.c |  8 ++++----
> >   lib/efi_loader/efi_runtime.c |  8 ++++++++
> >   3 files changed, 36 insertions(+), 4 deletions(-)
> > 
> > diff --git a/include/efi_loader.h b/include/efi_loader.h
> > index 0a9c82a257e..426d1c72d7d 100644
> > --- a/include/efi_loader.h
> > +++ b/include/efi_loader.h
> > @@ -910,6 +910,30 @@ extern const struct efi_firmware_management_protocol efi_fmp_fit;
> >   extern const struct efi_firmware_management_protocol efi_fmp_raw;
> > 
> >   /* Capsule update */
> > +static inline efi_status_t
> > +efi_valid_update_capsule_params(struct efi_capsule_header
> > +						**capsule_header_array,
> > +				efi_uintn_t capsule_count,
> > +				u64 scatter_gather_list)
> > +{
> > +	u32 flags;
> > +
> > +	if (!capsule_count)
> > +		return EFI_INVALID_PARAMETER;
> 
> If capsule count > 1, don't you have to check all capsules headers?
> 
> > +
> > +	flags = capsule_header_array[0]->flags;
> > +
> > +	if (((flags & CAPSULE_FLAGS_PERSIST_ACROSS_RESET) &&
> > +	     !scatter_gather_list) ||
> > +	    ((flags & CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE) &&
> > +	     !(flags & CAPSULE_FLAGS_PERSIST_ACROSS_RESET)) ||
> > +	    ((flags & CAPSULE_FLAGS_INITIATE_RESET) &&
> > +	     !(flags & CAPSULE_FLAGS_PERSIST_ACROSS_RESET)))
> > +		return EFI_INVALID_PARAMETER;
> 
> What happens if capsule(0) has CAPSULE_FLAGS_INITIATE_RESET and
> capsule(4) has !CAPSULE_FLAGS_PERSIST_ACROSS_RESET?
> 
> Best regards
> 
> Heinrich
> 
> > +
> > +	return EFI_SUCCESS;
> > +}
> > +
> >   efi_status_t EFIAPI efi_update_capsule(
> >   		struct efi_capsule_header **capsule_header_array,
> >   		efi_uintn_t capsule_count,
> > diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c
> > index 60309d4a07d..380cfd70290 100644
> > --- a/lib/efi_loader/efi_capsule.c
> > +++ b/lib/efi_loader/efi_capsule.c
> > @@ -442,12 +442,12 @@ efi_status_t EFIAPI efi_update_capsule(
> >   	EFI_ENTRY("%p, %zu, %llu\n", capsule_header_array, capsule_count,
> >   		  scatter_gather_list);
> > 
> > -	if (!capsule_count) {
> > -		ret = EFI_INVALID_PARAMETER;
> > +	ret = efi_valid_update_capsule_params(capsule_header_array,
> > +					      capsule_count,
> > +					      scatter_gather_list);
> > +	if (ret != EFI_SUCCESS)
> >   		goto out;
> > -	}
> > 
> > -	ret = EFI_SUCCESS;
> >   	for (i = 0, capsule = *capsule_header_array; i < capsule_count;
> >   	     i++, capsule = *(++capsule_header_array)) {
> >   		/* sanity check */
> > diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c
> > index 93a695fc27e..449ad8b9f36 100644
> > --- a/lib/efi_loader/efi_runtime.c
> > +++ b/lib/efi_loader/efi_runtime.c
> > @@ -467,6 +467,14 @@ efi_status_t __efi_runtime EFIAPI efi_update_capsule_unsupported(
> >   			efi_uintn_t capsule_count,
> >   			u64 scatter_gather_list)
> >   {
> > +	efi_status_t ret;
> > +
> > +	ret = efi_valid_update_capsule_params(capsule_header_array,
> > +					      capsule_count,
> > +					      scatter_gather_list);

This is "efi_update_capsule_unsupported" function.
We don't have to check parameters.

-Takahiro Akashi

> > +	if (ret != EFI_SUCCESS)
> > +		return ret;
> > +
> >   	return EFI_UNSUPPORTED;
> >   }
> > 
> > 
>
Heinrich Schuchardt June 21, 2021, 6:04 a.m. UTC | #3
On 6/12/21 2:13 AM, AKASHI Takahiro wrote:
> Hi Vincent,
>
> Thank you for the update.
>
> On Fri, Jun 11, 2021 at 07:06:01PM +0200, Heinrich Schuchardt wrote:
>> Cc: Takahiro, Sughosh, Ilias
>>
>> On 6/11/21 6:15 PM, Vincent Stehlé wrote:
>>> UpdateCapsule() must return EFI_INVALID_PARAMETER in a number of cases,
>>> listed by the UEFI specification and tested by the SCT. Add a common
>>> function to do that.
>
> First of all, I would like to make clear one thing:
> My initial implementation of capsule support does *not*
> intend to support "UpdateCapsule" as runtime API.
> Instead, the sole objective is to provide "delivery of
> capsules via file" (or capsule on disk) method.
>
> This is because the initially-expected use case was
> that we would adopt capsules as an alternative for
> updating UEFI variables without using SetVariable runtime API.
> (This idea is no longer upheld, though.)
>
> This is why the API does not handle nor check parameters
> in the current code. I even regret to have added
> EFI_RUNTIME_UPDATE_CAPSULE option, which makes people confused.
>
> Nevertheless, I'm more than happy if other folks implement
> full semantics of UpdateCapsule.
>
>>> This fixes SCT UpdateCapsule_Conf failures.
>>>
>>> Reviewed-by: Grant Likely <grant.likely@arm.com>
>>> Signed-off-by: Vincent Stehlé <vincent.stehle@arm.com>
>>> Cc: Heinrich Schuchardt <xypron.glpk@gmx.de>
>>> Cc: Alexander Graf <agraf@csgraf.de>
>>> ---
>>>    include/efi_loader.h         | 24 ++++++++++++++++++++++++
>>>    lib/efi_loader/efi_capsule.c |  8 ++++----
>>>    lib/efi_loader/efi_runtime.c |  8 ++++++++
>>>    3 files changed, 36 insertions(+), 4 deletions(-)
>>>
>>> diff --git a/include/efi_loader.h b/include/efi_loader.h
>>> index 0a9c82a257e..426d1c72d7d 100644
>>> --- a/include/efi_loader.h
>>> +++ b/include/efi_loader.h
>>> @@ -910,6 +910,30 @@ extern const struct efi_firmware_management_protocol efi_fmp_fit;
>>>    extern const struct efi_firmware_management_protocol efi_fmp_raw;
>>>
>>>    /* Capsule update */
>>> +static inline efi_status_t
>>> +efi_valid_update_capsule_params(struct efi_capsule_header
>>> +						**capsule_header_array,
>>> +				efi_uintn_t capsule_count,
>>> +				u64 scatter_gather_list)
>>> +{
>>> +	u32 flags;
>>> +
>>> +	if (!capsule_count)
>>> +		return EFI_INVALID_PARAMETER;
>>
>> If capsule count > 1, don't you have to check all capsules headers?
>>
>>> +
>>> +	flags = capsule_header_array[0]->flags;
>>> +
>>> +	if (((flags & CAPSULE_FLAGS_PERSIST_ACROSS_RESET) &&
>>> +	     !scatter_gather_list) ||
>>> +	    ((flags & CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE) &&
>>> +	     !(flags & CAPSULE_FLAGS_PERSIST_ACROSS_RESET)) ||
>>> +	    ((flags & CAPSULE_FLAGS_INITIATE_RESET) &&
>>> +	     !(flags & CAPSULE_FLAGS_PERSIST_ACROSS_RESET)))
>>> +		return EFI_INVALID_PARAMETER;
>>
>> What happens if capsule(0) has CAPSULE_FLAGS_INITIATE_RESET and
>> capsule(4) has !CAPSULE_FLAGS_PERSIST_ACROSS_RESET?
>>
>> Best regards
>>
>> Heinrich
>>
>>> +
>>> +	return EFI_SUCCESS;
>>> +}
>>> +
>>>    efi_status_t EFIAPI efi_update_capsule(
>>>    		struct efi_capsule_header **capsule_header_array,
>>>    		efi_uintn_t capsule_count,
>>> diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c
>>> index 60309d4a07d..380cfd70290 100644
>>> --- a/lib/efi_loader/efi_capsule.c
>>> +++ b/lib/efi_loader/efi_capsule.c
>>> @@ -442,12 +442,12 @@ efi_status_t EFIAPI efi_update_capsule(
>>>    	EFI_ENTRY("%p, %zu, %llu\n", capsule_header_array, capsule_count,
>>>    		  scatter_gather_list);
>>>
>>> -	if (!capsule_count) {
>>> -		ret = EFI_INVALID_PARAMETER;
>>> +	ret = efi_valid_update_capsule_params(capsule_header_array,
>>> +					      capsule_count,
>>> +					      scatter_gather_list);
>>> +	if (ret != EFI_SUCCESS)
>>>    		goto out;
>>> -	}
>>>
>>> -	ret = EFI_SUCCESS;
>>>    	for (i = 0, capsule = *capsule_header_array; i < capsule_count;
>>>    	     i++, capsule = *(++capsule_header_array)) {
>>>    		/* sanity check */
>>> diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c
>>> index 93a695fc27e..449ad8b9f36 100644
>>> --- a/lib/efi_loader/efi_runtime.c
>>> +++ b/lib/efi_loader/efi_runtime.c
>>> @@ -467,6 +467,14 @@ efi_status_t __efi_runtime EFIAPI efi_update_capsule_unsupported(
>>>    			efi_uintn_t capsule_count,
>>>    			u64 scatter_gather_list)
>>>    {
>>> +	efi_status_t ret;
>>> +
>>> +	ret = efi_valid_update_capsule_params(capsule_header_array,
>>> +					      capsule_count,
>>> +					      scatter_gather_list);
>
> This is "efi_update_capsule_unsupported" function.
> We don't have to check parameters.
>
> -Takahiro Akashi

Hello Vincent,

I will mark this patch as "changes requested". Please, send an updated
version.

Best regards

Heinrich

>
>>> +	if (ret != EFI_SUCCESS)
>>> +		return ret;
>>> +
>>>    	return EFI_UNSUPPORTED;
>>>    }
>>>
>>>
>>
diff mbox series

Patch

diff --git a/include/efi_loader.h b/include/efi_loader.h
index 0a9c82a257e..426d1c72d7d 100644
--- a/include/efi_loader.h
+++ b/include/efi_loader.h
@@ -910,6 +910,30 @@  extern const struct efi_firmware_management_protocol efi_fmp_fit;
 extern const struct efi_firmware_management_protocol efi_fmp_raw;
 
 /* Capsule update */
+static inline efi_status_t
+efi_valid_update_capsule_params(struct efi_capsule_header
+						**capsule_header_array,
+				efi_uintn_t capsule_count,
+				u64 scatter_gather_list)
+{
+	u32 flags;
+
+	if (!capsule_count)
+		return EFI_INVALID_PARAMETER;
+
+	flags = capsule_header_array[0]->flags;
+
+	if (((flags & CAPSULE_FLAGS_PERSIST_ACROSS_RESET) &&
+	     !scatter_gather_list) ||
+	    ((flags & CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE) &&
+	     !(flags & CAPSULE_FLAGS_PERSIST_ACROSS_RESET)) ||
+	    ((flags & CAPSULE_FLAGS_INITIATE_RESET) &&
+	     !(flags & CAPSULE_FLAGS_PERSIST_ACROSS_RESET)))
+		return EFI_INVALID_PARAMETER;
+
+	return EFI_SUCCESS;
+}
+
 efi_status_t EFIAPI efi_update_capsule(
 		struct efi_capsule_header **capsule_header_array,
 		efi_uintn_t capsule_count,
diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c
index 60309d4a07d..380cfd70290 100644
--- a/lib/efi_loader/efi_capsule.c
+++ b/lib/efi_loader/efi_capsule.c
@@ -442,12 +442,12 @@  efi_status_t EFIAPI efi_update_capsule(
 	EFI_ENTRY("%p, %zu, %llu\n", capsule_header_array, capsule_count,
 		  scatter_gather_list);
 
-	if (!capsule_count) {
-		ret = EFI_INVALID_PARAMETER;
+	ret = efi_valid_update_capsule_params(capsule_header_array,
+					      capsule_count,
+					      scatter_gather_list);
+	if (ret != EFI_SUCCESS)
 		goto out;
-	}
 
-	ret = EFI_SUCCESS;
 	for (i = 0, capsule = *capsule_header_array; i < capsule_count;
 	     i++, capsule = *(++capsule_header_array)) {
 		/* sanity check */
diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c
index 93a695fc27e..449ad8b9f36 100644
--- a/lib/efi_loader/efi_runtime.c
+++ b/lib/efi_loader/efi_runtime.c
@@ -467,6 +467,14 @@  efi_status_t __efi_runtime EFIAPI efi_update_capsule_unsupported(
 			efi_uintn_t capsule_count,
 			u64 scatter_gather_list)
 {
+	efi_status_t ret;
+
+	ret = efi_valid_update_capsule_params(capsule_header_array,
+					      capsule_count,
+					      scatter_gather_list);
+	if (ret != EFI_SUCCESS)
+		return ret;
+
 	return EFI_UNSUPPORTED;
 }