From da47b5969576d60e8864b5ecc4fd0db67e7e4601 Mon Sep 17 00:00:00 2001
From: Andrii Sultanov <andriy.sultanov@vates.tech>
Date: Thu, 20 Aug 2026 15:00:02 +0000
Subject: oxenstored: Reset the watches trie on domain reconnect

oxenstored maintains two datastructures about watches; one global trie, and
one hashtable tracked per domain.  Both need keeping in sync, and right now
the global trie is not emptied when a xenbus reconnect is requested.

This is basically the same bug as XSA-330, commit 910388bb4f37
("tools/ocaml/xenstored: delete watch from trie too when resetting watches"),
just tickled via another path.

Arrange for both Process.do_reset_watches() and Process.do_reconnect() to
share a common codepath for the resetting of watches and transactions.
Notably, this means that the latter now calls Connections.del_watches() which
clears the global trie too.

Connections.del_watches() already calls Connection.del_watches() so remove the
re-clearing of the state from Connection.do_reconnect().

Move History.trim() into reset_watches_and_transactions() so it's on the
common path, and place it after removing the transactions rather than before.

This is part of XSA-512 / CVE-2026-79604.

Reported-by: David Korczynski <David@Adalogics.com>
Fixes: 674ad2be409d ("xenstore: extend the xenstore ring with a 'closing' signal")
Signed-off-by: Andrii Sultanov <andriy.sultanov@vates.tech>
Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
Reviewed-by: Andrii Sultanov <andriy.sultanov@vates.tech>

diff --git a/oxenstored/connection.ml b/oxenstored/connection.ml
index 29d1911f3179..58b90a8faa5c 100644
--- a/oxenstored/connection.ml
+++ b/oxenstored/connection.ml
@@ -193,13 +193,11 @@ let mark_as_bad con =
 let initial_next_tid = 1
 
 let do_reconnect con =
+  (* transactions and watches handled by caller *)
   Xenbus.Xb.reconnect con.xb ;
   (* dom is the same *)
-  Hashtbl.clear con.transactions ;
   con.next_tid <- initial_next_tid ;
-  Hashtbl.clear con.watches ;
   (* anonid is the same *)
-  con.nb_watches <- 0 ;
   con.stat_nb_ops <- 0 ;
   (* perm is the same *)
   ()
diff --git a/oxenstored/process.ml b/oxenstored/process.ml
index 523ad4faafd8..038cdb72be0b 100644
--- a/oxenstored/process.ml
+++ b/oxenstored/process.ml
@@ -472,15 +472,18 @@ let do_isintroduced con _t domains _cons data =
   else
     "F\000"
 
-(* only in xen >= 4.2 *)
-let do_reset_watches con _t _domains cons _data =
+let reset_watches_and_transactions cons con =
   Connections.del_watches cons con ;
-  Connection.del_transactions con
+  Connection.del_transactions con ;
+  History.trim ()
+
+let do_reset_watches con _t _domains cons _data =
+  reset_watches_and_transactions cons con
 
 let do_reconnect cons con =
   let domstr = Connection.get_domstr con in
   info "%s requests a reconnect" domstr ;
-  History.trim () ;
+  reset_watches_and_transactions cons con ;
   Connection.do_reconnect con ;
   info "%s reconnection complete" domstr
 
