diff options
| author | Nick Kralevich <nnk@google.com> | 2013-08-28 10:47:43 -0700 |
|---|---|---|
| committer | Nick Kralevich <nnk@google.com> | 2013-08-28 12:39:06 -0700 |
| commit | 93501d3ab81156bcef251bb817a49e9ca46a6ec1 (patch) | |
| tree | 38deed513bd1e1ec2cc316a4bd5de6689ee991da /libc | |
| parent | 2557433d8ea4c489f061170822f34f038b930b96 (diff) | |
| download | bionic-93501d3ab81156bcef251bb817a49e9ca46a6ec1.zip bionic-93501d3ab81156bcef251bb817a49e9ca46a6ec1.tar.gz bionic-93501d3ab81156bcef251bb817a49e9ca46a6ec1.tar.bz2 | |
FORTIFY_SOURCE: introduce __strncpy_chk2
This change detects programs reading beyond the end of "src" when
calling strncpy.
Change-Id: Ie1b42de923385d62552b22c27b2d4713ab77ee03
Diffstat (limited to 'libc')
| -rw-r--r-- | libc/bionic/__strncpy_chk.cpp | 48 | ||||
| -rw-r--r-- | libc/include/string.h | 18 |
2 files changed, 58 insertions, 8 deletions
diff --git a/libc/bionic/__strncpy_chk.cpp b/libc/bionic/__strncpy_chk.cpp index b01879c..6b1a3c7 100644 --- a/libc/bionic/__strncpy_chk.cpp +++ b/libc/bionic/__strncpy_chk.cpp @@ -41,13 +41,51 @@ * This strncpy check is called if _FORTIFY_SOURCE is defined and * greater than 0. */ -extern "C" char *__strncpy_chk (char *dest, const char *src, +extern "C" char* __strncpy_chk(char* __restrict dest, const char* __restrict src, size_t len, size_t dest_len) { - if (__predict_false(len > dest_len)) { - __fortify_chk_fail("strncpy buffer overflow", - BIONIC_EVENT_STRNCPY_BUFFER_OVERFLOW); + if (__predict_false(len > dest_len)) { + __fortify_chk_fail("strncpy dest buffer overflow", + BIONIC_EVENT_STRNCPY_BUFFER_OVERFLOW); + } + + return strncpy(dest, src, len); +} + +/* + * __strncpy_chk2 + * + * This is a variant of __strncpy_chk, but it also checks to make + * sure we don't read beyond the end of "src". The code for this is + * based on the original version of strncpy, but modified to check + * how much we read from "src" at the end of the copy operation. + */ +extern "C" char* __strncpy_chk2(char* __restrict dst, const char* __restrict src, + size_t n, size_t dest_len, size_t src_len) +{ + if (__predict_false(n > dest_len)) { + __fortify_chk_fail("strncpy dest buffer overflow", + BIONIC_EVENT_STRNCPY_BUFFER_OVERFLOW); + } + if (n != 0) { + char* d = dst; + const char* s = src; + + do { + if ((*d++ = *s++) == 0) { + /* NUL pad the remaining n-1 bytes */ + while (--n != 0) { + *d++ = 0; + } + break; + } + } while (--n != 0); + + size_t s_copy_len = static_cast<size_t>(s - src); + if (__predict_false(s_copy_len > src_len)) { + __fortify_chk_fail("strncpy read beyond end of src buffer", 0); } + } - return strncpy(dest, src, len); + return dst; } diff --git a/libc/include/string.h b/libc/include/string.h index 7801ee9..5409391 100644 --- a/libc/include/string.h +++ b/libc/include/string.h @@ -119,14 +119,26 @@ char* strcpy(char* __restrict dest, const char* __restrict src) { } __errordecl(__strncpy_error, "strncpy called with size bigger than buffer"); +extern char* __strncpy_chk2(char* __restrict, const char* __restrict, size_t, size_t, size_t); __BIONIC_FORTIFY_INLINE char* strncpy(char* __restrict dest, const char* __restrict src, size_t n) { - size_t bos = __bos(dest); - if (__builtin_constant_p(n) && (n > bos)) { + size_t bos_dest = __bos(dest); + size_t bos_src = __bos(src); + if (__builtin_constant_p(n) && (n > bos_dest)) { __strncpy_error(); } - return __builtin___strncpy_chk(dest, src, n, bos); + + if (bos_src == __BIONIC_FORTIFY_UNKNOWN_SIZE) { + return __builtin___strncpy_chk(dest, src, n, bos_dest); + } + + size_t slen = __builtin_strlen(src); + if (__builtin_constant_p(slen)) { + return __builtin___strncpy_chk(dest, src, n, bos_dest); + } + + return __strncpy_chk2(dest, src, n, bos_dest, bos_src); } __BIONIC_FORTIFY_INLINE |
