ARM: uaccess: provide uaccess_save_and_enable() and uaccess_restore()
Provide uaccess_save_and_enable() and uaccess_restore() to permit control of userspace visibility to the kernel, and hook these into the appropriate places in the kernel where we need to access userspace. Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
This commit is contained in:
parent
08446b129b
commit
3fba7e23f7
|
@ -22,8 +22,11 @@
|
|||
#ifdef CONFIG_SMP
|
||||
|
||||
#define __futex_atomic_op(insn, ret, oldval, tmp, uaddr, oparg) \
|
||||
({ \
|
||||
unsigned int __ua_flags; \
|
||||
smp_mb(); \
|
||||
prefetchw(uaddr); \
|
||||
__ua_flags = uaccess_save_and_enable(); \
|
||||
__asm__ __volatile__( \
|
||||
"1: ldrex %1, [%3]\n" \
|
||||
" " insn "\n" \
|
||||
|
@ -34,12 +37,15 @@
|
|||
__futex_atomic_ex_table("%5") \
|
||||
: "=&r" (ret), "=&r" (oldval), "=&r" (tmp) \
|
||||
: "r" (uaddr), "r" (oparg), "Ir" (-EFAULT) \
|
||||
: "cc", "memory")
|
||||
: "cc", "memory"); \
|
||||
uaccess_restore(__ua_flags); \
|
||||
})
|
||||
|
||||
static inline int
|
||||
futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
|
||||
u32 oldval, u32 newval)
|
||||
{
|
||||
unsigned int __ua_flags;
|
||||
int ret;
|
||||
u32 val;
|
||||
|
||||
|
@ -49,6 +55,7 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
|
|||
smp_mb();
|
||||
/* Prefetching cannot fault */
|
||||
prefetchw(uaddr);
|
||||
__ua_flags = uaccess_save_and_enable();
|
||||
__asm__ __volatile__("@futex_atomic_cmpxchg_inatomic\n"
|
||||
"1: ldrex %1, [%4]\n"
|
||||
" teq %1, %2\n"
|
||||
|
@ -61,6 +68,7 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
|
|||
: "=&r" (ret), "=&r" (val)
|
||||
: "r" (oldval), "r" (newval), "r" (uaddr), "Ir" (-EFAULT)
|
||||
: "cc", "memory");
|
||||
uaccess_restore(__ua_flags);
|
||||
smp_mb();
|
||||
|
||||
*uval = val;
|
||||
|
@ -73,6 +81,8 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
|
|||
#include <asm/domain.h>
|
||||
|
||||
#define __futex_atomic_op(insn, ret, oldval, tmp, uaddr, oparg) \
|
||||
({ \
|
||||
unsigned int __ua_flags = uaccess_save_and_enable(); \
|
||||
__asm__ __volatile__( \
|
||||
"1: " TUSER(ldr) " %1, [%3]\n" \
|
||||
" " insn "\n" \
|
||||
|
@ -81,12 +91,15 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
|
|||
__futex_atomic_ex_table("%5") \
|
||||
: "=&r" (ret), "=&r" (oldval), "=&r" (tmp) \
|
||||
: "r" (uaddr), "r" (oparg), "Ir" (-EFAULT) \
|
||||
: "cc", "memory")
|
||||
: "cc", "memory"); \
|
||||
uaccess_restore(__ua_flags); \
|
||||
})
|
||||
|
||||
static inline int
|
||||
futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
|
||||
u32 oldval, u32 newval)
|
||||
{
|
||||
unsigned int __ua_flags;
|
||||
int ret = 0;
|
||||
u32 val;
|
||||
|
||||
|
@ -94,6 +107,7 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
|
|||
return -EFAULT;
|
||||
|
||||
preempt_disable();
|
||||
__ua_flags = uaccess_save_and_enable();
|
||||
__asm__ __volatile__("@futex_atomic_cmpxchg_inatomic\n"
|
||||
"1: " TUSER(ldr) " %1, [%4]\n"
|
||||
" teq %1, %2\n"
|
||||
|
@ -103,6 +117,7 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
|
|||
: "+r" (ret), "=&r" (val)
|
||||
: "r" (oldval), "r" (newval), "r" (uaddr), "Ir" (-EFAULT)
|
||||
: "cc", "memory");
|
||||
uaccess_restore(__ua_flags);
|
||||
|
||||
*uval = val;
|
||||
preempt_enable();
|
||||
|
|
|
@ -49,6 +49,21 @@ struct exception_table_entry
|
|||
|
||||
extern int fixup_exception(struct pt_regs *regs);
|
||||
|
||||
/*
|
||||
* These two functions allow hooking accesses to userspace to increase
|
||||
* system integrity by ensuring that the kernel can not inadvertantly
|
||||
* perform such accesses (eg, via list poison values) which could then
|
||||
* be exploited for priviledge escalation.
|
||||
*/
|
||||
static inline unsigned int uaccess_save_and_enable(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void uaccess_restore(unsigned int flags)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* These two are intentionally not defined anywhere - if the kernel
|
||||
* code generates any references to them, that's a bug.
|
||||
|
@ -165,6 +180,7 @@ extern int __get_user_64t_4(void *);
|
|||
register typeof(x) __r2 asm("r2"); \
|
||||
register unsigned long __l asm("r1") = __limit; \
|
||||
register int __e asm("r0"); \
|
||||
unsigned int __ua_flags = uaccess_save_and_enable(); \
|
||||
switch (sizeof(*(__p))) { \
|
||||
case 1: \
|
||||
if (sizeof((x)) >= 8) \
|
||||
|
@ -192,6 +208,7 @@ extern int __get_user_64t_4(void *);
|
|||
break; \
|
||||
default: __e = __get_user_bad(); break; \
|
||||
} \
|
||||
uaccess_restore(__ua_flags); \
|
||||
x = (typeof(*(p))) __r2; \
|
||||
__e; \
|
||||
})
|
||||
|
@ -224,6 +241,7 @@ extern int __put_user_8(void *, unsigned long long);
|
|||
register const typeof(*(p)) __user *__p asm("r0") = __tmp_p; \
|
||||
register unsigned long __l asm("r1") = __limit; \
|
||||
register int __e asm("r0"); \
|
||||
unsigned int __ua_flags = uaccess_save_and_enable(); \
|
||||
switch (sizeof(*(__p))) { \
|
||||
case 1: \
|
||||
__put_user_x(__r2, __p, __e, __l, 1); \
|
||||
|
@ -239,6 +257,7 @@ extern int __put_user_8(void *, unsigned long long);
|
|||
break; \
|
||||
default: __e = __put_user_bad(); break; \
|
||||
} \
|
||||
uaccess_restore(__ua_flags); \
|
||||
__e; \
|
||||
})
|
||||
|
||||
|
@ -300,14 +319,17 @@ static inline void set_fs(mm_segment_t fs)
|
|||
do { \
|
||||
unsigned long __gu_addr = (unsigned long)(ptr); \
|
||||
unsigned long __gu_val; \
|
||||
unsigned int __ua_flags; \
|
||||
__chk_user_ptr(ptr); \
|
||||
might_fault(); \
|
||||
__ua_flags = uaccess_save_and_enable(); \
|
||||
switch (sizeof(*(ptr))) { \
|
||||
case 1: __get_user_asm_byte(__gu_val, __gu_addr, err); break; \
|
||||
case 2: __get_user_asm_half(__gu_val, __gu_addr, err); break; \
|
||||
case 4: __get_user_asm_word(__gu_val, __gu_addr, err); break; \
|
||||
default: (__gu_val) = __get_user_bad(); \
|
||||
} \
|
||||
uaccess_restore(__ua_flags); \
|
||||
(x) = (__typeof__(*(ptr)))__gu_val; \
|
||||
} while (0)
|
||||
|
||||
|
@ -381,9 +403,11 @@ do { \
|
|||
#define __put_user_err(x, ptr, err) \
|
||||
do { \
|
||||
unsigned long __pu_addr = (unsigned long)(ptr); \
|
||||
unsigned int __ua_flags; \
|
||||
__typeof__(*(ptr)) __pu_val = (x); \
|
||||
__chk_user_ptr(ptr); \
|
||||
might_fault(); \
|
||||
__ua_flags = uaccess_save_and_enable(); \
|
||||
switch (sizeof(*(ptr))) { \
|
||||
case 1: __put_user_asm_byte(__pu_val, __pu_addr, err); break; \
|
||||
case 2: __put_user_asm_half(__pu_val, __pu_addr, err); break; \
|
||||
|
@ -391,6 +415,7 @@ do { \
|
|||
case 8: __put_user_asm_dword(__pu_val, __pu_addr, err); break; \
|
||||
default: __put_user_bad(); \
|
||||
} \
|
||||
uaccess_restore(__ua_flags); \
|
||||
} while (0)
|
||||
|
||||
#define __put_user_asm_byte(x, __pu_addr, err) \
|
||||
|
@ -474,11 +499,46 @@ do { \
|
|||
|
||||
|
||||
#ifdef CONFIG_MMU
|
||||
extern unsigned long __must_check __copy_from_user(void *to, const void __user *from, unsigned long n);
|
||||
extern unsigned long __must_check __copy_to_user(void __user *to, const void *from, unsigned long n);
|
||||
extern unsigned long __must_check __copy_to_user_std(void __user *to, const void *from, unsigned long n);
|
||||
extern unsigned long __must_check __clear_user(void __user *addr, unsigned long n);
|
||||
extern unsigned long __must_check __clear_user_std(void __user *addr, unsigned long n);
|
||||
extern unsigned long __must_check
|
||||
arm_copy_from_user(void *to, const void __user *from, unsigned long n);
|
||||
|
||||
static inline unsigned long __must_check
|
||||
__copy_from_user(void *to, const void __user *from, unsigned long n)
|
||||
{
|
||||
unsigned int __ua_flags = uaccess_save_and_enable();
|
||||
n = arm_copy_from_user(to, from, n);
|
||||
uaccess_restore(__ua_flags);
|
||||
return n;
|
||||
}
|
||||
|
||||
extern unsigned long __must_check
|
||||
arm_copy_to_user(void __user *to, const void *from, unsigned long n);
|
||||
extern unsigned long __must_check
|
||||
__copy_to_user_std(void __user *to, const void *from, unsigned long n);
|
||||
|
||||
static inline unsigned long __must_check
|
||||
__copy_to_user(void __user *to, const void *from, unsigned long n)
|
||||
{
|
||||
unsigned int __ua_flags = uaccess_save_and_enable();
|
||||
n = arm_copy_to_user(to, from, n);
|
||||
uaccess_restore(__ua_flags);
|
||||
return n;
|
||||
}
|
||||
|
||||
extern unsigned long __must_check
|
||||
arm_clear_user(void __user *addr, unsigned long n);
|
||||
extern unsigned long __must_check
|
||||
__clear_user_std(void __user *addr, unsigned long n);
|
||||
|
||||
static inline unsigned long __must_check
|
||||
__clear_user(void __user *addr, unsigned long n)
|
||||
{
|
||||
unsigned int __ua_flags = uaccess_save_and_enable();
|
||||
n = arm_clear_user(addr, n);
|
||||
uaccess_restore(__ua_flags);
|
||||
return n;
|
||||
}
|
||||
|
||||
#else
|
||||
#define __copy_from_user(to, from, n) (memcpy(to, (void __force *)from, n), 0)
|
||||
#define __copy_to_user(to, from, n) (memcpy((void __force *)to, from, n), 0)
|
||||
|
@ -511,6 +571,7 @@ static inline unsigned long __must_check clear_user(void __user *to, unsigned lo
|
|||
return n;
|
||||
}
|
||||
|
||||
/* These are from lib/ code, and use __get_user() and friends */
|
||||
extern long strncpy_from_user(char *dest, const char __user *src, long count);
|
||||
|
||||
extern __must_check long strlen_user(const char __user *str);
|
||||
|
|
|
@ -91,9 +91,9 @@ EXPORT_SYMBOL(__memzero);
|
|||
#ifdef CONFIG_MMU
|
||||
EXPORT_SYMBOL(copy_page);
|
||||
|
||||
EXPORT_SYMBOL(__copy_from_user);
|
||||
EXPORT_SYMBOL(__copy_to_user);
|
||||
EXPORT_SYMBOL(__clear_user);
|
||||
EXPORT_SYMBOL(arm_copy_from_user);
|
||||
EXPORT_SYMBOL(arm_copy_to_user);
|
||||
EXPORT_SYMBOL(arm_clear_user);
|
||||
|
||||
EXPORT_SYMBOL(__get_user_1);
|
||||
EXPORT_SYMBOL(__get_user_2);
|
||||
|
|
|
@ -12,14 +12,14 @@
|
|||
|
||||
.text
|
||||
|
||||
/* Prototype: int __clear_user(void *addr, size_t sz)
|
||||
/* Prototype: unsigned long arm_clear_user(void *addr, size_t sz)
|
||||
* Purpose : clear some user memory
|
||||
* Params : addr - user memory address to clear
|
||||
* : sz - number of bytes to clear
|
||||
* Returns : number of bytes NOT cleared
|
||||
*/
|
||||
ENTRY(__clear_user_std)
|
||||
WEAK(__clear_user)
|
||||
WEAK(arm_clear_user)
|
||||
stmfd sp!, {r1, lr}
|
||||
mov r2, #0
|
||||
cmp r1, #4
|
||||
|
@ -44,7 +44,7 @@ WEAK(__clear_user)
|
|||
USER( strnebt r2, [r0])
|
||||
mov r0, #0
|
||||
ldmfd sp!, {r1, pc}
|
||||
ENDPROC(__clear_user)
|
||||
ENDPROC(arm_clear_user)
|
||||
ENDPROC(__clear_user_std)
|
||||
|
||||
.pushsection .text.fixup,"ax"
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
/*
|
||||
* Prototype:
|
||||
*
|
||||
* size_t __copy_from_user(void *to, const void *from, size_t n)
|
||||
* size_t arm_copy_from_user(void *to, const void *from, size_t n)
|
||||
*
|
||||
* Purpose:
|
||||
*
|
||||
|
@ -89,11 +89,11 @@
|
|||
|
||||
.text
|
||||
|
||||
ENTRY(__copy_from_user)
|
||||
ENTRY(arm_copy_from_user)
|
||||
|
||||
#include "copy_template.S"
|
||||
|
||||
ENDPROC(__copy_from_user)
|
||||
ENDPROC(arm_copy_from_user)
|
||||
|
||||
.pushsection .fixup,"ax"
|
||||
.align 0
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
/*
|
||||
* Prototype:
|
||||
*
|
||||
* size_t __copy_to_user(void *to, const void *from, size_t n)
|
||||
* size_t arm_copy_to_user(void *to, const void *from, size_t n)
|
||||
*
|
||||
* Purpose:
|
||||
*
|
||||
|
@ -93,11 +93,11 @@
|
|||
.text
|
||||
|
||||
ENTRY(__copy_to_user_std)
|
||||
WEAK(__copy_to_user)
|
||||
WEAK(arm_copy_to_user)
|
||||
|
||||
#include "copy_template.S"
|
||||
|
||||
ENDPROC(__copy_to_user)
|
||||
ENDPROC(arm_copy_to_user)
|
||||
ENDPROC(__copy_to_user_std)
|
||||
|
||||
.pushsection .text.fixup,"ax"
|
||||
|
|
|
@ -136,7 +136,7 @@ out:
|
|||
}
|
||||
|
||||
unsigned long
|
||||
__copy_to_user(void __user *to, const void *from, unsigned long n)
|
||||
arm_copy_to_user(void __user *to, const void *from, unsigned long n)
|
||||
{
|
||||
/*
|
||||
* This test is stubbed out of the main function above to keep
|
||||
|
@ -190,7 +190,7 @@ out:
|
|||
return n;
|
||||
}
|
||||
|
||||
unsigned long __clear_user(void __user *addr, unsigned long n)
|
||||
unsigned long arm_clear_user(void __user *addr, unsigned long n)
|
||||
{
|
||||
/* See rational for this in __copy_to_user() above. */
|
||||
if (n < 64)
|
||||
|
|
Loading…
Reference in New Issue