forked from mirrors/linux
		
	fortify: Allow strlen() and strnlen() to pass compile-time known lengths
Under CONFIG_FORTIFY_SOURCE, it is possible for the compiler to perform strlen() and strnlen() at compile-time when the string size is known. This is required to support compile-time overflow checking in strlcpy(). Signed-off-by: Kees Cook <keescook@chromium.org>
This commit is contained in:
		
							parent
							
								
									369cd2165d
								
							
						
					
					
						commit
						3009f891bb
					
				
					 1 changed files with 38 additions and 11 deletions
				
			
		|  | @ -10,6 +10,20 @@ void __read_overflow(void) __compiletime_error("detected read beyond size of obj | |||
| void __read_overflow2(void) __compiletime_error("detected read beyond size of object (2nd parameter)"); | ||||
| void __write_overflow(void) __compiletime_error("detected write beyond size of object (1st parameter)"); | ||||
| 
 | ||||
| #define __compiletime_strlen(p)				\ | ||||
| ({							\ | ||||
| 	unsigned char *__p = (unsigned char *)(p);      \ | ||||
| 	size_t ret = (size_t)-1;			\ | ||||
| 	size_t p_size = __builtin_object_size(p, 1);	\ | ||||
| 	if (p_size != (size_t)-1) {			\ | ||||
| 		size_t p_len = p_size - 1;		\ | ||||
| 		if (__builtin_constant_p(__p[p_len]) &&	\ | ||||
| 		    __p[p_len] == '\0')			\ | ||||
| 			ret = __builtin_strlen(__p);	\ | ||||
| 	}						\ | ||||
| 	ret;						\ | ||||
| }) | ||||
| 
 | ||||
| #if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS) | ||||
| extern void *__underlying_memchr(const void *p, int c, __kernel_size_t size) __RENAME(memchr); | ||||
| extern int __underlying_memcmp(const void *p, const void *q, __kernel_size_t size) __RENAME(memcmp); | ||||
|  | @ -60,21 +74,31 @@ extern __kernel_size_t __real_strnlen(const char *, __kernel_size_t) __RENAME(st | |||
| __FORTIFY_INLINE __kernel_size_t strnlen(const char *p, __kernel_size_t maxlen) | ||||
| { | ||||
| 	size_t p_size = __builtin_object_size(p, 1); | ||||
| 	__kernel_size_t ret = __real_strnlen(p, maxlen < p_size ? maxlen : p_size); | ||||
| 	size_t p_len = __compiletime_strlen(p); | ||||
| 	size_t ret; | ||||
| 
 | ||||
| 	/* We can take compile-time actions when maxlen is const. */ | ||||
| 	if (__builtin_constant_p(maxlen) && p_len != (size_t)-1) { | ||||
| 		/* If p is const, we can use its compile-time-known len. */ | ||||
| 		if (maxlen >= p_size) | ||||
| 			return p_len; | ||||
| 	} | ||||
| 
 | ||||
| 	/* 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__); | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| /* defined after fortified strnlen to reuse it. */ | ||||
| __FORTIFY_INLINE __kernel_size_t strlen(const char *p) | ||||
| { | ||||
| 	__kernel_size_t ret; | ||||
| 	size_t p_size = __builtin_object_size(p, 1); | ||||
| 
 | ||||
| 	/* Work around gcc excess stack consumption issue */ | ||||
| 	if (p_size == (size_t)-1 || | ||||
| 		(__builtin_constant_p(p[p_size - 1]) && p[p_size - 1] == '\0')) | ||||
| 	/* Give up if we don't know how large p is. */ | ||||
| 	if (p_size == (size_t)-1) | ||||
| 		return __underlying_strlen(p); | ||||
| 	ret = strnlen(p, p_size); | ||||
| 	if (p_size <= ret) | ||||
|  | @ -86,24 +110,27 @@ __FORTIFY_INLINE __kernel_size_t strlen(const char *p) | |||
| extern size_t __real_strlcpy(char *, const char *, size_t) __RENAME(strlcpy); | ||||
| __FORTIFY_INLINE size_t strlcpy(char *p, const char *q, size_t size) | ||||
| { | ||||
| 	size_t ret; | ||||
| 	size_t p_size = __builtin_object_size(p, 1); | ||||
| 	size_t q_size = __builtin_object_size(q, 1); | ||||
| 	size_t q_len;	/* Full count of source string length. */ | ||||
| 	size_t len;	/* Count of characters going into destination. */ | ||||
| 
 | ||||
| 	if (p_size == (size_t)-1 && q_size == (size_t)-1) | ||||
| 		return __real_strlcpy(p, q, size); | ||||
| 	ret = strlen(q); | ||||
| 	if (size) { | ||||
| 		size_t len = (ret >= size) ? size - 1 : ret; | ||||
| 
 | ||||
| 		if (__builtin_constant_p(len) && len >= p_size) | ||||
| 	q_len = strlen(q); | ||||
| 	len = (q_len >= size) ? size - 1 : q_len; | ||||
| 	if (__builtin_constant_p(size) && __builtin_constant_p(q_len) && size) { | ||||
| 		/* Write size is always larger than destination. */ | ||||
| 		if (len >= p_size) | ||||
| 			__write_overflow(); | ||||
| 	} | ||||
| 	if (size) { | ||||
| 		if (len >= p_size) | ||||
| 			fortify_panic(__func__); | ||||
| 		__underlying_memcpy(p, q, len); | ||||
| 		p[len] = '\0'; | ||||
| 	} | ||||
| 	return ret; | ||||
| 	return q_len; | ||||
| } | ||||
| 
 | ||||
| /* defined after fortified strnlen to reuse it */ | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Kees Cook
						Kees Cook