Merge branch 'work.quota-compat' of git://git.kernel.org/pub/scm/linux/kernel/git...
authorLinus Torvalds <torvalds@linux-foundation.org>
Mon, 12 Oct 2020 23:37:13 +0000 (16:37 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Mon, 12 Oct 2020 23:37:13 +0000 (16:37 -0700)
Pull compat quotactl cleanups from Al Viro:
 "More Christoph's compat cleanups: quotactl(2)"

* 'work.quota-compat' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs:
  quota: simplify the quotactl compat handling
  compat: add a compat_need_64bit_alignment_fixup() helper
  compat: lift compat_s64 and compat_u64 to <asm-generic/compat.h>

17 files changed:
arch/arm64/include/asm/compat.h
arch/mips/include/asm/compat.h
arch/parisc/include/asm/compat.h
arch/powerpc/include/asm/compat.h
arch/s390/include/asm/compat.h
arch/sparc/include/asm/compat.h
arch/x86/entry/syscalls/syscall_32.tbl
arch/x86/include/asm/compat.h
fs/quota/Kconfig
fs/quota/Makefile
fs/quota/compat.c [deleted file]
fs/quota/compat.h [new file with mode: 0644]
fs/quota/quota.c
include/asm-generic/compat.h
include/linux/compat.h
include/linux/quotaops.h
kernel/sys_ni.c

index 935d2aa231bf066e21d08d15d11eb5246b40d203..23a9fb73c04ff84f57518775b6ec98a24a0ad36d 100644 (file)
@@ -35,8 +35,6 @@ typedef s32           compat_nlink_t;
 typedef u16            compat_ipc_pid_t;
 typedef u32            compat_caddr_t;
 typedef __kernel_fsid_t        compat_fsid_t;
-typedef s64            compat_s64;
-typedef u64            compat_u64;
 
 struct compat_stat {
 #ifdef __AARCH64EB__
index 255afcdd79c94be96fd4116c20aeb609f80e5221..65975712a22dcfaa7d5fabe1a2d7b2b847a4d94c 100644 (file)
@@ -26,8 +26,6 @@ typedef s32           compat_caddr_t;
 typedef struct {
        s32     val[2];
 } compat_fsid_t;
-typedef s64            compat_s64;
-typedef u64            compat_u64;
 
 struct compat_stat {
        compat_dev_t    st_dev;
index 2f4f66a3bac079aa6434514d91da8581891b9e3e..8f33085ff1bd88ad7a46f32adeb4794587027668 100644 (file)
@@ -22,8 +22,6 @@ typedef u32   compat_dev_t;
 typedef u16    compat_nlink_t;
 typedef u16    compat_ipc_pid_t;
 typedef u32    compat_caddr_t;
-typedef s64    compat_s64;
-typedef u64    compat_u64;
 
 struct compat_stat {
        compat_dev_t            st_dev; /* dev_t is 32 bits on parisc */
index 3e3cdfaa76c6a5cac084d7ce8451f9dcc6fd5535..9191fc29e6ed11e4d183e09125f6cb73e0dd7943 100644 (file)
@@ -27,8 +27,6 @@ typedef s16           compat_nlink_t;
 typedef u16            compat_ipc_pid_t;
 typedef u32            compat_caddr_t;
 typedef __kernel_fsid_t        compat_fsid_t;
-typedef s64            compat_s64;
-typedef u64            compat_u64;
 
 struct compat_stat {
        compat_dev_t    st_dev;
index 9547cd5d6cdc21dad62657159b4a764a2bcbaee6..ea5b9c34b7be5bbd7915abbbec211df022004989 100644 (file)
@@ -63,8 +63,6 @@ typedef u16           compat_nlink_t;
 typedef u16            compat_ipc_pid_t;
 typedef u32            compat_caddr_t;
 typedef __kernel_fsid_t        compat_fsid_t;
-typedef s64            compat_s64;
-typedef u64            compat_u64;
 
 typedef struct {
        u32 mask;
index 40a267b3bd5208ce26685b8ce7fe96d7bc703740..b85842cda99fe0d534b168f87559df6cf6e9b190 100644 (file)
@@ -21,8 +21,7 @@ typedef s16           compat_nlink_t;
 typedef u16            compat_ipc_pid_t;
 typedef u32            compat_caddr_t;
 typedef __kernel_fsid_t        compat_fsid_t;
-typedef s64            compat_s64;
-typedef u64            compat_u64;
+
 struct compat_stat {
        compat_dev_t    st_dev;
        compat_ino_t    st_ino;
index 5fbe10ad8a23fca13e858ad31bce1dbb2e69ea60..1324be86cabc167ae0f247ae4c5af10954e9e288 100644 (file)
 128    i386    init_module             sys_init_module
 129    i386    delete_module           sys_delete_module
 130    i386    get_kernel_syms
-131    i386    quotactl                sys_quotactl                    compat_sys_quotactl32
+131    i386    quotactl                sys_quotactl
 132    i386    getpgid                 sys_getpgid
 133    i386    fchdir                  sys_fchdir
 134    i386    bdflush                 sys_bdflush
index d4edf281fff49db852fa9149e1b51249a49ae66d..0e327a01f50fbbff551727d38ee51fe351011254 100644 (file)
@@ -27,8 +27,6 @@ typedef u16           compat_nlink_t;
 typedef u16            compat_ipc_pid_t;
 typedef u32            compat_caddr_t;
 typedef __kernel_fsid_t        compat_fsid_t;
-typedef s64 __attribute__((aligned(4))) compat_s64;
-typedef u64 __attribute__((aligned(4))) compat_u64;
 
 struct compat_stat {
        compat_dev_t    st_dev;
@@ -211,6 +209,7 @@ static inline bool in_compat_syscall(void)
        return in_32bit_syscall();
 }
 #define in_compat_syscall in_compat_syscall    /* override the generic impl */
+#define compat_need_64bit_alignment_fixup in_ia32_syscall
 #endif
 
 struct compat_siginfo;
index d1ceb76adb71e71e69e78464e795376b51899f4e..b59cd172b5f97c8958db42abe91a1180e22d3bfa 100644 (file)
@@ -70,8 +70,3 @@ config QFMT_V2
 config QUOTACTL
        bool
        default n
-
-config QUOTACTL_COMPAT
-       bool
-       depends on QUOTACTL && COMPAT_FOR_U64_ALIGNMENT
-       default y
index f2b49d0f0287c95e3481a2d5f566b5f925f694c1..9160639daffa758520574729689523946cc84f4b 100644 (file)
@@ -4,5 +4,4 @@ obj-$(CONFIG_QFMT_V1)           += quota_v1.o
 obj-$(CONFIG_QFMT_V2)          += quota_v2.o
 obj-$(CONFIG_QUOTA_TREE)       += quota_tree.o
 obj-$(CONFIG_QUOTACTL)         += quota.o kqid.o
-obj-$(CONFIG_QUOTACTL_COMPAT)  += compat.o
 obj-$(CONFIG_QUOTA_NETLINK_INTERFACE)  += netlink.o
diff --git a/fs/quota/compat.c b/fs/quota/compat.c
deleted file mode 100644 (file)
index c305728..0000000
+++ /dev/null
@@ -1,120 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-
-#include <linux/syscalls.h>
-#include <linux/compat.h>
-#include <linux/quotaops.h>
-
-/*
- * This code works only for 32 bit quota tools over 64 bit OS (x86_64, ia64)
- * and is necessary due to alignment problems.
- */
-struct compat_if_dqblk {
-       compat_u64 dqb_bhardlimit;
-       compat_u64 dqb_bsoftlimit;
-       compat_u64 dqb_curspace;
-       compat_u64 dqb_ihardlimit;
-       compat_u64 dqb_isoftlimit;
-       compat_u64 dqb_curinodes;
-       compat_u64 dqb_btime;
-       compat_u64 dqb_itime;
-       compat_uint_t dqb_valid;
-};
-
-/* XFS structures */
-struct compat_fs_qfilestat {
-       compat_u64 dqb_bhardlimit;
-       compat_u64 qfs_nblks;
-       compat_uint_t qfs_nextents;
-};
-
-struct compat_fs_quota_stat {
-       __s8            qs_version;
-       __u16           qs_flags;
-       __s8            qs_pad;
-       struct compat_fs_qfilestat      qs_uquota;
-       struct compat_fs_qfilestat      qs_gquota;
-       compat_uint_t   qs_incoredqs;
-       compat_int_t    qs_btimelimit;
-       compat_int_t    qs_itimelimit;
-       compat_int_t    qs_rtbtimelimit;
-       __u16           qs_bwarnlimit;
-       __u16           qs_iwarnlimit;
-};
-
-COMPAT_SYSCALL_DEFINE4(quotactl32, unsigned int, cmd,
-                      const char __user *, special, qid_t, id,
-                      void __user *, addr)
-{
-       unsigned int cmds;
-       struct if_dqblk __user *dqblk;
-       struct compat_if_dqblk __user *compat_dqblk;
-       struct fs_quota_stat __user *fsqstat;
-       struct compat_fs_quota_stat __user *compat_fsqstat;
-       compat_uint_t data;
-       u16 xdata;
-       long ret;
-
-       cmds = cmd >> SUBCMDSHIFT;
-
-       switch (cmds) {
-       case Q_GETQUOTA:
-               dqblk = compat_alloc_user_space(sizeof(struct if_dqblk));
-               compat_dqblk = addr;
-               ret = kernel_quotactl(cmd, special, id, dqblk);
-               if (ret)
-                       break;
-               if (copy_in_user(compat_dqblk, dqblk, sizeof(*compat_dqblk)) ||
-                       get_user(data, &dqblk->dqb_valid) ||
-                       put_user(data, &compat_dqblk->dqb_valid))
-                       ret = -EFAULT;
-               break;
-       case Q_SETQUOTA:
-               dqblk = compat_alloc_user_space(sizeof(struct if_dqblk));
-               compat_dqblk = addr;
-               ret = -EFAULT;
-               if (copy_in_user(dqblk, compat_dqblk, sizeof(*compat_dqblk)) ||
-                       get_user(data, &compat_dqblk->dqb_valid) ||
-                       put_user(data, &dqblk->dqb_valid))
-                       break;
-               ret = kernel_quotactl(cmd, special, id, dqblk);
-               break;
-       case Q_XGETQSTAT:
-               fsqstat = compat_alloc_user_space(sizeof(struct fs_quota_stat));
-               compat_fsqstat = addr;
-               ret = kernel_quotactl(cmd, special, id, fsqstat);
-               if (ret)
-                       break;
-               ret = -EFAULT;
-               /* Copying qs_version, qs_flags, qs_pad */
-               if (copy_in_user(compat_fsqstat, fsqstat,
-                       offsetof(struct compat_fs_quota_stat, qs_uquota)))
-                       break;
-               /* Copying qs_uquota */
-               if (copy_in_user(&compat_fsqstat->qs_uquota,
-                       &fsqstat->qs_uquota,
-                       sizeof(compat_fsqstat->qs_uquota)) ||
-                       get_user(data, &fsqstat->qs_uquota.qfs_nextents) ||
-                       put_user(data, &compat_fsqstat->qs_uquota.qfs_nextents))
-                       break;
-               /* Copying qs_gquota */
-               if (copy_in_user(&compat_fsqstat->qs_gquota,
-                       &fsqstat->qs_gquota,
-                       sizeof(compat_fsqstat->qs_gquota)) ||
-                       get_user(data, &fsqstat->qs_gquota.qfs_nextents) ||
-                       put_user(data, &compat_fsqstat->qs_gquota.qfs_nextents))
-                       break;
-               /* Copying the rest */
-               if (copy_in_user(&compat_fsqstat->qs_incoredqs,
-                       &fsqstat->qs_incoredqs,
-                       sizeof(struct compat_fs_quota_stat) -
-                       offsetof(struct compat_fs_quota_stat, qs_incoredqs)) ||
-                       get_user(xdata, &fsqstat->qs_iwarnlimit) ||
-                       put_user(xdata, &compat_fsqstat->qs_iwarnlimit))
-                       break;
-               ret = 0;
-               break;
-       default:
-               ret = kernel_quotactl(cmd, special, id, addr);
-       }
-       return ret;
-}
diff --git a/fs/quota/compat.h b/fs/quota/compat.h
new file mode 100644 (file)
index 0000000..ef7d1e1
--- /dev/null
@@ -0,0 +1,34 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/compat.h>
+
+struct compat_if_dqblk {
+       compat_u64                      dqb_bhardlimit;
+       compat_u64                      dqb_bsoftlimit;
+       compat_u64                      dqb_curspace;
+       compat_u64                      dqb_ihardlimit;
+       compat_u64                      dqb_isoftlimit;
+       compat_u64                      dqb_curinodes;
+       compat_u64                      dqb_btime;
+       compat_u64                      dqb_itime;
+       compat_uint_t                   dqb_valid;
+};
+
+struct compat_fs_qfilestat {
+       compat_u64                      dqb_bhardlimit;
+       compat_u64                      qfs_nblks;
+       compat_uint_t                   qfs_nextents;
+};
+
+struct compat_fs_quota_stat {
+       __s8                            qs_version;
+       __u16                           qs_flags;
+       __s8                            qs_pad;
+       struct compat_fs_qfilestat      qs_uquota;
+       struct compat_fs_qfilestat      qs_gquota;
+       compat_uint_t                   qs_incoredqs;
+       compat_int_t                    qs_btimelimit;
+       compat_int_t                    qs_itimelimit;
+       compat_int_t                    qs_rtbtimelimit;
+       __u16                           qs_bwarnlimit;
+       __u16                           qs_iwarnlimit;
+};
index 47f9e151988b3eda074b4e2eef05273392091a38..6b37d58f1067d4f28da5a172610b3ffdb1c4e90f 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/types.h>
 #include <linux/writeback.h>
 #include <linux/nospec.h>
+#include "compat.h"
 
 static int check_quotactl_permission(struct super_block *sb, int type, int cmd,
                                     qid_t id)
@@ -211,8 +212,18 @@ static int quota_getquota(struct super_block *sb, int type, qid_t id,
        if (ret)
                return ret;
        copy_to_if_dqblk(&idq, &fdq);
-       if (copy_to_user(addr, &idq, sizeof(idq)))
-               return -EFAULT;
+
+       if (compat_need_64bit_alignment_fixup()) {
+               struct compat_if_dqblk __user *compat_dqblk = addr;
+
+               if (copy_to_user(compat_dqblk, &idq, sizeof(*compat_dqblk)))
+                       return -EFAULT;
+               if (put_user(idq.dqb_valid, &compat_dqblk->dqb_valid))
+                       return -EFAULT;
+       } else {
+               if (copy_to_user(addr, &idq, sizeof(idq)))
+                       return -EFAULT;
+       }
        return 0;
 }
 
@@ -277,8 +288,16 @@ static int quota_setquota(struct super_block *sb, int type, qid_t id,
        struct if_dqblk idq;
        struct kqid qid;
 
-       if (copy_from_user(&idq, addr, sizeof(idq)))
-               return -EFAULT;
+       if (compat_need_64bit_alignment_fixup()) {
+               struct compat_if_dqblk __user *compat_dqblk = addr;
+
+               if (copy_from_user(&idq, compat_dqblk, sizeof(*compat_dqblk)) ||
+                   get_user(idq.dqb_valid, &compat_dqblk->dqb_valid))
+                       return -EFAULT;
+       } else {
+               if (copy_from_user(&idq, addr, sizeof(idq)))
+                       return -EFAULT;
+       }
        if (!sb->s_qcop->set_dqblk)
                return -ENOSYS;
        qid = make_kqid(current_user_ns(), type, id);
@@ -382,6 +401,33 @@ static int quota_getstate(struct super_block *sb, int type,
        return 0;
 }
 
+static int compat_copy_fs_qfilestat(struct compat_fs_qfilestat __user *to,
+               struct fs_qfilestat *from)
+{
+       if (copy_to_user(to, from, sizeof(*to)) ||
+           put_user(from->qfs_nextents, &to->qfs_nextents))
+               return -EFAULT;
+       return 0;
+}
+
+static int compat_copy_fs_quota_stat(struct compat_fs_quota_stat __user *to,
+               struct fs_quota_stat *from)
+{
+       if (put_user(from->qs_version, &to->qs_version) ||
+           put_user(from->qs_flags, &to->qs_flags) ||
+           put_user(from->qs_pad, &to->qs_pad) ||
+           compat_copy_fs_qfilestat(&to->qs_uquota, &from->qs_uquota) ||
+           compat_copy_fs_qfilestat(&to->qs_gquota, &from->qs_gquota) ||
+           put_user(from->qs_incoredqs, &to->qs_incoredqs) ||
+           put_user(from->qs_btimelimit, &to->qs_btimelimit) ||
+           put_user(from->qs_itimelimit, &to->qs_itimelimit) ||
+           put_user(from->qs_rtbtimelimit, &to->qs_rtbtimelimit) ||
+           put_user(from->qs_bwarnlimit, &to->qs_bwarnlimit) ||
+           put_user(from->qs_iwarnlimit, &to->qs_iwarnlimit))
+               return -EFAULT;
+       return 0;
+}
+
 static int quota_getxstate(struct super_block *sb, int type, void __user *addr)
 {
        struct fs_quota_stat fqs;
@@ -390,9 +436,14 @@ static int quota_getxstate(struct super_block *sb, int type, void __user *addr)
        if (!sb->s_qcop->get_state)
                return -ENOSYS;
        ret = quota_getstate(sb, type, &fqs);
-       if (!ret && copy_to_user(addr, &fqs, sizeof(fqs)))
+       if (ret)
+               return ret;
+
+       if (compat_need_64bit_alignment_fixup())
+               return compat_copy_fs_quota_stat(addr, &fqs);
+       if (copy_to_user(addr, &fqs, sizeof(fqs)))
                return -EFAULT;
-       return ret;
+       return 0;
 }
 
 static int quota_getstatev(struct super_block *sb, int type,
@@ -816,8 +867,8 @@ static struct super_block *quotactl_block(const char __user *special, int cmd)
  * calls. Maybe we need to add the process quotas etc. in the future,
  * but we probably should use rlimits for that.
  */
-int kernel_quotactl(unsigned int cmd, const char __user *special,
-                   qid_t id, void __user *addr)
+SYSCALL_DEFINE4(quotactl, unsigned int, cmd, const char __user *, special,
+               qid_t, id, void __user *, addr)
 {
        uint cmds, type;
        struct super_block *sb = NULL;
@@ -871,9 +922,3 @@ out:
                path_put(pathp);
        return ret;
 }
-
-SYSCALL_DEFINE4(quotactl, unsigned int, cmd, const char __user *, special,
-               qid_t, id, void __user *, addr)
-{
-       return kernel_quotactl(cmd, special, id, addr);
-}
index a86f65bffab8d0da0038215c0f1797c8f5936f1f..30f7b18a36f9393a50cc65f3fe7380ffa6461e33 100644 (file)
@@ -22,4 +22,12 @@ typedef u32 compat_ulong_t;
 typedef u32 compat_uptr_t;
 typedef u32 compat_aio_context_t;
 
+#ifdef CONFIG_COMPAT_FOR_U64_ALIGNMENT
+typedef s64 __attribute__((aligned(4))) compat_s64;
+typedef u64 __attribute__((aligned(4))) compat_u64;
+#else
+typedef s64 compat_s64;
+typedef u64 compat_u64;
+#endif
+
 #endif
index 59b8c8544ae6c290ccfdb4d2ccc0d6616f69b45d..34c48eb1e773b921410c142ee24299b3f101e55e 100644 (file)
@@ -910,6 +910,15 @@ static inline bool in_compat_syscall(void) { return false; }
 
 #endif /* CONFIG_COMPAT */
 
+/*
+ * Some legacy ABIs like the i386 one use less than natural alignment for 64-bit
+ * types, and will need special compat treatment for that.  Most architectures
+ * don't need that special handling even for compat syscalls.
+ */
+#ifndef compat_need_64bit_alignment_fixup
+#define compat_need_64bit_alignment_fixup()            false
+#endif
+
 /*
  * A pointer passed in from user mode. This should not
  * be used for syscall parameters, just declare them
index 9cf0cd3dc88c6864ff8fa5fc498ea2f9feac83c5..a0f6668924d3ef068635e54ceef71d59421fe099 100644 (file)
@@ -27,9 +27,6 @@ static inline bool is_quota_modification(struct inode *inode, struct iattr *ia)
                (ia->ia_valid & ATTR_GID && !gid_eq(ia->ia_gid, inode->i_gid));
 }
 
-int kernel_quotactl(unsigned int cmd, const char __user *special,
-                   qid_t id, void __user *addr);
-
 #if defined(CONFIG_QUOTA)
 
 #define quota_error(sb, fmt, args...) \
index 4d59775ea79c1e6c2ecf10a566c8cd06f593a0a3..c925d1e1777efcf828e2796ce135d0e4c546dc31 100644 (file)
@@ -369,7 +369,6 @@ COND_SYSCALL_COMPAT(fanotify_mark);
 /* x86 */
 COND_SYSCALL(vm86old);
 COND_SYSCALL(modify_ldt);
-COND_SYSCALL_COMPAT(quotactl32);
 COND_SYSCALL(vm86);
 COND_SYSCALL(kexec_file_load);