Blame SOURCES/0055-nspawn-beef-up-netns-checking-a-bit-for-compat-with-.patch

a3e2b5
From 2115fcc1e673079fe76e949ac0904267075c25a4 Mon Sep 17 00:00:00 2001
a3e2b5
From: Lennart Poettering <lennart@poettering.net>
a3e2b5
Date: Wed, 31 Oct 2018 13:04:20 +0100
a3e2b5
Subject: [PATCH] nspawn: beef up netns checking a bit, for compat with old
a3e2b5
 kernels
a3e2b5
a3e2b5
Fixes: #10544
a3e2b5
a3e2b5
Cherry-picked from: 6619ad889da260cf83079cc74a85d571acd1df5a
a3e2b5
---
a3e2b5
 src/basic/stat-util.c     | 40 +++++++++++++++++++++++++++++++++++----
a3e2b5
 src/nspawn/nspawn.c       |  8 +++++---
a3e2b5
 src/test/test-stat-util.c | 15 +++++++++++++++
a3e2b5
 3 files changed, 56 insertions(+), 7 deletions(-)
a3e2b5
a3e2b5
diff --git a/src/basic/stat-util.c b/src/basic/stat-util.c
a3e2b5
index 07154e25bb..26aee9bad6 100644
a3e2b5
--- a/src/basic/stat-util.c
a3e2b5
+++ b/src/basic/stat-util.c
a3e2b5
@@ -204,15 +204,47 @@ int fd_is_network_fs(int fd) {
a3e2b5
 }
a3e2b5
 
a3e2b5
 int fd_is_network_ns(int fd) {
a3e2b5
+        struct statfs s;
a3e2b5
         int r;
a3e2b5
 
a3e2b5
-        r = fd_is_fs_type(fd, NSFS_MAGIC);
a3e2b5
-        if (r <= 0)
a3e2b5
-                return r;
a3e2b5
+        /* Checks whether the specified file descriptor refers to a network namespace. On old kernels there's no nice
a3e2b5
+         * way to detect that, hence on those we'll return a recognizable error (EUCLEAN), so that callers can handle
a3e2b5
+         * this somewhat nicely.
a3e2b5
+         *
a3e2b5
+         * This function returns > 0 if the fd definitely refers to a network namespace, 0 if it definitely does not
a3e2b5
+         * refer to a network namespace, -EUCLEAN if we can't determine, and other negative error codes on error. */
a3e2b5
+
a3e2b5
+        if (fstatfs(fd, &s) < 0)
a3e2b5
+                return -errno;
a3e2b5
+
a3e2b5
+        if (!is_fs_type(&s, NSFS_MAGIC)) {
a3e2b5
+                /* On really old kernels, there was no "nsfs", and network namespace sockets belonged to procfs
a3e2b5
+                 * instead. Handle that in a somewhat smart way. */
a3e2b5
+
a3e2b5
+                if (is_fs_type(&s, PROC_SUPER_MAGIC)) {
a3e2b5
+                        struct statfs t;
a3e2b5
+
a3e2b5
+                        /* OK, so it is procfs. Let's see if our own network namespace is procfs, too. If so, then the
a3e2b5
+                         * passed fd might refer to a network namespace, but we can't know for sure. In that case,
a3e2b5
+                         * return a recognizable error. */
a3e2b5
+
a3e2b5
+                        if (statfs("/proc/self/ns/net", &t) < 0)
a3e2b5
+                                return -errno;
a3e2b5
+
a3e2b5
+                        if (s.f_type == t.f_type)
a3e2b5
+                                return -EUCLEAN; /* It's possible, we simply don't know */
a3e2b5
+                }
a3e2b5
+
a3e2b5
+                return 0; /* No! */
a3e2b5
+        }
a3e2b5
 
a3e2b5
         r = ioctl(fd, NS_GET_NSTYPE);
a3e2b5
-        if (r < 0)
a3e2b5
+        if (r < 0) {
a3e2b5
+                if (errno == ENOTTY) /* Old kernels didn't know this ioctl, let's also return a recognizable error in that case */
a3e2b5
+                        return -EUCLEAN;
a3e2b5
+
a3e2b5
                 return -errno;
a3e2b5
+        }
a3e2b5
 
a3e2b5
         return r == CLONE_NEWNET;
a3e2b5
 }
a3e2b5
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
a3e2b5
index 56877bd932..8aec893a69 100644
a3e2b5
--- a/src/nspawn/nspawn.c
a3e2b5
+++ b/src/nspawn/nspawn.c
a3e2b5
@@ -3701,10 +3701,12 @@ static int run(int master,
a3e2b5
                         return log_error_errno(errno, "Cannot open file %s: %m", arg_network_namespace_path);
a3e2b5
 
a3e2b5
                 r = fd_is_network_ns(netns_fd);
a3e2b5
-                if (r < 0 && r != -ENOTTY)
a3e2b5
+                if (r == -EUCLEAN)
a3e2b5
+                        log_debug_errno(r, "Cannot determine if passed network namespace path '%s' really refers to a network namespace, assuming it does.", arg_network_namespace_path);
a3e2b5
+                else if (r < 0)
a3e2b5
                         return log_error_errno(r, "Failed to check %s fs type: %m", arg_network_namespace_path);
a3e2b5
-                if (r == 0) {
a3e2b5
-                        log_error("Path %s doesn't refer to a network namespace", arg_network_namespace_path);
a3e2b5
+                else if (r == 0) {
a3e2b5
+                        log_error("Path %s doesn't refer to a network namespace, refusing.", arg_network_namespace_path);
a3e2b5
                         return -EINVAL;
a3e2b5
                 }
a3e2b5
         }
a3e2b5
diff --git a/src/test/test-stat-util.c b/src/test/test-stat-util.c
a3e2b5
index 43f56a6c20..2b0564d8a0 100644
a3e2b5
--- a/src/test/test-stat-util.c
a3e2b5
+++ b/src/test/test-stat-util.c
a3e2b5
@@ -67,11 +67,26 @@ static void test_path_is_temporary_fs(void) {
a3e2b5
         assert_se(path_is_temporary_fs("/i-dont-exist") == -ENOENT);
a3e2b5
 }
a3e2b5
 
a3e2b5
+static void test_fd_is_network_ns(void) {
a3e2b5
+        _cleanup_close_ int fd = -1;
a3e2b5
+        assert_se(fd_is_network_ns(STDIN_FILENO) == 0);
a3e2b5
+        assert_se(fd_is_network_ns(STDERR_FILENO) == 0);
a3e2b5
+        assert_se(fd_is_network_ns(STDOUT_FILENO) == 0);
a3e2b5
+
a3e2b5
+        assert_se((fd = open("/proc/self/ns/mnt", O_CLOEXEC|O_RDONLY)) >= 0);
a3e2b5
+        assert_se(IN_SET(fd_is_network_ns(fd), 0, -EUCLEAN));
a3e2b5
+        fd = safe_close(fd);
a3e2b5
+
a3e2b5
+        assert_se((fd = open("/proc/self/ns/net", O_CLOEXEC|O_RDONLY)) >= 0);
a3e2b5
+        assert_se(IN_SET(fd_is_network_ns(fd), 1, -EUCLEAN));
a3e2b5
+}
a3e2b5
+
a3e2b5
 int main(int argc, char *argv[]) {
a3e2b5
         test_files_same();
a3e2b5
         test_is_symlink();
a3e2b5
         test_path_is_fs_type();
a3e2b5
         test_path_is_temporary_fs();
a3e2b5
+        test_fd_is_network_ns();
a3e2b5
 
a3e2b5
         return 0;
a3e2b5
 }