debugfs: Only clobber mode/uid/gid on remount if asked
authorBrian Norris <briannorris@chromium.org>
Mon, 12 Sep 2022 23:31:42 +0000 (16:31 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 24 Sep 2022 12:01:37 +0000 (14:01 +0200)
Users may have explicitly configured their debugfs permissions; we
shouldn't overwrite those just because a second mount appeared.

Only clobber if the options were provided at mount time.

Existing behavior:

  ## Pre-existing status: debugfs is 0755.
  # chmod 755 /sys/kernel/debug/
  # stat -c '%A' /sys/kernel/debug/
  drwxr-xr-x

  ## New mount sets kernel-default permissions:
  # mount -t debugfs none /mnt/foo
  # stat -c '%A' /mnt/foo
  drwx------

  ## Unexpected: the original mount changed permissions:
  # stat -c '%A' /sys/kernel/debug
  drwx------

New behavior:

  ## Pre-existing status: debugfs is 0755.
  # chmod 755 /sys/kernel/debug/
  # stat -c '%A' /sys/kernel/debug/
  drwxr-xr-x

  ## New mount inherits existing permissions:
  # mount -t debugfs none /mnt/foo
  # stat -c '%A' /mnt/foo
  drwxr-xr-x

  ## Expected: old mount is unchanged:
  # stat -c '%A' /sys/kernel/debug
  drwxr-xr-x

Full test cases are being submitted to LTP.

Signed-off-by: Brian Norris <briannorris@chromium.org>
Link: https://lore.kernel.org/r/20220912163042.v3.1.Icbd40fce59f55ad74b80e5d435ea233579348a78@changeid
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
fs/debugfs/inode.c

index 232cfdf095aeb3ec6c67b1ee512f791a7192cdc2..2e8e112b19930dcfd8a308645692258d96879de9 100644 (file)
@@ -82,6 +82,8 @@ struct debugfs_mount_opts {
        kuid_t uid;
        kgid_t gid;
        umode_t mode;
+       /* Opt_* bitfield. */
+       unsigned int opts;
 };
 
 enum {
@@ -111,6 +113,7 @@ static int debugfs_parse_options(char *data, struct debugfs_mount_opts *opts)
        kgid_t gid;
        char *p;
 
+       opts->opts = 0;
        opts->mode = DEBUGFS_DEFAULT_MODE;
 
        while ((p = strsep(&data, ",")) != NULL) {
@@ -145,24 +148,44 @@ static int debugfs_parse_options(char *data, struct debugfs_mount_opts *opts)
                 * but traditionally debugfs has ignored all mount options
                 */
                }
+
+               opts->opts |= BIT(token);
        }
 
        return 0;
 }
 
-static int debugfs_apply_options(struct super_block *sb)
+static void _debugfs_apply_options(struct super_block *sb, bool remount)
 {
        struct debugfs_fs_info *fsi = sb->s_fs_info;
        struct inode *inode = d_inode(sb->s_root);
        struct debugfs_mount_opts *opts = &fsi->mount_opts;
 
-       inode->i_mode &= ~S_IALLUGO;
-       inode->i_mode |= opts->mode;
+       /*
+        * On remount, only reset mode/uid/gid if they were provided as mount
+        * options.
+        */
 
-       inode->i_uid = opts->uid;
-       inode->i_gid = opts->gid;
+       if (!remount || opts->opts & BIT(Opt_mode)) {
+               inode->i_mode &= ~S_IALLUGO;
+               inode->i_mode |= opts->mode;
+       }
 
-       return 0;
+       if (!remount || opts->opts & BIT(Opt_uid))
+               inode->i_uid = opts->uid;
+
+       if (!remount || opts->opts & BIT(Opt_gid))
+               inode->i_gid = opts->gid;
+}
+
+static void debugfs_apply_options(struct super_block *sb)
+{
+       _debugfs_apply_options(sb, false);
+}
+
+static void debugfs_apply_options_remount(struct super_block *sb)
+{
+       _debugfs_apply_options(sb, true);
 }
 
 static int debugfs_remount(struct super_block *sb, int *flags, char *data)
@@ -175,7 +198,7 @@ static int debugfs_remount(struct super_block *sb, int *flags, char *data)
        if (err)
                goto fail;
 
-       debugfs_apply_options(sb);
+       debugfs_apply_options_remount(sb);
 
 fail:
        return err;