Xen Security Advisory 484 v2 (CVE-2026-23557) - Xenstored DoS via XS_RESET_WATCHES command

Xen.org security team posted 1 patch 6 days, 20 hours ago
Failed in applying to current master (apply log)
tools/xenstored/transaction.c | 20 +++++++++-----------
1 file changed, 9 insertions(+), 11 deletions(-)
Xen Security Advisory 484 v2 (CVE-2026-23557) - Xenstored DoS via XS_RESET_WATCHES command
Posted by Xen.org security team 6 days, 20 hours ago
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

            Xen Security Advisory CVE-2026-23557 / XSA-484
                               version 2

              Xenstored DoS via XS_RESET_WATCHES command

UPDATES IN VERSION 2
====================

Public release.

ISSUE DESCRIPTION
=================

Any guest can cause xenstored to crash by issuing a XS_RESET_WATCHES
command within a transaction due to an assert() triggering.

In case xenstored was built with NDEBUG #defined nothing bad will
happen, as assert() is doing nothing in this case. Note that the
default is not to define NDEBUG for xenstored builds even in release
builds of Xen.

IMPACT
======

Any unprivileged domain can cause xenstored to crash, causing a
DoS (denial of service) for any Xenstore action. This will result
in an inability to perform further domain administration on the host.

VULNERABLE SYSTEMS
==================

All Xen systems from Xen 4.2 onwards are vulnerable. Systems up to
Xen 4.1 are not vulnerable.

Systems using the C variant of xenstored or xenstore-stubdom built
without NDEBUG are vulnerable. Systems using the OCaml variant of
Xenstore (oxenstored), or the C variant (xenstored or xenstore-stubdom)
built with NDEBUG defined are not vulnerable.

MITIGATION
==========

There is no known mitigation available.

CREDITS
=======

This issue was discovered by Andrii Sultanov of Vates.

RESOLUTION
==========

Applying the appropriate attached patch resolves this issue.

Note that patches for released versions are generally prepared to
apply to the stable branches, and may not apply cleanly to the most
recent release tarball.  Downstreams are encouraged to update to the
tip of the stable branch before applying these patches.

xsa484.patch           xen-unstable - Xen 4.18.x
xsa484-4.17.patch      Xen 4.17.x

$ sha256sum xsa484*
77c489191d40acd807eb19344a0e1bbb67a04551e89aff726fbb2006f235aacf  xsa484.patch
6c8d8146d136956c59ee77da6aa6340272d1ea670a6b0d9cf37fe759d4b96b19  xsa484-4.17.patch
$

DEPLOYMENT DURING EMBARGO
=========================

Deployment of the patches and/or mitigations described above (or
others which are substantially similar) is permitted during the
embargo, even on public-facing systems with untrusted guest users and
administrators.

But: Distribution of updated software is prohibited (except to other
members of the predisclosure list).

Predisclosure list members who wish to deploy significantly different
patches and/or mitigations, please contact the Xen Project Security
Team.

(Note: this during-embargo deployment notice is retained in
post-embargo publicly released Xen Project advisories, even though it
is then no longer applicable.  This is to enable the community to have
oversight of the Xen Project Security Team's decisionmaking.)

For more information about permissible uses of embargoed information,
consult the Xen Project community's agreed Security Policy:
  http://www.xenproject.org/security-policy.html
-----BEGIN PGP SIGNATURE-----

iQFABAEBCAAqFiEEI+MiLBRfRHX6gGCng/4UyVfoK9kFAmnwoQEMHHBncEB4ZW4u
b3JnAAoJEIP+FMlX6CvZGVoIALBKECpaWxXD7ivkbFpFlmt9a2TOXxnD1LjbSnzI
VAdyFECK4ng0uRaUXHMcd0Dkzw+dOrm/SA7jI+brumyyxsO44eLz5fysAQYXDHca
qsn5h7To34Fow8ejQIt1E9DmqNlZP7Y261MhYSdWN6Z2lEa4cMPyJKA/xTpQ2uUq
Cy9Ss7jrl/v98MOZb2Tkn+H8XiNsPJb57sWeaOPoUMh+42y/5qMyRgqWa3/N3iHn
ZVZEhTbrNvGYKW+DUq5KswUjxw9FAmtQ1PA/w3ItWWdsb0Gd8AE02FzdIuoIt/xk
zB9BEchspV1Gfouz0alFV+d4gDyclQmmViYojNfXYfKdWp8=
=j/SA
-----END PGP SIGNATURE-----
From 3d0d19ad17f29c64dde4a7baf392da4fd58f3654 Mon Sep 17 00:00:00 2001
From: Juergen Gross <jgross@suse.com>
Date: Mon, 16 Mar 2026 15:06:11 +0100
Subject: [PATCH] tools/xenstored: make conn_delete_all_transactions()
 idempotent

conn_delete_all_transactions() should be callable in any context,
resetting ALL transaction related data.

This includes number of active transactions and the transaction
pointer in struct connection.

So reset conn->trans to NULL in conn_delete_all_transactions() and
do the cleanup for each transaction in destroy_transaction().

This avoids triggering the assert() in conn_delete_all_transactions()
in case e.g. ignore_connection() was called while an operation inside
a transaction was performed, or XS_RESET_WATCHES was called in a
transaction.

This is XSA-484 / CVE-2026-23557.

Reported-by: Andrii Sultanov <andriy.sultanov@vates.tech>
Fixes: 1f9d04fb021c ("xenstored: allow guest to shutdown all its watches/transactions")
Signed-off-by: Juergen Gross <jgross@suse.com>
---
 tools/xenstored/transaction.c | 20 +++++++++-----------
 1 file changed, 9 insertions(+), 11 deletions(-)

diff --git a/tools/xenstored/transaction.c b/tools/xenstored/transaction.c
index 167cd597fd..0825c48859 100644
--- a/tools/xenstored/transaction.c
+++ b/tools/xenstored/transaction.c
@@ -432,17 +432,23 @@ static int finalize_transaction(struct connection *conn,
 static int destroy_transaction(void *_transaction)
 {
 	struct transaction *trans = _transaction;
+	struct connection *conn = trans->conn;
 	struct accessed_node *i;
 
 	wrl_ntransactions--;
 	trace_destroy(trans, "transaction");
 	while ((i = list_top(&trans->accessed, struct accessed_node, list))) {
 		if (i->ta_node)
-			db_delete(trans->conn, i->trans_name, NULL);
+			db_delete(conn, i->trans_name, NULL);
 		list_del(&i->list);
 		talloc_free(i);
 	}
 
+	list_del(&trans->list);
+	domain_transaction_dec(conn);
+	if (list_empty(&conn->transaction_list))
+		conn->ta_start_time = 0;
+
 	return 0;
 }
 
@@ -523,10 +529,6 @@ int do_transaction_end(const void *ctx, struct connection *conn,
 		return ENOENT;
 
 	conn->transaction = NULL;
-	list_del(&trans->list);
-	domain_transaction_dec(conn);
-	if (list_empty(&conn->transaction_list))
-		conn->ta_start_time = 0;
 
 	chk_quota = trans->node_created && domain_is_unprivileged(conn);
 
@@ -572,14 +574,10 @@ void conn_delete_all_transactions(struct connection *conn)
 	struct transaction *trans;
 
 	while ((trans = list_top(&conn->transaction_list,
-				 struct transaction, list))) {
-		list_del(&trans->list);
+				 struct transaction, list)))
 		talloc_free(trans);
-	}
-
-	assert(conn->transaction == NULL);
 
