Skip to content

Commit f0a2f2a

Browse files
author
Paolo Abeni
committed
Merge branch 'vsock-add-write-once-semantics-to-child_ns_mode'
Bobby Eshleman says: ==================== vsock: add write-once semantics to child_ns_mode Two administrator processes may race when setting child_ns_mode: one sets it to "local" and creates a namespace, but another changes it to "global" in between. The first process ends up with a namespace in the wrong mode. Make child_ns_mode write-once so that a namespace manager can set it once, check the value, and be guaranteed it won't change before creating its namespaces. Writing a different value after the first write returns -EBUSY. One patch for the implementation, one for docs, and one for tests. v2: https://lore.kernel.org/r/[email protected] v1: https://lore.kernel.org/r/[email protected] ==================== Link: https://patch.msgid.link/[email protected] Signed-off-by: Paolo Abeni <[email protected]>
2 parents 97f87e5 + b6302e0 commit f0a2f2a

5 files changed

Lines changed: 48 additions & 27 deletions

File tree

Documentation/admin-guide/sysctl/net.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -594,6 +594,9 @@ Values:
594594
their sockets will only be able to connect within their own
595595
namespace.
596596

597+
The first write to ``child_ns_mode`` locks its value. Subsequent writes of the
598+
same value succeed, but writing a different value returns ``-EBUSY``.
599+
597600
Changing ``child_ns_mode`` only affects namespaces created after the change;
598601
it does not modify the current namespace or any existing children.
599602

include/net/af_vsock.h

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -276,10 +276,19 @@ static inline bool vsock_net_mode_global(struct vsock_sock *vsk)
276276
return vsock_net_mode(sock_net(sk_vsock(vsk))) == VSOCK_NET_MODE_GLOBAL;
277277
}
278278

279-
static inline void vsock_net_set_child_mode(struct net *net,
279+
static inline bool vsock_net_set_child_mode(struct net *net,
280280
enum vsock_net_mode mode)
281281
{
282-
WRITE_ONCE(net->vsock.child_ns_mode, mode);
282+
int new_locked = mode + 1;
283+
int old_locked = 0; /* unlocked */
284+
285+
if (try_cmpxchg(&net->vsock.child_ns_mode_locked,
286+
&old_locked, new_locked)) {
287+
WRITE_ONCE(net->vsock.child_ns_mode, mode);
288+
return true;
289+
}
290+
291+
return old_locked == new_locked;
283292
}
284293

285294
static inline enum vsock_net_mode vsock_net_child_mode(struct net *net)

include/net/netns/vsock.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,8 @@ struct netns_vsock {
1717

1818
enum vsock_net_mode mode;
1919
enum vsock_net_mode child_ns_mode;
20+
21+
/* 0 = unlocked, 1 = locked to global, 2 = locked to local */
22+
int child_ns_mode_locked;
2023
};
2124
#endif /* __NET_NET_NAMESPACE_VSOCK_H */

net/vmw_vsock/af_vsock.c

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -90,16 +90,20 @@
9090
*
9191
* - /proc/sys/net/vsock/ns_mode (read-only) reports the current namespace's
9292
* mode, which is set at namespace creation and immutable thereafter.
93-
* - /proc/sys/net/vsock/child_ns_mode (writable) controls what mode future
93+
* - /proc/sys/net/vsock/child_ns_mode (write-once) controls what mode future
9494
* child namespaces will inherit when created. The initial value matches
9595
* the namespace's own ns_mode.
9696
*
9797
* Changing child_ns_mode only affects newly created namespaces, not the
9898
* current namespace or existing children. A "local" namespace cannot set
99-
* child_ns_mode to "global". At namespace creation, ns_mode is inherited
100-
* from the parent's child_ns_mode.
99+
* child_ns_mode to "global". child_ns_mode is write-once, so that it may be
100+
* configured and locked down by a namespace manager. Writing a different
101+
* value after the first write returns -EBUSY. At namespace creation, ns_mode
102+
* is inherited from the parent's child_ns_mode.
101103
*
102-
* The init_net mode is "global" and cannot be modified.
104+
* The init_net mode is "global" and cannot be modified. The init_net
105+
* child_ns_mode is also write-once, so an init process (e.g. systemd) can
106+
* set it to "local" to ensure all new namespaces inherit local mode.
103107
*
104108
* The modes affect the allocation and accessibility of CIDs as follows:
105109
*
@@ -2853,7 +2857,8 @@ static int vsock_net_child_mode_string(const struct ctl_table *table, int write,
28532857
new_mode == VSOCK_NET_MODE_GLOBAL)
28542858
return -EPERM;
28552859

2856-
vsock_net_set_child_mode(net, new_mode);
2860+
if (!vsock_net_set_child_mode(net, new_mode))
2861+
return -EBUSY;
28572862
}
28582863

28592864
return 0;

tools/testing/selftests/vsock/vmtest.sh

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -210,16 +210,21 @@ check_result() {
210210
}
211211

212212
add_namespaces() {
213-
local orig_mode
214-
orig_mode=$(cat /proc/sys/net/vsock/child_ns_mode)
215-
216-
for mode in "${NS_MODES[@]}"; do
217-
echo "${mode}" > /proc/sys/net/vsock/child_ns_mode
218-
ip netns add "${mode}0" 2>/dev/null
219-
ip netns add "${mode}1" 2>/dev/null
220-
done
221-
222-
echo "${orig_mode}" > /proc/sys/net/vsock/child_ns_mode
213+
ip netns add "global-parent" 2>/dev/null
214+
echo "global" | ip netns exec "global-parent" \
215+
tee /proc/sys/net/vsock/child_ns_mode &>/dev/null
216+
ip netns add "local-parent" 2>/dev/null
217+
echo "local" | ip netns exec "local-parent" \
218+
tee /proc/sys/net/vsock/child_ns_mode &>/dev/null
219+
220+
nsenter --net=/var/run/netns/global-parent \
221+
ip netns add "global0" 2>/dev/null
222+
nsenter --net=/var/run/netns/global-parent \
223+
ip netns add "global1" 2>/dev/null
224+
nsenter --net=/var/run/netns/local-parent \
225+
ip netns add "local0" 2>/dev/null
226+
nsenter --net=/var/run/netns/local-parent \
227+
ip netns add "local1" 2>/dev/null
223228
}
224229

225230
init_namespaces() {
@@ -237,6 +242,8 @@ del_namespaces() {
237242
log_host "removed ns ${mode}0"
238243
log_host "removed ns ${mode}1"
239244
done
245+
ip netns del "global-parent" &>/dev/null
246+
ip netns del "local-parent" &>/dev/null
240247
}
241248

242249
vm_ssh() {
@@ -287,7 +294,7 @@ check_args() {
287294
}
288295

289296
check_deps() {
290-
for dep in vng ${QEMU} busybox pkill ssh ss socat; do
297+
for dep in vng ${QEMU} busybox pkill ssh ss socat nsenter; do
291298
if [[ ! -x $(command -v "${dep}") ]]; then
292299
echo -e "skip: dependency ${dep} not found!\n"
293300
exit "${KSFT_SKIP}"
@@ -1231,12 +1238,8 @@ test_ns_local_same_cid_ok() {
12311238
}
12321239

12331240
test_ns_host_vsock_child_ns_mode_ok() {
1234-
local orig_mode
1235-
local rc
1236-
1237-
orig_mode=$(cat /proc/sys/net/vsock/child_ns_mode)
1241+
local rc="${KSFT_PASS}"
12381242

1239-
rc="${KSFT_PASS}"
12401243
for mode in "${NS_MODES[@]}"; do
12411244
local ns="${mode}0"
12421245

@@ -1246,15 +1249,13 @@ test_ns_host_vsock_child_ns_mode_ok() {
12461249
continue
12471250
fi
12481251

1249-
if ! echo "${mode}" > /proc/sys/net/vsock/child_ns_mode; then
1250-
log_host "child_ns_mode should be writable to ${mode}"
1252+
if ! echo "${mode}" | ip netns exec "${ns}" \
1253+
tee /proc/sys/net/vsock/child_ns_mode &>/dev/null; then
12511254
rc="${KSFT_FAIL}"
12521255
continue
12531256
fi
12541257
done
12551258

1256-
echo "${orig_mode}" > /proc/sys/net/vsock/child_ns_mode
1257-
12581259
return "${rc}"
12591260
}
12601261

0 commit comments

Comments
 (0)