This answerGiven that it only affects the mount namespace, I am extremely suspicious that this is copied & reworded fromdue to one of the question post What code prevents mount namespace loops? In a more complex case involving mount propagationloop prevention checks .
The following commands return an error:
# touch /tmp/a
# mount --bind /proc/self/ns/mnt /tmp/a
mount: /tmp/a: wrong fs type, bad option, bad superblock on /proc/self/ns/mnt, missing codepage or helper program, or other error.
This is because the kernel code (see extracts below) prevents a simplefor mount namespace loopnamespaces. The code comments explain why this is I do not allowed. The lifetime of a mount namespacethink it is tracked by a simple reference count. If you have a loop where mount namespaces A and B both reference the other, then both A and B will always have at least one referenceexact same case as the link talks about, and they wouldbecause never be freed. The allocated memory would be lostunshare --mount defaults to setting mount propagation to private, until you rebooted the entire systemi.e. disabling it.
For comparisonHowever, the kernel allows the followingto protect against certain race conditions, which is not a loop:
# unshare -m
# echo $$
8456
# kill -STOP $$
[1]+ Stopped unshare -m
# touch /tmp/a
# mount --bind /proc/8456/ns/mnt /tmp/a
#
# umount /tmp/a # cleanup
#
If I try to createthink full correctness might indeed require that you mount your mount namespaces inside a loop usingdirectory which has private mount propagation,. I also think it willmight be cleanest also fail(easiest to debug) if you use unbindable. I suspect this is what happens in your question. Shared mounts (i.eI think unbindable already includes all the effects of private). pr
# mount --make-shared /tmp
# unshare -m --propagation shared
# echo $$
8456
# kill -STOP $$
[1]+ Stopped unshare -m
# mount --bind /proc/8456/ns/mnt /tmp/a
mount: /tmp/a: wrong fs type, bad option, bad superblock on /proc/9061/ns/mnt, missing codepage or helper program, or other error.
But ifIn general I remove the mount propagation, no loopthink this is createdthe safest approach, and it succeeds:
# unshare -m --propagation private
# echo $$
8456
# kill -STOP $$
[1]+ Stopped unshare -m
# mount --bind /proc/8456/ns/mnt /tmp/a
#
# umount /tmp/a # cleanup
Kernel code which handles the simpler case
https://elixir.bootlin.com/linux/v4.18/source/fs/namespace.c
static bool mnt_ns_loop(struct dentry *dentry)
{
/* Could bind mounting the mount namespace inode cause a
* mount namespace loop?
*/
struct mnt_namespace *mnt_ns;
if (!is_mnt_ns_file(dentry))
return false;
mnt_ns = to_mnt_ns(get_proc_ns(dentry->d_inode));
return current->nsproxy->mnt_ns->seq >= mnt_ns->seq;
}
..to avoid ever triggering such a problem.
err = -EINVAL;
if (mnt_ns_loop(old_path.dentry))
goto out;
My race condition is hypothetical. I would not expect you to be hitting it most of the time. So I do not know what your actual problem is.
* Assign a sequence number so we can detect when we attempt to bind
* mount a reference to an older mount namespace into the current
* mount namespace, preventing reference counting loops. A 64bit
* number incrementing at 10Ghz will take 12,427 years to wrap which
* is effectively never, so we can ignore the possibility.
*/
static atomic64_t mnt_ns_seq = ATOMIC64_INIT(1);
static struct mnt_namespace *alloc_mnt_ns(struct user_namespace *user_ns)