|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [PATCH 2/8] x86/shadow: properly handle get_page() failing
On 26/07/2022 17:04, Jan Beulich wrote:
> We should not blindly (in a release build) insert the new entry in the
> hash if a reference to the guest page cannot be obtained, or else an
> excess reference would be put when removing the hash entry again. Crash
> the domain in that case instead. The sole caller doesn't further care
> about the state of the guest page: All it does is return the
> corresponding shadow page (which was obtained successfully before) to
> its caller.
>
> To compensate we further need to adjust hash removal: Since the shadow
> page already has had its backlink set, domain cleanup code would try to
> destroy the shadow, and hence still cause a put_page() without
> corresponding get_page(). Leverage that the failed get_page() leads to
> no hash insertion, making shadow_hash_delete() no longer assume it will
> find the requested entry. Instead return back whether the entry was
> found. This way delete_shadow_status() can avoid calling put_page() in
> the problem scenario.
>
> For the other caller of shadow_hash_delete() simply reinstate the
> otherwise dropped assertion at the call site.
>
> While touching the conditionals in {set,delete}_shadow_status() anyway,
> also switch around their two pre-existing parts, to have the cheap one
> first (frequently allowing to avoid evaluation of the expensive - due to
> evaluate_nospec() - one altogether).
>
> Signed-off-by: Jan Beulich <jbeulich@xxxxxxxx>
Reviewed-by: Andrew Cooper <andrew.cooper3@xxxxxxxxxx>, although with
some observations and a suggestion.
>
> --- a/xen/arch/x86/mm/shadow/common.c
> +++ b/xen/arch/x86/mm/shadow/common.c
> @@ -1581,7 +1581,7 @@ void shadow_hash_insert(struct domain *d
> sh_hash_audit_bucket(d, key);
> }
>
> -void shadow_hash_delete(struct domain *d, unsigned long n, unsigned int t,
> +bool shadow_hash_delete(struct domain *d, unsigned long n, unsigned int t,
> mfn_t smfn)
> /* Excise the mapping (n,t)->smfn from the hash table */
> {
> @@ -1606,10 +1606,8 @@ void shadow_hash_delete(struct domain *d
> {
> /* Need to search for the one we want */
> x = d->arch.paging.shadow.hash_table[key];
> - while ( 1 )
> + while ( x )
> {
> - ASSERT(x); /* We can't have hit the end, since our target is
> - * still in the chain somehwere... */
> if ( next_shadow(x) == sp )
> {
> x->next_shadow = sp->next_shadow;
> @@ -1617,10 +1615,14 @@ void shadow_hash_delete(struct domain *d
> }
> x = next_shadow(x);
> }
> + if ( !x )
> + return false;
> }
This entire if/else block is "delete matching element from single linked
list", opencoded in a very confusing way to follow. I can't help but
feel there's a way to simplify it. (Not in this patch, but for future
clean-up.)
> set_next_shadow(sp, NULL);
>
> sh_hash_audit_bucket(d, key);
> +
> + return true;
> }
>
> typedef int (*hash_vcpu_callback_t)(struct vcpu *v, mfn_t smfn, mfn_t
> other_mfn);
> --- a/xen/arch/x86/mm/shadow/multi.c
> +++ b/xen/arch/x86/mm/shadow/multi.c
> @@ -132,7 +132,8 @@ delete_fl1_shadow_status(struct domain *
> SHADOW_PRINTK("gfn=%"SH_PRI_gfn", type=%08x, smfn=%"PRI_mfn"\n",
> gfn_x(gfn), SH_type_fl1_shadow, mfn_x(smfn));
> ASSERT(mfn_to_page(smfn)->u.sh.head);
> - shadow_hash_delete(d, gfn_x(gfn), SH_type_fl1_shadow, smfn);
> + if ( !shadow_hash_delete(d, gfn_x(gfn), SH_type_fl1_shadow, smfn) )
> + ASSERT_UNREACHABLE();
I'd recommend crashing the domain here too. I know the we've already
got the return value we want, but this represents corruption in the
shadow pagetable metadata, and I highly doubt it is safe to let the
guest continue to execute in such circumstances.
> }
>
>
> --- a/xen/arch/x86/mm/shadow/private.h
> +++ b/xen/arch/x86/mm/shadow/private.h
> @@ -375,7 +375,7 @@ shadow_size(unsigned int shadow_type)
> mfn_t shadow_hash_lookup(struct domain *d, unsigned long n, unsigned int t);
> void shadow_hash_insert(struct domain *d,
> unsigned long n, unsigned int t, mfn_t smfn);
> -void shadow_hash_delete(struct domain *d,
> +bool shadow_hash_delete(struct domain *d,
> unsigned long n, unsigned int t, mfn_t smfn);
>
> /* shadow promotion */
> @@ -773,18 +773,19 @@ static inline void
> set_shadow_status(struct domain *d, mfn_t gmfn, u32 shadow_type, mfn_t smfn)
> /* Put a shadow into the hash table */
> {
> - int res;
> -
> SHADOW_PRINTK("d%d gmfn=%lx, type=%08x, smfn=%lx\n",
> d->domain_id, mfn_x(gmfn), shadow_type, mfn_x(smfn));
>
> ASSERT(mfn_to_page(smfn)->u.sh.head);
>
> /* 32-bit PV guests don't own their l4 pages so can't get_page them */
> - if ( !is_pv_32bit_domain(d) || shadow_type != SH_type_l4_64_shadow )
> + if ( (shadow_type != SH_type_l4_64_shadow || !is_pv_32bit_domain(d)) &&
This is the sensible way around anyway, but this is also a great example
of a predicate which really doesn't want to be eval_nospec().
We've got a growing pile of such usecases, so really do need to find a
predicate we can use which indicates "safely not speculation relevant".
~Andrew
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |