binfmt_flat: flat_{get,put}_addr_from_rp() should be able to fail
authorAl Viro <viro@zeniv.linux.org.uk>
Tue, 2 May 2017 23:52:17 +0000 (19:52 -0400)
committerAl Viro <viro@zeniv.linux.org.uk>
Mon, 3 Jul 2017 22:44:02 +0000 (18:44 -0400)
on MMU targets EFAULT is possible here.  Make both return 0 or error,
passing what used to be the return value of flat_get_addr_from_rp()
by reference.

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
12 files changed:
arch/arm/include/asm/flat.h
arch/blackfin/include/asm/flat.h
arch/blackfin/kernel/flat.c
arch/c6x/include/asm/flat.h
arch/h8300/include/asm/flat.h
arch/m32r/include/asm/flat.h
arch/m68k/include/asm/flat.h
arch/microblaze/include/asm/flat.h
arch/sh/include/asm/flat.h
arch/xtensa/include/asm/flat.h
fs/binfmt_flat.c
include/linux/flat.h

index acf1d14b89a65ff1364329e543db2f289dd399d1..29d3a1524bce818a287e32b754b8ba9e7ac427eb 100644 (file)
@@ -5,12 +5,31 @@
 #ifndef __ARM_FLAT_H__
 #define __ARM_FLAT_H__
 
+#include <linux/uaccess.h>
+
 #define        flat_argvp_envp_on_stack()              1
 #define        flat_old_ram_flag(flags)                (flags)
 #define        flat_reloc_valid(reloc, size)           ((reloc) <= (size))
-#define        flat_get_addr_from_rp(rp, relval, flags, persistent) \
-       ({ unsigned long __val; __get_user_unaligned(__val, rp); __val; })
-#define        flat_put_addr_at_rp(rp, val, relval)    __put_user_unaligned(val, rp)
+
+static inline int flat_get_addr_from_rp(u32 __user *rp, u32 relval, u32 flags,
+                                       u32 *addr, u32 *persistent)
+{
+#ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
+       return copy_from_user(addr, rp, 4) ? -EFAULT : 0;
+#else
+       return get_user(*addr, rp);
+#endif
+}
+
+static inline int flat_put_addr_at_rp(u32 __user *rp, u32 addr, u32 rel)
+{
+#ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
+       return copy_to_user(rp, &addr, 4) ? -EFAULT : 0;
+#else
+       return put_user(addr, rp);
+#endif
+}
+
 #define        flat_get_relocate_addr(rel)             (rel)
 #define        flat_set_persistent(relval, p)          0
 
index c1314c56dd1898d467baac3b9b3ed70fa1f8c926..296d7f56fbfd005bb77b264474a179bcfb81bc61 100644 (file)
 #define        flat_argvp_envp_on_stack()              0
 #define        flat_old_ram_flag(flags)                (flags)
 
-extern unsigned long bfin_get_addr_from_rp (unsigned long *ptr,
-                                       unsigned long relval,
-                                       unsigned long flags,
-                                       unsigned long *persistent);
+extern unsigned long bfin_get_addr_from_rp (u32 *ptr, u32 relval,
+                                       u32 flags, u32 *persistent);
 
-extern void bfin_put_addr_at_rp(unsigned long *ptr, unsigned long addr,
-                               unsigned long relval);
+extern void bfin_put_addr_at_rp(u32 *ptr, u32 addr, u32 relval);
 
 /* The amount by which a relocation can exceed the program image limits
    without being regarded as an error.  */
 
 #define        flat_reloc_valid(reloc, size)   ((reloc) <= (size))
 
-#define        flat_get_addr_from_rp(rp, relval, flags, persistent)    \
-       bfin_get_addr_from_rp(rp, relval, flags, persistent)
-#define        flat_put_addr_at_rp(rp, val, relval)    \
-       bfin_put_addr_at_rp(rp, val, relval)
+static inline int flat_get_addr_from_rp(u32 __user *rp, u32 relval, u32 flags,
+                                       u32 *addr, u32 *persistent)
+{
+       *addr = bfin_get_addr_from_rp(rp, relval, flags, persistent);
+       return 0;
+}
+
+static inline int flat_put_addr_at_rp(u32 __user *rp, u32 val, u32 relval)
+{
+       bfin_put_addr_at_rp(rp, val, relval);
+       return 0;
+}
 
 /* Convert a relocation entry into an address.  */
 static inline unsigned long
