mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-04-04 21:42:31 -04:00
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/20260218-vsock-ns-write-once-v2-0-19e4c50d509a@meta.com v1: https://lore.kernel.org/r/20260217-vsock-ns-write-once-v1-1-a1fb30f289a9@meta.com ==================== Link: https://patch.msgid.link/20260223-vsock-ns-write-once-v3-0-c0cde6959923@meta.com Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
@@ -594,6 +594,9 @@ Values:
|
||||
their sockets will only be able to connect within their own
|
||||
namespace.
|
||||
|
||||
The first write to ``child_ns_mode`` locks its value. Subsequent writes of the
|
||||
same value succeed, but writing a different value returns ``-EBUSY``.
|
||||
|
||||
Changing ``child_ns_mode`` only affects namespaces created after the change;
|
||||
it does not modify the current namespace or any existing children.
|
||||
|
||||
|
||||
@@ -276,10 +276,19 @@ static inline bool vsock_net_mode_global(struct vsock_sock *vsk)
|
||||
return vsock_net_mode(sock_net(sk_vsock(vsk))) == VSOCK_NET_MODE_GLOBAL;
|
||||
}
|
||||
|
||||
static inline void vsock_net_set_child_mode(struct net *net,
|
||||
static inline bool vsock_net_set_child_mode(struct net *net,
|
||||
enum vsock_net_mode mode)
|
||||
{
|
||||
WRITE_ONCE(net->vsock.child_ns_mode, mode);
|
||||
int new_locked = mode + 1;
|
||||
int old_locked = 0; /* unlocked */
|
||||
|
||||
if (try_cmpxchg(&net->vsock.child_ns_mode_locked,
|
||||
&old_locked, new_locked)) {
|
||||
WRITE_ONCE(net->vsock.child_ns_mode, mode);
|
||||
return true;
|
||||
}
|
||||
|
||||
return old_locked == new_locked;
|
||||
}
|
||||
|
||||
static inline enum vsock_net_mode vsock_net_child_mode(struct net *net)
|
||||
|
||||
@@ -17,5 +17,8 @@ struct netns_vsock {
|
||||
|
||||
enum vsock_net_mode mode;
|
||||
enum vsock_net_mode child_ns_mode;
|
||||
|
||||
/* 0 = unlocked, 1 = locked to global, 2 = locked to local */
|
||||
int child_ns_mode_locked;
|
||||
};
|
||||
#endif /* __NET_NET_NAMESPACE_VSOCK_H */
|
||||
|
||||
@@ -90,16 +90,20 @@
|
||||
*
|
||||
* - /proc/sys/net/vsock/ns_mode (read-only) reports the current namespace's
|
||||
* mode, which is set at namespace creation and immutable thereafter.
|
||||
* - /proc/sys/net/vsock/child_ns_mode (writable) controls what mode future
|
||||
* - /proc/sys/net/vsock/child_ns_mode (write-once) controls what mode future
|
||||
* child namespaces will inherit when created. The initial value matches
|
||||
* the namespace's own ns_mode.
|
||||
*
|
||||
* Changing child_ns_mode only affects newly created namespaces, not the
|
||||
* current namespace or existing children. A "local" namespace cannot set
|
||||
* child_ns_mode to "global". At namespace creation, ns_mode is inherited
|
||||
* from the parent's child_ns_mode.
|
||||
* child_ns_mode to "global". child_ns_mode is write-once, so that it may be
|
||||
* configured and locked down by a namespace manager. Writing a different
|
||||
* value after the first write returns -EBUSY. At namespace creation, ns_mode
|
||||
* is inherited from the parent's child_ns_mode.
|
||||
*
|
||||
* The init_net mode is "global" and cannot be modified.
|
||||
* The init_net mode is "global" and cannot be modified. The init_net
|
||||
* child_ns_mode is also write-once, so an init process (e.g. systemd) can
|
||||
* set it to "local" to ensure all new namespaces inherit local mode.
|
||||
*
|
||||
* The modes affect the allocation and accessibility of CIDs as follows:
|
||||
*
|
||||
@@ -2853,7 +2857,8 @@ static int vsock_net_child_mode_string(const struct ctl_table *table, int write,
|
||||
new_mode == VSOCK_NET_MODE_GLOBAL)
|
||||
return -EPERM;
|
||||
|
||||
vsock_net_set_child_mode(net, new_mode);
|
||||
if (!vsock_net_set_child_mode(net, new_mode))
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -210,16 +210,21 @@ check_result() {
|
||||
}
|
||||
|
||||
add_namespaces() {
|
||||
local orig_mode
|
||||
orig_mode=$(cat /proc/sys/net/vsock/child_ns_mode)
|
||||
ip netns add "global-parent" 2>/dev/null
|
||||
echo "global" | ip netns exec "global-parent" \
|
||||
tee /proc/sys/net/vsock/child_ns_mode &>/dev/null
|
||||
ip netns add "local-parent" 2>/dev/null
|
||||
echo "local" | ip netns exec "local-parent" \
|
||||
tee /proc/sys/net/vsock/child_ns_mode &>/dev/null
|
||||
|
||||
for mode in "${NS_MODES[@]}"; do
|
||||
echo "${mode}" > /proc/sys/net/vsock/child_ns_mode
|
||||
ip netns add "${mode}0" 2>/dev/null
|
||||
ip netns add "${mode}1" 2>/dev/null
|
||||
done
|
||||
|
||||
echo "${orig_mode}" > /proc/sys/net/vsock/child_ns_mode
|
||||
nsenter --net=/var/run/netns/global-parent \
|
||||
ip netns add "global0" 2>/dev/null
|
||||
nsenter --net=/var/run/netns/global-parent \
|
||||
ip netns add "global1" 2>/dev/null
|
||||
nsenter --net=/var/run/netns/local-parent \
|
||||
ip netns add "local0" 2>/dev/null
|
||||
nsenter --net=/var/run/netns/local-parent \
|
||||
ip netns add "local1" 2>/dev/null
|
||||
}
|
||||
|
||||
init_namespaces() {
|
||||
@@ -237,6 +242,8 @@ del_namespaces() {
|
||||
log_host "removed ns ${mode}0"
|
||||
log_host "removed ns ${mode}1"
|
||||
done
|
||||
ip netns del "global-parent" &>/dev/null
|
||||
ip netns del "local-parent" &>/dev/null
|
||||
}
|
||||
|
||||
vm_ssh() {
|
||||
@@ -287,7 +294,7 @@ check_args() {
|
||||
}
|
||||
|
||||
check_deps() {
|
||||
for dep in vng ${QEMU} busybox pkill ssh ss socat; do
|
||||
for dep in vng ${QEMU} busybox pkill ssh ss socat nsenter; do
|
||||
if [[ ! -x $(command -v "${dep}") ]]; then
|
||||
echo -e "skip: dependency ${dep} not found!\n"
|
||||
exit "${KSFT_SKIP}"
|
||||
@@ -1231,12 +1238,8 @@ test_ns_local_same_cid_ok() {
|
||||
}
|
||||
|
||||
test_ns_host_vsock_child_ns_mode_ok() {
|
||||
local orig_mode
|
||||
local rc
|
||||
local rc="${KSFT_PASS}"
|
||||
|
||||
orig_mode=$(cat /proc/sys/net/vsock/child_ns_mode)
|
||||
|
||||
rc="${KSFT_PASS}"
|
||||
for mode in "${NS_MODES[@]}"; do
|
||||
local ns="${mode}0"
|
||||
|
||||
@@ -1246,15 +1249,13 @@ test_ns_host_vsock_child_ns_mode_ok() {
|
||||
continue
|
||||
fi
|
||||
|
||||
if ! echo "${mode}" > /proc/sys/net/vsock/child_ns_mode; then
|
||||
log_host "child_ns_mode should be writable to ${mode}"
|
||||
if ! echo "${mode}" | ip netns exec "${ns}" \
|
||||
tee /proc/sys/net/vsock/child_ns_mode &>/dev/null; then
|
||||
rc="${KSFT_FAIL}"
|
||||
continue
|
||||
fi
|
||||
done
|
||||
|
||||
echo "${orig_mode}" > /proc/sys/net/vsock/child_ns_mode
|
||||
|
||||
return "${rc}"
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user