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