Files
android_kernel_xiaomi_sm8450/arch/m68k/include/asm/uaccess_no.h
Greg Ungerer 8044aad70a m68knommu: fix memcpy() out of bounds warning in get_user()
Newer versions of gcc are giving warnings in the non-MMU m68k version
of the get_user() macro:

    ./arch/m68k/include/asm/string.h:72:25: warning: ‘__builtin_memcpy’ forming offset [3, 4] is out of the bounds [0, 2] of object ‘__gu_val’ with type ‘short unsigned int’ [-Warray-bounds]

The warnings are generated when smaller sized variables are used as the
result of user space pointers to larger values. For example a
short/2-byte variable stores the result of a user space int (4-byte)
pointer. The warning is in the 8-byte branch of get_user() - even
though that branch is not the taken branch in the warning cases.

Refactor the 8-byte branch of get_user() so that it uses a correctly
formed union type to read and write the source and destination objects.
Keep using the memcpy() just in case the user space pointer is not
naturaly aligned (not required for ColdFire, but needed for early
68000).

Signed-off-by: Greg Ungerer <gerg@linux-m68k.org>
2020-02-03 14:43:35 +10:00

162 lines
3.7 KiB
C

/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __M68KNOMMU_UACCESS_H
#define __M68KNOMMU_UACCESS_H
/*
* User space memory access functions
*/
#include <linux/mm.h>
#include <linux/string.h>
#include <asm/segment.h>
#define access_ok(addr,size) _access_ok((unsigned long)(addr),(size))
/*
* It is not enough to just have access_ok check for a real RAM address.
* This would disallow the case of code/ro-data running XIP in flash/rom.
* Ideally we would check the possible flash ranges too, but that is
* currently not so easy.
*/
static inline int _access_ok(unsigned long addr, unsigned long size)
{
return 1;
}
/*
* These are the main single-value transfer routines. They automatically
* use the right size if we just have the right pointer type.
*/
#define put_user(x, ptr) \
({ \
int __pu_err = 0; \
typeof(*(ptr)) __pu_val = (x); \
switch (sizeof (*(ptr))) { \
case 1: \
__put_user_asm(__pu_err, __pu_val, ptr, b); \
break; \
case 2: \
__put_user_asm(__pu_err, __pu_val, ptr, w); \
break; \
case 4: \
__put_user_asm(__pu_err, __pu_val, ptr, l); \
break; \
case 8: \
memcpy(ptr, &__pu_val, sizeof (*(ptr))); \
break; \
default: \
__pu_err = __put_user_bad(); \
break; \
} \
__pu_err; \
})
#define __put_user(x, ptr) put_user(x, ptr)
extern int __put_user_bad(void);
/*
* Tell gcc we read from memory instead of writing: this is because
* we do not write to any memory gcc knows about, so there are no
* aliasing issues.
*/
#define __ptr(x) ((unsigned long *)(x))
#define __put_user_asm(err,x,ptr,bwl) \
__asm__ ("move" #bwl " %0,%1" \
: /* no outputs */ \
:"d" (x),"m" (*__ptr(ptr)) : "memory")
#define get_user(x, ptr) \
({ \
int __gu_err = 0; \
switch (sizeof(*(ptr))) { \
case 1: \
__get_user_asm(__gu_err, x, ptr, b, "=d"); \
break; \
case 2: \
__get_user_asm(__gu_err, x, ptr, w, "=r"); \
break; \
case 4: \
__get_user_asm(__gu_err, x, ptr, l, "=r"); \
break; \
case 8: { \
union { \
u64 l; \
__typeof__(*(ptr)) t; \
} __gu_val; \
memcpy(&__gu_val.l, ptr, sizeof(__gu_val.l)); \
(x) = __gu_val.t; \
break; \
} \
default: \
__gu_err = __get_user_bad(); \
break; \
} \
__gu_err; \
})
#define __get_user(x, ptr) get_user(x, ptr)
extern int __get_user_bad(void);
#define __get_user_asm(err,x,ptr,bwl,reg) \
__asm__ ("move" #bwl " %1,%0" \
: "=d" (x) \
: "m" (*__ptr(ptr)))
static inline unsigned long
raw_copy_from_user(void *to, const void __user *from, unsigned long n)
{
memcpy(to, (__force const void *)from, n);
return 0;
}
static inline unsigned long
raw_copy_to_user(void __user *to, const void *from, unsigned long n)
{
memcpy((__force void *)to, from, n);
return 0;
}
#define INLINE_COPY_FROM_USER
#define INLINE_COPY_TO_USER
/*
* Copy a null terminated string from userspace.
*/
static inline long
strncpy_from_user(char *dst, const char *src, long count)
{
char *tmp;
strncpy(dst, src, count);
for (tmp = dst; *tmp && count > 0; tmp++, count--)
;
return(tmp - dst); /* DAVIDM should we count a NUL ? check getname */
}
/*
* Return the size of a string (including the ending 0)
*
* Return 0 on exception, a value greater than N if too long
*/
static inline long strnlen_user(const char *src, long n)
{
return(strlen(src) + 1); /* DAVIDM make safer */
}
/*
* Zero Userspace
*/
static inline unsigned long
__clear_user(void *to, unsigned long n)
{
memset(to, 0, n);
return 0;
}
#define clear_user(to,n) __clear_user(to,n)
#endif /* _M68KNOMMU_UACCESS_H */