From 3d0d19ad17f29c64dde4a7baf392da4fd58f3654 Mon Sep 17 00:00:00 2001 From: Juergen Gross 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 Fixes: 1f9d04fb021c ("xenstored: allow guest to shutdown all its watches/transactions") Signed-off-by: Juergen Gross --- 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