|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [PATCH v8 06/16] xen/memory: Fix mapping grant tables with XENMEM_acquire_resource
On 01/02/2021 10:10, Roger Pau Monné wrote:
> On Sat, Jan 30, 2021 at 02:58:42AM +0000, Andrew Cooper wrote:
>> A guest's default number of grant frames is 64, and XENMEM_acquire_resource
>> will reject an attempt to map more than 32 frames. This limit is caused by
>> the size of mfn_list[] on the stack.
>>
>> Fix mapping of arbitrary size requests by looping over batches of 32 in
>> acquire_resource(), and using hypercall continuations when necessary.
>>
>> To start with, break _acquire_resource() out of acquire_resource() to cope
>> with type-specific dispatching, and update the return semantics to indicate
>> the number of mfns returned. Update gnttab_acquire_resource() and x86's
>> arch_acquire_resource() to match these new semantics.
>>
>> Have do_memory_op() pass start_extent into acquire_resource() so it can pick
>> up where it left off after a continuation, and loop over batches of 32 until
>> all the work is done, or a continuation needs to occur.
>>
>> compat_memory_op() is a bit more complicated, because it also has to marshal
>> frame_list in the XLAT buffer. Have it account for continuation information
>> itself and hide details from the upper layer, so it can marshal the buffer in
>> chunks if necessary.
>>
>> With these fixes in place, it is now possible to map the whole grant table
>> for
>> a guest.
>>
>> Signed-off-by: Andrew Cooper <andrew.cooper3@xxxxxxxxxx>
> Just one comment/question regarding a continuation below.
>
> I have to admit I had a hard time reviewing this, all this compat code
> plus the continuation stuff is quite hard to follow.
>
>> ---
>> CC: George Dunlap <George.Dunlap@xxxxxxxxxxxxx>
>> CC: Ian Jackson <iwj@xxxxxxxxxxxxxx>
>> CC: Jan Beulich <JBeulich@xxxxxxxx>
>> CC: Stefano Stabellini <sstabellini@xxxxxxxxxx>
>> CC: Wei Liu <wl@xxxxxxx>
>> CC: Julien Grall <julien@xxxxxxx>
>> CC: Paul Durrant <paul@xxxxxxx>
>> CC: Michał Leszczyński <michal.leszczynski@xxxxxxx>
>> CC: Hubert Jasudowicz <hubert.jasudowicz@xxxxxxx>
>> CC: Tamas K Lengyel <tamas@xxxxxxxxxxxxx>
>>
>> v8:
>> * nat => cmp change in the start_extent check.
>> * Rebase over 'frame' and ARM/IOREQ series.
>>
>> v3:
>> * Spelling fixes
>> ---
>> xen/common/compat/memory.c | 94 +++++++++++++++++++++++++++-------
>> xen/common/grant_table.c | 3 ++
>> xen/common/memory.c | 124
>> +++++++++++++++++++++++++++++++++------------
>> 3 files changed, 169 insertions(+), 52 deletions(-)
>>
>> diff --git a/xen/common/compat/memory.c b/xen/common/compat/memory.c
>> index 834c5e19d1..4c9cd9c05a 100644
>> --- a/xen/common/compat/memory.c
>> +++ b/xen/common/compat/memory.c
>> @@ -402,23 +402,10 @@ int compat_memory_op(unsigned int cmd,
>> XEN_GUEST_HANDLE_PARAM(void) compat)
>> case XENMEM_acquire_resource:
>> {
>> xen_pfn_t *xen_frame_list = NULL;
>> - unsigned int max_nr_frames;
>>
>> if ( copy_from_guest(&cmp.mar, compat, 1) )
>> return -EFAULT;
>>
>> - /*
>> - * The number of frames handled is currently limited to a
>> - * small number by the underlying implementation, so the
>> - * scratch space should be sufficient for bouncing the
>> - * frame addresses.
>> - */
>> - max_nr_frames = (COMPAT_ARG_XLAT_SIZE - sizeof(*nat.mar)) /
>> - sizeof(*xen_frame_list);
>> -
>> - if ( cmp.mar.nr_frames > max_nr_frames )
>> - return -E2BIG;
>> -
>> /* Marshal the frame list in the remainder of the xlat space. */
>> if ( !compat_handle_is_null(cmp.mar.frame_list) )
>> xen_frame_list = (xen_pfn_t *)(nat.mar + 1);
>> @@ -432,6 +419,28 @@ int compat_memory_op(unsigned int cmd,
>> XEN_GUEST_HANDLE_PARAM(void) compat)
>>
>> if ( xen_frame_list && cmp.mar.nr_frames )
>> {
>> + unsigned int xlat_max_frames =
> Could be made const static I think?
It is a compile time constant, but the compiler can already figure that
out. static is definitely out of the question.
>
>> + (COMPAT_ARG_XLAT_SIZE - sizeof(*nat.mar)) /
>> + sizeof(*xen_frame_list);
>> +
>> + if ( start_extent >= cmp.mar.nr_frames )
>> + return -EINVAL;
>> +
>> + /*
>> + * Adjust nat to account for work done on previous
>> + * continuations, leaving cmp pristine. Hide the
>> continaution
>> + * from the native code to prevent double accounting.
>> + */
>> + nat.mar->nr_frames -= start_extent;
>> + nat.mar->frame += start_extent;
>> + cmd &= MEMOP_CMD_MASK;
>> +
>> + /*
>> + * If there are two many frames to fit within the xlat
>> buffer,
>> + * we'll need to loop to marshal them all.
>> + */
>> + nat.mar->nr_frames = min(nat.mar->nr_frames,
>> xlat_max_frames);
>> +
>> /*
>> * frame_list is an input for translated guests, and an
>> output
>> * for untranslated guests. Only copy in for translated
>> guests.
>> @@ -444,14 +453,14 @@ int compat_memory_op(unsigned int cmd,
>> XEN_GUEST_HANDLE_PARAM(void) compat)
>> cmp.mar.nr_frames) ||
>> __copy_from_compat_offset(
>> compat_frame_list, cmp.mar.frame_list,
>> - 0, cmp.mar.nr_frames) )
>> + start_extent, nat.mar->nr_frames) )
>> return -EFAULT;
>>
>> /*
>> * Iterate backwards over compat_frame_list[] expanding
>> * compat_pfn_t to xen_pfn_t in place.
>> */
>> - for ( int x = cmp.mar.nr_frames - 1; x >= 0; --x )
>> + for ( int x = nat.mar->nr_frames - 1; x >= 0; --x )
>> xen_frame_list[x] = compat_frame_list[x];
> Unrelated question, but I don't really see the point of iterating
> backwards, wouldn't it be easy to use use the existing 'i' loop
> counter and for a for ( i = 0; i < nat.mar->nr_frames; i++ )?
>
> (Not that you need to fix it here, just curious about why we use that
> construct instead).
Iterating backwards is totally critical.
xen_frame_list and compat_frame_list are the same numerical pointer.
We've just filled it 50% full with compat_pfn_t's, and need to turn
these into xen_pfn_t's which are double the size.
Iterating forwards would clobber every entry but the first.
>
>> }
>> }
>> @@ -600,9 +609,11 @@ int compat_memory_op(unsigned int cmd,
>> XEN_GUEST_HANDLE_PARAM(void) compat)
>> case XENMEM_acquire_resource:
>> {
>> DEFINE_XEN_GUEST_HANDLE(compat_mem_acquire_resource_t);
>> + unsigned int done;
>>
>> if ( compat_handle_is_null(cmp.mar.frame_list) )
>> {
>> + ASSERT(split == 0 && rc == 0);
>> if ( __copy_field_to_guest(
>> guest_handle_cast(compat,
>> compat_mem_acquire_resource_t),
>> @@ -611,6 +622,21 @@ int compat_memory_op(unsigned int cmd,
>> XEN_GUEST_HANDLE_PARAM(void) compat)
>> break;
>> }
>>
>> + if ( split < 0 )
>> + {
>> + /* Continuation occurred. */
>> + ASSERT(rc != XENMEM_acquire_resource);
>> + done = cmd >> MEMOP_EXTENT_SHIFT;
>> + }
>> + else
>> + {
>> + /* No continuation. */
>> + ASSERT(rc == 0);
>> + done = nat.mar->nr_frames;
>> + }
>> +
>> + ASSERT(done <= nat.mar->nr_frames);
>> +
>> /*
>> * frame_list is an input for translated guests, and an output
>> for
>> * untranslated guests. Only copy out for untranslated guests.
>> @@ -626,7 +652,7 @@ int compat_memory_op(unsigned int cmd,
>> XEN_GUEST_HANDLE_PARAM(void) compat)
>> */
>> BUILD_BUG_ON(sizeof(compat_pfn_t) > sizeof(xen_pfn_t));
>>
>> - for ( i = 0; i < cmp.mar.nr_frames; i++ )
>> + for ( i = 0; i < done; i++ )
>> {
>> compat_pfn_t frame = xen_frame_list[i];
>>
>> @@ -636,15 +662,45 @@ int compat_memory_op(unsigned int cmd,
>> XEN_GUEST_HANDLE_PARAM(void) compat)
>> compat_frame_list[i] = frame;
>> }
>>
>> - if ( __copy_to_compat_offset(cmp.mar.frame_list, 0,
>> + if ( __copy_to_compat_offset(cmp.mar.frame_list,
>> start_extent,
>> compat_frame_list,
>> - cmp.mar.nr_frames) )
>> + done) )
>> return -EFAULT;
> Is it fine to return with a possibly pending continuation already
> encoded here?
>
> Other places seem to crash the domain when that happens.
Hmm. This is all a total mess. (Elsewhere the error handling is also
broken - a caller who receives an error can't figure out how to recover)
But yes - I think you're right - the only thing we can do here is `goto
crash;` and woe betide any 32bit kernel which passes a pointer to a
read-only buffer.
~Andrew
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |