summaryrefslogtreecommitdiff
blob: cfb6b855efdcbd3a457e7f76ee675104e521980a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
From 3382512b9f5e0d8cf37709d7cb47389d2ce8e624 Mon Sep 17 00:00:00 2001
From: Julien Grall <jgrall@amazon.com>
Date: Fri, 22 Sep 2023 11:32:16 +0100
Subject: [PATCH 13/30] tools/xenstored: domain_entry_fix(): Handle conflicting
 transaction

The function domain_entry_fix() will be initially called to check if the
quota is correct before attempt to commit any nodes. So it would be
possible that accounting is temporarily negative. This is the case
in the following sequence:

  1) Create 50 nodes
  2) Start two transactions
  3) Delete all the nodes in each transaction
  4) Commit the two transactions

Because the first transaction will have succeed and updated the
accounting, there is no guarantee that 'd->nbentry + num' will still
be above 0. So the assert() would be triggered.
The assert() was introduced in dbef1f748289 ("tools/xenstore: simplify
and fix per domain node accounting") with the assumption that the
value can't be negative. As this is not true revert to the original
check but restricted to the path where we don't update. Take the
opportunity to explain the rationale behind the check.

This CVE-2023-34323 / XSA-440.

Fixes: dbef1f748289 ("tools/xenstore: simplify and fix per domain node accounting")
Signed-off-by: Julien Grall <jgrall@amazon.com>
Reviewed-by: Juergen Gross <jgross@suse.com>
(cherry picked from commit c4e05c97f57d236040d1da5c1fbf6e3699dc86ea)
---
 tools/xenstore/xenstored_domain.c | 14 ++++++++++++--
 1 file changed, 12 insertions(+), 2 deletions(-)

diff --git a/tools/xenstore/xenstored_domain.c b/tools/xenstore/xenstored_domain.c
index ddd49eddfa..a3475284ea 100644
--- a/tools/xenstore/xenstored_domain.c
+++ b/tools/xenstore/xenstored_domain.c
@@ -1062,10 +1062,20 @@ int domain_entry_fix(unsigned int domid, int num, bool update)
 	}
 
 	cnt = d->nbentry + num;
-	assert(cnt >= 0);
 
-	if (update)
+	if (update) {
+		assert(cnt >= 0);
 		d->nbentry = cnt;
+	} else if (cnt < 0) {
+		/*
+		 * In a transaction when a node is being added/removed AND
+		 * the same node has been added/removed outside the
+		 * transaction in parallel, the result value may be negative.
+		 * This is no problem, as the transaction will fail due to
+		 * the resulting conflict. So override 'cnt'.
+		 */
+		cnt = 0;
+	}
 
 	return domid_is_unprivileged(domid) ? cnt : 0;
 }
-- 
2.43.0