diff options
Diffstat (limited to 'include/linux/fortify-string.h')
| -rw-r--r-- | include/linux/fortify-string.h | 122 | 
1 files changed, 75 insertions, 47 deletions
diff --git a/include/linux/fortify-string.h b/include/linux/fortify-string.h index 89a6888f2f9e..6aeebe0a6777 100644 --- a/include/linux/fortify-string.h +++ b/include/linux/fortify-string.h @@ -2,6 +2,7 @@  #ifndef _LINUX_FORTIFY_STRING_H_  #define _LINUX_FORTIFY_STRING_H_ +#include <linux/bitfield.h>  #include <linux/bug.h>  #include <linux/const.h>  #include <linux/limits.h> @@ -9,7 +10,46 @@  #define __FORTIFY_INLINE extern __always_inline __gnu_inline __overloadable  #define __RENAME(x) __asm__(#x) -void fortify_panic(const char *name) __noreturn __cold; +#define FORTIFY_REASON_DIR(r)		FIELD_GET(BIT(0), r) +#define FORTIFY_REASON_FUNC(r)		FIELD_GET(GENMASK(7, 1), r) +#define FORTIFY_REASON(func, write)	(FIELD_PREP(BIT(0), write) | \ +					 FIELD_PREP(GENMASK(7, 1), func)) + +#ifndef fortify_panic +# define fortify_panic(func, write, avail, size, retfail)	\ +	 __fortify_panic(FORTIFY_REASON(func, write), avail, size) +#endif + +#define FORTIFY_READ		 0 +#define FORTIFY_WRITE		 1 + +#define EACH_FORTIFY_FUNC(macro)	\ +	macro(strncpy),			\ +	macro(strnlen),			\ +	macro(strlen),			\ +	macro(strscpy),			\ +	macro(strlcat),			\ +	macro(strcat),			\ +	macro(strncat),			\ +	macro(memset),			\ +	macro(memcpy),			\ +	macro(memmove),			\ +	macro(memscan),			\ +	macro(memcmp),			\ +	macro(memchr),			\ +	macro(memchr_inv),		\ +	macro(kmemdup),			\ +	macro(strcpy),			\ +	macro(UNKNOWN), + +#define MAKE_FORTIFY_FUNC(func)	FORTIFY_FUNC_##func + +enum fortify_func { +	EACH_FORTIFY_FUNC(MAKE_FORTIFY_FUNC) +}; + +void __fortify_report(const u8 reason, const size_t avail, const size_t size); +void __fortify_panic(const u8 reason, const size_t avail, const size_t size) __cold __noreturn;  void __read_overflow(void) __compiletime_error("detected read beyond size of object (1st parameter)");  void __read_overflow2(void) __compiletime_error("detected read beyond size of object (2nd parameter)");  void __read_overflow2_field(size_t avail, size_t wanted) __compiletime_warning("detected read beyond size of field (2nd parameter); maybe use struct_group()?"); @@ -143,7 +183,7 @@ char *strncpy(char * const POS p, const char *q, __kernel_size_t size)  	if (__compiletime_lessthan(p_size, size))  		__write_overflow();  	if (p_size < size) -		fortify_panic(__func__); +		fortify_panic(FORTIFY_FUNC_strncpy, FORTIFY_WRITE, p_size, size, p);  	return __underlying_strncpy(p, q, size);  } @@ -174,7 +214,7 @@ __FORTIFY_INLINE __kernel_size_t strnlen(const char * const POS p, __kernel_size  	/* Do not check characters beyond the end of p. */  	ret = __real_strnlen(p, maxlen < p_size ? maxlen : p_size);  	if (p_size <= ret && maxlen != ret) -		fortify_panic(__func__); +		fortify_panic(FORTIFY_FUNC_strnlen, FORTIFY_READ, p_size, ret + 1, ret);  	return ret;  } @@ -210,31 +250,13 @@ __kernel_size_t __fortify_strlen(const char * const POS p)  		return __underlying_strlen(p);  	ret = strnlen(p, p_size);  	if (p_size <= ret) -		fortify_panic(__func__); +		fortify_panic(FORTIFY_FUNC_strlen, FORTIFY_READ, p_size, ret + 1, ret);  	return ret;  }  /* Defined after fortified strnlen() to reuse it. */ -extern ssize_t __real_strscpy(char *, const char *, size_t) __RENAME(strscpy); -/** - * strscpy - Copy a C-string into a sized buffer - * - * @p: Where to copy the string to - * @q: Where to copy the string from - * @size: Size of destination buffer - * - * Copy the source string @q, or as much of it as fits, into the destination - * @p buffer. The behavior is undefined if the string buffers overlap. The - * destination @p buffer is always NUL terminated, unless it's zero-sized. - * - * Preferred to strncpy() since it always returns a valid string, and - * doesn't unnecessarily force the tail of the destination buffer to be - * zero padded. If padding is desired please use strscpy_pad(). - * - * Returns the number of characters copied in @p (not including the - * trailing %NUL) or -E2BIG if @size is 0 or the copy of @q was truncated. - */ -__FORTIFY_INLINE ssize_t strscpy(char * const POS p, const char * const POS q, size_t size) +extern ssize_t __real_strscpy(char *, const char *, size_t) __RENAME(sized_strscpy); +__FORTIFY_INLINE ssize_t sized_strscpy(char * const POS p, const char * const POS q, size_t size)  {  	/* Use string size rather than possible enclosing struct size. */  	const size_t p_size = __member_size(p); @@ -278,8 +300,8 @@ __FORTIFY_INLINE ssize_t strscpy(char * const POS p, const char * const POS q, s  	 * Generate a runtime write overflow error if len is greater than  	 * p_size.  	 */ -	if (len > p_size) -		fortify_panic(__func__); +	if (p_size < len) +		fortify_panic(FORTIFY_FUNC_strscpy, FORTIFY_WRITE, p_size, len, -E2BIG);  	/*  	 * We can now safely call vanilla strscpy because we are protected from: @@ -337,7 +359,7 @@ size_t strlcat(char * const POS p, const char * const POS q, size_t avail)  	/* Give up if string is already overflowed. */  	if (p_size <= p_len) -		fortify_panic(__func__); +		fortify_panic(FORTIFY_FUNC_strlcat, FORTIFY_READ, p_size, p_len + 1, wanted);  	if (actual >= avail) {  		copy_len = avail - p_len - 1; @@ -346,7 +368,7 @@ size_t strlcat(char * const POS p, const char * const POS q, size_t avail)  	/* Give up if copy will overflow. */  	if (p_size <= actual) -		fortify_panic(__func__); +		fortify_panic(FORTIFY_FUNC_strlcat, FORTIFY_WRITE, p_size, actual + 1, wanted);  	__underlying_memcpy(p + p_len, q, copy_len);  	p[actual] = '\0'; @@ -373,9 +395,10 @@ __FORTIFY_INLINE __diagnose_as(__builtin_strcat, 1, 2)  char *strcat(char * const POS p, const char *q)  {  	const size_t p_size = __member_size(p); +	const size_t wanted = strlcat(p, q, p_size); -	if (strlcat(p, q, p_size) >= p_size) -		fortify_panic(__func__); +	if (p_size <= wanted) +		fortify_panic(FORTIFY_FUNC_strcat, FORTIFY_WRITE, p_size, wanted + 1, p);  	return p;  } @@ -404,20 +427,21 @@ char *strncat(char * const POS p, const char * const POS q, __kernel_size_t coun  {  	const size_t p_size = __member_size(p);  	const size_t q_size = __member_size(q); -	size_t p_len, copy_len; +	size_t p_len, copy_len, total;  	if (p_size == SIZE_MAX && q_size == SIZE_MAX)  		return __underlying_strncat(p, q, count);  	p_len = strlen(p);  	copy_len = strnlen(q, count); -	if (p_size < p_len + copy_len + 1) -		fortify_panic(__func__); +	total = p_len + copy_len + 1; +	if (p_size < total) +		fortify_panic(FORTIFY_FUNC_strncat, FORTIFY_WRITE, p_size, total, p);  	__underlying_memcpy(p + p_len, q, copy_len);  	p[p_len + copy_len] = '\0';  	return p;  } -__FORTIFY_INLINE void fortify_memset_chk(__kernel_size_t size, +__FORTIFY_INLINE bool fortify_memset_chk(__kernel_size_t size,  					 const size_t p_size,  					 const size_t p_size_field)  { @@ -452,7 +476,8 @@ __FORTIFY_INLINE void fortify_memset_chk(__kernel_size_t size,  	 * lengths are unknown.)  	 */  	if (p_size != SIZE_MAX && p_size < size) -		fortify_panic("memset"); +		fortify_panic(FORTIFY_FUNC_memset, FORTIFY_WRITE, p_size, size, true); +	return false;  }  #define __fortify_memset_chk(p, c, size, p_size, p_size_field) ({	\ @@ -506,7 +531,7 @@ __FORTIFY_INLINE bool fortify_memcpy_chk(__kernel_size_t size,  					 const size_t q_size,  					 const size_t p_size_field,  					 const size_t q_size_field, -					 const char *func) +					 const u8 func)  {  	if (__builtin_constant_p(size)) {  		/* @@ -550,9 +575,10 @@ __FORTIFY_INLINE bool fortify_memcpy_chk(__kernel_size_t size,  	 * (The SIZE_MAX test is to optimize away checks where the buffer  	 * lengths are unknown.)  	 */ -	if ((p_size != SIZE_MAX && p_size < size) || -	    (q_size != SIZE_MAX && q_size < size)) -		fortify_panic(func); +	if (p_size != SIZE_MAX && p_size < size) +		fortify_panic(func, FORTIFY_WRITE, p_size, size, true); +	else if (q_size != SIZE_MAX && q_size < size) +		fortify_panic(func, FORTIFY_READ, p_size, size, true);  	/*  	 * Warn when writing beyond destination field size. @@ -585,7 +611,7 @@ __FORTIFY_INLINE bool fortify_memcpy_chk(__kernel_size_t size,  	const size_t __q_size_field = (q_size_field);			\  	WARN_ONCE(fortify_memcpy_chk(__fortify_size, __p_size,		\  				     __q_size, __p_size_field,		\ -				     __q_size_field, #op),		\ +				     __q_size_field, FORTIFY_FUNC_ ##op), \  		  #op ": detected field-spanning write (size %zu) of single %s (size %zu)\n", \  		  __fortify_size,					\  		  "field \"" #p "\" at " FILE_LINE,			\ @@ -652,7 +678,7 @@ __FORTIFY_INLINE void *memscan(void * const POS0 p, int c, __kernel_size_t size)  	if (__compiletime_lessthan(p_size, size))  		__read_overflow();  	if (p_size < size) -		fortify_panic(__func__); +		fortify_panic(FORTIFY_FUNC_memscan, FORTIFY_READ, p_size, size, NULL);  	return __real_memscan(p, c, size);  } @@ -668,8 +694,10 @@ int memcmp(const void * const POS0 p, const void * const POS0 q, __kernel_size_t  		if (__compiletime_lessthan(q_size, size))  			__read_overflow2();  	} -	if (p_size < size || q_size < size) -		fortify_panic(__func__); +	if (p_size < size) +		fortify_panic(FORTIFY_FUNC_memcmp, FORTIFY_READ, p_size, size, INT_MIN); +	else if (q_size < size) +		fortify_panic(FORTIFY_FUNC_memcmp, FORTIFY_READ, q_size, size, INT_MIN);  	return __underlying_memcmp(p, q, size);  } @@ -681,7 +709,7 @@ void *memchr(const void * const POS0 p, int c, __kernel_size_t size)  	if (__compiletime_lessthan(p_size, size))  		__read_overflow();  	if (p_size < size) -		fortify_panic(__func__); +		fortify_panic(FORTIFY_FUNC_memchr, FORTIFY_READ, p_size, size, NULL);  	return __underlying_memchr(p, c, size);  } @@ -693,7 +721,7 @@ __FORTIFY_INLINE void *memchr_inv(const void * const POS0 p, int c, size_t size)  	if (__compiletime_lessthan(p_size, size))  		__read_overflow();  	if (p_size < size) -		fortify_panic(__func__); +		fortify_panic(FORTIFY_FUNC_memchr_inv, FORTIFY_READ, p_size, size, NULL);  	return __real_memchr_inv(p, c, size);  } @@ -706,7 +734,7 @@ __FORTIFY_INLINE void *kmemdup(const void * const POS0 p, size_t size, gfp_t gfp  	if (__compiletime_lessthan(p_size, size))  		__read_overflow();  	if (p_size < size) -		fortify_panic(__func__); +		fortify_panic(FORTIFY_FUNC_kmemdup, FORTIFY_READ, p_size, size, NULL);  	return __real_kmemdup(p, size, gfp);  } @@ -743,7 +771,7 @@ char *strcpy(char * const POS p, const char * const POS q)  		__write_overflow();  	/* Run-time check for dynamic size overflow. */  	if (p_size < size) -		fortify_panic(__func__); +		fortify_panic(FORTIFY_FUNC_strcpy, FORTIFY_WRITE, p_size, size, p);  	__underlying_memcpy(p, q, size);  	return p;  }  | 
