diff options
author | David Vrabel <david.vrabel@citrix.com> | 2013-09-13 10:13:30 -0400 |
---|---|---|
committer | Konrad Rzeszutek Wilk <konrad.wilk@oracle.com> | 2013-09-25 09:00:03 -0400 |
commit | 0160676bba69523e8b0ac83f306cce7d342ed7c8 (patch) | |
tree | 05c8d811cc464de81b50e146e81453a9c61c434f /arch | |
parent | 24f69373e212d4356268fbb1d01c5735423c300d (diff) |
xen/p2m: check MFN is in range before using the m2p table
On hosts with more than 168 GB of memory, a 32-bit guest may attempt
to grant map an MFN that is error cannot lookup in its mapping of the
m2p table. There is an m2p lookup as part of m2p_add_override() and
m2p_remove_override(). The lookup falls off the end of the mapped
portion of the m2p and (because the mapping is at the highest virtual
address) wraps around and the lookup causes a fault on what appears to
be a user space address.
do_page_fault() (thinking it's a fault to a userspace address), tries
to lock mm->mmap_sem. If the gntdev device is used for the grant map,
m2p_add_override() is called from from gnttab_mmap() with mm->mmap_sem
already locked. do_page_fault() then deadlocks.
The deadlock would most commonly occur when a 64-bit guest is started
and xenconsoled attempts to grant map its console ring.
Introduce mfn_to_pfn_no_overrides() which checks the MFN is within the
mapped portion of the m2p table before accessing the table and use
this in m2p_add_override(), m2p_remove_override(), and mfn_to_pfn()
(which already had the correct range check).
All faults caused by accessing the non-existant parts of the m2p are
thus within the kernel address space and exception_fixup() is called
without trying to lock mm->mmap_sem.
This means that for MFNs that are outside the mapped range of the m2p
then mfn_to_pfn() will always look in the m2p overrides. This is
correct because it must be a foreign MFN (and the PFN in the m2p in
this case is only relevant for the other domain).
Signed-off-by: David Vrabel <david.vrabel@citrix.com>
Cc: Stefano Stabellini <stefano.stabellini@citrix.com>
Cc: Jan Beulich <JBeulich@suse.com>
--
v3: check for auto_translated_physmap in mfn_to_pfn_no_overrides()
v2: in mfn_to_pfn() look in m2p_overrides if the MFN is out of
range as it's probably foreign.
Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
Acked-by: Stefano Stabellini <stefano.stabellini@eu.citrix.com>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/x86/include/asm/xen/page.h | 31 | ||||
-rw-r--r-- | arch/x86/xen/p2m.c | 10 |
2 files changed, 24 insertions, 17 deletions
diff --git a/arch/x86/include/asm/xen/page.h b/arch/x86/include/asm/xen/page.h index 6aef9fbc09b7..b913915e8e63 100644 --- a/arch/x86/include/asm/xen/page.h +++ b/arch/x86/include/asm/xen/page.h | |||
@@ -79,30 +79,38 @@ static inline int phys_to_machine_mapping_valid(unsigned long pfn) | |||
79 | return get_phys_to_machine(pfn) != INVALID_P2M_ENTRY; | 79 | return get_phys_to_machine(pfn) != INVALID_P2M_ENTRY; |
80 | } | 80 | } |
81 | 81 | ||
82 | static inline unsigned long mfn_to_pfn(unsigned long mfn) | 82 | static inline unsigned long mfn_to_pfn_no_overrides(unsigned long mfn) |
83 | { | 83 | { |
84 | unsigned long pfn; | 84 | unsigned long pfn; |
85 | int ret = 0; | 85 | int ret; |
86 | 86 | ||
87 | if (xen_feature(XENFEAT_auto_translated_physmap)) | 87 | if (xen_feature(XENFEAT_auto_translated_physmap)) |
88 | return mfn; | 88 | return mfn; |
89 | 89 | ||
90 | if (unlikely(mfn >= machine_to_phys_nr)) { | 90 | if (unlikely(mfn >= machine_to_phys_nr)) |
91 | pfn = ~0; | 91 | return ~0; |
92 | goto try_override; | 92 | |
93 | } | ||
94 | pfn = 0; | ||
95 | /* | 93 | /* |
96 | * The array access can fail (e.g., device space beyond end of RAM). | 94 | * The array access can fail (e.g., device space beyond end of RAM). |
97 | * In such cases it doesn't matter what we return (we return garbage), | 95 | * In such cases it doesn't matter what we return (we return garbage), |
98 | * but we must handle the fault without crashing! | 96 | * but we must handle the fault without crashing! |
99 | */ | 97 | */ |
100 | ret = __get_user(pfn, &machine_to_phys_mapping[mfn]); | 98 | ret = __get_user(pfn, &machine_to_phys_mapping[mfn]); |
101 | try_override: | ||
102 | /* ret might be < 0 if there are no entries in the m2p for mfn */ | ||
103 | if (ret < 0) | 99 | if (ret < 0) |
104 | pfn = ~0; | 100 | return ~0; |
105 | else if (get_phys_to_machine(pfn) != mfn) | 101 | |
102 | return pfn; | ||
103 | } | ||
104 | |||
105 | static inline unsigned long mfn_to_pfn(unsigned long mfn) | ||
106 | { | ||
107 | unsigned long pfn; | ||
108 | |||
109 | if (xen_feature(XENFEAT_auto_translated_physmap)) | ||
110 | return mfn; | ||
111 | |||
112 | pfn = mfn_to_pfn_no_overrides(mfn); | ||
113 | if (get_phys_to_machine(pfn) != mfn) { | ||
106 | /* | 114 | /* |
107 | * If this appears to be a foreign mfn (because the pfn | 115 | * If this appears to be a foreign mfn (because the pfn |
108 | * doesn't map back to the mfn), then check the local override | 116 | * doesn't map back to the mfn), then check the local override |
@@ -111,6 +119,7 @@ try_override: | |||
111 | * m2p_find_override_pfn returns ~0 if it doesn't find anything. | 119 | * m2p_find_override_pfn returns ~0 if it doesn't find anything. |
112 | */ | 120 | */ |
113 | pfn = m2p_find_override_pfn(mfn, ~0); | 121 | pfn = m2p_find_override_pfn(mfn, ~0); |
122 | } | ||
114 | 123 | ||
115 | /* | 124 | /* |
116 | * pfn is ~0 if there are no entries in the m2p for mfn or if the | 125 | * pfn is ~0 if there are no entries in the m2p for mfn or if the |
diff --git a/arch/x86/xen/p2m.c b/arch/x86/xen/p2m.c index 8b901e8d782d..a61c7d5811be 100644 --- a/arch/x86/xen/p2m.c +++ b/arch/x86/xen/p2m.c | |||
@@ -879,7 +879,6 @@ int m2p_add_override(unsigned long mfn, struct page *page, | |||
879 | unsigned long uninitialized_var(address); | 879 | unsigned long uninitialized_var(address); |
880 | unsigned level; | 880 | unsigned level; |
881 | pte_t *ptep = NULL; | 881 | pte_t *ptep = NULL; |
882 | int ret = 0; | ||
883 | 882 | ||
884 | pfn = page_to_pfn(page); | 883 | pfn = page_to_pfn(page); |
885 | if (!PageHighMem(page)) { | 884 | if (!PageHighMem(page)) { |
@@ -926,8 +925,8 @@ int m2p_add_override(unsigned long mfn, struct page *page, | |||
926 | * frontend pages while they are being shared with the backend, | 925 | * frontend pages while they are being shared with the backend, |
927 | * because mfn_to_pfn (that ends up being called by GUPF) will | 926 | * because mfn_to_pfn (that ends up being called by GUPF) will |
928 | * return the backend pfn rather than the frontend pfn. */ | 927 | * return the backend pfn rather than the frontend pfn. */ |
929 | ret = __get_user(pfn, &machine_to_phys_mapping[mfn]); | 928 | pfn = mfn_to_pfn_no_overrides(mfn); |
930 | if (ret == 0 && get_phys_to_machine(pfn) == mfn) | 929 | if (get_phys_to_machine(pfn) == mfn) |
931 | set_phys_to_machine(pfn, FOREIGN_FRAME(mfn)); | 930 | set_phys_to_machine(pfn, FOREIGN_FRAME(mfn)); |
932 | 931 | ||
933 | return 0; | 932 | return 0; |
@@ -942,7 +941,6 @@ int m2p_remove_override(struct page *page, | |||
942 | unsigned long uninitialized_var(address); | 941 | unsigned long uninitialized_var(address); |
943 | unsigned level; | 942 | unsigned level; |
944 | pte_t *ptep = NULL; | 943 | pte_t *ptep = NULL; |
945 | int ret = 0; | ||
946 | 944 | ||
947 | pfn = page_to_pfn(page); | 945 | pfn = page_to_pfn(page); |
948 | mfn = get_phys_to_machine(pfn); | 946 | mfn = get_phys_to_machine(pfn); |
@@ -1029,8 +1027,8 @@ int m2p_remove_override(struct page *page, | |||
1029 | * the original pfn causes mfn_to_pfn(mfn) to return the frontend | 1027 | * the original pfn causes mfn_to_pfn(mfn) to return the frontend |
1030 | * pfn again. */ | 1028 | * pfn again. */ |
1031 | mfn &= ~FOREIGN_FRAME_BIT; | 1029 | mfn &= ~FOREIGN_FRAME_BIT; |
1032 | ret = __get_user(pfn, &machine_to_phys_mapping[mfn]); | 1030 | pfn = mfn_to_pfn_no_overrides(mfn); |
1033 | if (ret == 0 && get_phys_to_machine(pfn) == FOREIGN_FRAME(mfn) && | 1031 | if (get_phys_to_machine(pfn) == FOREIGN_FRAME(mfn) && |
1034 | m2p_find_override(mfn) == NULL) | 1032 | m2p_find_override(mfn) == NULL) |
1035 | set_phys_to_machine(pfn, mfn); | 1033 | set_phys_to_machine(pfn, mfn); |
1036 | 1034 | ||