In the same spirit as commit 393f203f5fd5 ("x86_64: kasan: add interceptors for memset/memmove/memcpy functions"), this patch adds interceptors for string manipulation functions so that we can compile lib/string.o without kasan support hence allow the string functions to also be used from places where kasan has to be disabled. Signed-off-by: Christophe Leroy <christophe.leroy@xxxxxx> --- This is the generic part. If we agree on the principle, then I'll go through the arches and see if adaptations need to be done there. include/linux/string.h | 79 ++++++++++++ lib/Makefile | 2 + mm/kasan/string.c | 334 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 415 insertions(+) diff --git a/include/linux/string.h b/include/linux/string.h index 7927b875f80c..7e7441f4c420 100644 --- a/include/linux/string.h +++ b/include/linux/string.h @@ -19,54 +19,117 @@ extern void *memdup_user_nul(const void __user *, size_t); */ #include <asm/string.h> +#if defined(CONFIG_KASAN) && !defined(__SANITIZE_ADDRESS__) +/* + * For files that are not instrumented (e.g. mm/slub.c) we + * should use not instrumented version of mem* functions. + */ +#define memset16 __memset16 +#define memset32 __memset32 +#define memset64 __memset64 +#define memzero_explicit __memzero_explicit +#define strcpy __strcpy +#define strncpy __strncpy +#define strlcpy __strlcpy +#define strscpy __strscpy +#define strcat __strcat +#define strncat __strncat +#define strlcat __strlcat +#define strcmp __strcmp +#define strncmp __strncmp +#define strcasecmp __strcasecmp +#define strncasecmp __strncasecmp +#define strchr __strchr +#define strchrnul __strchrnul +#define strrchr __strrchr +#define strnchr __strnchr +#define skip_spaces __skip_spaces +#define strim __strim +#define strstr __strstr +#define strnstr __strnstr +#define strlen __strlen +#define strnlen __strnlen +#define strpbrk __strpbrk +#define strsep __strsep +#define strspn __strspn +#define strcspn __strcspn +#define memscan __memscan +#define memcmp __memcmp +#define memchr __memchr +#define memchr_inv __memchr_inv +#define strreplace __strreplace + +#ifndef __NO_FORTIFY +#define __NO_FORTIFY /* FORTIFY_SOURCE uses __builtin_memcpy, etc. */ +#endif + +#endif + #ifndef __HAVE_ARCH_STRCPY extern char * strcpy(char *,const char *); +char *__strcpy(char *,const char *); #endif #ifndef __HAVE_ARCH_STRNCPY extern char * strncpy(char *,const char *, __kernel_size_t); +char *__strncpy(char *,const char *, __kernel_size_t); #endif #ifndef __HAVE_ARCH_STRLCPY size_t strlcpy(char *, const char *, size_t); +size_t __strlcpy(char *, const char *, size_t); #endif #ifndef __HAVE_ARCH_STRSCPY ssize_t strscpy(char *, const char *, size_t); +ssize_t __strscpy(char *, const char *, size_t); #endif #ifndef __HAVE_ARCH_STRCAT extern char * strcat(char *, const char *); +char *__strcat(char *, const char *); #endif #ifndef __HAVE_ARCH_STRNCAT extern char * strncat(char *, const char *, __kernel_size_t); +char *__strncat(char *, const char *, __kernel_size_t); #endif #ifndef __HAVE_ARCH_STRLCAT extern size_t strlcat(char *, const char *, __kernel_size_t); +size_t __strlcat(char *, const char *, __kernel_size_t); #endif #ifndef __HAVE_ARCH_STRCMP extern int strcmp(const char *,const char *); +int __strcmp(const char *,const char *); #endif #ifndef __HAVE_ARCH_STRNCMP extern int strncmp(const char *,const char *,__kernel_size_t); +int __strncmp(const char *,const char *,__kernel_size_t); #endif #ifndef __HAVE_ARCH_STRCASECMP extern int strcasecmp(const char *s1, const char *s2); +int __strcasecmp(const char *s1, const char *s2); #endif #ifndef __HAVE_ARCH_STRNCASECMP extern int strncasecmp(const char *s1, const char *s2, size_t n); +int __strncasecmp(const char *s1, const char *s2, size_t n); #endif #ifndef __HAVE_ARCH_STRCHR extern char * strchr(const char *,int); +char *__strchr(const char *,int); #endif #ifndef __HAVE_ARCH_STRCHRNUL extern char * strchrnul(const char *,int); +char *__strchrnul(const char *,int); #endif #ifndef __HAVE_ARCH_STRNCHR extern char * strnchr(const char *, size_t, int); +char *__strnchr(const char *, size_t, int); #endif #ifndef __HAVE_ARCH_STRRCHR extern char * strrchr(const char *,int); +char *__strrchr(const char *,int); #endif extern char * __must_check skip_spaces(const char *); +char * __must_check __skip_spaces(const char *); extern char *strim(char *); +char *__strim(char *); static inline __must_check char *strstrip(char *str) { @@ -75,27 +138,35 @@ static inline __must_check char *strstrip(char *str) #ifndef __HAVE_ARCH_STRSTR extern char * strstr(const char *, const char *); +char *__strstr(const char *, const char *); #endif #ifndef __HAVE_ARCH_STRNSTR extern char * strnstr(const char *, const char *, size_t); +char *__strnstr(const char *, const char *, size_t); #endif #ifndef __HAVE_ARCH_STRLEN extern __kernel_size_t strlen(const char *); +__kernel_size_t __strlen(const char *); #endif #ifndef __HAVE_ARCH_STRNLEN extern __kernel_size_t strnlen(const char *,__kernel_size_t); +__kernel_size_t __strnlen(const char *,__kernel_size_t); #endif #ifndef __HAVE_ARCH_STRPBRK extern char * strpbrk(const char *,const char *); +char *__strpbrk(const char *,const char *); #endif #ifndef __HAVE_ARCH_STRSEP extern char * strsep(char **,const char *); +char *__strsep(char **,const char *); #endif #ifndef __HAVE_ARCH_STRSPN extern __kernel_size_t strspn(const char *,const char *); +__kernel_size_t __strspn(const char *,const char *); #endif #ifndef __HAVE_ARCH_STRCSPN extern __kernel_size_t strcspn(const char *,const char *); +__kernel_size_t __strcspn(const char *,const char *); #endif #ifndef __HAVE_ARCH_MEMSET @@ -104,14 +175,17 @@ extern void * memset(void *,int,__kernel_size_t); #ifndef __HAVE_ARCH_MEMSET16 extern void *memset16(uint16_t *, uint16_t, __kernel_size_t); +void *__memset16(uint16_t *, uint16_t, __kernel_size_t); #endif #ifndef __HAVE_ARCH_MEMSET32 extern void *memset32(uint32_t *, uint32_t, __kernel_size_t); +void *__memset32(uint32_t *, uint32_t, __kernel_size_t); #endif #ifndef __HAVE_ARCH_MEMSET64 extern void *memset64(uint64_t *, uint64_t, __kernel_size_t); +void *__memset64(uint64_t *, uint64_t, __kernel_size_t); #endif static inline void *memset_l(unsigned long *p, unsigned long v, @@ -146,12 +220,15 @@ extern void * memmove(void *,const void *,__kernel_size_t); #endif #ifndef __HAVE_ARCH_MEMSCAN extern void * memscan(void *,int,__kernel_size_t); +void *__memscan(void *,int,__kernel_size_t); #endif #ifndef __HAVE_ARCH_MEMCMP extern int memcmp(const void *,const void *,__kernel_size_t); +int __memcmp(const void *,const void *,__kernel_size_t); #endif #ifndef __HAVE_ARCH_MEMCHR extern void * memchr(const void *,int,__kernel_size_t); +void *__memchr(const void *,int,__kernel_size_t); #endif #ifndef __HAVE_ARCH_MEMCPY_MCSAFE static inline __must_check unsigned long memcpy_mcsafe(void *dst, @@ -168,7 +245,9 @@ static inline void memcpy_flushcache(void *dst, const void *src, size_t cnt) } #endif void *memchr_inv(const void *s, int c, size_t n); +void *__memchr_inv(const void *s, int c, size_t n); char *strreplace(char *s, char old, char new); +char *__strreplace(char *s, char old, char new); extern void kfree_const(const void *x); diff --git a/lib/Makefile b/lib/Makefile index 30b9b0bfbba9..19d0237f9b9c 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -18,6 +18,8 @@ KCOV_INSTRUMENT_list_debug.o := n KCOV_INSTRUMENT_debugobjects.o := n KCOV_INSTRUMENT_dynamic_debug.o := n +KASAN_SANITIZE_string.o := n + lib-y := ctype.o string.o string_sysfs.o vsprintf.o cmdline.o \ rbtree.o radix-tree.o timerqueue.o xarray.o \ idr.o int_sqrt.o extable.o \ diff --git a/mm/kasan/string.c b/mm/kasan/string.c index f23a740ff985..9a86b422e2e7 100644 --- a/mm/kasan/string.c +++ b/mm/kasan/string.c @@ -16,6 +16,38 @@ void *memset(void *addr, int c, size_t len) return __memset(addr, c, len); } +#undef memset16 +void *memset16(uint16_t *s, uint16_t v, size_t count) +{ + check_memory_region((unsigned long)s, count << 1, true, _RET_IP_); + + return __memset16(s, v, count); +} + +#undef memset32 +void *memset32(uint32_t *s, uint32_t v, size_t count) +{ + check_memory_region((unsigned long)s, count << 2, true, _RET_IP_); + + return __memset32(s, v, count); +} + +#undef memset64 +void *memset64(uint64_t *s, uint64_t v, size_t count) +{ + check_memory_region((unsigned long)s, count << 3, true, _RET_IP_); + + return __memset64(s, v, count); +} + +#undef memzero_explicit +void memzero_explicit(void *s, size_t count) +{ + check_memory_region((unsigned long)s, count, true, _RET_IP_); + + return __memzero_explicit(s, count); +} + #undef memmove void *memmove(void *dest, const void *src, size_t len) { @@ -33,3 +65,305 @@ void *memcpy(void *dest, const void *src, size_t len) return __memcpy(dest, src, len); } + +#undef strcpy +char *strcpy(char *dest, const char *src) +{ + size_t len = __strlen(src) + 1; + + check_memory_region((unsigned long)src, len, false, _RET_IP_); + check_memory_region((unsigned long)dest, len, true, _RET_IP_); + + return __strcpy(dest, src); +} + +#undef strncpy +char *strncpy(char *dest, const char *src, size_t count) +{ + size_t len = min(__strlen(src) + 1, count); + + check_memory_region((unsigned long)src, len, false, _RET_IP_); + check_memory_region((unsigned long)dest, count, true, _RET_IP_); + + return __strncpy(dest, src, count); +} + +#undef strlcpy +size_t strlcpy(char *dest, const char *src, size_t size) +{ + size_t len = __strlen(src) + 1; + + check_memory_region((unsigned long)src, len, false, _RET_IP_); + check_memory_region((unsigned long)dest, min(len, size), true, _RET_IP_); + + return __strlcpy(dest, src, size); +} + +#undef strscpy +ssize_t strscpy(char *dest, const char *src, size_t count) +{ + int len = min(__strlen(src) + 1, count); + + check_memory_region((unsigned long)src, len, false, _RET_IP_); + check_memory_region((unsigned long)dest, len, true, _RET_IP_); + + return __strscpy(dest, src, count); +} + +#undef strcat +char *strcat(char *dest, const char *src) +{ + size_t slen = __strlen(src) + 1; + size_t dlen = __strlen(dest); + + check_memory_region((unsigned long)src, slen, false, _RET_IP_); + check_memory_region((unsigned long)dest, dlen, false, _RET_IP_); + check_memory_region((unsigned long)(dest + dlen), slen, true, _RET_IP_); + + return __strcat(dest, src); +} + +char *strncat(char *dest, const char *src, size_t count) +{ + size_t slen = min(__strlen(src) + 1, count); + size_t dlen = __strlen(dest); + + check_memory_region((unsigned long)src, slen, false, _RET_IP_); + check_memory_region((unsigned long)dest, dlen, false, _RET_IP_); + check_memory_region((unsigned long)(dest + dlen), slen , true, _RET_IP_); + + return __strncat(dest, src, count); +} + +size_t strlcat(char *dest, const char *src, size_t count) +{ + size_t slen = min(__strlen(src) + 1, count); + size_t dlen = __strlen(dest); + + check_memory_region((unsigned long)src, slen, false, _RET_IP_); + check_memory_region((unsigned long)dest, dlen, false, _RET_IP_); + check_memory_region((unsigned long)(dest + dlen), slen , true, _RET_IP_); + + return __strlcat(dest, src, count); +} + +int strcmp(const char *cs, const char *ct) +{ + size_t len = min(__strlen(cs) + 1, __strlen(ct) + 1); + + check_memory_region((unsigned long)cs, len, false, _RET_IP_); + check_memory_region((unsigned long)ct, len, false, _RET_IP_); + + return __strcmp(cs, ct); +} + +int strncmp(const char *cs, const char *ct, size_t count) +{ + size_t len = min3(__strlen(cs) + 1, __strlen(ct) + 1, count); + + check_memory_region((unsigned long)cs, len, false, _RET_IP_); + check_memory_region((unsigned long)ct, len, false, _RET_IP_); + + return __strncmp(cs, ct, count); +} + +int strcasecmp(const char *s1, const char *s2) +{ + size_t len = min(__strlen(s1) + 1, __strlen(s2) + 1); + + check_memory_region((unsigned long)s1, len, false, _RET_IP_); + check_memory_region((unsigned long)s2, len, false, _RET_IP_); + + return __strcasecmp(s1, s2); +} + +int strncasecmp(const char *s1, const char *s2, size_t len) +{ + size_t sz = min3(__strlen(s1) + 1, __strlen(s2) + 1, len); + + check_memory_region((unsigned long)s1, sz, false, _RET_IP_); + check_memory_region((unsigned long)s2, sz, false, _RET_IP_); + + return __strncasecmp(s1, s2, len); +} + +char *strchr(const char *s, int c) +{ + size_t len = __strlen(s) + 1; + + check_memory_region((unsigned long)s, len, false, _RET_IP_); + + return __strchr(s, c); +} + +char *strchrnul(const char *s, int c) +{ + size_t len = __strlen(s) + 1; + + check_memory_region((unsigned long)s, len, false, _RET_IP_); + + return __strchrnul(s, c); +} + +char *strrchr(const char *s, int c) +{ + size_t len = __strlen(s) + 1; + + check_memory_region((unsigned long)s, len, false, _RET_IP_); + + return __strrchr(s, c); +} + +char *strnchr(const char *s, size_t count, int c) +{ + size_t len = __strlen(s) + 1; + + check_memory_region((unsigned long)s, len, false, _RET_IP_); + + return __strnchr(s, count, c); +} + +char *skip_spaces(const char *str) +{ + size_t len = __strlen(str) + 1; + + check_memory_region((unsigned long)str, len, false, _RET_IP_); + + return __skip_spaces(str); +} + +char *strim(char *s) +{ + size_t len = __strlen(s) + 1; + + check_memory_region((unsigned long)s, len, false, _RET_IP_); + + return __strim(s); +} + +char *strstr(const char *s1, const char *s2) +{ + size_t l1 = __strlen(s1) + 1; + size_t l2 = __strlen(s2) + 1; + + check_memory_region((unsigned long)s1, l1, false, _RET_IP_); + check_memory_region((unsigned long)s2, l2, false, _RET_IP_); + + return __strstr(s1, s2); +} + +char *strnstr(const char *s1, const char *s2, size_t len) +{ + size_t l1 = min(__strlen(s1) + 1, len); + size_t l2 = __strlen(s2) + 1; + + check_memory_region((unsigned long)s1, l1, false, _RET_IP_); + check_memory_region((unsigned long)s2, l2, false, _RET_IP_); + + return __strnstr(s1, s2, len); +} + +size_t strlen(const char *s) +{ + size_t len = __strlen(s); + + check_memory_region((unsigned long)s, len + 1, false, _RET_IP_); + + return len; +} + +size_t strnlen(const char *s, size_t count) +{ + size_t len = __strnlen(s, count); + + check_memory_region((unsigned long)s, min(len + 1, count), false, _RET_IP_); + + return len; +} + +char *strpbrk(const char *cs, const char *ct) +{ + size_t ls = __strlen(cs) + 1; + size_t lt = __strlen(ct) + 1; + + check_memory_region((unsigned long)cs, ls, false, _RET_IP_); + check_memory_region((unsigned long)ct, lt, false, _RET_IP_); + + return __strpbrk(cs, ct); +} +char *strsep(char **s, const char *ct) +{ + char *cs = *s; + + check_memory_region((unsigned long)s, sizeof(*s), true, _RET_IP_); + + if (cs) { + int ls = __strlen(cs) + 1; + int lt = __strlen(ct) + 1; + + check_memory_region((unsigned long)cs, ls, false, _RET_IP_); + check_memory_region((unsigned long)ct, lt, false, _RET_IP_); + } + + return __strsep(s, ct); +} + +size_t strspn(const char *s, const char *accept) +{ + size_t ls = __strlen(s) + 1; + size_t la = __strlen(accept) + 1; + + check_memory_region((unsigned long)s, ls, false, _RET_IP_); + check_memory_region((unsigned long)accept, la, false, _RET_IP_); + + return __strspn(s, accept); +} + +size_t strcspn(const char *s, const char *reject) +{ + size_t ls = __strlen(s) + 1; + size_t lr = __strlen(reject) + 1; + + check_memory_region((unsigned long)s, ls, false, _RET_IP_); + check_memory_region((unsigned long)reject, lr, false, _RET_IP_); + + return __strcspn(s, reject); +} + +void *memscan(void *addr, int c, size_t size) +{ + check_memory_region((unsigned long)addr, size, false, _RET_IP_); + + return __memscan(addr, c, size); +} + +int memcmp(const void *cs, const void *ct, size_t count) +{ + check_memory_region((unsigned long)cs, count, false, _RET_IP_); + check_memory_region((unsigned long)ct, count, false, _RET_IP_); + + return __memcmp(cs, ct, count); +} + +void *memchr(const void *s, int c, size_t n) +{ + check_memory_region((unsigned long)s, n, false, _RET_IP_); + + return __memchr(s, c, n); +} + +void *memchr_inv(const void *start, int c, size_t bytes) +{ + check_memory_region((unsigned long)start, bytes, false, _RET_IP_); + + return __memchr_inv(start, c, bytes); +} + +char *strreplace(char *s, char old, char new) +{ + size_t len = __strlen(s) + 1; + + check_memory_region((unsigned long)s, len, true, _RET_IP_); + + return __strreplace(s, old, new); +} -- 2.13.3