mirror of
https://github.com/torvalds/linux.git
synced 2024-11-20 11:01:38 +00:00
0d025d271e
There are three usercopy warnings which are currently being silenced for
gcc 4.6 and newer:
1) "copy_from_user() buffer size is too small" compile warning/error
This is a static warning which happens when object size and copy size
are both const, and copy size > object size. I didn't see any false
positives for this one. So the function warning attribute seems to
be working fine here.
Note this scenario is always a bug and so I think it should be
changed to *always* be an error, regardless of
CONFIG_DEBUG_STRICT_USER_COPY_CHECKS.
2) "copy_from_user() buffer size is not provably correct" compile warning
This is another static warning which happens when I enable
__compiletime_object_size() for new compilers (and
CONFIG_DEBUG_STRICT_USER_COPY_CHECKS). It happens when object size
is const, but copy size is *not*. In this case there's no way to
compare the two at build time, so it gives the warning. (Note the
warning is a byproduct of the fact that gcc has no way of knowing
whether the overflow function will be called, so the call isn't dead
code and the warning attribute is activated.)
So this warning seems to only indicate "this is an unusual pattern,
maybe you should check it out" rather than "this is a bug".
I get 102(!) of these warnings with allyesconfig and the
__compiletime_object_size() gcc check removed. I don't know if there
are any real bugs hiding in there, but from looking at a small
sample, I didn't see any. According to Kees, it does sometimes find
real bugs. But the false positive rate seems high.
3) "Buffer overflow detected" runtime warning
This is a runtime warning where object size is const, and copy size >
object size.
All three warnings (both static and runtime) were completely disabled
for gcc 4.6 with the following commit:
2fb0815c9e
("gcc4: disable __compiletime_object_size for GCC 4.6+")
That commit mistakenly assumed that the false positives were caused by a
gcc bug in __compiletime_object_size(). But in fact,
__compiletime_object_size() seems to be working fine. The false
positives were instead triggered by #2 above. (Though I don't have an
explanation for why the warnings supposedly only started showing up in
gcc 4.6.)
So remove warning #2 to get rid of all the false positives, and re-enable
warnings #1 and #3 by reverting the above commit.
Furthermore, since #1 is a real bug which is detected at compile time,
upgrade it to always be an error.
Having done all that, CONFIG_DEBUG_STRICT_USER_COPY_CHECKS is no longer
needed.
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
Cc: Kees Cook <keescook@chromium.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: "H . Peter Anvin" <hpa@zytor.com>
Cc: Andy Lutomirski <luto@amacapital.net>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Brian Gerst <brgerst@gmail.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Byungchul Park <byungchul.park@lge.com>
Cc: Nilay Vaish <nilayvaish@gmail.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
350 lines
7.7 KiB
C
350 lines
7.7 KiB
C
#ifndef __ASM_GENERIC_UACCESS_H
|
|
#define __ASM_GENERIC_UACCESS_H
|
|
|
|
/*
|
|
* User space memory access functions, these should work
|
|
* on any machine that has kernel and user data in the same
|
|
* address space, e.g. all NOMMU machines.
|
|
*/
|
|
#include <linux/sched.h>
|
|
#include <linux/string.h>
|
|
|
|
#include <asm/segment.h>
|
|
|
|
#define MAKE_MM_SEG(s) ((mm_segment_t) { (s) })
|
|
|
|
#ifndef KERNEL_DS
|
|
#define KERNEL_DS MAKE_MM_SEG(~0UL)
|
|
#endif
|
|
|
|
#ifndef USER_DS
|
|
#define USER_DS MAKE_MM_SEG(TASK_SIZE - 1)
|
|
#endif
|
|
|
|
#ifndef get_fs
|
|
#define get_ds() (KERNEL_DS)
|
|
#define get_fs() (current_thread_info()->addr_limit)
|
|
|
|
static inline void set_fs(mm_segment_t fs)
|
|
{
|
|
current_thread_info()->addr_limit = fs;
|
|
}
|
|
#endif
|
|
|
|
#ifndef segment_eq
|
|
#define segment_eq(a, b) ((a).seg == (b).seg)
|
|
#endif
|
|
|
|
#define VERIFY_READ 0
|
|
#define VERIFY_WRITE 1
|
|
|
|
#define access_ok(type, addr, size) __access_ok((unsigned long)(addr),(size))
|
|
|
|
/*
|
|
* The architecture should really override this if possible, at least
|
|
* doing a check on the get_fs()
|
|
*/
|
|
#ifndef __access_ok
|
|
static inline int __access_ok(unsigned long addr, unsigned long size)
|
|
{
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* The exception table consists of pairs of addresses: the first is the
|
|
* address of an instruction that is allowed to fault, and the second is
|
|
* the address at which the program should continue. No registers are
|
|
* modified, so it is entirely up to the continuation code to figure out
|
|
* what to do.
|
|
*
|
|
* All the routines below use bits of fixup code that are out of line
|
|
* with the main instruction path. This means when everything is well,
|
|
* we don't even have to jump over them. Further, they do not intrude
|
|
* on our cache or tlb entries.
|
|
*/
|
|
|
|
struct exception_table_entry
|
|
{
|
|
unsigned long insn, fixup;
|
|
};
|
|
|
|
/* Returns 0 if exception not found and fixup otherwise. */
|
|
extern unsigned long search_exception_table(unsigned long);
|
|
|
|
|
|
/*
|
|
* architectures with an MMU should override these two
|
|
*/
|
|
#ifndef __copy_from_user
|
|
static inline __must_check long __copy_from_user(void *to,
|
|
const void __user * from, unsigned long n)
|
|
{
|
|
if (__builtin_constant_p(n)) {
|
|
switch(n) {
|
|
case 1:
|
|
*(u8 *)to = *(u8 __force *)from;
|
|
return 0;
|
|
case 2:
|
|
*(u16 *)to = *(u16 __force *)from;
|
|
return 0;
|
|
case 4:
|
|
*(u32 *)to = *(u32 __force *)from;
|
|
return 0;
|
|
#ifdef CONFIG_64BIT
|
|
case 8:
|
|
*(u64 *)to = *(u64 __force *)from;
|
|
return 0;
|
|
#endif
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
memcpy(to, (const void __force *)from, n);
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
#ifndef __copy_to_user
|
|
static inline __must_check long __copy_to_user(void __user *to,
|
|
const void *from, unsigned long n)
|
|
{
|
|
if (__builtin_constant_p(n)) {
|
|
switch(n) {
|
|
case 1:
|
|
*(u8 __force *)to = *(u8 *)from;
|
|
return 0;
|
|
case 2:
|
|
*(u16 __force *)to = *(u16 *)from;
|
|
return 0;
|
|
case 4:
|
|
*(u32 __force *)to = *(u32 *)from;
|
|
return 0;
|
|
#ifdef CONFIG_64BIT
|
|
case 8:
|
|
*(u64 __force *)to = *(u64 *)from;
|
|
return 0;
|
|
#endif
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
memcpy((void __force *)to, from, n);
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* These are the main single-value transfer routines. They automatically
|
|
* use the right size if we just have the right pointer type.
|
|
* This version just falls back to copy_{from,to}_user, which should
|
|
* provide a fast-path for small values.
|
|
*/
|
|
#define __put_user(x, ptr) \
|
|
({ \
|
|
__typeof__(*(ptr)) __x = (x); \
|
|
int __pu_err = -EFAULT; \
|
|
__chk_user_ptr(ptr); \
|
|
switch (sizeof (*(ptr))) { \
|
|
case 1: \
|
|
case 2: \
|
|
case 4: \
|
|
case 8: \
|
|
__pu_err = __put_user_fn(sizeof (*(ptr)), \
|
|
ptr, &__x); \
|
|
break; \
|
|
default: \
|
|
__put_user_bad(); \
|
|
break; \
|
|
} \
|
|
__pu_err; \
|
|
})
|
|
|
|
#define put_user(x, ptr) \
|
|
({ \
|
|
void *__p = (ptr); \
|
|
might_fault(); \
|
|
access_ok(VERIFY_WRITE, __p, sizeof(*ptr)) ? \
|
|
__put_user((x), ((__typeof__(*(ptr)) *)__p)) : \
|
|
-EFAULT; \
|
|
})
|
|
|
|
#ifndef __put_user_fn
|
|
|
|
static inline int __put_user_fn(size_t size, void __user *ptr, void *x)
|
|
{
|
|
size = __copy_to_user(ptr, x, size);
|
|
return size ? -EFAULT : size;
|
|
}
|
|
|
|
#define __put_user_fn(sz, u, k) __put_user_fn(sz, u, k)
|
|
|
|
#endif
|
|
|
|
extern int __put_user_bad(void) __attribute__((noreturn));
|
|
|
|
#define __get_user(x, ptr) \
|
|
({ \
|
|
int __gu_err = -EFAULT; \
|
|
__chk_user_ptr(ptr); \
|
|
switch (sizeof(*(ptr))) { \
|
|
case 1: { \
|
|
unsigned char __x; \
|
|
__gu_err = __get_user_fn(sizeof (*(ptr)), \
|
|
ptr, &__x); \
|
|
(x) = *(__force __typeof__(*(ptr)) *) &__x; \
|
|
break; \
|
|
}; \
|
|
case 2: { \
|
|
unsigned short __x; \
|
|
__gu_err = __get_user_fn(sizeof (*(ptr)), \
|
|
ptr, &__x); \
|
|
(x) = *(__force __typeof__(*(ptr)) *) &__x; \
|
|
break; \
|
|
}; \
|
|
case 4: { \
|
|
unsigned int __x; \
|
|
__gu_err = __get_user_fn(sizeof (*(ptr)), \
|
|
ptr, &__x); \
|
|
(x) = *(__force __typeof__(*(ptr)) *) &__x; \
|
|
break; \
|
|
}; \
|
|
case 8: { \
|
|
unsigned long long __x; \
|
|
__gu_err = __get_user_fn(sizeof (*(ptr)), \
|
|
ptr, &__x); \
|
|
(x) = *(__force __typeof__(*(ptr)) *) &__x; \
|
|
break; \
|
|
}; \
|
|
default: \
|
|
__get_user_bad(); \
|
|
break; \
|
|
} \
|
|
__gu_err; \
|
|
})
|
|
|
|
#define get_user(x, ptr) \
|
|
({ \
|
|
const void *__p = (ptr); \
|
|
might_fault(); \
|
|
access_ok(VERIFY_READ, __p, sizeof(*ptr)) ? \
|
|
__get_user((x), (__typeof__(*(ptr)) *)__p) : \
|
|
-EFAULT; \
|
|
})
|
|
|
|
#ifndef __get_user_fn
|
|
static inline int __get_user_fn(size_t size, const void __user *ptr, void *x)
|
|
{
|
|
size = __copy_from_user(x, ptr, size);
|
|
return size ? -EFAULT : size;
|
|
}
|
|
|
|
#define __get_user_fn(sz, u, k) __get_user_fn(sz, u, k)
|
|
|
|
#endif
|
|
|
|
extern int __get_user_bad(void) __attribute__((noreturn));
|
|
|
|
#ifndef __copy_from_user_inatomic
|
|
#define __copy_from_user_inatomic __copy_from_user
|
|
#endif
|
|
|
|
#ifndef __copy_to_user_inatomic
|
|
#define __copy_to_user_inatomic __copy_to_user
|
|
#endif
|
|
|
|
static inline long copy_from_user(void *to,
|
|
const void __user * from, unsigned long n)
|
|
{
|
|
might_fault();
|
|
if (access_ok(VERIFY_READ, from, n))
|
|
return __copy_from_user(to, from, n);
|
|
else
|
|
return n;
|
|
}
|
|
|
|
static inline long copy_to_user(void __user *to,
|
|
const void *from, unsigned long n)
|
|
{
|
|
might_fault();
|
|
if (access_ok(VERIFY_WRITE, to, n))
|
|
return __copy_to_user(to, from, n);
|
|
else
|
|
return n;
|
|
}
|
|
|
|
/*
|
|
* Copy a null terminated string from userspace.
|
|
*/
|
|
#ifndef __strncpy_from_user
|
|
static inline long
|
|
__strncpy_from_user(char *dst, const char __user *src, long count)
|
|
{
|
|
char *tmp;
|
|
strncpy(dst, (const char __force *)src, count);
|
|
for (tmp = dst; *tmp && count > 0; tmp++, count--)
|
|
;
|
|
return (tmp - dst);
|
|
}
|
|
#endif
|
|
|
|
static inline long
|
|
strncpy_from_user(char *dst, const char __user *src, long count)
|
|
{
|
|
if (!access_ok(VERIFY_READ, src, 1))
|
|
return -EFAULT;
|
|
return __strncpy_from_user(dst, src, count);
|
|
}
|
|
|
|
/*
|
|
* Return the size of a string (including the ending 0)
|
|
*
|
|
* Return 0 on exception, a value greater than N if too long
|
|
*/
|
|
#ifndef __strnlen_user
|
|
#define __strnlen_user(s, n) (strnlen((s), (n)) + 1)
|
|
#endif
|
|
|
|
/*
|
|
* Unlike strnlen, strnlen_user includes the nul terminator in
|
|
* its returned count. Callers should check for a returned value
|
|
* greater than N as an indication the string is too long.
|
|
*/
|
|
static inline long strnlen_user(const char __user *src, long n)
|
|
{
|
|
if (!access_ok(VERIFY_READ, src, 1))
|
|
return 0;
|
|
return __strnlen_user(src, n);
|
|
}
|
|
|
|
static inline long strlen_user(const char __user *src)
|
|
{
|
|
return strnlen_user(src, 32767);
|
|
}
|
|
|
|
/*
|
|
* Zero Userspace
|
|
*/
|
|
#ifndef __clear_user
|
|
static inline __must_check unsigned long
|
|
__clear_user(void __user *to, unsigned long n)
|
|
{
|
|
memset((void __force *)to, 0, n);
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static inline __must_check unsigned long
|
|
clear_user(void __user *to, unsigned long n)
|
|
{
|
|
might_fault();
|
|
if (!access_ok(VERIFY_WRITE, to, n))
|
|
return n;
|
|
|
|
return __clear_user(to, n);
|
|
}
|
|
|
|
#endif /* __ASM_GENERIC_UACCESS_H */
|