From 9349f63abc39371b074a6b3e9aa317f4f3c9b08c Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 18 May 2021 22:27:24 +0200 Subject: [PATCH] alloc-util: introduce MALLOC_SIZEOF_SAFE() helper It's a wrapper around malloc_usable_size() that is supposed to be compatible with _FORTIFY_SOURCES=1, by taking the __builtin_object_size() data into account, the same way as the _FORTIFY_SOURCES=1 logic does. Fixes: #19203 --- src/basic/alloc-util.h | 12 ++++++++++++ src/basic/fileio.c | 4 ++-- src/basic/memory-util.h | 2 +- src/basic/string-util.c | 4 ++-- 4 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/basic/alloc-util.h b/src/basic/alloc-util.h index 698a6583c5cd2..66bee6cb87127 100644 --- a/src/basic/alloc-util.h +++ b/src/basic/alloc-util.h @@ -163,3 +163,15 @@ void* greedy_realloc0(void **p, size_t *allocated, size_t need, size_t size); #else # define msan_unpoison(r, s) #endif + +/* This returns the number of usable bytes in a malloc()ed region as per malloc_usable_size(), in a way that + * is compatible with _FORTIFY_SOURCES. If _FORTIFY_SOURCES is used many memory operations will take the + * object size as returned by __builtin_object_size() into account. Hence, let's return the smaller size of + * malloc_usable_size() and __builtin_object_size() here, so that we definitely operate in safe territory by + * both the compiler's and libc's standards. Note that __builtin_object_size() evaluates to SIZE_MAX if the + * size cannot be determined, hence the MIN() expression should be safe with dynamically sized memory, + * too. Moreover, when NULL is passed malloc_usable_size() is documented to return zero, and + * __builtin_object_size() returns SIZE_MAX too, hence we also return a sensible value of 0 in this corner + * case. */ +#define MALLOC_SIZEOF_SAFE(x) \ + MIN(malloc_usable_size(x), __builtin_object_size(x, 0)) diff --git a/src/basic/fileio.c b/src/basic/fileio.c index dabdf5b5170a9..4afab84ed3e22 100644 --- a/src/basic/fileio.c +++ b/src/basic/fileio.c @@ -424,7 +424,7 @@ int read_virtual_file(const char *filename, size_t max_size, char **ret_contents if (!buf) return -ENOMEM; /* Use a bigger allocation if we got it anyway, but not more than the limit. */ - size = MIN3(malloc_usable_size(buf) - 1, max_size, READ_FULL_BYTES_MAX); + size = MIN3(MALLOC_SIZEOF_SAFE(buf) - 1, max_size, READ_FULL_BYTES_MAX); for (;;) { ssize_t k; @@ -570,7 +570,7 @@ int read_full_stream_full( buf = t; /* Unless a size has been explicitly specified, try to read as much as fits into the memory * we allocated (minus 1, to leave one byte for the safety NUL byte) */ - n = size == SIZE_MAX ? malloc_usable_size(buf) - 1 : n_next; + n = size == SIZE_MAX ? MALLOC_SIZEOF_SAFE(buf) - 1 : n_next; errno = 0; k = fread(buf + l, 1, n - l, f); diff --git a/src/basic/memory-util.h b/src/basic/memory-util.h index 179edd247bf63..e3f7980d12fd0 100644 --- a/src/basic/memory-util.h +++ b/src/basic/memory-util.h @@ -88,7 +88,7 @@ static inline void* erase_and_free(void *p) { if (!p) return NULL; - l = malloc_usable_size(p); + l = MALLOC_SIZEOF_SAFE(p); explicit_bzero_safe(p, l); return mfree(p); } diff --git a/src/basic/string-util.c b/src/basic/string-util.c index c1f368dbd89b3..f4b6515a4377f 100644 --- a/src/basic/string-util.c +++ b/src/basic/string-util.c @@ -805,7 +805,7 @@ int strextendf(char **x, const char *format, ...) { /* Let's try to use the allocated buffer, if there's room at the end still. Otherwise let's extend by 64 chars. */ if (*x) { m = strlen(*x); - a = malloc_usable_size(*x); + a = MALLOC_SIZEOF_SAFE(*x); assert(a >= m + 1); } else m = a = 0; @@ -821,7 +821,7 @@ int strextendf(char **x, const char *format, ...) { return -ENOMEM; *x = n; - a = malloc_usable_size(*x); + a = MALLOC_SIZEOF_SAFE(*x); } /* Now, let's try to format the string into it */