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().
Unified · Split
| @@ -10,6 +10,20 @@ void __read_overflow(void) __compiletime_error("detected read beyond size of obj · 9 unmodified lines | |||
| 10 | void __read_overflow2(void) __compiletime_error("detected read beyond size of object (2nd parameter)"); | 10 | void __read_overflow2(void) __compiletime_error("detected read beyond size of object (2nd parameter)"); |
| 11 | void __write_overflow(void) __compiletime_error("detected write beyond size of object (1st parameter)"); | 11 | void __write_overflow(void) __compiletime_error("detected write beyond size of object (1st parameter)"); |
| 12 | 12 | ||
| 13 | #define __compiletime_strlen(p) \ | ||
| 14 | ({ \ | ||
| 15 | unsigned char *__p = (unsigned char *)(p); \ | ||
| 16 | size_t ret = (size_t)-1; \ | ||
| 17 | size_t p_size = __builtin_object_size(p, 1); \ | ||
| 18 | if (p_size != (size_t)-1) { \ | ||
| 19 | size_t p_len = p_size - 1; \ | ||
| 20 | if (__builtin_constant_p(__p[p_len]) && \ | ||
| 21 | __p[p_len] == '\0') \ | ||
| 22 | ret = __builtin_strlen(__p); \ | ||
| 23 | } \ | ||
| 24 | ret; \ | ||
| 25 | }) | ||
| 26 | |||
| 13 | #if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS) | 27 | #if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS) |
| 14 | extern void *__underlying_memchr(const void *p, int c, __kernel_size_t size) __RENAME(memchr); | 28 | extern void *__underlying_memchr(const void *p, int c, __kernel_size_t size) __RENAME(memchr); |
| 15 | extern int __underlying_memcmp(const void *p, const void *q, __kernel_size_t size) __RENAME(memcmp); | 29 | 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 · 44 unmodified lines | |||
| 60 | __FORTIFY_INLINE __kernel_size_t strnlen(const char *p, __kernel_size_t maxlen) | 74 | __FORTIFY_INLINE __kernel_size_t strnlen(const char *p, __kernel_size_t maxlen) |
| 61 | { | 75 | { |
| 62 | size_t p_size = __builtin_object_size(p, 1); | 76 | size_t p_size = __builtin_object_size(p, 1); |
| 63 | __kernel_size_t ret = __real_strnlen(p, maxlen < p_size ? maxlen : p_size); | 77 | size_t p_len = __compiletime_strlen(p); |
| 78 | size_t ret; | ||
| 79 | |||
| 80 | /* We can take compile-time actions when maxlen is const. */ | ||
| 81 | if (__builtin_constant_p(maxlen) && p_len != (size_t)-1) { | ||
| 82 | /* If p is const, we can use its compile-time-known len. */ | ||
| 83 | if (maxlen >= p_size) | ||
| 84 | return p_len; | ||
| 85 | } | ||
| 64 | 86 | ||
| 87 | /* Do not check characters beyond the end of p. */ | ||
| 88 | ret = __real_strnlen(p, maxlen < p_size ? maxlen : p_size); | ||
| 65 | if (p_size <= ret && maxlen != ret) | 89 | if (p_size <= ret && maxlen != ret) |
| 66 | fortify_panic(__func__); | 90 | fortify_panic(__func__); |
| 67 | return ret; | 91 | return ret; |
| 68 | } | 92 | } |
| 69 | 93 | ||
| 94 | /* defined after fortified strnlen to reuse it. */ | ||
| 70 | __FORTIFY_INLINE __kernel_size_t strlen(const char *p) | 95 | __FORTIFY_INLINE __kernel_size_t strlen(const char *p) |
| 71 | { | 96 | { |
| 72 | __kernel_size_t ret; | 97 | __kernel_size_t ret; |
| 73 | size_t p_size = __builtin_object_size(p, 1); | 98 | size_t p_size = __builtin_object_size(p, 1); |
| 74 | 99 | ||
| 75 | /* Work around gcc excess stack consumption issue */ | 100 | /* Give up if we don't know how large p is. */ |
| 76 | if (p_size == (size_t)-1 || | 101 | if (p_size == (size_t)-1) |
| 77 | (__builtin_constant_p(p[p_size - 1]) && p[p_size - 1] == '\0')) | ||
| 78 | return __underlying_strlen(p); | 102 | return __underlying_strlen(p); |
| 79 | ret = strnlen(p, p_size); | 103 | ret = strnlen(p, p_size); |
| 80 | if (p_size <= ret) | 104 | if (p_size <= ret) |
| @@ -86,24 +110,27 @@ __FORTIFY_INLINE __kernel_size_t strlen(const char *p) · 5 unmodified lines | |||
| 86 | extern size_t __real_strlcpy(char *, const char *, size_t) __RENAME(strlcpy); | 110 | extern size_t __real_strlcpy(char *, const char *, size_t) __RENAME(strlcpy); |
| 87 | __FORTIFY_INLINE size_t strlcpy(char *p, const char *q, size_t size) | 111 | __FORTIFY_INLINE size_t strlcpy(char *p, const char *q, size_t size) |
| 88 | { | 112 | { |
| 89 | size_t ret; | ||
| 90 | size_t p_size = __builtin_object_size(p, 1); | 113 | size_t p_size = __builtin_object_size(p, 1); |
| 91 | size_t q_size = __builtin_object_size(q, 1); | 114 | size_t q_size = __builtin_object_size(q, 1); |
| 115 | size_t q_len; /* Full count of source string length. */ | ||
| 116 | size_t len; /* Count of characters going into destination. */ | ||
| 92 | 117 | ||
| 93 | if (p_size == (size_t)-1 && q_size == (size_t)-1) | 118 | if (p_size == (size_t)-1 && q_size == (size_t)-1) |
| 94 | return __real_strlcpy(p, q, size); | 119 | return __real_strlcpy(p, q, size); |
| 95 | ret = strlen(q); | 120 | q_len = strlen(q); |
| 96 | if (size) { | 121 | len = (q_len >= size) ? size - 1 : q_len; |
| 97 | size_t len = (ret >= size) ? size - 1 : ret; | 122 | if (__builtin_constant_p(size) && __builtin_constant_p(q_len) && size) { |
| 98 | 123 | /* Write size is always larger than destination. */ | |
| 99 | if (__builtin_constant_p(len) && len >= p_size) | 124 | if (len >= p_size) |
| 100 | __write_overflow(); | 125 | __write_overflow(); |
| 126 | } | ||
| 127 | if (size) { | ||
| 101 | if (len >= p_size) | 128 | if (len >= p_size) |
| 102 | fortify_panic(__func__); | 129 | fortify_panic(__func__); |
| 103 | __underlying_memcpy(p, q, len); | 130 | __underlying_memcpy(p, q, len); |
| 104 | p[len] = '\0'; | 131 | p[len] = '\0'; |
| 105 | } | 132 | } |
| 106 | return ret; | 133 | return q_len; |
| 107 | } | 134 | } |
| 108 | 135 | ||
| 109 | /* defined after fortified strnlen to reuse it */ | 136 | /* defined after fortified strnlen to reuse it */ |