x86/mm: relinquish_memory: Grab an extra type ref when setting PGT_partial
authorGeorge Dunlap <george.dunlap@citrix.com>
Mon, 28 Oct 2019 14:33:51 +0000 (14:33 +0000)
committerJan Beulich <jbeulich@suse.com>
Wed, 11 Dec 2019 13:55:08 +0000 (14:55 +0100)
commit66bdc16aeed8ddb2ae724adc5ea6bde0dea78c3d
treec4fa64eaa1de851622d59ef71e6924f49526e02b
parent4e70f4476c0c543559f971faecdd5f1300cddb0a
x86/mm: relinquish_memory: Grab an extra type ref when setting PGT_partial

The PGT_partial bit in page->type_info holds both a type count and a
general ref count.  During domain tear-down, when free_page_type()
returns -ERESTART, relinquish_memory() correctly handles the general
ref count, but fails to grab an extra type count when setting
PGT_partial.  When this bit is eventually cleared, type_count underflows
and triggers the following BUG in page_alloc.c:free_domheap_pages():

    BUG_ON((pg[i].u.inuse.type_info & PGT_count_mask) != 0);

As far as we can tell, this page underflow cannot be exploited any any
other way: The page can't be used as a pagetable by the dying domain
because it's dying; it can't be used as a pagetable by any other
domain since it belongs to the dying domain; and ownership can't
transfer to any other domain without hitting the BUG_ON() in
free_domheap_pages().

(steal_page() won't work on a page in this state, since it requires
PGC_allocated to be set, and PGC_allocated will already have been
cleared.)

Fix this by grabbing an extra type ref if setting PGT_partial in
relinquish_memory.

This is part of XSA-310.

Signed-off-by: George Dunlap <george.dunlap@citrix.com>
Acked-by: Jan Beulich <jbeulich@suse.com>
xen/arch/x86/domain.c