From: David Vrabel <david.vrabel@citrix.com>
To: xen-devel@lists.xen.org
Cc: Keir Fraser <keir@xen.org>,
David Vrabel <david.vrabel@citrix.com>,
Jan Beulich <jbeulich@suse.com>
Subject: [PATCH 1/2] evtchn/fifo: don't spin indefinitely when setting LINK
Date: Tue, 12 Nov 2013 11:38:47 +0000 [thread overview]
Message-ID: <1384256328-20223-2-git-send-email-david.vrabel@citrix.com> (raw)
In-Reply-To: <1384256328-20223-1-git-send-email-david.vrabel@citrix.com>
From: David Vrabel <david.vrabel@citrix.com>
A malicious or buggy guest can cause another domain to spin
indefinitely by repeatedly writing to an event word when the other
guest is trying to link a new event. The cmpxchg() in
evtchn_fifo_set_link() will repeatedly fail and the loop may never
terminate.
Fixing this requires a change to the ABI which is documented in draft
H of the design.
http://xenbits.xen.org/people/dvrabel/event-channels-H.pdf
Since a well-behaved guest only makes a limited set of state changes,
the loop can terminate early if the guest makes an invalid state
transition.
The guest may:
- clear LINKED and LINK.
- clear PENDING
- set MASKED
- clear MASKED
It is valid for the guest to mask and unmask an event at any time so
specify that it is not valid for a guest to clear MASKED if Xen is
trying to update LINK. Indicate this to the guest with an additional
BUSY bit in the event word. The guest must not clear MASKED if BUSY
is set and it should spin until BUSY is cleared.
The remaining valid writes (clear LINKED, clear PENDING, set MASKED,
clear MASKED by Xen) will limit the number of failures of the
cmpxchg() to at most 4. A clear of LINKED will also terminate the
loop early. Therefore, the loop can then be limited to at most 4
iterations.
If the buggy or malicious guest does cause the loop to exit with
LINKED set and LINK unset then that buggy guest will lose events.
Signed-off-by: David Vrabel <david.vrabel@citrix.com>
Reported-by: Anthony Liguori <aliguori@amazon.com>
---
xen/common/event_fifo.c | 75 +++++++++++++++++++++++++++++------
xen/include/public/event_channel.h | 1 +
2 files changed, 63 insertions(+), 13 deletions(-)
diff --git a/xen/common/event_fifo.c b/xen/common/event_fifo.c
index 64dbfff..9106c55 100644
--- a/xen/common/event_fifo.c
+++ b/xen/common/event_fifo.c
@@ -34,19 +34,67 @@ static inline event_word_t *evtchn_fifo_word_from_port(struct domain *d,
return d->evtchn_fifo->event_array[p] + w;
}
-static bool_t evtchn_fifo_set_link(event_word_t *word, uint32_t link)
+static int try_set_link(event_word_t *word, event_word_t *w, uint32_t link)
{
- event_word_t n, o, w;
+ event_word_t new, old;
- w = *word;
+ if ( !(*w & (1 << EVTCHN_FIFO_LINKED)) )
+ return 0;
- do {
- if ( !(w & (1 << EVTCHN_FIFO_LINKED)) )
- return 0;
- o = w;
- n = (w & ~EVTCHN_FIFO_LINK_MASK) | link;
- } while ( (w = cmpxchg(word, o, n)) != o );
+ old = *w;
+ new = (old & ~((1 << EVTCHN_FIFO_BUSY) | EVTCHN_FIFO_LINK_MASK)) | link;
+ *w = cmpxchg(word, old, new);
+ if ( *w == old )
+ return 1;
+ return -EAGAIN;
+}
+
+/*
+ * Atomically set the LINK field iff it is still LINKED.
+ *
+ * The guest is only permitted to make the following changes to a
+ * LINKED event.
+ *
+ * - set MASKED
+ * - clear MASKED
+ * - clear PENDING
+ * - clear LINKED (and LINK)
+ *
+ * We block unmasking by the guest by marking the tail word as BUSY,
+ * therefore, the cmpxchg() may fail at most 4 times.
+ */
+static bool_t evtchn_fifo_set_link(const struct domain *d, event_word_t *word,
+ uint32_t link)
+{
+ event_word_t w;
+ unsigned int try;
+ int ret;
+
+ w = read_atomic(word);
+
+ ret = try_set_link(word, &w, link);
+ if ( ret >= 0 )
+ return ret;
+
+ /* Lock the word to prevent guest unmasking. */
+ set_bit(EVTCHN_FIFO_BUSY, word);
+
+ w = read_atomic(word);
+
+ for ( try = 0; try < 4; try++ )
+ {
+ ret = try_set_link(word, &w, link);
+ if ( ret >= 0 )
+ {
+ if ( ret == 0 )
+ clear_bit(EVTCHN_FIFO_BUSY, word);
+ return ret;
+ }
+ }
+ gdprintk(XENLOG_WARNING, "domain %d, port %d not linked\n",
+ d->domain_id, link);
+ clear_bit(EVTCHN_FIFO_BUSY, word);
return 1;
}
@@ -105,7 +153,7 @@ static void evtchn_fifo_set_pending(struct vcpu *v, struct evtchn *evtchn)
if ( port != q->tail )
{
tail_word = evtchn_fifo_word_from_port(d, q->tail);
- linked = evtchn_fifo_set_link(tail_word, port);
+ linked = evtchn_fifo_set_link(d, tail_word, port);
}
if ( !linked )
write_atomic(q->head, port);
@@ -202,11 +250,12 @@ static void evtchn_fifo_print_state(struct domain *d,
word = evtchn_fifo_word_from_port(d, evtchn->port);
if ( !word )
- printk("? ");
+ printk("? ");
else if ( test_bit(EVTCHN_FIFO_LINKED, word) )
- printk("%-4u", *word & EVTCHN_FIFO_LINK_MASK);
+ printk("%c %-4u", test_bit(EVTCHN_FIFO_BUSY, word) ? 'B' : ' ',
+ *word & EVTCHN_FIFO_LINK_MASK);
else
- printk("- ");
+ printk("%c - ", test_bit(EVTCHN_FIFO_BUSY, word) ? 'B' : ' ');
}
static const struct evtchn_port_ops evtchn_port_ops_fifo =
diff --git a/xen/include/public/event_channel.h b/xen/include/public/event_channel.h
index 4a53484..b78ee32 100644
--- a/xen/include/public/event_channel.h
+++ b/xen/include/public/event_channel.h
@@ -343,6 +343,7 @@ typedef uint32_t event_word_t;
#define EVTCHN_FIFO_PENDING 31
#define EVTCHN_FIFO_MASKED 30
#define EVTCHN_FIFO_LINKED 29
+#define EVTCHN_FIFO_BUSY 28
#define EVTCHN_FIFO_LINK_BITS 17
#define EVTCHN_FIFO_LINK_MASK ((1 << EVTCHN_FIFO_LINK_BITS) - 1)
--
1.7.2.5
next prev parent reply other threads:[~2013-11-12 11:38 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2013-11-12 11:38 [PATCHv4 0/2] Xen: FIFO-based event channel ABI fixes David Vrabel
2013-11-12 11:38 ` David Vrabel [this message]
2013-11-20 15:19 ` [PATCH 1/2] evtchn/fifo: don't spin indefinitely when setting LINK David Vrabel
2013-11-22 12:08 ` Jan Beulich
2013-11-12 11:38 ` [PATCH 2/2] evtchn/fifo: don't corrupt queues if an old tail moves queues David Vrabel
2013-11-15 13:15 ` David Vrabel
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1384256328-20223-2-git-send-email-david.vrabel@citrix.com \
--to=david.vrabel@citrix.com \
--cc=jbeulich@suse.com \
--cc=keir@xen.org \
--cc=xen-devel@lists.xen.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).