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 | 10 | void __read_overflow2(void) __compiletime_error("detected read beyond size of object (2nd parameter)"); |
| 11 | 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 | 27 | #if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS) |
| 14 | 28 | extern void *__underlying_memchr(const void *p, int c, __kernel_size_t size) __RENAME(memchr); |
| 15 | 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 | 74 | __FORTIFY_INLINE __kernel_size_t strnlen(const char *p, __kernel_size_t maxlen) |
| 61 | 75 | { |
| 62 | 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 | 89 | if (p_size <= ret && maxlen != ret) |
| 66 | 90 | fortify_panic(__func__); |
| 67 | 91 | return ret; |
| 68 | 92 | } |
| 69 | 93 | |
| 94 | +/* defined after fortified strnlen to reuse it. */ | |
| 70 | 95 | __FORTIFY_INLINE __kernel_size_t strlen(const char *p) |
| 71 | 96 | { |
| 72 | 97 | __kernel_size_t ret; |
| 73 | 98 | size_t p_size = __builtin_object_size(p, 1); |
| 74 | 99 | |
| 75 | − /* Work around gcc excess stack consumption issue */ | |
| 76 | − if (p_size == (size_t)-1 || | |
| 77 | − (__builtin_constant_p(p[p_size - 1]) && p[p_size - 1] == '\0')) | |
| 100 | + /* Give up if we don't know how large p is. */ | |
| 101 | + if (p_size == (size_t)-1) | |
| 78 | 102 | return __underlying_strlen(p); |
| 79 | 103 | ret = strnlen(p, p_size); |
| 80 | 104 | if (p_size <= ret) |
| @@ -86,24 +110,27 @@ __FORTIFY_INLINE __kernel_size_t strlen(const char *p) · 5 unmodified lines | ||
| 86 | 110 | extern size_t __real_strlcpy(char *, const char *, size_t) __RENAME(strlcpy); |
| 87 | 111 | __FORTIFY_INLINE size_t strlcpy(char *p, const char *q, size_t size) |
| 88 | 112 | { |
| 89 | − size_t ret; | |
| 90 | 113 | size_t p_size = __builtin_object_size(p, 1); |
| 91 | 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 | 118 | if (p_size == (size_t)-1 && q_size == (size_t)-1) |
| 94 | 119 | return __real_strlcpy(p, q, size); |
| 95 | − ret = strlen(q); | |
| 96 | − if (size) { | |
| 97 | − size_t len = (ret >= size) ? size - 1 : ret; | |
| 98 | − | |
| 99 | − if (__builtin_constant_p(len) && len >= p_size) | |
| 120 | + q_len = strlen(q); | |
| 121 | + len = (q_len >= size) ? size - 1 : q_len; | |
| 122 | + if (__builtin_constant_p(size) && __builtin_constant_p(q_len) && size) { | |
| 123 | + /* Write size is always larger than destination. */ | |
| 124 | + if (len >= p_size) | |
| 100 | 125 | __write_overflow(); |
| 126 | + } | |
| 127 | + if (size) { | |
| 101 | 128 | if (len >= p_size) |
| 102 | 129 | fortify_panic(__func__); |
| 103 | 130 | __underlying_memcpy(p, q, len); |
| 104 | 131 | p[len] = '\0'; |
| 105 | 132 | } |
| 106 | − return ret; | |
| 133 | + return q_len; | |
| 107 | 134 | } |
| 108 | 135 | |
| 109 | 136 | /* defined after fortified strnlen to reuse it */ |