summaryrefslogtreecommitdiffstats
path: root/system/xen/xsa/xsa221.patch
blob: c7fec966683ff3c356c3affb69b07d9b11d42336 (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
188
189
190
191
192
193
194
From: Jan Beulich <jbeulich@suse.com>
Subject: evtchn: avoid NULL derefs

Commit fbbd5009e6 ("evtchn: refactor low-level event channel port ops")
added a de-reference of the struct evtchn pointer for a port without
first making sure the bucket pointer is non-NULL. This de-reference is
actually entirely unnecessary, as all relevant callers (beyond the
problematic do_poll()) already hold the port number in their hands, and
the actual leaf functions need nothing else.

For FIFO event channels there's a second problem in that the ordering
of reads and updates to ->num_evtchns and ->event_array[] was so far
undefined (the read side isn't always holding the domain's event lock).
Add respective barriers.

This is XSA-221.

Reported-by: Ankur Arora <ankur.a.arora@oracle.com>
Signed-off-by: Jan Beulich <jbeulich@suse.com>

--- a/xen/arch/x86/irq.c
+++ b/xen/arch/x86/irq.c
@@ -1486,7 +1486,7 @@ int pirq_guest_unmask(struct domain *d)
         {
             pirq = pirqs[i]->pirq;
             if ( pirqs[i]->masked &&
-                 !evtchn_port_is_masked(d, evtchn_from_port(d, pirqs[i]->evtchn)) )
+                 !evtchn_port_is_masked(d, pirqs[i]->evtchn) )
                 pirq_guest_eoi(pirqs[i]);
         }
     } while ( ++pirq < d->nr_pirqs && n == ARRAY_SIZE(pirqs) );
@@ -2244,7 +2244,6 @@ static void dump_irqs(unsigned char key)
     int i, irq, pirq;
     struct irq_desc *desc;
     irq_guest_action_t *action;
-    struct evtchn *evtchn;
     struct domain *d;
     const struct pirq *info;
     unsigned long flags;
@@ -2287,11 +2286,10 @@ static void dump_irqs(unsigned char key)
                 d = action->guest[i];
                 pirq = domain_irq_to_pirq(d, irq);
                 info = pirq_info(d, pirq);
-                evtchn = evtchn_from_port(d, info->evtchn);
                 printk("%u:%3d(%c%c%c)",
                        d->domain_id, pirq,
-                       (evtchn_port_is_pending(d, evtchn) ? 'P' : '-'),
-                       (evtchn_port_is_masked(d, evtchn) ? 'M' : '-'),
+                       evtchn_port_is_pending(d, info->evtchn) ? 'P' : '-',
+                       evtchn_port_is_masked(d, info->evtchn) ? 'M' : '-',
                        (info->masked ? 'M' : '-'));
                 if ( i != action->nr_guests )
                     printk(",");
--- a/xen/common/event_2l.c
+++ b/xen/common/event_2l.c
@@ -61,16 +61,20 @@ static void evtchn_2l_unmask(struct doma
     }
 }
 
-static bool_t evtchn_2l_is_pending(struct domain *d,
-                                   const struct evtchn *evtchn)
+static bool_t evtchn_2l_is_pending(struct domain *d, evtchn_port_t port)
 {
-    return test_bit(evtchn->port, &shared_info(d, evtchn_pending));
+    unsigned int max_ports = BITS_PER_EVTCHN_WORD(d) * BITS_PER_EVTCHN_WORD(d);
+
+    ASSERT(port < max_ports);
+    return port < max_ports && test_bit(port, &shared_info(d, evtchn_pending));
 }
 
