aboutsummaryrefslogtreecommitdiffstats
path: root/main/xen/xsa255-4.6-2.patch
blob: 5fdfd1296647d9a86a8c1ee5c56d15b459997b56 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
From: Jan Beulich <jbeulich@suse.com>
Subject: gnttab: don't blindly free status pages upon version change

There may still be active mappings, which would trigger the respective
BUG_ON(). Split the loop into one dealing with the page attributes and
the second (when the first fully passed) freeing the pages. Return an
error if any pages still have pending references.

This is part of XSA-255.

Signed-off-by: Jan Beulich <jbeulich@suse.com>
Reviewed-by: Stefano Stabellini <sstabellini@kernel.org>
Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>

--- a/xen/arch/arm/mm.c
+++ b/xen/arch/arm/mm.c
@@ -1089,12 +1089,23 @@ int xenmem_add_to_physmap_one(
                 mfn = INVALID_MFN;
         }
         
+        if ( mfn != INVALID_MFN &&
+             gfn_x(gnttab_get_frame_gfn(d, status, idx)) != INVALID_GFN )
+        {
+            rc = guest_physmap_remove_page(d,
+                                           gfn_x(gnttab_get_frame_gfn(d, status,
+                                                                      idx)),
+                                           mfn, 0);
+            if ( rc )
+            {
+                write_unlock(&d->grant_table->lock);
+                return rc;
+            }
+        }
+
         if ( mfn != INVALID_MFN )
         {
-            if ( status )
-                d->arch.grant_status_gfn[idx] = _gfn(gpfn);
-            else
-                d->arch.grant_shared_gfn[idx] = _gfn(gpfn);
+            gnttab_set_frame_gfn(d, status, idx, _gfn(gpfn));
 
             t = p2m_ram_rw;
         }
--- a/xen/common/grant_table.c
+++ b/xen/common/grant_table.c
@@ -1512,23 +1512,74 @@ status_alloc_failed:
     return -ENOMEM;
 }
 
-static void
+static int
 gnttab_unpopulate_status_frames(struct domain *d, struct grant_table *gt)
 {
-    int i;
+    unsigned int i;
 
     for ( i = 0; i < nr_status_frames(gt); i++ )
     {
         struct page_info *pg = virt_to_page(gt->status[i]);
+        gfn_t gfn = gnttab_get_frame_gfn(d, 1, i);
+
+        /*
+         * For translated domains, recovering from failure after partial
+         * changes were made is more complicated than it seems worth
+         * implementing at this time. Hence respective error paths below
+         * crash the domain in such a case.
+         */
+        if ( paging_mode_translate(d) )
+        {
+            int rc = gfn_x(gfn) == INVALID_GFN
+                     ? 0
+                     : guest_physmap_remove_page(d, gfn_x(gfn),
+                                                 page_to_mfn(pg), 0);
+
+            if ( rc )
+            {
+                gprintk(XENLOG_ERR,
+                        "Could not remove status frame %u (GFN %#lx) from P2M\n",
+                        i, gfn_x(gfn));
+                domain_crash(d);
+                return rc;
+            }
+            gnttab_set_frame_gfn(d, 1, i, _gfn(INVALID_GFN));
+        }
 
         BUG_ON(page_get_owner(pg) != d);
         if ( test_and_clear_bit(_PGC_allocated, &pg->count_info) )
             put_page(pg);
-        BUG_ON(pg->count_info & ~PGC_xen_heap);
+
+        if ( pg->count_info & ~PGC_xen_heap )
+        {
+            if ( paging_mode_translate(d) )
+            {
+                gprintk(XENLOG_ERR,
+                        "Wrong page state %#lx of status frame %u (GFN %#lx)\n",
+                        pg->count_info, i, gfn_x(gfn));
+                domain_crash(d);
+            }
+            else
+            {
+                if ( get_page(pg, d) )
+                    set_bit(_PGC_allocated, &pg->count_info);
+                while ( i-- )
+                    gnttab_create_status_page(d, gt, i);
+            }
+            return -EBUSY;
+        }
+
+        page_set_owner(pg, NULL);
+    }
+
+    for ( i = 0; i < nr_status_frames(gt); i++ )
+    {
         free_xenheap_page(gt->status[i]);
         gt->status[i] = NULL;
     }
     gt->nr_status_frames = 0;
+
+    return 0;
 }
 
 /*
@@ -2768,8 +2819,9 @@ gnttab_set_version(XEN_GUEST_HANDLE_PARA
         break;
     }
 
-    if ( op.version < 2 && gt->gt_version == 2 )
-        gnttab_unpopulate_status_frames(currd, gt);
+    if ( op.version < 2 && gt->gt_version == 2 &&
+         (res = gnttab_unpopulate_status_frames(currd, gt)) != 0 )
+        goto out_unlock;
 
     /* Make sure there's no crud left over from the old version. */
     for ( i = 0; i < nr_grant_frames(gt); i++ )
--- a/xen/include/asm-arm/grant_table.h
+++ b/xen/include/asm-arm/grant_table.h
@@ -20,6 +20,17 @@ static inline int replace_grant_supporte
     return 1;
 }
 
+#define gnttab_set_frame_gfn(d, st, idx, gfn)                            \
+    do {                                                                 \
+        ((st) ? (d)->arch.grant_status_gfn                               \
+              : (d)->arch.grant_shared_gfn)[idx] = (gfn);                \
+    } while ( 0 )
+
+#define gnttab_get_frame_gfn(d, st, idx) ({                              \
+   _gfn((st) ? gnttab_status_gmfn(d, (d)->grant_table, idx)              \
+             : gnttab_shared_gmfn(d, (d)->grant_table, idx));            \
+})
+
 #define gnttab_create_shared_page(d, t, i)                               \
     do {                                                                 \
         share_xen_page_with_guest(                                       \
--- a/xen/include/asm-x86/grant_table.h
+++ b/xen/include/asm-x86/grant_table.h
@@ -18,6 +18,14 @@ int create_grant_host_mapping(uint64_t a
 int replace_grant_host_mapping(
     uint64_t addr, unsigned long frame, uint64_t new_addr, unsigned int flags);
 
+#define gnttab_set_frame_gfn(d, st, idx, gfn) do {} while ( 0 )
+#define gnttab_get_frame_gfn(d, st, idx) ({                              \
+    unsigned long mfn_ = (st) ? gnttab_status_mfn((d)->grant_table, idx) \
+                              : gnttab_shared_mfn((d)->grant_table, idx); \
+    unsigned long gpfn_ = get_gpfn_from_mfn(mfn_);                       \
+    _gfn(VALID_M2P(gpfn_) ? gpfn_ : INVALID_GFN);                        \
+})
+
 #define gnttab_create_shared_page(d, t, i)                               \
     do {                                                                 \
         share_xen_page_with_guest(                                       \
@@ -33,11 +41,11 @@ int replace_grant_host_mapping(
     } while ( 0 )
 
 
-#define gnttab_shared_mfn(d, t, i)                      \
+#define gnttab_shared_mfn(t, i)                         \
     ((virt_to_maddr((t)->shared_raw[i]) >> PAGE_SHIFT))
 
 #define gnttab_shared_gmfn(d, t, i)                     \
-    (mfn_to_gmfn(d, gnttab_shared_mfn(d, t, i)))
+    (mfn_to_gmfn(d, gnttab_shared_mfn(t, i)))
 
 
 #define gnttab_status_mfn(t, i)                         \