diff options
Diffstat (limited to 'system/xen/xsa/xsa206-4.8-0001-xenstored-apply-a-write-transaction-rate-limit.patch')
-rw-r--r-- | system/xen/xsa/xsa206-4.8-0001-xenstored-apply-a-write-transaction-rate-limit.patch | 456 |
1 files changed, 0 insertions, 456 deletions
diff --git a/system/xen/xsa/xsa206-4.8-0001-xenstored-apply-a-write-transaction-rate-limit.patch b/system/xen/xsa/xsa206-4.8-0001-xenstored-apply-a-write-transaction-rate-limit.patch deleted file mode 100644 index ee39918cb7..0000000000 --- a/system/xen/xsa/xsa206-4.8-0001-xenstored-apply-a-write-transaction-rate-limit.patch +++ /dev/null @@ -1,456 +0,0 @@ -From 8b2d563e6693527e0747fbb22c5f01eeb89a6c53 Mon Sep 17 00:00:00 2001 -From: Ian Jackson <ian.jackson@eu.citrix.com> -Date: Tue, 7 Mar 2017 16:09:12 +0000 -Subject: [PATCH 01/15] xenstored: apply a write transaction rate limit - -This avoids a rogue client being about to stall another client (eg the -toolstack) indefinitely. - -This is XSA-206. - -Signed-off-by: Ian Jackson <Ian.Jackson@eu.citrix.com> - -Backported to 4.8 (not entirely trivial). - -Reported-by: Juergen Gross <jgross@suse.com> -Signed-off-by: George Dunlap <george.dunlap@citrix.com> -Acked-by: Ian Jackson <Ian.Jackson@eu.citrix.com> ---- - tools/xenstore/Makefile | 3 +- - tools/xenstore/xenstored_core.c | 9 ++ - tools/xenstore/xenstored_core.h | 6 + - tools/xenstore/xenstored_domain.c | 215 +++++++++++++++++++++++++++++++++ - tools/xenstore/xenstored_domain.h | 25 ++++ - tools/xenstore/xenstored_transaction.c | 5 + - 6 files changed, 262 insertions(+), 1 deletion(-) - -diff --git a/tools/xenstore/Makefile b/tools/xenstore/Makefile -index 36b6fd4..9cb54de 100644 ---- a/tools/xenstore/Makefile -+++ b/tools/xenstore/Makefile -@@ -32,6 +32,7 @@ XENSTORED_OBJS_$(CONFIG_FreeBSD) = xenstored_posix.o - XENSTORED_OBJS_$(CONFIG_MiniOS) = xenstored_minios.o - - XENSTORED_OBJS += $(XENSTORED_OBJS_y) -+LDLIBS_xenstored += -lrt - - ifneq ($(XENSTORE_STATIC_CLIENTS),y) - LIBXENSTORE := libxenstore.so -@@ -73,7 +74,7 @@ endif - $(XENSTORED_OBJS): CFLAGS += $(CFLAGS_libxengnttab) - - xenstored: $(XENSTORED_OBJS) -- $(CC) $^ $(LDFLAGS) $(LDLIBS_libxenevtchn) $(LDLIBS_libxengnttab) $(LDLIBS_libxenctrl) $(SOCKET_LIBS) -o $@ $(APPEND_LDFLAGS) -+ $(CC) $^ $(LDFLAGS) $(LDLIBS_libxenevtchn) $(LDLIBS_libxengnttab) $(LDLIBS_libxenctrl) $(LDLIBS_xenstored) $(SOCKET_LIBS) -o $@ $(APPEND_LDFLAGS) - - xenstored.a: $(XENSTORED_OBJS) - $(AR) cr $@ $^ -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index 3df977b..d14f096 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -358,6 +358,7 @@ static void initialize_fds(int sock, int *p_sock_pollfd_idx, - int *ptimeout) - { - struct connection *conn; -+ struct wrl_timestampt now; - - if (fds) - memset(fds, 0, sizeof(struct pollfd) * current_array_size); -@@ -377,8 +378,11 @@ static void initialize_fds(int sock, int *p_sock_pollfd_idx, - xce_pollfd_idx = set_fd(xenevtchn_fd(xce_handle), - POLLIN|POLLPRI); - -+ wrl_gettime_now(&now); -+ - list_for_each_entry(conn, &connections, list) { - if (conn->domain) { -+ wrl_check_timeout(conn->domain, now, ptimeout); - if (domain_can_read(conn) || - (domain_can_write(conn) && - !list_empty(&conn->out_list))) -@@ -833,6 +837,7 @@ static void delete_node_single(struct connection *conn, struct node *node) - corrupt(conn, "Could not delete '%s'", node->name); - return; - } -+ - domain_entry_dec(conn, node); - } - -@@ -972,6 +977,7 @@ static void do_write(struct connection *conn, struct buffered_data *in) - } - - add_change_node(conn->transaction, name, false); -+ wrl_apply_debit_direct(conn); - fire_watches(conn, in, name, false); - send_ack(conn, XS_WRITE); - } -@@ -1003,6 +1009,7 @@ static void do_mkdir(struct connection *conn, struct buffered_data *in) - return; - } - add_change_node(conn->transaction, name, false); -+ wrl_apply_debit_direct(conn); - fire_watches(conn, in, name, false); - } - send_ack(conn, XS_MKDIR); -@@ -1129,6 +1136,7 @@ static void do_rm(struct connection *conn, struct buffered_data *in) - - if (_rm(conn, node, name)) { - add_change_node(conn->transaction, name, true); -+ wrl_apply_debit_direct(conn); - fire_watches(conn, in, name, true); - send_ack(conn, XS_RM); - } -@@ -1205,6 +1213,7 @@ static void do_set_perms(struct connection *conn, struct buffered_data *in) - } - - add_change_node(conn->transaction, name, false); -+ wrl_apply_debit_direct(conn); - fire_watches(conn, in, name, false); - send_ack(conn, XS_SET_PERMS); - } -diff --git a/tools/xenstore/xenstored_core.h b/tools/xenstore/xenstored_core.h -index ecc614f..9e9d960 100644 ---- a/tools/xenstore/xenstored_core.h -+++ b/tools/xenstore/xenstored_core.h -@@ -33,6 +33,12 @@ - #include "list.h" - #include "tdb.h" - -+#define MIN(a, b) (((a) < (b))? (a) : (b)) -+ -+typedef int32_t wrl_creditt; -+#define WRL_CREDIT_MAX (1000*1000*1000) -+/* ^ satisfies non-overflow condition for wrl_xfer_credit */ -+ - struct buffered_data - { - struct list_head list; -diff --git a/tools/xenstore/xenstored_domain.c b/tools/xenstore/xenstored_domain.c -index 5de93d4..012dfe6 100644 ---- a/tools/xenstore/xenstored_domain.c -+++ b/tools/xenstore/xenstored_domain.c -@@ -21,6 +21,7 @@ - #include <unistd.h> - #include <stdlib.h> - #include <stdarg.h> -+#include <time.h> - - #include "utils.h" - #include "talloc.h" -@@ -74,6 +75,10 @@ struct domain - - /* number of watch for this domain */ - int nbwatch; -+ -+ /* write rate limit */ -+ wrl_creditt wrl_credit; /* [ -wrl_config_writecost, +_dburst ] */ -+ struct wrl_timestampt wrl_timestamp; - }; - - static LIST_HEAD(domains); -@@ -206,6 +211,8 @@ static int destroy_domain(void *_domain) - - fire_watches(NULL, domain, "@releaseDomain", false); - -+ wrl_domain_destroy(domain); -+ - return 0; - } - -@@ -253,6 +260,9 @@ void handle_event(void) - bool domain_can_read(struct connection *conn) - { - struct xenstore_domain_interface *intf = conn->domain->interface; -+ -+ if (domain_is_unprivileged(conn) && conn->domain->wrl_credit < 0) -+ return false; - return (intf->req_cons != intf->req_prod); - } - -@@ -284,6 +294,8 @@ static struct domain *new_domain(void *context, unsigned int domid, - domain->domid = domid; - domain->path = talloc_domain_path(domain, domid); - -+ wrl_domain_new(domain); -+ - list_add(&domain->list, &domains); - talloc_set_destructor(domain, destroy_domain); - -@@ -751,6 +763,209 @@ int domain_watch(struct connection *conn) - : 0; - } - -+static wrl_creditt wrl_config_writecost = WRL_FACTOR; -+static wrl_creditt wrl_config_rate = WRL_RATE * WRL_FACTOR; -+static wrl_creditt wrl_config_dburst = WRL_DBURST * WRL_FACTOR; -+static wrl_creditt wrl_config_gburst = WRL_GBURST * WRL_FACTOR; -+static wrl_creditt wrl_config_newdoms_dburst = -+ WRL_DBURST * WRL_NEWDOMS * WRL_FACTOR; -+ -+long wrl_ntransactions; -+ -+static long wrl_ndomains; -+static wrl_creditt wrl_reserve; /* [-wrl_config_newdoms_dburst, +_gburst ] */ -+ -+void wrl_gettime_now(struct wrl_timestampt *now_wt) -+{ -+ struct timespec now_ts; -+ int r; -+ -+ r = clock_gettime(CLOCK_MONOTONIC, &now_ts); -+ if (r) -+ barf_perror("Could not find time (clock_gettime failed)"); -+ -+ now_wt->sec = now_ts.tv_sec; -+ now_wt->msec = now_ts.tv_nsec / 1000000; -+} -+ -+static void wrl_xfer_credit(wrl_creditt *debit, wrl_creditt debit_floor, -+ wrl_creditt *credit, wrl_creditt credit_ceil) -+ /* -+ * Transfers zero or more credit from "debit" to "credit". -+ * Transfers as much as possible while maintaining -+ * debit >= debit_floor and credit <= credit_ceil. -+ * (If that's violated already, does nothing.) -+ * -+ * Sufficient conditions to avoid overflow, either of: -+ * |every argument| <= 0x3fffffff -+ * |every argument| <= 1E9 -+ * |every argument| <= WRL_CREDIT_MAX -+ * (And this condition is preserved.) -+ */ -+{ -+ wrl_creditt xfer = MIN( *debit - debit_floor, -+ credit_ceil - *credit ); -+ if (xfer > 0) { -+ *debit -= xfer; -+ *credit += xfer; -+ } -+} -+ -+void wrl_domain_new(struct domain *domain) -+{ -+ domain->wrl_credit = 0; -+ wrl_gettime_now(&domain->wrl_timestamp); -+ wrl_ndomains++; -+ /* Steal up to DBURST from the reserve */ -+ wrl_xfer_credit(&wrl_reserve, -wrl_config_newdoms_dburst, -+ &domain->wrl_credit, wrl_config_dburst); -+} -+ -+void wrl_domain_destroy(struct domain *domain) -+{ -+ wrl_ndomains--; -+ /* -+ * Don't bother recalculating domain's credit - this just -+ * means we don't give the reserve the ending domain's credit -+ * for time elapsed since last update. -+ */ -+ wrl_xfer_credit(&domain->wrl_credit, 0, -+ &wrl_reserve, wrl_config_dburst); -+} -+ -+void wrl_credit_update(struct domain *domain, struct wrl_timestampt now) -+{ -+ /* -+ * We want to calculate -+ * credit += (now - timestamp) * RATE / ndoms; -+ * But we want it to saturate, and to avoid floating point. -+ * To avoid rounding errors from constantly adding small -+ * amounts of credit, we only add credit for whole milliseconds. -+ */ -+ long seconds = now.sec - domain->wrl_timestamp.sec; -+ long milliseconds = now.msec - domain->wrl_timestamp.msec; -+ long msec; -+ int64_t denom, num; -+ wrl_creditt surplus; -+ -+ seconds = MIN(seconds, 1000*1000); /* arbitrary, prevents overflow */ -+ msec = seconds * 1000 + milliseconds; -+ -+ if (msec < 0) -+ /* shouldn't happen with CLOCK_MONOTONIC */ -+ msec = 0; -+ -+ /* 32x32 -> 64 cannot overflow */ -+ denom = (int64_t)msec * wrl_config_rate; -+ num = (int64_t)wrl_ndomains * 1000; -+ /* denom / num <= 1E6 * wrl_config_rate, so with -+ reasonable wrl_config_rate, denom / num << 2^64 */ -+ -+ /* at last! */ -+ domain->wrl_credit = MIN( (int64_t)domain->wrl_credit + denom / num, -+ WRL_CREDIT_MAX ); -+ /* (maybe briefly violating the DBURST cap on wrl_credit) */ -+ -+ /* maybe take from the reserve to make us nonnegative */ -+ wrl_xfer_credit(&wrl_reserve, 0, -+ &domain->wrl_credit, 0); -+ -+ /* return any surplus (over DBURST) to the reserve */ -+ surplus = 0; -+ wrl_xfer_credit(&domain->wrl_credit, wrl_config_dburst, -+ &surplus, WRL_CREDIT_MAX); -+ wrl_xfer_credit(&surplus, 0, -+ &wrl_reserve, wrl_config_gburst); -+ /* surplus is now implicitly discarded */ -+ -+ domain->wrl_timestamp = now; -+ -+ trace("wrl: dom %4d %6ld msec %9ld credit %9ld reserve" -+ " %9ld discard\n", -+ domain->domid, -+ msec, -+ (long)domain->wrl_credit, (long)wrl_reserve, -+ (long)surplus); -+} -+ -+void wrl_check_timeout(struct domain *domain, -+ struct wrl_timestampt now, -+ int *ptimeout) -+{ -+ uint64_t num, denom; -+ int wakeup; -+ -+ wrl_credit_update(domain, now); -+ -+ if (domain->wrl_credit >= 0) -+ /* not blocked */ -+ return; -+ -+ if (!*ptimeout) -+ /* already decided on immediate wakeup, -+ so no need to calculate our timeout */ -+ return; -+ -+ /* calculate wakeup = now + -credit / (RATE / ndoms); */ -+ -+ /* credit cannot go more -ve than one transaction, -+ * so the first multiplication cannot overflow even 32-bit */ -+ num = (uint64_t)(-domain->wrl_credit * 1000) * wrl_ndomains; -+ denom = wrl_config_rate; -+ -+ wakeup = MIN( num / denom /* uint64_t */, INT_MAX ); -+ if (*ptimeout==-1 || wakeup < *ptimeout) -+ *ptimeout = wakeup; -+ -+ trace("wrl: domain %u credit=%ld (reserve=%ld) SLEEPING for %d\n", -+ domain->domid, -+ (long)domain->wrl_credit, (long)wrl_reserve, -+ wakeup); -+} -+ -+void wrl_apply_debit_actual(struct domain *domain) -+{ -+ struct wrl_timestampt now; -+ -+ if (!domain) -+ /* sockets escape the write rate limit */ -+ return; -+ -+ wrl_gettime_now(&now); -+ wrl_credit_update(domain, now); -+ -+ domain->wrl_credit -= wrl_config_writecost; -+ trace("wrl: domain %u credit=%ld (reserve=%ld)\n", -+ domain->domid, -+ (long)domain->wrl_credit, (long)wrl_reserve); -+} -+ -+void wrl_apply_debit_direct(struct connection *conn) -+{ -+ if (!conn) -+ /* some writes are generated internally */ -+ return; -+ -+ if (conn->transaction) -+ /* these are accounted for when the transaction ends */ -+ return; -+ -+ if (!wrl_ntransactions) -+ /* we don't conflict with anyone */ -+ return; -+ -+ wrl_apply_debit_actual(conn->domain); -+} -+ -+void wrl_apply_debit_trans_commit(struct connection *conn) -+{ -+ if (wrl_ntransactions <= 1) -+ /* our own transaction appears in the counter */ -+ return; -+ -+ wrl_apply_debit_actual(conn->domain); -+} -+ - /* - * Local variables: - * c-file-style: "linux" -diff --git a/tools/xenstore/xenstored_domain.h b/tools/xenstore/xenstored_domain.h -index 2554423..cec341e 100644 ---- a/tools/xenstore/xenstored_domain.h -+++ b/tools/xenstore/xenstored_domain.h -@@ -65,4 +65,29 @@ void domain_watch_inc(struct connection *conn); - void domain_watch_dec(struct connection *conn); - int domain_watch(struct connection *conn); - -+/* Write rate limiting */ -+ -+#define WRL_FACTOR 1000 /* for fixed-point arithmetic */ -+#define WRL_RATE 200 -+#define WRL_DBURST 10 -+#define WRL_GBURST 1000 -+#define WRL_NEWDOMS 5 -+ -+struct wrl_timestampt { -+ time_t sec; -+ int msec; -+}; -+ -+extern long wrl_ntransactions; -+ -+void wrl_gettime_now(struct wrl_timestampt *now_ts); -+void wrl_domain_new(struct domain *domain); -+void wrl_domain_destroy(struct domain *domain); -+void wrl_credit_update(struct domain *domain, struct wrl_timestampt now); -+void wrl_check_timeout(struct domain *domain, -+ struct wrl_timestampt now, -+ int *ptimeout); -+void wrl_apply_debit_direct(struct connection *conn); -+void wrl_apply_debit_trans_commit(struct connection *conn); -+ - #endif /* _XENSTORED_DOMAIN_H */ -diff --git a/tools/xenstore/xenstored_transaction.c b/tools/xenstore/xenstored_transaction.c -index 84cb0bf..5059a11 100644 ---- a/tools/xenstore/xenstored_transaction.c -+++ b/tools/xenstore/xenstored_transaction.c -@@ -120,6 +120,7 @@ static int destroy_transaction(void *_transaction) - { - struct transaction *trans = _transaction; - -+ wrl_ntransactions--; - trace_destroy(trans, "transaction"); - if (trans->tdb) - tdb_close(trans->tdb); -@@ -183,6 +184,7 @@ void do_transaction_start(struct connection *conn, struct buffered_data *in) - talloc_steal(conn, trans); - talloc_set_destructor(trans, destroy_transaction); - conn->transaction_started++; -+ wrl_ntransactions++; - - snprintf(id_str, sizeof(id_str), "%u", trans->id); - send_reply(conn, XS_TRANSACTION_START, id_str, strlen(id_str)+1); -@@ -218,6 +220,9 @@ void do_transaction_end(struct connection *conn, struct buffered_data *in) - send_error(conn, EAGAIN); - return; - } -+ -+ wrl_apply_debit_trans_commit(conn); -+ - if (!replace_tdb(trans->tdb_name, trans->tdb)) { - send_error(conn, errno); - return; --- -2.1.4 - |