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)
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 */
*
* - /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:
*
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;