Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d193785a authored by Juergen Gross's avatar Juergen Gross Committed by Greg Kroah-Hartman
Browse files

xen/gntalloc: don't use gnttab_query_foreign_access()



Commit d3b6372c5881cb54925212abb62c521df8ba4809 upstream.

Using gnttab_query_foreign_access() is unsafe, as it is racy by design.

The use case in the gntalloc driver is not needed at all. While at it
replace the call of gnttab_end_foreign_access_ref() with a call of
gnttab_end_foreign_access(), which is what is really wanted there. In
case the grant wasn't used due to an allocation failure, just free the
grant via gnttab_free_grant_reference().

This is CVE-2022-23039 / part of XSA-396.

Reported-by: default avatarDemi Marie Obenour <demi@invisiblethingslab.com>
Signed-off-by: default avatarJuergen Gross <jgross@suse.com>
Reviewed-by: default avatarJan Beulich <jbeulich@suse.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 089a8e49
Loading
Loading
Loading
Loading
+7 −18
Original line number Diff line number Diff line
@@ -169,20 +169,14 @@ static int add_grefs(struct ioctl_gntalloc_alloc_gref *op,
		__del_gref(gref);
	}

	/* It's possible for the target domain to map the just-allocated grant
	 * references by blindly guessing their IDs; if this is done, then
	 * __del_gref will leave them in the queue_gref list. They need to be
	 * added to the global list so that we can free them when they are no
	 * longer referenced.
	 */
	if (unlikely(!list_empty(&queue_gref)))
		list_splice_tail(&queue_gref, &gref_list);
	mutex_unlock(&gref_mutex);
	return rc;
}

static void __del_gref(struct gntalloc_gref *gref)
{
	unsigned long addr;

	if (gref->notify.flags & UNMAP_NOTIFY_CLEAR_BYTE) {
		uint8_t *tmp = kmap(gref->page);
		tmp[gref->notify.pgoff] = 0;
@@ -196,21 +190,16 @@ static void __del_gref(struct gntalloc_gref *gref)
	gref->notify.flags = 0;

	if (gref->gref_id) {
		if (gnttab_query_foreign_access(gref->gref_id))
			return;

		if (!gnttab_end_foreign_access_ref(gref->gref_id, 0))
			return;

		if (gref->page) {
			addr = (unsigned long)page_to_virt(gref->page);
			gnttab_end_foreign_access(gref->gref_id, 0, addr);
		} else
			gnttab_free_grant_reference(gref->gref_id);
	}

	gref_size--;
	list_del(&gref->next_gref);

	if (gref->page)
		__free_page(gref->page);

	kfree(gref);
}