diff options
author | David Vrabel <david.vrabel@citrix.com> | 2013-09-13 10:13:30 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2014-02-22 15:41:25 -0500 |
commit | 3d048e581b88ddb85934615cd77690dff4878ac4 (patch) | |
tree | 363afdb1b79c5f4cc62609bec760bd1d3764b48c | |
parent | 80ead821ddf2740d83d26a8be4c377880879e108 (diff) |
xen/p2m: check MFN is in range before using the m2p table
commit 0160676bba69523e8b0ac83f306cce7d342ed7c8 upstream.
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).
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: David Vrabel <david.vrabel@citrix.com>
Cc: Stefano Stabellini <stefano.stabellini@citrix.com>
Cc: Jan Beulich <JBeulich@suse.com>
Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
Acked-by: Stefano Stabellini <stefano.stabellini@eu.citrix.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-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 95fb2aa5927e..156344448d19 100644 --- a/arch/x86/xen/p2m.c +++ b/arch/x86/xen/p2m.c | |||
@@ -878,7 +878,6 @@ int m2p_add_override(unsigned long mfn, struct page *page, | |||
878 | unsigned long uninitialized_var(address); | 878 | unsigned long uninitialized_var(address); |
879 | unsigned level; | 879 | unsigned level; |
880 | pte_t *ptep = NULL; | 880 | pte_t *ptep = NULL; |
881 | int ret = 0; | ||
882 | 881 | ||
883 | pfn = page_to_pfn(page); | 882 | pfn = page_to_pfn(page); |
884 | if (!PageHighMem(page)) { | 883 | if (!PageHighMem(page)) { |
@@ -925,8 +924,8 @@ int m2p_add_override(unsigned long mfn, struct page *page, | |||
925 | * frontend pages while they are being shared with the backend, | 924 | * frontend pages while they are being shared with the backend, |
926 | * because mfn_to_pfn (that ends up being called by GUPF) will | 925 | * because mfn_to_pfn (that ends up being called by GUPF) will |
927 | * return the backend pfn rather than the frontend pfn. */ | 926 | * return the backend pfn rather than the frontend pfn. */ |
928 | ret = __get_user(pfn, &machine_to_phys_mapping[mfn]); | 927 | pfn = mfn_to_pfn_no_overrides(mfn); |
929 | if (ret == 0 && get_phys_to_machine(pfn) == mfn) | 928 | if (get_phys_to_machine(pfn) == mfn) |
930 | set_phys_to_machine(pfn, FOREIGN_FRAME(mfn)); | 929 | set_phys_to_machine(pfn, FOREIGN_FRAME(mfn)); |
931 | 930 | ||
932 | return 0; | 931 | return 0; |
@@ -941,7 +940,6 @@ int m2p_remove_override(struct page *page, | |||
941 | unsigned long uninitialized_var(address); | 940 | unsigned long uninitialized_var(address); |
942 | unsigned level; | 941 | unsigned level; |
943 | pte_t *ptep = NULL; | 942 | pte_t *ptep = NULL; |
944 | int ret = 0; | ||
945 | 943 | ||
946 | pfn = page_to_pfn(page); | 944 | pfn = page_to_pfn(page); |
947 | mfn = get_phys_to_machine(pfn); | 945 | mfn = get_phys_to_machine(pfn); |
@@ -1019,8 +1017,8 @@ int m2p_remove_override(struct page *page, | |||
1019 | * the original pfn causes mfn_to_pfn(mfn) to return the frontend | 1017 | * the original pfn causes mfn_to_pfn(mfn) to return the frontend |
1020 | * pfn again. */ | 1018 | * pfn again. */ |
1021 | mfn &= ~FOREIGN_FRAME_BIT; | 1019 | mfn &= ~FOREIGN_FRAME_BIT; |
1022 | ret = __get_user(pfn, &machine_to_phys_mapping[mfn]); | 1020 | pfn = mfn_to_pfn_no_overrides(mfn); |
1023 | if (ret == 0 && get_phys_to_machine(pfn) == FOREIGN_FRAME(mfn) && | 1021 | if (get_phys_to_machine(pfn) == FOREIGN_FRAME(mfn) && |
1024 | m2p_find_override(mfn) == NULL) | 1022 | m2p_find_override(mfn) == NULL) |
1025 | set_phys_to_machine(pfn, mfn); | 1023 | set_phys_to_machine(pfn, mfn); |
1026 | 1024 | ||