forked from QubesOS/qubes-vmm-xen
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathpatch-0001-libxl-devd-fix-a-race-with-concurrent-device-additio.patch
169 lines (155 loc) · 6.25 KB
/
patch-0001-libxl-devd-fix-a-race-with-concurrent-device-additio.patch
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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
From 7f04e79880a9eefcc542a91886c5a7d38001c775 Mon Sep 17 00:00:00 2001
From: Roger Pau Monne <[email protected]>
Date: Tue, 16 May 2017 08:59:23 +0100
Subject: [PATCH] libxl/devd: fix a race with concurrent device
addition/removal
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Current code can free the libxl__device inside of the libxl__ddomain_device
before the addition has finished if a removal happens while an addition is
still in process:
backend_watch_callback
|
v
add_device
| backend_watch_callback
(async operation) |
| v
| remove_device
| |
| V
| device_complete
| (free libxl__device)
v
device_complete
(deref libxl__device)
Fix this by creating a temporary copy of the libxl__device, that's tracked by
the GC of the nested async operation. This ensures that the libxl__device used
by the async operations cannot be freed while being used.
Signed-off-by: Roger Pau Monné <[email protected]>
Reported-by: Ian Jackson <[email protected]>
Reviewed-by: Wei Liu <[email protected]>
Acked-by: Ian Jackson <[email protected]>
Release-acked-by: Julien Grall <[email protected]>
Backported-by: M. Vefa Bicakci <[email protected]>
(cherry picked from commit fd519a51192b97168ab1a9ca3405d75d89341ee2)
---
tools/libxl/libxl.c | 32 +++++++++++++++++++-------------
tools/libxl/libxl_internal.h | 4 ++++
2 files changed, 23 insertions(+), 13 deletions(-)
diff --git a/tools/libxl/libxl.c b/tools/libxl/libxl.c
index acf714e1f918..dcd58c703f84 100644
--- a/tools/libxl/libxl.c
+++ b/tools/libxl/libxl.c
@@ -3721,9 +3721,6 @@ static void device_complete(libxl__egc *egc, libxl__ao_device *aodev)
libxl__device_action_to_string(aodev->action),
aodev->rc ? "failed" : "succeed");
- if (aodev->action == LIBXL__DEVICE_ACTION_REMOVE)
- free(aodev->dev);
-
libxl__nested_ao_free(aodev->ao);
}
@@ -3763,7 +3760,12 @@ static int add_device(libxl__egc *egc, libxl__ao *ao,
GCNEW(aodev);
libxl__prepare_ao_device(ao, aodev);
- aodev->dev = dev;
+ /*
+ * Clone the libxl__device to avoid races if remove_device is called
+ * before the device addition has finished.
+ */
+ GCNEW(aodev->dev);
+ *aodev->dev = *dev;
aodev->action = LIBXL__DEVICE_ACTION_ADD;
aodev->callback = device_complete;
libxl__wait_device_connection(egc, aodev);
@@ -3806,7 +3808,12 @@ static int remove_device(libxl__egc *egc, libxl__ao *ao,
GCNEW(aodev);
libxl__prepare_ao_device(ao, aodev);
- aodev->dev = dev;
+ /*
+ * Clone the libxl__device to avoid races if there's a add_device
+ * running in parallel.
+ */
+ GCNEW(aodev->dev);
+ *aodev->dev = *dev;
aodev->action = LIBXL__DEVICE_ACTION_REMOVE;
aodev->callback = device_complete;
libxl__initiate_device_generic_remove(egc, aodev);
@@ -3818,7 +3825,6 @@ static int remove_device(libxl__egc *egc, libxl__ao *ao,
goto out;
}
libxl__device_destroy(gc, dev);
- free(dev);
/* Fall through to return > 0, no ao has been dispatched */
default:
rc = 1;
@@ -3839,7 +3845,7 @@ static void backend_watch_callback(libxl__egc *egc, libxl__ev_xswatch *watch,
char *p, *path;
const char *sstate, *sonline;
int state, online, rc, num_devs;
- libxl__device *dev = NULL;
+ libxl__device *dev;
libxl__ddomain_device *ddev = NULL;
libxl__ddomain_guest *dguest = NULL;
bool free_ao = false;
@@ -3867,7 +3873,7 @@ static void backend_watch_callback(libxl__egc *egc, libxl__ev_xswatch *watch,
goto skip;
online = atoi(sonline);
- dev = libxl__zalloc(NOGC, sizeof(*dev));
+ GCNEW(dev);
rc = libxl__parse_backend_path(gc, path, dev);
if (rc)
goto skip;
@@ -3902,7 +3908,8 @@ static void backend_watch_callback(libxl__egc *egc, libxl__ev_xswatch *watch,
* to the list of active devices for a given guest.
*/
ddev = libxl__zalloc(NOGC, sizeof(*ddev));
- ddev->dev = dev;
+ ddev->dev = libxl__zalloc(NOGC, sizeof(*ddev->dev));
+ *ddev->dev = *dev;
LIBXL_SLIST_INSERT_HEAD(&dguest->devices, ddev, next);
LOG(DEBUG, "added device %s to the list of active devices", path);
rc = add_device(egc, nested_ao, dguest, ddev);
@@ -3912,9 +3919,6 @@ static void backend_watch_callback(libxl__egc *egc, libxl__ev_xswatch *watch,
/*
* Removal of an active device, remove it from the list and
* free it's data structures if they are no longer needed.
- *
- * The free of the associated libxl__device is left to the
- * helper remove_device function.
*/
LIBXL_SLIST_REMOVE(&dguest->devices, ddev, libxl__ddomain_device,
next);
@@ -3923,6 +3927,7 @@ static void backend_watch_callback(libxl__egc *egc, libxl__ev_xswatch *watch,
if (rc > 0)
free_ao = true;
+ free(ddev->dev);
free(ddev);
/* If this was the last device in the domain, remove it from the list */
num_devs = dguest->num_vifs + dguest->num_vbds + dguest->num_qdisks;
@@ -3945,7 +3950,8 @@ static void backend_watch_callback(libxl__egc *egc, libxl__ev_xswatch *watch,
skip:
libxl__nested_ao_free(nested_ao);
- free(dev);
+ if (ddev)
+ free(ddev->dev);
free(ddev);
free(dguest);
return;
diff --git a/tools/libxl/libxl_internal.h b/tools/libxl/libxl_internal.h
index c32a40576a9d..dd23f369f52e 100644
--- a/tools/libxl/libxl_internal.h
+++ b/tools/libxl/libxl_internal.h
@@ -490,6 +490,10 @@ struct libxl__ctx {
libxl_version_info version_info;
};
+/*
+ * libxl__device is a transparent structure that doesn't contain private fields
+ * or external memory references, and as such can be copied by assignment.
+ */
typedef struct {
uint32_t backend_devid;
uint32_t backend_domid;
--
2.21.3