-	conn->ta_start_time = 0;
+	conn->transaction = NULL;
 }
 
 int check_transactions(struct hashtable *hash)
-- 
2.53.0

From: Juergen Gross <jgross@suse.com>
Subject: tools/xenstored: make conn_delete_all_transactions() idempotent

conn_delete_all_transactions() should be callable in any context,
resetting ALL transaction related data.

This includes number of active transactions and the transaction
pointer in struct connection.

So reset conn->trans to NULL in conn_delete_all_transactions() and
do the cleanup for each transaction in destroy_transaction().

This avoids triggering the assert() in conn_delete_all_transactions()
in case e.g. ignore_connection() was called while an operation inside
a transaction was performed, or XS_RESET_WATCHES was called in a
transaction.

This is XSA-484 / CVE-2026-23557.

Reported-by: Andrii Sultanov <andriy.sultanov@vates.tech>
Fixes: 1f9d04fb021c ("xenstored: allow guest to shutdown all its watches/transactions")
Signed-off-by: Juergen Gross <jgross@suse.com>

--- a/tools/xenstore/xenstored_transaction.c
+++ b/tools/xenstore/xenstored_transaction.c
@@ -445,6 +445,7 @@ static int finalize_transaction(struct c
 static int destroy_transaction(void *_transaction)
 {
 	struct transaction *trans = _transaction;
+	struct connection *conn = trans->conn;
 	struct accessed_node *i;
 	TDB_DATA key;
 
@@ -453,12 +454,17 @@ static int destroy_transaction(void *_tr
 	while ((i = list_top(&trans->accessed, struct accessed_node, list))) {
 		if (i->ta_node) {
 			set_tdb_key(i->trans_name, &key);
-			do_tdb_delete(trans->conn, &key, NULL);
+			do_tdb_delete(conn, &key, NULL);
 		}
 		list_del(&i->list);
 		talloc_free(i);
 	}
 
+	list_del(&trans->list);
+	conn->transaction_started--;
+	if (!conn->transaction_started)
+		conn->ta_start_time = 0;
+
 	return 0;
 }
 
@@ -561,10 +567,6 @@ int do_transaction_end(const void *ctx,
 		return ENOENT;
 
 	conn->transaction = NULL;
-	list_del(&trans->list);
-	conn->transaction_started--;
-	if (!conn->transaction_started)
-		conn->ta_start_time = 0;
 
 	chk_quota = trans->node_created && domain_is_unprivileged(conn);
 
@@ -646,15 +648,11 @@ void conn_delete_all_transactions(struct
 	struct transaction *trans;
 
 	while ((trans = list_top(&conn->transaction_list,
-				 struct transaction, list))) {
-		list_del(&trans->list);
+				 struct transaction, list)))
 		talloc_free(trans);
-	}
-
-	assert(conn->transaction == NULL);
 
 	conn->transaction_started = 0;
-	conn->ta_start_time = 0;
+	conn->transaction = NULL;
 }
 
 int check_transactions(struct hashtable *hash)