|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [PATCH v3 2/6] x86/HVM: split restore state checking from state loading
On 05.12.2023 15:29, Roger Pau Monné wrote:
> On Tue, Dec 05, 2023 at 09:52:31AM +0100, Jan Beulich wrote:
>> On 04.12.2023 18:27, Roger Pau Monné wrote:
>>> On Tue, Nov 28, 2023 at 11:34:04AM +0100, Jan Beulich wrote:
>>>> ..., at least as reasonably feasible without making a check hook
>>>> mandatory (in particular strict vs relaxed/zero-extend length checking
>>>> can't be done early this way).
>>>>
>>>> Note that only one of the two uses of hvm_load() is accompanied with
>>>> hvm_check(). The other directly consumes hvm_save() output, which ought
>>>> to be well-formed. This means that while input data related checks don't
>>>> need repeating in the "load" function when already done by the "check"
>>>> one (albeit assertions to this effect may be desirable), domain state
>>>> related checks (e.g. has_xyz(d)) will be required in both places.
>>>>
>>>> Suggested-by: Roger Pau Monné <roger.pau@xxxxxxxxxx>
>>>> Signed-off-by: Jan Beulich <jbeulich@xxxxxxxx>
>>>> ---
>>>> Do we really need all the copying involved in use of _hvm_read_entry()
>>>> (backing hvm_load_entry()? Zero-extending loads are likely easier to
>>>> handle that way, but for strict loads all we gain is a reduced risk of
>>>> unaligned accesses (compared to simply pointing into h->data[]).
>>>
>>> See below, but I wonder whether the checks could be performed as part
>>> of hvm_load() without having to introduce a separate handler and loop
>>> over the context entries.
>>
>> Specifically not. State loading (in the longer run) would better not fail
>> once started. (Imo it should have been this way from the beginning.) Only
>> then will the vCPU still be in a predictable state even after a possible
>> error.
>
> Looking at the callers, does such predictable state after failure
> matter?
>
> One caller is an hypercall used by the toolstack at domain create,
> failing can just lead to the domain being destroyed. The other caller
> is vm fork, which will also lead to the fork being destroyed if
> context loading fails.
>
> Maybe I'm overlooking something.
You don't (I think), but existing callers necessarily have to behave the
way you describe. From an abstract perspective, though, failed state
loading would better allow a retry. And really I thought that when you
suggested to split checking from loading, you had exactly that in mind.
>>>> Would the hvm_sr_handlers[] better use array_access_nospec()?
>>>
>>> Maybe? Given this is a domctl I do wonder whether a domain already
>>> having access to such interface won't have easier ways to leak data
>>> from Xen. Maybe for a disaggregated setup.
>>
>> Hmm, now we're in the middle - Andrew effectively said "no need to".
>
> I'm certainly not an expert on whether array_access_nospec() should be
> used, so if Andrew says no need, that's likely better advice.
>
> Maybe the xsm check used in such desegregated setups would already
> stop speculation?
There's no XSM check anywhere near, and even if there was I don't see
how it would stop mis-speculation on those array accesses.
>>>> @@ -275,6 +281,78 @@ int hvm_save(struct domain *d, hvm_domai
>>>> return 0;
>>>> }
>>>>
>>>> +int hvm_check(const struct domain *d, hvm_domain_context_t *h)
>>>> +{
>>>> + const struct hvm_save_header *hdr;
>>>> + int rc;
>>>> +
>>>> + if ( d->is_dying )
>>>> + return -EINVAL;
>>>> +
>>>> + /* Get at the save header, which must be first. */
>>>> + hdr = hvm_get_entry(HEADER, h);
>>>> + if ( !hdr )
>>>> + return -ENODATA;
>>>> +
>>>> + rc = arch_hvm_check(d, hdr);
>>>> + if ( rc )
>>>> + return rc;
>>>> +
>>>> + for ( ; ; )
>>>> + {
>>>> + const struct hvm_save_descriptor *desc;
>>>> + hvm_check_handler handler;
>>>> +
>>>> + if ( h->size - h->cur < sizeof(*desc) )
>>>> + {
>>>> + /* Run out of data */
>>>> + printk(XENLOG_G_ERR
>>>> + "HVM restore %pd: save did not end with a null
>>>> entry\n",
>>>> + d);
>>>> + return -ENODATA;
>>>> + }
>>>> +
>>>> + /* Read the typecode of the next entry and check for the
>>>> end-marker. */
>>>> + desc = (const void *)&h->data[h->cur];
>>>> + if ( desc->typecode == HVM_SAVE_CODE(END) )
>>>> + {
>>>> + /* Reset cursor for hvm_load(). */
>>>> + h->cur = 0;
>>>> + return 0;
>>>> + }
>>>> +
>>>> + /* Find the handler for this entry. */
>>>> + if ( desc->typecode >= ARRAY_SIZE(hvm_sr_handlers) ||
>>>> + !hvm_sr_handlers[desc->typecode].name ||
>>>> + !hvm_sr_handlers[desc->typecode].load )
>>>> + {
>>>> + printk(XENLOG_G_ERR "HVM restore %pd: unknown entry typecode
>>>> %u\n",
>>>> + d, desc->typecode);
>>>> + return -EINVAL;
>>>> + }
>>>> +
>>>> + /* Check the entry. */
>>>> + handler = hvm_sr_handlers[desc->typecode].check;
>>>> + if ( !handler )
>>>> + {
>>>> + if ( desc->length > h->size - h->cur - sizeof(*desc) )
>>>> + return -ENODATA;
>>>> + h->cur += sizeof(*desc) + desc->length;
>>>> + }
>>>> + else if ( (rc = handler(d, h)) )
>>>> + {
>>>> + printk(XENLOG_G_ERR
>>>> + "HVM restore %pd: failed to check %s:%u rc %d\n",
>>>> + d, hvm_sr_handlers[desc->typecode].name,
>>>> desc->instance, rc);
>>>> + return rc;
>>>> + }
>>>> +
>>>> + process_pending_softirqs();
>>>
>>> Looking at this, won't it be better to call the check() hooks inside
>>> the hvm_load() function instead of duplicating the loop?
>>>
>>> I realize that you only perform the checks when the state is loaded
>>> from a domctl, but still seems quite a lot of code duplication for
>>> little benefit.
>>>
>>> hvm_load() could gain an extra parameter to select whether the input
>>> must be checked or not, and that would avoid having to iterate twice
>>> over the context.
>>
>> Well, see above.
>>
>>>> + }
>>>> +
>>>> + /* Not reached */
>>>
>>> ASSERT_UNREACHABLE() maybe?
>>
>> Hmm, I'd find it kind of odd to have such here. While hvm_load() doesn't
>> have such either, perhaps that's not a meaningful reference. Adding this
>> would make me fear introducing a Misra violation (adding dead code).
>
> But isn't this the purpose of ASSERT_UNREACHABLE() exactly? IOW:
> Misra will need an exception for all usage of ASSERT_UNREACHABLE()
> already.
>
> I think ASSERT_UNREACHABLE() is much better than a Not reached
> comment: conveys the same information to readers of the code and has
> a run-time consequence on debug builds.
I see a difference between uses on paths were we assert that a certain
state cannot be reached (if all our logic is right) vs a case like the
one here where the compiler (or another tool) can actually prove that
the loop can't be exited the "normal" way.
Jan
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |