mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 02:01:24 +01:00
x86: don't store possibly stale TLB flush time stamp
While the timing window is extremely narrow, it is theoretically
possible for an update to the TLB flush clock and a subsequent flush
IPI to happen between the read and write parts of the update of the
per-page stamp. Exclude this possibility by disabling interrupts
across the update, preventing the IPI to be serviced in the middle.
This is XSA-241.
Reported-by: Jann Horn <jannh@google.com>
Suggested-by: George Dunlap <george.dunlap@citrix.com>
Signed-off-by: Jan Beulich <jbeulich@suse.com>
Reviewed-by: George Dunlap <george.dunlap@citrix.com>
master commit: 23a183607a
master date: 2017-10-12 14:48:25 +0200
This commit is contained in:
1 parent
b7582acc76
commit
da4f24dcdf
6 files changed
+26
-6
No files matched your search
@@ -1,4 +1,5 @@
|
||||
#include <xen/config.h>
|
||||
#include <xen/mm.h>
|
||||
#include <asm/system.h>
|
||||
#include <asm/smp.h>
|
||||
#include <asm/cpregs.h>
|
||||
|
||||
+4
-4
@@ -2334,7 +2334,7 @@ static int _put_final_page_type(struct page_info *page, unsigned long type,
|
||||
*/
|
||||
if ( !(shadow_mode_enabled(page_get_owner(page)) &&
|
||||
(page->count_info & PGC_page_table)) )
|
||||
page->tlbflush_timestamp = tlbflush_current_time();
|
||||
page_set_tlbflush_timestamp(page);
|
||||
wmb();
|
||||
page->u.inuse.type_info--;
|
||||
}
|
||||
@@ -2344,7 +2344,7 @@ static int _put_final_page_type(struct page_info *page, unsigned long type,
|
||||
(PGT_count_mask|PGT_validated|PGT_partial)) == 1);
|
||||
if ( !(shadow_mode_enabled(page_get_owner(page)) &&
|
||||
(page->count_info & PGC_page_table)) )
|
||||
page->tlbflush_timestamp = tlbflush_current_time();
|
||||
page_set_tlbflush_timestamp(page);
|
||||
wmb();
|
||||
page->u.inuse.type_info |= PGT_validated;
|
||||
}
|
||||
@@ -2398,7 +2398,7 @@ static int _put_page_type(struct page_info *page, bool_t preemptible,
|
||||
if ( ptpg && PGT_type_equal(x, ptpg->u.inuse.type_info) )
|
||||
{
|
||||
/*
|
||||
* page_set_tlbflush_timestamp() accesses the same union
|
||||
* set_tlbflush_timestamp() accesses the same union
|
||||
* linear_pt_count lives in. Unvalidated page table pages,
|
||||
* however, should occur during domain destruction only
|
||||
* anyway. Updating of linear_pt_count luckily is not
|
||||
@@ -2419,7 +2419,7 @@ static int _put_page_type(struct page_info *page, bool_t preemptible,
|
||||
*/
|
||||
if ( !(shadow_mode_enabled(page_get_owner(page)) &&
|
||||
(page->count_info & PGC_page_table)) )
|
||||
page->tlbflush_timestamp = tlbflush_current_time();
|
||||
page_set_tlbflush_timestamp(page);
|
||||
}
|
||||
|
||||
if ( likely((y = cmpxchg(&page->u.inuse.type_info, x, nx)) == x) )
|
||||
|
||||
@@ -1583,7 +1583,7 @@ void shadow_free(struct domain *d, mfn_t smfn)
|
||||
* TLBs when we reuse the page. Because the destructors leave the
|
||||
* contents of the pages in place, we can delay TLB flushes until
|
||||
* just before the allocator hands the page out again. */
|
||||
sp->tlbflush_timestamp = tlbflush_current_time();
|
||||
page_set_tlbflush_timestamp(sp);
|
||||
perfc_decr(shadow_alloc_count);
|
||||
page_list_add_tail(sp, &d->arch.paging.shadow.freelist);
|
||||
sp = next;
|
||||
|
||||
@@ -881,7 +881,7 @@ static void free_heap_pages(
|
||||
/* If a page has no owner it will need no safety TLB flush. */
|
||||
pg[i].u.free.need_tlbflush = (page_get_owner(&pg[i]) != NULL);
|
||||
if ( pg[i].u.free.need_tlbflush )
|
||||
pg[i].tlbflush_timestamp = tlbflush_current_time();
|
||||
page_set_tlbflush_timestamp(&pg[i]);
|
||||
|
||||
/* This page is not a guest frame any more. */
|
||||
page_set_owner(&pg[i], NULL); /* set_gpfn_from_mfn snoops pg owner */
|
||||
|
||||
@@ -14,6 +14,11 @@ do { \
|
||||
|
||||
#define tlbflush_current_time() (0)
|
||||
|
||||
static inline void page_set_tlbflush_timestamp(struct page_info *page)
|
||||
{
|
||||
page->tlbflush_timestamp = tlbflush_current_time();
|
||||
}
|
||||
|
||||
#if defined(CONFIG_ARM_32)
|
||||
# include <asm/arm32/flushtlb.h>
|
||||
#elif defined(CONFIG_ARM_64)
|
||||
|
||||
@@ -24,6 +24,20 @@ DECLARE_PER_CPU(u32, tlbflush_time);
|
||||
|
||||
#define tlbflush_current_time() tlbflush_clock
|
||||
|
||||
static inline void page_set_tlbflush_timestamp(struct page_info *page)
|
||||
{
|
||||
/*
|
||||
* Prevent storing a stale time stamp, which could happen if an update
|
||||
* to tlbflush_clock plus a subsequent flush IPI happen between the
|
||||
* reading of tlbflush_clock and the writing of the struct page_info
|
||||
* field.
|
||||
*/
|
||||
ASSERT(local_irq_is_enabled());
|
||||
local_irq_disable();
|
||||
page->tlbflush_timestamp = tlbflush_current_time();
|
||||
local_irq_enable();
|
||||
}
|
||||
|
||||
/*
|
||||
* @cpu_stamp is the timestamp at last TLB flush for the CPU we are testing.
|
||||
* @lastuse_stamp is a timestamp taken when the PFN we are testing was last
|
||||
|
||||