|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [Xen-devel] [PATCH L1TF v10 7/8] common/grant_table: block speculative out-of-bound accesses
>>> On 14.03.19 at 13:50, <nmanthey@xxxxxxxxx> wrote:
> Guests can issue grant table operations and provide guest controlled
> data to them. This data is also used for memory loads. To avoid
> speculative out-of-bound accesses, we use the array_index_nospec macro
> where applicable. However, there are also memory accesses that cannot
> be protected by a single array protection, or multiple accesses in a
> row. To protect these, a nospec barrier is placed between the actual
> range check and the access via the block_speculation macro.
>
> Speculative execution is not blocked in case one of the following
> properties is true:
> - path cannot be triggered by the guest
> - path does not return to the guest
> - path does not result in an out-of-bound access
> - path cannot be executed repeatedly
> Only the combination of the above properties allows to actually leak
> continuous chunks of memory. Therefore, we only add the penalty of
> protective mechanisms in case a potential speculative out-of-bound
> access matches all the above properties.
>
> As different versions of grant tables use structures of different size,
> and the status is encoded in an array for version 2, speculative
> execution might perform out-of-bound accesses of version 2 while
> the table is actually using version 1. Hence, speculation is prevented
> when accessing new memory based on the grant table version. In cases,
> where no different memory locations are accessed on the code path that
> follow an if statement, no protection is required. No different memory
> locations are accessed in the following functionsi after a version check:
>
> * _set_status, as the header memory layout is the same
Isn't this rather by virtue of shared_entry_header() having got
hardened? I don't think the memory layout alone can serve as a
reason for there to be no issue - the position in memory matters
as well.
> * unmap_common, as potentially touched memory locations are allocated
> and initialized
I can't seem to spot any explicit version checks in unmap_common().
Do you mean unmap_common_complete()? If so I'm afraid I don't
understand what "allocated and initialized" is supposed to mean.
The version check there looks potentially problematic to me, at
least from a purely theoretical pov.
> * gnttab_grow_table, as the touched memory is the same for each
> branch after the conditionals
How that? gnttab_populate_status_frames() could be speculated
into for a v1 guest.
Next there's a version check in gnttab_setup_table(), but the function
doesn't get changed and also isn't listed here.
> * gnttab_transfer, as no memory access depends on the conditional
> * release_grant_for_copy, as no out-of-bound access depends on this
> conditional
But you add evaluate_nospec() there, and memory accesses very well
look to depend on the condition, just not inside the bodies of the if/else.
> * gnttab_set_version, as in case of a version change all the memory is
> touched in both cases
And you're sure speculation through NULL pointers is impossible? And
the offset-into-table differences between v1 and v2 don't matter?
> * gnttab_release_mappings, as this function is called only during domain
> destruction and control is not returned to the guest
> * mem_sharing_gref_to_gfn, as potential dangerous memory accesses are
> covered by the next evaluate_nospec
> * gnttab_get_status_frame, as the potential dangerous memory accesses
> are protected in gnttab_get_status_frame_mfn
But there's quite a bit of code in gnttab_get_status_frame_mfn()
before the addition you make. But I guess speculation in particular
into gnttab_grow_table() might be safe?
> @@ -963,9 +988,13 @@ map_grant_ref(
> PIN_FAIL(unlock_out, GNTST_bad_gntref, "Bad ref %#x for d%d\n",
> op->ref, rgt->domain->domain_id);
>
> - act = active_entry_acquire(rgt, op->ref);
> + /* This call ensures the above check cannot be bypassed speculatively */
> shah = shared_entry_header(rgt, op->ref);
I know I've come across this several times by now, but I'm afraid I
now get the impression that the comment kind of suggests that
the call is just for this purpose, instead of fulfilling the purpose as
a side effect. Would you mind adding "also" to this (and possible
further instances)? To avoid the line growing too long, perhaps
"call" could be dropped instead.
> @@ -2410,9 +2448,11 @@ acquire_grant_for_copy(
> PIN_FAIL(gt_unlock_out, GNTST_bad_gntref,
> "Bad grant reference %#x\n", gref);
>
> - act = active_entry_acquire(rgt, gref);
> + /* This call ensures the above check cannot be bypassed speculatively */
> shah = shared_entry_header(rgt, gref);
> - if ( rgt->gt_version == 1 )
> + act = active_entry_acquire(rgt, gref);
> +
> + if ( evaluate_nospec(rgt->gt_version == 1) )
> {
> sha2 = NULL;
> status = &shah->flags;
What about the second version check further down in this function?
> @@ -3838,6 +3886,9 @@ static int gnttab_get_status_frame_mfn(struct domain *d,
> return -EINVAL;
> }
>
> + /* Make sure idx is bounded wrt nr_status_frames */
> + block_speculation();
> +
> *mfn = _mfn(virt_to_mfn(gt->status[idx]));
Why not array_access_nospec()? And how is this different from
gnttab_get_shared_frame_mfn(), which you don't change?
Jan
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxxx
https://lists.xenproject.org/mailman/listinfo/xen-devel
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |