Blob Blame History Raw
From ae7042f024af7584251f776a12d9bb24d13fecaf Mon Sep 17 00:00:00 2001
From: Juergen Gross <jgross@suse.com>
Date: Tue, 13 Sep 2022 07:35:09 +0200
Subject: tools/xenstore: add memory accounting for responses

Add the memory accounting for queued responses.

In case adding a watch event for a guest is causing the hard memory
quota of that guest to be violated, the event is dropped. This will
ensure that it is impossible to drive another guest past its memory
quota by generating insane amounts of events for that guest. This is
especially important for protecting driver domains from that attack
vector.

This is part of XSA-326 / CVE-2022-42315.

Reported-by: Julien Grall <jgrall@amazon.com>
Signed-off-by: Juergen Gross <jgross@suse.com>
Reviewed-by: Julien Grall <jgrall@amazon.com>

diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c
index b2bf6740d430..ecab6cfbbe15 100644
--- a/tools/xenstore/xenstored_core.c
+++ b/tools/xenstore/xenstored_core.c
@@ -260,6 +260,8 @@ static void free_buffered_data(struct buffered_data *out,
 		}
 	}
 
+	domain_memory_add_nochk(conn->id, -out->hdr.msg.len - sizeof(out->hdr));
+
 	if (out->hdr.msg.type == XS_WATCH_EVENT) {
 		req = out->pend.req;
 		if (req) {
@@ -938,11 +940,14 @@ void send_reply(struct connection *conn, enum xsd_sockmsg_type type,
 	bdata->timeout_msec = 0;
 	bdata->watch_event = false;
 
-	if (len <= DEFAULT_BUFFER_SIZE)
+	if (len <= DEFAULT_BUFFER_SIZE) {
 		bdata->buffer = bdata->default_buffer;
-	else {
+		/* Don't check quota, path might be used for returning error. */
+		domain_memory_add_nochk(conn->id, len + sizeof(bdata->hdr));
+	} else {
 		bdata->buffer = talloc_array(bdata, char, len);
-		if (!bdata->buffer) {
+		if (!bdata->buffer ||
+		    domain_memory_add_chk(conn->id, len + sizeof(bdata->hdr))) {
 			send_error(conn, ENOMEM);
 			return;
 		}
@@ -1007,6 +1012,11 @@ void send_event(struct buffered_data *req, struct connection *conn,
 		}
 	}
 
+	if (domain_memory_add_chk(conn->id, len + sizeof(bdata->hdr))) {
+		talloc_free(bdata);
+		return;
+	}
+
 	if (timeout_watch_event_msec && domain_is_unprivileged(conn)) {
 		bdata->timeout_msec = get_now_msec() + timeout_watch_event_msec;
 		if (!conn->timeout_msec)
@@ -3039,6 +3049,12 @@ static void add_buffered_data(struct buffered_data *bdata,
 	 */
 	if (bdata->hdr.msg.type != XS_WATCH_EVENT)
 		domain_outstanding_inc(conn);
+	/*
+	 * We are restoring the state after Live-Update and the new quota may
+	 * be smaller. So ignore it. The limit will be applied for any resource
+	 * after the state has been fully restored.
+	 */
+	domain_memory_add_nochk(conn->id, len + sizeof(bdata->hdr));
 }
 
 void read_state_buffered_data(const void *ctx, struct connection *conn,