-static bool_t evtchn_2l_is_masked(struct domain *d,
-                                  const struct evtchn *evtchn)
+static bool_t evtchn_2l_is_masked(struct domain *d, evtchn_port_t port)
 {
-    return test_bit(evtchn->port, &shared_info(d, evtchn_mask));
+    unsigned int max_ports = BITS_PER_EVTCHN_WORD(d) * BITS_PER_EVTCHN_WORD(d);
+
+    ASSERT(port < max_ports);
+    return port >= max_ports || test_bit(port, &shared_info(d, evtchn_mask));
 }
 
 static void evtchn_2l_print_state(struct domain *d,
--- a/xen/common/event_channel.c
+++ b/xen/common/event_channel.c
@@ -1380,8 +1380,8 @@ static void domain_dump_evtchn_info(stru
 
         printk("    %4u [%d/%d/",
                port,
-               !!evtchn_port_is_pending(d, chn),
-               !!evtchn_port_is_masked(d, chn));
+               evtchn_port_is_pending(d, port),
+               evtchn_port_is_masked(d, port));
         evtchn_port_print_state(d, chn);
         printk("]: s=%d n=%d x=%d",
                chn->state, chn->notify_vcpu_id, chn->xen_consumer);
--- a/xen/common/event_fifo.c
+++ b/xen/common/event_fifo.c
@@ -27,6 +27,12 @@ static inline event_word_t *evtchn_fifo_
     if ( unlikely(port >= d->evtchn_fifo->num_evtchns) )
         return NULL;
 
+    /*
+     * Callers aren't required to hold d->event_lock, so we need to synchronize
+     * with add_page_to_event_array().
+     */
+    smp_rmb();
+
     p = port / EVTCHN_FIFO_EVENT_WORDS_PER_PAGE;
     w = port % EVTCHN_FIFO_EVENT_WORDS_PER_PAGE;
 
@@ -287,24 +293,22 @@ static void evtchn_fifo_unmask(struct do
         evtchn_fifo_set_pending(v, evtchn);
 }
 
-static bool_t evtchn_fifo_is_pending(struct domain *d,
-                                     const struct evtchn *evtchn)
+static bool_t evtchn_fifo_is_pending(struct domain *d, evtchn_port_t port)
 {
     event_word_t *word;
 
-    word = evtchn_fifo_word_from_port(d, evtchn->port);
+    word = evtchn_fifo_word_from_port(d, port);
     if ( unlikely(!word) )
         return 0;
 
     return test_bit(EVTCHN_FIFO_PENDING, word);
 }
 
-static bool_t evtchn_fifo_is_masked(struct domain *d,
-                                    const struct evtchn *evtchn)
+static bool_t evtchn_fifo_is_masked(struct domain *d, evtchn_port_t port)
 {
     event_word_t *word;
 
-    word = evtchn_fifo_word_from_port(d, evtchn->port);
+    word = evtchn_fifo_word_from_port(d, port);
     if ( unlikely(!word) )
         return 1;
 
@@ -593,6 +597,10 @@ static int add_page_to_event_array(struc
         return rc;
 
     d->evtchn_fifo->event_array[slot] = virt;
+
+    /* Synchronize with evtchn_fifo_word_from_port(). */
+    smp_wmb();
+
     d->evtchn_fifo->num_evtchns += EVTCHN_FIFO_EVENT_WORDS_PER_PAGE;
 
     /*
--- a/xen/common/schedule.c
+++ b/xen/common/schedule.c
@@ -965,7 +965,7 @@ static long do_poll(struct sched_poll *s
             goto out;
 
         rc = 0;
-        if ( evtchn_port_is_pending(d, evtchn_from_port(d, port)) )
+        if ( evtchn_port_is_pending(d, port) )
             goto out;
     }
 
--- a/xen/include/xen/event.h
+++ b/xen/include/xen/event.h
@@ -137,8 +137,8 @@ struct evtchn_port_ops {
     void (*set_pending)(struct vcpu *v, struct evtchn *evtchn);
     void (*clear_pending)(struct domain *d, struct evtchn *evtchn);
     void (*unmask)(struct domain *d, struct evtchn *evtchn);
-    bool_t (*is_pending)(struct domain *d, const struct evtchn *evtchn);
-    bool_t (*is_masked)(struct domain *d, const struct evtchn *evtchn);
+    bool_t (*is_pending)(struct domain *d, evtchn_port_t port);
+    bool_t (*is_masked)(struct domain *d, evtchn_port_t port);
     /*
      * Is the port unavailable because it's still being cleaned up
      * after being closed?
@@ -175,15 +175,15 @@ static inline void evtchn_port_unmask(st
 }
 
 static inline bool_t evtchn_port_is_pending(struct domain *d,
-                                            const struct evtchn *evtchn)
+                                            evtchn_port_t port)
 {
-    return d->evtchn_port_ops->is_pending(d, evtchn);
+    return d->evtchn_port_ops->is_pending(d, port);
 }
 
 static inline bool_t evtchn_port_is_masked(struct domain *d,
-                                           const struct evtchn *evtchn)
+                                           evtchn_port_t port)
 {
-    return d->evtchn_port_ops->is_masked(d, evtchn);
+    return d->evtchn_port_ops->is_masked(d, port);
 }
 
 static inline bool_t evtchn_port_is_busy(struct domain *d, evtchn_port_t port)