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)
