diff mbox series

[HVM,v2,1/1] hvm: refactor set param

Message ID 20210205203905.8824-1-nmanthey@amazon.de (mailing list archive)
State Superseded
Headers show
Series [HVM,v2,1/1] hvm: refactor set param | expand

Commit Message

Norbert Manthey Feb. 5, 2021, 8:39 p.m. UTC
To prevent leaking HVM params via L1TF and similar issues on a
hyperthread pair, let's load values of domains as late as possible.

Furthermore, speculative barriers are re-arranged to make sure we do not
allow guests running on co-located VCPUs to leak hvm parameter values of
other domains.

This is part of the speculative hardening effort.

Signed-off-by: Norbert Manthey <nmanthey@amazon.de>
Reported-by: Hongyan Xia <hongyxia@amazon.co.uk>

---
v2: Add another speculative blocker, which protects the return code check
    of the function hvm_allow_set_param.


 xen/arch/x86/hvm/hvm.c | 19 +++++++++++++++----
 1 file changed, 15 insertions(+), 4 deletions(-)

Comments

Jan Beulich Feb. 8, 2021, 2:21 p.m. UTC | #1
On 05.02.2021 21:39, Norbert Manthey wrote:
> To prevent leaking HVM params via L1TF and similar issues on a
> hyperthread pair, let's load values of domains as late as possible.
> 
> Furthermore, speculative barriers are re-arranged to make sure we do not
> allow guests running on co-located VCPUs to leak hvm parameter values of
> other domains.
> 
> This is part of the speculative hardening effort.
> 
> Signed-off-by: Norbert Manthey <nmanthey@amazon.de>
> Reported-by: Hongyan Xia <hongyxia@amazon.co.uk>

Did you lose Ian's release-ack, or did you drop it for a specific
reason?

