nsfs: validate ioctls
authorChristian Brauner <brauner@kernel.org>
Wed, 19 Feb 2025 16:40:28 +0000 (17:40 +0100)
committerChristian Brauner <brauner@kernel.org>
Thu, 20 Feb 2025 08:13:49 +0000 (09:13 +0100)
Nsfs supports extensible and non-extensible ioctls. Validate both types
to prevent confusion.

Link: https://lore.kernel.org/r/20250219-work-nsfs-v1-1-21128d73c5e8@kernel.org
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Christian Brauner <brauner@kernel.org>
fs/nsfs.c

index 663f8656158d52d391ba80ef1d320197d3d654e0..1ab705bb938672e6b6c8d3224a0a73edaadfc98a 100644 (file)
--- a/fs/nsfs.c
+++ b/fs/nsfs.c
@@ -152,19 +152,49 @@ static int copy_ns_info_to_user(const struct mnt_namespace *mnt_ns,
        return 0;
 }
 
+static bool nsfs_ioctl_valid(unsigned int cmd)
+{
+       switch (cmd) {
+       case NS_GET_USERNS:
+       case NS_GET_PARENT:
+       case NS_GET_NSTYPE:
+       case NS_GET_OWNER_UID:
+       case NS_GET_MNTNS_ID:
+       case NS_GET_PID_FROM_PIDNS:
+       case NS_GET_TGID_FROM_PIDNS:
+       case NS_GET_PID_IN_PIDNS:
+       case NS_GET_TGID_IN_PIDNS:
+               return (_IOC_TYPE(cmd) == _IOC_TYPE(cmd));
+       }
+
+       /* Extensible ioctls require some extra handling. */
+       switch (_IOC_NR(cmd)) {
+       case _IOC_NR(NS_MNT_GET_INFO):
+       case _IOC_NR(NS_MNT_GET_NEXT):
+       case _IOC_NR(NS_MNT_GET_PREV):
+               return (_IOC_TYPE(cmd) == _IOC_TYPE(cmd));
+       }
+
+       return false;
+}
+
 static long ns_ioctl(struct file *filp, unsigned int ioctl,
                        unsigned long arg)
 {
        struct user_namespace *user_ns;
        struct pid_namespace *pid_ns;
        struct task_struct *tsk;
-       struct ns_common *ns = get_proc_ns(file_inode(filp));
+       struct ns_common *ns;
        struct mnt_namespace *mnt_ns;
        bool previous = false;
        uid_t __user *argp;
        uid_t uid;
        int ret;
 
+       if (!nsfs_ioctl_valid(ioctl))
+               return -ENOIOCTLCMD;
+
+       ns = get_proc_ns(file_inode(filp));
        switch (ioctl) {
        case NS_GET_USERNS:
                return open_related_ns(ns, ns_get_owner);