index b5b6584496164d83db5246f2756d4a99addb906f..d29ab6a2e909f301154a65409847f2b0a5256871 100644 (file)
 #define FLAT_BFIN_RELOC_TYPE_16H_BIT 1
 #define FLAT_BFIN_RELOC_TYPE_32_BIT 2
 
-unsigned long bfin_get_addr_from_rp(unsigned long *ptr,
-               unsigned long relval,
-               unsigned long flags,
-               unsigned long *persistent)
+unsigned long bfin_get_addr_from_rp(u32 *ptr,
+               u32 relval,
+               u32 flags,
+               u32 *persistent)
 {
        unsigned short *usptr = (unsigned short *)ptr;
        int type = (relval >> 26) & 7;
-       unsigned long val;
+       u32 val;
 
        switch (type) {
        case FLAT_BFIN_RELOC_TYPE_16_BIT:
@@ -59,8 +59,7 @@ EXPORT_SYMBOL(bfin_get_addr_from_rp);
  * Insert the address ADDR into the symbol reference at RP;
  * RELVAL is the raw relocation-table entry from which RP is derived
  */
-void bfin_put_addr_at_rp(unsigned long *ptr, unsigned long addr,
-               unsigned long relval)
+void bfin_put_addr_at_rp(u32 *ptr, u32 addr, u32 relval)
 {
        unsigned short *usptr = (unsigned short *)ptr;
        int type = (relval >> 26) & 7;
index a1858bd5f6c8a4b349b48f0c49b0b79695b97e38..6f1feb00bd52bc0cb70dc866dd4b31c58965b311 100644 (file)
@@ -1,11 +1,22 @@
 #ifndef __ASM_C6X_FLAT_H
 #define __ASM_C6X_FLAT_H
 
+#include <asm/unaligned.h>
+
 #define flat_argvp_envp_on_stack()                     0
 #define flat_old_ram_flag(flags)                       (flags)
 #define flat_reloc_valid(reloc, size)                  ((reloc) <= (size))
-#define flat_get_addr_from_rp(rp, relval, flags, p)    get_unaligned(rp)
-#define flat_put_addr_at_rp(rp, val, relval)           put_unaligned(val, rp)
+static inline int flat_get_addr_from_rp(u32 __user *rp, u32 relval, u32 flags,
+                                       u32 *addr, u32 *persistent)
+{
+       *addr = get_unaligned((__force u32 *)rp);
+       return 0;
+}
+static inline int flat_put_addr_at_rp(u32 __user *rp, u32 addr, u32 rel)
+{
+       put_unaligned(addr, (__force u32 *)rp);
+       return 0;
+}
 #define flat_get_relocate_addr(rel)                    (rel)
 #define flat_set_persistent(relval, p)                 0
 
index a4898eccf2bf655b11a52e9ea2662301e9a1f2f0..18d024251738dd4e8499f0687842ce776c59816f 100644 (file)
@@ -5,6 +5,8 @@
 #ifndef __H8300_FLAT_H__
 #define __H8300_FLAT_H__
 
+#include <asm/unaligned.h>
+
 #define        flat_argvp_envp_on_stack()              1
 #define        flat_old_ram_flag(flags)                1
 #define        flat_reloc_valid(reloc, size)           ((reloc) <= (size))
  */
 
 #define        flat_get_relocate_addr(rel)             (rel & ~0x00000001)
-#define flat_get_addr_from_rp(rp, relval, flags, persistent) \
-       ({(void)persistent; \
-               get_unaligned(rp) & (((flags) & FLAT_FLAG_GOTPIC) ?     \
-                                    0xffffffff : 0x00ffffff); })
-#define flat_put_addr_at_rp(rp, addr, rel) \
-       put_unaligned(((*(char *)(rp)) << 24) | ((addr) & 0x00ffffff), (rp))
+static inline int flat_get_addr_from_rp(u32 __user *rp, u32 relval, u32 flags,
+                                       u32 *addr, u32 *persistent)
+{
+       u32 val = get_unaligned((__force u32 *)rp);
+       if (!(flags & FLAT_FLAG_GOTPIC)
+               val &= 0x00ffffff;
+       *addr = val;
+       return 0;
+}
+
+static inline int flat_put_addr_at_rp(u32 __user *rp, u32 addr, u32 rel)
+{
+       u32 *p = (__force u32 *)rp;
+       put_unaligned((addr & 0x00ffffff) | (*(char *)p << 24), p);
+       return 0;
+}
 
 #endif /* __H8300_FLAT_H__ */
index 5d711c4688fb9f8162fd017a7e35451ad4c41da6..455ce7ddbf142e9b5878d4259ea1c92d5f7ec05b 100644 (file)
 #define        flat_set_persistent(relval, p)          0
 #define        flat_reloc_valid(reloc, size)           \
        (((reloc) - textlen_for_m32r_lo16_data) <= (size))
-#define flat_get_addr_from_rp(rp, relval, flags, persistent) \
-       m32r_flat_get_addr_from_rp(rp, relval, (text_len) )
-
-#define flat_put_addr_at_rp(rp, addr, relval) \
-       m32r_flat_put_addr_at_rp(rp, addr, relval)
 
 /* Convert a relocation entry into an address.  */
 static inline unsigned long
@@ -57,9 +52,9 @@ flat_get_relocate_addr (unsigned long relval)
 
 static unsigned long textlen_for_m32r_lo16_data = 0;
 
-static inline unsigned long m32r_flat_get_addr_from_rp (unsigned long *rp,
-                                                        unsigned long relval,
-                                                       unsigned long textlen)
+static inline unsigned long m32r_flat_get_addr_from_rp (u32 *rp,
+                                                        u32 relval,
+                                                       u32 textlen)
 {
         unsigned int reloc = flat_m32r_get_reloc_type (relval);
        textlen_for_m32r_lo16_data = 0;
@@ -100,9 +95,7 @@ static inline unsigned long m32r_flat_get_addr_from_rp (unsigned long *rp,
        return ~0;      /* bogus value */
 }
 
-static inline void m32r_flat_put_addr_at_rp (unsigned long *rp,
-                                            unsigned long addr,
-                                             unsigned long relval)
+static inline void flat_put_addr_at_rp(u32 *rp, u32 addr, u32 relval)
 {
         unsigned int reloc = flat_m32r_get_reloc_type (relval);
        if (reloc & 0xf0) {
@@ -142,4 +135,8 @@ static inline void m32r_flat_put_addr_at_rp (unsigned long *rp,
        }
 }
 
+// kludge - text_len is a local variable in the only user.
+#define flat_get_addr_from_rp(rp, relval, flags, addr, persistent) \
+       (m32r_flat_get_addr_from_rp(rp, relval, text_len), 0)
+
 #endif /* __ASM_M32R_FLAT_H */
index 00c392b0cabdb8288c46b4b27008e4c364ed2ac4..48b62790fe70f1a620b0f5e47d0546e07ed77664 100644 (file)
@@ -5,12 +5,29 @@
 #ifndef __M68KNOMMU_FLAT_H__
 #define __M68KNOMMU_FLAT_H__
 
+#include <linux/uaccess.h>
+
 #define        flat_argvp_envp_on_stack()              1
 #define        flat_old_ram_flag(flags)                (flags)
 #define        flat_reloc_valid(reloc, size)           ((reloc) <= (size))
-#define        flat_get_addr_from_rp(rp, relval, flags, p) \
-       ({ unsigned long __val; __get_user_unaligned(__val, rp); __val; })
-#define        flat_put_addr_at_rp(rp, val, relval)    __put_user_unaligned(val, rp)
+static inline int flat_get_addr_from_rp(u32 __user *rp, u32 relval, u32 flags,
+                                       u32 *addr, u32 *persistent)
+{
+#ifdef CONFIG_CPU_HAS_NO_UNALIGNED
+       return copy_from_user(addr, rp, 4) ? -EFAULT : 0;
+#else
+       return get_user(*addr, rp);
+#endif
+}
+
+static inline int flat_put_addr_at_rp(u32 __user *rp, u32 addr, u32 rel)
+{
+#ifdef CONFIG_CPU_HAS_NO_UNALIGNED
+       return copy_to_user(rp, &addr, 4) ? -EFAULT : 0;
+#else
+       return put_user(addr, rp);
+#endif
+}
 #define        flat_get_relocate_addr(rel)             (rel)
 
 static inline int flat_set_persistent(unsigned long relval,
index 6847c1512c7baf17f6f58a000566e79631ce3db8..f23c3d266bae3e4e8eb006deca5bbfeed10cecbe 100644 (file)
  * reference
  */
 
-static inline unsigned long
-flat_get_addr_from_rp(unsigned long *rp, unsigned long relval,
-                       unsigned long flags, unsigned long *persistent)
+static inline int flat_get_addr_from_rp(u32 __user *rp, u32 relval, u32 flags,
+                                       u32 *addr, u32 *persistent)
 {
-       unsigned long addr;
-       (void)flags;
+       u32 *p = (__force u32 *)rp;
 
        /* Is it a split 64/32 reference? */
        if (relval & 0x80000000) {
                /* Grab the two halves of the reference */
-               unsigned long val_hi, val_lo;
+               u32 val_hi, val_lo;
 
-               val_hi = get_unaligned(rp);
-               val_lo = get_unaligned(rp+1);
+               val_hi = get_unaligned(p);
+               val_lo = get_unaligned(p+1);
 
                /* Crack the address out */
-               addr = ((val_hi & 0xffff) << 16) + (val_lo & 0xffff);
+               *addr = ((val_hi & 0xffff) << 16) + (val_lo & 0xffff);
        } else {
                /* Get the address straight out */
-               addr = get_unaligned(rp);
+               *addr = get_unaligned(p);
        }
 
-       return addr;
+       return 0;
 }
 
 /*
@@ -63,25 +61,27 @@ flat_get_addr_from_rp(unsigned long *rp, unsigned long relval,
  */
 
 static inline void
-flat_put_addr_at_rp(unsigned long *rp, unsigned long addr, unsigned long relval)
+flat_put_addr_at_rp(u32 __user *rp, u32 addr, u32 relval)
 {
+       u32 *p = (__force u32 *)rp;
        /* Is this a split 64/32 reloc? */
        if (relval & 0x80000000) {
                /* Get the two "halves" */
-               unsigned long val_hi = get_unaligned(rp);
-               unsigned long val_lo = get_unaligned(rp + 1);
+               unsigned long val_hi = get_unaligned(p);
+               unsigned long val_lo = get_unaligned(p + 1);
 
                /* insert the address */
                val_hi = (val_hi & 0xffff0000) | addr >> 16;
                val_lo = (val_lo & 0xffff0000) | (addr & 0xffff);
 
                /* store the two halves back into memory */
-               put_unaligned(val_hi, rp);
-               put_unaligned(val_lo, rp+1);
+               put_unaligned(val_hi, p);
+               put_unaligned(val_lo, p+1);
        } else {
                /* Put it straight in, no messing around */
-               put_unaligned(addr, rp);
+               put_unaligned(addr, p);
        }
+       return 0;
 }
 
 #define        flat_get_relocate_addr(rel)     (rel & 0x7fffffff)
index 5d84df5e27f658972bf9b3acc3c9dd2267bf0176..275fcae2353920098bb95393873671e2d578c39a 100644 (file)
 #ifndef __ASM_SH_FLAT_H
 #define __ASM_SH_FLAT_H
 
+#include <asm/unaligned.h>
+
 #define        flat_argvp_envp_on_stack()              0
 #define        flat_old_ram_flag(flags)                (flags)
 #define        flat_reloc_valid(reloc, size)           ((reloc) <= (size))
-#define        flat_get_addr_from_rp(rp, relval, flags, p)     get_unaligned(rp)
-#define        flat_put_addr_at_rp(rp, val, relval)    put_unaligned(val,rp)
+static inline int flat_get_addr_from_rp(u32 __user *rp, u32 relval, u32 flags,
+                                       u32 *addr, u32 *persistent)
+{
+       *addr = get_unaligned((__force u32 *)rp);
+       return 0;
+}
+static inline int flat_put_addr_at_rp(u32 __user *rp, u32 addr, u32 rel)
+{
+       put_unaligned(addr, (__force u32 *)rp);
+       return 0;
+}
 #define        flat_get_relocate_addr(rel)             (rel)
 #define        flat_set_persistent(relval, p)          ({ (void)p; 0; })
 
index 94c44abf15e44247afec27060e14e34c8f071da7..60e0d6a4579569e228b8b232a646abf6a8d3fcc4 100644 (file)
@@ -1,11 +1,22 @@
 #ifndef __ASM_XTENSA_FLAT_H
 #define __ASM_XTENSA_FLAT_H
 
+#include <asm/unaligned.h>
+
 #define flat_argvp_envp_on_stack()                     0
 #define flat_old_ram_flag(flags)                       (flags)
 #define flat_reloc_valid(reloc, size)                  ((reloc) <= (size))
-#define flat_get_addr_from_rp(rp, relval, flags, p)    get_unaligned(rp)
-#define flat_put_addr_at_rp(rp, val, relval    )       put_unaligned(val, rp)
+static inline int flat_get_addr_from_rp(u32 __user *rp, u32 relval, u32 flags,
+                                       u32 *addr, u32 *persistent)
+{
+       *addr = get_unaligned((__force u32 *)rp);
+       return 0;
+}
+static inline int flat_put_addr_at_rp(u32 __user *rp, u32 addr, u32 rel)
+{
+       put_unaligned(addr, (__force u32 *)rp);
+       return 0;
+}
 #define flat_get_relocate_addr(rel)                    (rel)
 #define flat_set_persistent(relval, p)                 0
 
index 2edcefc0a2949af4d302b9060885a4e8bba79d7c..69ec23daa25e003ff94ef306c93a0a77c51b00a3 100644 (file)
@@ -422,9 +422,9 @@ static int load_flat_file(struct linux_binprm *bprm,
 {
        struct flat_hdr *hdr;
        unsigned long textpos, datapos, realdatastart;
-       unsigned long text_len, data_len, bss_len, stack_len, full_data, flags;
+       u32 text_len, data_len, bss_len, stack_len, full_data, flags;
        unsigned long len, memp, memp_size, extra, rlim;
-       unsigned long __user *reloc, *rp;
+       u32 __user *reloc, *rp;
        struct inode *inode;
        int i, rev, relocs;
        loff_t fpos;
@@ -596,13 +596,13 @@ static int load_flat_file(struct linux_binprm *bprm,
                        goto err;
                }
 
-               reloc = (unsigned long __user *)
+               reloc = (u32 __user *)
                        (datapos + (ntohl(hdr->reloc_start) - text_len));
                memp = realdatastart;
                memp_size = len;
        } else {
 
-               len = text_len + data_len + extra + MAX_SHARED_LIBS * sizeof(unsigned long);
+               len = text_len + data_len + extra + MAX_SHARED_LIBS * sizeof(u32);
                len = PAGE_ALIGN(len);
                textpos = vm_mmap(NULL, 0, len,
                        PROT_READ | PROT_EXEC | PROT_WRITE, MAP_PRIVATE, 0);
@@ -618,10 +618,10 @@ static int load_flat_file(struct linux_binprm *bprm,
 
                realdatastart = textpos + ntohl(hdr->data_start);
                datapos = ALIGN(realdatastart +
-                               MAX_SHARED_LIBS * sizeof(unsigned long),
+                               MAX_SHARED_LIBS * sizeof(u32),
                                FLAT_DATA_ALIGN);
 
-               reloc = (unsigned long __user *)
+               reloc = (u32 __user *)
                        (datapos + (ntohl(hdr->reloc_start) - text_len));
                memp = textpos;
                memp_size = len;
@@ -694,7 +694,7 @@ static int load_flat_file(struct linux_binprm *bprm,
                        ret = result;
                        pr_err("Unable to read code+data+bss, errno %d\n", ret);
                        vm_munmap(textpos, text_len + data_len + extra +
-                               MAX_SHARED_LIBS * sizeof(unsigned long));
+                               MAX_SHARED_LIBS * sizeof(u32));
                        goto err;
                }
        }
@@ -754,8 +754,8 @@ static int load_flat_file(struct linux_binprm *bprm,
         * image.
         */
        if (flags & FLAT_FLAG_GOTPIC) {
-               for (rp = (unsigned long __user *)datapos; ; rp++) {
-                       unsigned long addr, rp_val;
+               for (rp = (u32 __user *)datapos; ; rp++) {
+                       u32 addr, rp_val;
                        if (get_user(rp_val, rp))
                                return -EFAULT;
                        if (rp_val == 0xffffffff)
@@ -784,9 +784,9 @@ static int load_flat_file(struct linux_binprm *bprm,
         * __start to address 4 so that is okay).
         */
        if (rev > OLD_FLAT_VERSION) {
-               unsigned long __maybe_unused persistent = 0;
+               u32 __maybe_unused persistent = 0;
                for (i = 0; i < relocs; i++) {
-                       unsigned long addr, relval;
+                       u32 addr, relval;
 
                        /*
                         * Get the address of the pointer to be
@@ -799,15 +799,18 @@ static int load_flat_file(struct linux_binprm *bprm,
                        if (flat_set_persistent(relval, &persistent))
                                continue;
                        addr = flat_get_relocate_addr(relval);
-                       rp = (unsigned long __user *)calc_reloc(addr, libinfo, id, 1);
-                       if (rp == (unsigned long __user *)RELOC_FAILED) {
+                       rp = (u32 __user *)calc_reloc(addr, libinfo, id, 1);
+                       if (rp == (u32 __user *)RELOC_FAILED) {
                                ret = -ENOEXEC;
                                goto err;
                        }
 
                        /* Get the pointer's value.  */
-                       addr = flat_get_addr_from_rp(rp, relval, flags,
-                                                       &persistent);
+                       ret = flat_get_addr_from_rp(rp, relval, flags,
+                                                       &addr, &persistent);
+                       if (unlikely(ret))
+                               goto err;
+
                        if (addr != 0) {
                                /*
                                 * Do the relocation.  PIC relocs in the data section are
@@ -822,12 +825,14 @@ static int load_flat_file(struct linux_binprm *bprm,
                                }
 
                                /* Write back the relocated pointer.  */
-                               flat_put_addr_at_rp(rp, addr, relval);
+                               ret = flat_put_addr_at_rp(rp, addr, relval);
+                               if (unlikely(ret))
+                                       goto err;
                        }
                }
        } else {
                for (i = 0; i < relocs; i++) {
-                       unsigned long relval;
+                       u32 relval;
                        if (get_user(relval, reloc + i))
                                return -EFAULT;
                        relval = ntohl(relval);
index 2c1eb15c4ba4da60337075b2c4a6a579f361254a..7d542dfd0def53892cd421e22fc4930306253c1d 100644 (file)
@@ -9,8 +9,8 @@
 #ifndef _LINUX_FLAT_H
 #define _LINUX_FLAT_H
 
-#include <asm/flat.h>
 #include <uapi/linux/flat.h>
+#include <asm/flat.h>
 
 /*
  * While it would be nice to keep this header clean,  users of older