> --- a/xen/arch/x86/hvm/hvm.c
> +++ b/xen/arch/x86/hvm/hvm.c
> @@ -4060,7 +4060,7 @@ static int hvm_allow_set_param(struct domain *d,
>                                 uint32_t index,
>                                 uint64_t new_value)
>  {
> -    uint64_t value = d->arch.hvm.params[index];
> +    uint64_t value;
>      int rc;
>  
>      rc = xsm_hvm_param(XSM_TARGET, d, HVMOP_set_param);
> @@ -4108,6 +4108,13 @@ static int hvm_allow_set_param(struct domain *d,
>      if ( rc )
>          return rc;
>  
> +    if ( index >= HVM_NR_PARAMS )
> +        return -EINVAL;
> +
> +    /* Make sure we evaluate permissions before loading data of domains. */
> +    block_speculation();
> +
> +    value = d->arch.hvm.params[index];
>      switch ( index )
>      {
>      /* The following parameters should only be changed once. */

I don't see the need for the heavier block_speculation() here;
afaict array_access_nospec() should do fine. The switch() in
context above as well as the switch() further down in the
function don't have any speculation susceptible code.

Furthermore the first switch() doesn't use "value" at all, so
you could move the access even further down. This may have the
downside of adding latency, so may not be worth it, but in
this case at least the description should say half a word,
especially seeing it say "as late as possible" right now.

> @@ -4141,6 +4148,9 @@ static int hvm_set_param(struct domain *d, uint32_t index, uint64_t value)
>      if ( rc )
>          return rc;
>  
> +    /* Make sure we evaluate permissions before loading data of domains. */
> +    block_speculation();
> +
>      switch ( index )
>      {
>      case HVM_PARAM_CALLBACK_IRQ:

Like you do for the "get" path I think this similarly renders
pointless the use in hvmop_set_param() (and - see below - the
same consideration wrt is_hvm_domain() applies).

> @@ -4388,6 +4398,10 @@ int hvm_get_param(struct domain *d, uint32_t index, uint64_t *value)
>      if ( rc )
>          return rc;
>  
> +    /* Make sure the index bound check in hvm_get_param is respected, as well as
> +       the above domain permissions. */
> +    block_speculation();

Nit: Please fix comment style here.

> @@ -4428,9 +4442,6 @@ static int hvmop_get_param(
>      if ( a.index >= HVM_NR_PARAMS )
>          return -EINVAL;
>  
> -    /* Make sure the above bound check is not bypassed during speculation. */
> -    block_speculation();
> -
>      d = rcu_lock_domain_by_any_id(a.domid);
>      if ( d == NULL )
>          return -ESRCH;

This one really was pointless anyway, as is_hvm_domain() (used
down from here) already contains a suitable barrier.

Jan
Norbert Manthey Feb. 8, 2021, 7:47 p.m. UTC | #2
On 2/8/21 3:21 PM, Jan Beulich wrote:
> On 05.02.2021 21:39, Norbert Manthey wrote:
>> To prevent leaking HVM params via L1TF and similar issues on a
>> hyperthread pair, let's load values of domains as late as possible.
>>
>> Furthermore, speculative barriers are re-arranged to make sure we do not
>> allow guests running on co-located VCPUs to leak hvm parameter values of
>> other domains.
>>
>> This is part of the speculative hardening effort.
>>
>> Signed-off-by: Norbert Manthey <nmanthey@amazon.de>
>> Reported-by: Hongyan Xia <hongyxia@amazon.co.uk>
> Did you lose Ian's release-ack, or did you drop it for a specific
> reason?
That happened by accident, similarly to not chaining this v2 to the
former v1. I'll add it to the next revision.
>
>> --- a/xen/arch/x86/hvm/hvm.c
>> +++ b/xen/arch/x86/hvm/hvm.c
>> @@ -4060,7 +4060,7 @@ static int hvm_allow_set_param(struct domain *d,
>>                                 uint32_t index,
>>                                 uint64_t new_value)
>>  {
>> -    uint64_t value = d->arch.hvm.params[index];
>> +    uint64_t value;
>>      int rc;
>>
>>      rc = xsm_hvm_param(XSM_TARGET, d, HVMOP_set_param);
>> @@ -4108,6 +4108,13 @@ static int hvm_allow_set_param(struct domain *d,
>>      if ( rc )
>>          return rc;
>>
>> +    if ( index >= HVM_NR_PARAMS )
>> +        return -EINVAL;
>> +
>> +    /* Make sure we evaluate permissions before loading data of domains. */
>> +    block_speculation();
>> +
>> +    value = d->arch.hvm.params[index];
>>      switch ( index )
>>      {
>>      /* The following parameters should only be changed once. */
> I don't see the need for the heavier block_speculation() here;
> afaict array_access_nospec() should do fine. The switch() in
> context above as well as the switch() further down in the
> function don't have any speculation susceptible code.
The reason to block speculation instead of just using the hardened index
access is to not allow to speculatively load data from another domain.
>
> Furthermore the first switch() doesn't use "value" at all, so
> you could move the access even further down. This may have the
> downside of adding latency, so may not be worth it, but in
> this case at least the description should say half a word,
> especially seeing it say "as late as possible" right now.
Agreed, I can move this further down, or adapt the wording. The initial
intention was to move it only below the first possible speculative
blocker. Hence, let me adapt the wording.
>
>> @@ -4141,6 +4148,9 @@ static int hvm_set_param(struct domain *d, uint32_t index, uint64_t value)
>>      if ( rc )
>>          return rc;
>>
>> +    /* Make sure we evaluate permissions before loading data of domains. */
>> +    block_speculation();
>> +
>>      switch ( index )
>>      {
>>      case HVM_PARAM_CALLBACK_IRQ:
> Like you do for the "get" path I think this similarly renders
> pointless the use in hvmop_set_param() (and - see below - the
> same consideration wrt is_hvm_domain() applies).
Can you please be more specific why this is pointless? I understand that
the is_hvm_domain check comes with a barrier that can be used to not add
another barrier. However, I did not find such a barrier here, which
comes between the 'if (rc)' just above, and the potential next access
based on the value of 'index'. At least the access behind the switch
statement cannot be optimized and replaced with a constant value easily.
>
>> @@ -4388,6 +4398,10 @@ int hvm_get_param(struct domain *d, uint32_t index, uint64_t *value)
>>      if ( rc )
>>          return rc;
>>
>> +    /* Make sure the index bound check in hvm_get_param is respected, as well as
>> +       the above domain permissions. */
>> +    block_speculation();
> Nit: Please fix comment style here.
Will do.
>
>> @@ -4428,9 +4442,6 @@ static int hvmop_get_param(
>>      if ( a.index >= HVM_NR_PARAMS )
>>          return -EINVAL;
>>
>> -    /* Make sure the above bound check is not bypassed during speculation. */
>> -    block_speculation();
>> -
>>      d = rcu_lock_domain_by_any_id(a.domid);
>>      if ( d == NULL )
>>          return -ESRCH;
> This one really was pointless anyway, as is_hvm_domain() (used
> down from here) already contains a suitable barrier.

Yes, agreed.

Best,
Norbert

>
> Jan




Amazon Development Center Germany GmbH
Krausenstr. 38
10117 Berlin
Geschaeftsfuehrung: Christian Schlaeger, Jonathan Weiss
Eingetragen am Amtsgericht Charlottenburg unter HRB 149173 B
Sitz: Berlin
Ust-ID: DE 289 237 879
Jan Beulich Feb. 9, 2021, 9:40 a.m. UTC | #3
On 08.02.2021 20:47, Norbert Manthey wrote:
> On 2/8/21 3:21 PM, Jan Beulich wrote:
>> On 05.02.2021 21:39, Norbert Manthey wrote:
>>> @@ -4108,6 +4108,13 @@ static int hvm_allow_set_param(struct domain *d,
>>>      if ( rc )
>>>          return rc;
>>>
>>> +    if ( index >= HVM_NR_PARAMS )
>>> +        return -EINVAL;
>>> +
>>> +    /* Make sure we evaluate permissions before loading data of domains. */
>>> +    block_speculation();
>>> +
>>> +    value = d->arch.hvm.params[index];
>>>      switch ( index )
>>>      {
>>>      /* The following parameters should only be changed once. */
>> I don't see the need for the heavier block_speculation() here;
>> afaict array_access_nospec() should do fine. The switch() in
>> context above as well as the switch() further down in the
>> function don't have any speculation susceptible code.
> The reason to block speculation instead of just using the hardened index
> access is to not allow to speculatively load data from another domain.

Okay, looks like I got mislead by the added bounds check. Why
do you add that, when the sole caller already has one? It'll
suffice since you move the array access past the barrier,
won't it?

>>> @@ -4141,6 +4148,9 @@ static int hvm_set_param(struct domain *d, uint32_t index, uint64_t value)
>>>      if ( rc )
>>>          return rc;
>>>
>>> +    /* Make sure we evaluate permissions before loading data of domains. */
>>> +    block_speculation();
>>> +
>>>      switch ( index )
>>>      {
>>>      case HVM_PARAM_CALLBACK_IRQ:
>> Like you do for the "get" path I think this similarly renders
>> pointless the use in hvmop_set_param() (and - see below - the
>> same consideration wrt is_hvm_domain() applies).
> Can you please be more specific why this is pointless? I understand that
> the is_hvm_domain check comes with a barrier that can be used to not add
> another barrier. However, I did not find such a barrier here, which
> comes between the 'if (rc)' just above, and the potential next access
> based on the value of 'index'. At least the access behind the switch
> statement cannot be optimized and replaced with a constant value easily.

I'm suspecting a misunderstanding (the more that further down
you did agree to what I've said for hvmop_get_param()): I'm
not saying your addition is pointless. Instead I'm saying that
your addition should be accompanied by removal of the barrier
from hvmop_set_param(), paralleling what you do to
hvmop_get_param(). And additionally I'm saying that just like
in hvmop_get_param() the barrier there was already previously
redundant with that inside is_hvm_domain().

Jan
Norbert Manthey Feb. 9, 2021, 1:41 p.m. UTC | #4
On 2/9/21 10:40 AM, Jan Beulich wrote:
> CAUTION: This email originated from outside of the organization. Do not click links or open attachments unless you can confirm the sender and know the content is safe.
>
>
>
> On 08.02.2021 20:47, Norbert Manthey wrote:
>> On 2/8/21 3:21 PM, Jan Beulich wrote:
>>> On 05.02.2021 21:39, Norbert Manthey wrote:
>>>> @@ -4108,6 +4108,13 @@ static int hvm_allow_set_param(struct domain *d,
>>>>      if ( rc )
>>>>          return rc;
>>>>
>>>> +    if ( index >= HVM_NR_PARAMS )
>>>> +        return -EINVAL;
>>>> +
>>>> +    /* Make sure we evaluate permissions before loading data of domains. */
>>>> +    block_speculation();
>>>> +
>>>> +    value = d->arch.hvm.params[index];
>>>>      switch ( index )
>>>>      {
>>>>      /* The following parameters should only be changed once. */
>>> I don't see the need for the heavier block_speculation() here;
>>> afaict array_access_nospec() should do fine. The switch() in
>>> context above as well as the switch() further down in the
>>> function don't have any speculation susceptible code.
>> The reason to block speculation instead of just using the hardened index
>> access is to not allow to speculatively load data from another domain.
> Okay, looks like I got mislead by the added bounds check. Why
> do you add that, when the sole caller already has one? It'll
> suffice since you move the array access past the barrier,
> won't it?
I can drop that bound check again. This was added to make sure other
callers would be save as well. Thinking about this a little more, the
check could actually be moved into the hvm_allow_set_param function,
above the first index access in that function. Are there good reasons to
not move the index check into the allow function?
>
>>>> @@ -4141,6 +4148,9 @@ static int hvm_set_param(struct domain *d, uint32_t index, uint64_t value)
>>>>      if ( rc )
>>>>          return rc;
>>>>
>>>> +    /* Make sure we evaluate permissions before loading data of domains. */
>>>> +    block_speculation();
>>>> +
>>>>      switch ( index )
>>>>      {
>>>>      case HVM_PARAM_CALLBACK_IRQ:
>>> Like you do for the "get" path I think this similarly renders
>>> pointless the use in hvmop_set_param() (and - see below - the
>>> same consideration wrt is_hvm_domain() applies).
>> Can you please be more specific why this is pointless? I understand that
>> the is_hvm_domain check comes with a barrier that can be used to not add
>> another barrier. However, I did not find such a barrier here, which
>> comes between the 'if (rc)' just above, and the potential next access
>> based on the value of 'index'. At least the access behind the switch
>> statement cannot be optimized and replaced with a constant value easily.
> I'm suspecting a misunderstanding (the more that further down
> you did agree to what I've said for hvmop_get_param()): I'm
> not saying your addition is pointless. Instead I'm saying that
> your addition should be accompanied by removal of the barrier
> from hvmop_set_param(), paralleling what you do to
> hvmop_get_param(). And additionally I'm saying that just like
> in hvmop_get_param() the barrier there was already previously
> redundant with that inside is_hvm_domain().

I now understand, thank you. I agree, the already existing barrier in
the hvmop_set_param function can be dropped as well. I will update the
diff accordingly, after we concluded where to put the index check.

Best,
Norbert




Amazon Development Center Germany GmbH
Krausenstr. 38
10117 Berlin
Geschaeftsfuehrung: Christian Schlaeger, Jonathan Weiss
Eingetragen am Amtsgericht Charlottenburg unter HRB 149173 B
Sitz: Berlin
Ust-ID: DE 289 237 879
Jan Beulich Feb. 9, 2021, 1:45 p.m. UTC | #5
On 09.02.2021 14:41, Norbert Manthey wrote:
> On 2/9/21 10:40 AM, Jan Beulich wrote:
>> On 08.02.2021 20:47, Norbert Manthey wrote:
>>> On 2/8/21 3:21 PM, Jan Beulich wrote:
>>>> On 05.02.2021 21:39, Norbert Manthey wrote:
>>>>> @@ -4108,6 +4108,13 @@ static int hvm_allow_set_param(struct domain *d,
>>>>>      if ( rc )
>>>>>          return rc;
>>>>>
>>>>> +    if ( index >= HVM_NR_PARAMS )
>>>>> +        return -EINVAL;
>>>>> +
>>>>> +    /* Make sure we evaluate permissions before loading data of domains. */
>>>>> +    block_speculation();
>>>>> +
>>>>> +    value = d->arch.hvm.params[index];
>>>>>      switch ( index )
>>>>>      {
>>>>>      /* The following parameters should only be changed once. */
>>>> I don't see the need for the heavier block_speculation() here;
>>>> afaict array_access_nospec() should do fine. The switch() in
>>>> context above as well as the switch() further down in the
>>>> function don't have any speculation susceptible code.
>>> The reason to block speculation instead of just using the hardened index
>>> access is to not allow to speculatively load data from another domain.
>> Okay, looks like I got mislead by the added bounds check. Why
>> do you add that, when the sole caller already has one? It'll
>> suffice since you move the array access past the barrier,
>> won't it?
> I can drop that bound check again. This was added to make sure other
> callers would be save as well. Thinking about this a little more, the
> check could actually be moved into the hvm_allow_set_param function,
> above the first index access in that function. Are there good reasons to
> not move the index check into the allow function?

I guess I'm confused: We're talking about dropping the check
you add to hvm_allow_set_param() and you suggest to "move" it
right there?

Jan
Norbert Manthey Feb. 9, 2021, 1:56 p.m. UTC | #6
On 2/9/21 2:45 PM, Jan Beulich wrote:
> On 09.02.2021 14:41, Norbert Manthey wrote:
>> On 2/9/21 10:40 AM, Jan Beulich wrote:
>>> On 08.02.2021 20:47, Norbert Manthey wrote:
>>>> On 2/8/21 3:21 PM, Jan Beulich wrote:
>>>>> On 05.02.2021 21:39, Norbert Manthey wrote:
>>>>>> @@ -4108,6 +4108,13 @@ static int hvm_allow_set_param(struct domain *d,
>>>>>>      if ( rc )
>>>>>>          return rc;
>>>>>>
>>>>>> +    if ( index >= HVM_NR_PARAMS )
>>>>>> +        return -EINVAL;
>>>>>> +
>>>>>> +    /* Make sure we evaluate permissions before loading data of domains. */
>>>>>> +    block_speculation();
>>>>>> +
>>>>>> +    value = d->arch.hvm.params[index];
>>>>>>      switch ( index )
>>>>>>      {
>>>>>>      /* The following parameters should only be changed once. */
>>>>> I don't see the need for the heavier block_speculation() here;
>>>>> afaict array_access_nospec() should do fine. The switch() in
>>>>> context above as well as the switch() further down in the
>>>>> function don't have any speculation susceptible code.
>>>> The reason to block speculation instead of just using the hardened index
>>>> access is to not allow to speculatively load data from another domain.
>>> Okay, looks like I got mislead by the added bounds check. Why
>>> do you add that, when the sole caller already has one? It'll
>>> suffice since you move the array access past the barrier,
>>> won't it?
>> I can drop that bound check again. This was added to make sure other
>> callers would be save as well. Thinking about this a little more, the
>> check could actually be moved into the hvm_allow_set_param function,
>> above the first index access in that function. Are there good reasons to
>> not move the index check into the allow function?
> I guess I'm confused: We're talking about dropping the check
> you add to hvm_allow_set_param() and you suggest to "move" it
> right there?

Yes. I can either just no introduce that check in that function (which
is what you suggested). As an alternative, to have all checks in one
function, I proposed to instead move it into hvm_allow_set_param. If you
do not like this proposal, I'll follow your suggestion and will simply
not introduce the additional bound check as part of this patch.

Best,
Norbert




Amazon Development Center Germany GmbH
Krausenstr. 38
10117 Berlin
Geschaeftsfuehrung: Christian Schlaeger, Jonathan Weiss
Eingetragen am Amtsgericht Charlottenburg unter HRB 149173 B
Sitz: Berlin
Ust-ID: DE 289 237 879
Jan Beulich Feb. 9, 2021, 2:21 p.m. UTC | #7
On 09.02.2021 14:56, Norbert Manthey wrote:
> On 2/9/21 2:45 PM, Jan Beulich wrote:
>> On 09.02.2021 14:41, Norbert Manthey wrote:
>>> On 2/9/21 10:40 AM, Jan Beulich wrote:
>>>> On 08.02.2021 20:47, Norbert Manthey wrote:
>>>>> On 2/8/21 3:21 PM, Jan Beulich wrote:
>>>>>> On 05.02.2021 21:39, Norbert Manthey wrote:
>>>>>>> @@ -4108,6 +4108,13 @@ static int hvm_allow_set_param(struct domain *d,
>>>>>>>      if ( rc )
>>>>>>>          return rc;
>>>>>>>
>>>>>>> +    if ( index >= HVM_NR_PARAMS )
>>>>>>> +        return -EINVAL;
>>>>>>> +
>>>>>>> +    /* Make sure we evaluate permissions before loading data of domains. */
>>>>>>> +    block_speculation();
>>>>>>> +
>>>>>>> +    value = d->arch.hvm.params[index];
>>>>>>>      switch ( index )
>>>>>>>      {
>>>>>>>      /* The following parameters should only be changed once. */
>>>>>> I don't see the need for the heavier block_speculation() here;
>>>>>> afaict array_access_nospec() should do fine. The switch() in
>>>>>> context above as well as the switch() further down in the
>>>>>> function don't have any speculation susceptible code.
>>>>> The reason to block speculation instead of just using the hardened index
>>>>> access is to not allow to speculatively load data from another domain.
>>>> Okay, looks like I got mislead by the added bounds check. Why
>>>> do you add that, when the sole caller already has one? It'll
>>>> suffice since you move the array access past the barrier,
>>>> won't it?
>>> I can drop that bound check again. This was added to make sure other
>>> callers would be save as well. Thinking about this a little more, the
>>> check could actually be moved into the hvm_allow_set_param function,
>>> above the first index access in that function. Are there good reasons to
>>> not move the index check into the allow function?
>> I guess I'm confused: We're talking about dropping the check
>> you add to hvm_allow_set_param() and you suggest to "move" it
>> right there?
> 
> Yes. I can either just no introduce that check in that function (which
> is what you suggested). As an alternative, to have all checks in one
> function, I proposed to instead move it into hvm_allow_set_param.

Oh, I see. What I'd like to be the result of your re-arrangement is
symmetry between "get" and "set" where possible in this regard, and
asymmetry having a clear reason. Seeing that hvm_{get,set}_param()
are the non-static functions here, I think it would be quite
desirable for the bounds checking to live there. Since
hvm_allow_{get,set}_param() are specifically helpers of the two
earlier named functions, checks consistently living there would as
well be fine with me.

Jan
Norbert Manthey Feb. 11, 2021, 8:46 p.m. UTC | #8
On 2/9/21 3:21 PM, Jan Beulich wrote:
> On 09.02.2021 14:56, Norbert Manthey wrote:
>> On 2/9/21 2:45 PM, Jan Beulich wrote:
>>> On 09.02.2021 14:41, Norbert Manthey wrote:
>>>> On 2/9/21 10:40 AM, Jan Beulich wrote:
>>>>> On 08.02.2021 20:47, Norbert Manthey wrote:
>>>>>> On 2/8/21 3:21 PM, Jan Beulich wrote:
>>>>>>> On 05.02.2021 21:39, Norbert Manthey wrote:
>>>>>>>> @@ -4108,6 +4108,13 @@ static int hvm_allow_set_param(struct domain *d,
>>>>>>>>      if ( rc )
>>>>>>>>          return rc;
>>>>>>>>
>>>>>>>> +    if ( index >= HVM_NR_PARAMS )
>>>>>>>> +        return -EINVAL;
>>>>>>>> +
>>>>>>>> +    /* Make sure we evaluate permissions before loading data of domains. */
>>>>>>>> +    block_speculation();
>>>>>>>> +
>>>>>>>> +    value = d->arch.hvm.params[index];
>>>>>>>>      switch ( index )
>>>>>>>>      {
>>>>>>>>      /* The following parameters should only be changed once. */
>>>>>>> I don't see the need for the heavier block_speculation() here;
>>>>>>> afaict array_access_nospec() should do fine. The switch() in
>>>>>>> context above as well as the switch() further down in the
>>>>>>> function don't have any speculation susceptible code.
>>>>>> The reason to block speculation instead of just using the hardened index
>>>>>> access is to not allow to speculatively load data from another domain.
>>>>> Okay, looks like I got mislead by the added bounds check. Why
>>>>> do you add that, when the sole caller already has one? It'll
>>>>> suffice since you move the array access past the barrier,
>>>>> won't it?
>>>> I can drop that bound check again. This was added to make sure other
>>>> callers would be save as well. Thinking about this a little more, the
>>>> check could actually be moved into the hvm_allow_set_param function,
>>>> above the first index access in that function. Are there good reasons to
>>>> not move the index check into the allow function?
>>> I guess I'm confused: We're talking about dropping the check
>>> you add to hvm_allow_set_param() and you suggest to "move" it
>>> right there?
>> Yes. I can either just no introduce that check in that function (which
>> is what you suggested). As an alternative, to have all checks in one
>> function, I proposed to instead move it into hvm_allow_set_param.
> Oh, I see. What I'd like to be the result of your re-arrangement is
> symmetry between "get" and "set" where possible in this regard, and
> asymmetry having a clear reason. Seeing that hvm_{get,set}_param()
> are the non-static functions here, I think it would be quite
> desirable for the bounds checking to live there. Since
> hvm_allow_{get,set}_param() are specifically helpers of the two
> earlier named functions, checks consistently living there would as
> well be fine with me.

I agree with the symmetry for get and set. This is what I'd aim for:

 1. hvmop_set_param and hvmop_get_param (static) both check for the
index, and afterwards use the is_hvm_domain(d) function with its barrier
 2. hvm_set_param (static) and hvm_get_param both call their allow
helper function, evaluate the return code, and afterwards block speculation.
 2.1. hvm_get_param is declared in a public header, and cannot be turned
into a static function, hence needs the index check
 2.2. hvm_set_param is only called from hvmop_set_param, and index is
already checked there, hence, do not add check
 3. hvm_allow_set_param (static) and hvm_allow_get_param (static) do not
validate the index parameter
 3.1. hvm_allow_set_param blocks speculative execution with a barrier
after domain permissions have been evaluated, before accessing the
parameters of the domain. hvm_allow_get_param does not access the params
member of the domain, and hence does not require additional protection.

To simplify the code, I propose to furthermore make the hvmop_set_param
function static as well.

Please let me know whether the above would is acceptable.

Best,
Norbert

>
> Jan




Amazon Development Center Germany GmbH
Krausenstr. 38
10117 Berlin
Geschaeftsfuehrung: Christian Schlaeger, Jonathan Weiss
Eingetragen am Amtsgericht Charlottenburg unter HRB 149173 B
Sitz: Berlin
Ust-ID: DE 289 237 879
Jan Beulich Feb. 12, 2021, 10:04 a.m. UTC | #9
On 11.02.2021 21:46, Norbert Manthey wrote:
> I agree with the symmetry for get and set. This is what I'd aim for:
> 
>  1. hvmop_set_param and hvmop_get_param (static) both check for the
> index, and afterwards use the is_hvm_domain(d) function with its barrier
>  2. hvm_set_param (static) and hvm_get_param both call their allow
> helper function, evaluate the return code, and afterwards block speculation.
>  2.1. hvm_get_param is declared in a public header, and cannot be turned
> into a static function, hence needs the index check

But both further call sites are in bounded loops, with the bounds not
guest controlled. It can rely on the callers just as much as ...

>  2.2. hvm_set_param is only called from hvmop_set_param, and index is
> already checked there, hence, do not add check

... this.

>  3. hvm_allow_set_param (static) and hvm_allow_get_param (static) do not
> validate the index parameter
>  3.1. hvm_allow_set_param blocks speculative execution with a barrier
> after domain permissions have been evaluated, before accessing the
> parameters of the domain. hvm_allow_get_param does not access the params
> member of the domain, and hence does not require additional protection.
> 
> To simplify the code, I propose to furthermore make the hvmop_set_param
> function static as well.

Yes - this not being so already is likely simply an oversight,
supported by the fact that there's no declaration in any header.

Jan
Norbert Manthey Feb. 12, 2021, 12:28 p.m. UTC | #10
On 2/12/21 11:04 AM, Jan Beulich wrote:
> CAUTION: This email originated from outside of the organization. Do not click links or open attachments unless you can confirm the sender and know the content is safe.
>
>
>
> On 11.02.2021 21:46, Norbert Manthey wrote:
>> I agree with the symmetry for get and set. This is what I'd aim for:
>>
>>  1. hvmop_set_param and hvmop_get_param (static) both check for the
>> index, and afterwards use the is_hvm_domain(d) function with its barrier
>>  2. hvm_set_param (static) and hvm_get_param both call their allow
>> helper function, evaluate the return code, and afterwards block speculation.
>>  2.1. hvm_get_param is declared in a public header, and cannot be turned
>> into a static function, hence needs the index check
> But both further call sites are in bounded loops, with the bounds not
> guest controlled. It can rely on the callers just as much as ...
Okay, so I will not add the check there either. I thought about future
modifications that allow to call that function from other places, or
modified call environments with eventually guest control - but I am fine
to not consider these.
>
>>  2.2. hvm_set_param is only called from hvmop_set_param, and index is
>> already checked there, hence, do not add check
> ... this.
>
>>  3. hvm_allow_set_param (static) and hvm_allow_get_param (static) do not
>> validate the index parameter
>>  3.1. hvm_allow_set_param blocks speculative execution with a barrier
>> after domain permissions have been evaluated, before accessing the
>> parameters of the domain. hvm_allow_get_param does not access the params
>> member of the domain, and hence does not require additional protection.
>>
>> To simplify the code, I propose to furthermore make the hvmop_set_param
>> function static as well.
> Yes - this not being so already is likely simply an oversight,
> supported by the fact that there's no declaration in any header.

Okay.

Best,
Norbert




Amazon Development Center Germany GmbH
Krausenstr. 38
10117 Berlin
Geschaeftsfuehrung: Christian Schlaeger, Jonathan Weiss
Eingetragen am Amtsgericht Charlottenburg unter HRB 149173 B
Sitz: Berlin
Ust-ID: DE 289 237 879
diff mbox series

Patch

diff --git a/xen/arch/x86/hvm/hvm.c b/xen/arch/x86/hvm/hvm.c
--- a/xen/arch/x86/hvm/hvm.c
+++ b/xen/arch/x86/hvm/hvm.c
@@ -4060,7 +4060,7 @@  static int hvm_allow_set_param(struct domain *d,
                                uint32_t index,
                                uint64_t new_value)
 {
-    uint64_t value = d->arch.hvm.params[index];
+    uint64_t value;
     int rc;
 
     rc = xsm_hvm_param(XSM_TARGET, d, HVMOP_set_param);
@@ -4108,6 +4108,13 @@  static int hvm_allow_set_param(struct domain *d,
     if ( rc )
         return rc;
 
+    if ( index >= HVM_NR_PARAMS )
+        return -EINVAL;
+
+    /* Make sure we evaluate permissions before loading data of domains. */
+    block_speculation();
+
+    value = d->arch.hvm.params[index];
     switch ( index )
     {
     /* The following parameters should only be changed once. */
@@ -4141,6 +4148,9 @@  static int hvm_set_param(struct domain *d, uint32_t index, uint64_t value)
     if ( rc )
         return rc;
 
+    /* Make sure we evaluate permissions before loading data of domains. */
+    block_speculation();
+
     switch ( index )
     {
     case HVM_PARAM_CALLBACK_IRQ:
@@ -4388,6 +4398,10 @@  int hvm_get_param(struct domain *d, uint32_t index, uint64_t *value)
     if ( rc )
         return rc;
 
+    /* Make sure the index bound check in hvm_get_param is respected, as well as
+       the above domain permissions. */
+    block_speculation();
+
     switch ( index )
     {
     case HVM_PARAM_ACPI_S_STATE:
@@ -4428,9 +4442,6 @@  static int hvmop_get_param(
     if ( a.index >= HVM_NR_PARAMS )
         return -EINVAL;
 
-    /* Make sure the above bound check is not bypassed during speculation. */
-    block_speculation();
-
     d = rcu_lock_domain_by_any_id(a.domid);
     if ( d == NULL )
         return -ESRCH;