Files
systemd/src/basic/alloc-util.c
Siddhesh Poyarekar 7929e180aa Use dummy allocator to make accesses defined as per standard
systemd uses malloc_usable_size() everywhere to use memory blocks
obtained through malloc, but that is abuse since the
malloc_usable_size() interface isn't meant for this kind of use, it is
for diagnostics only.  This is also why systemd behaviour is flaky when
built with _FORTIFY_SOURCE.

One way to make this more standard (and hence safer) is to, at every
malloc_usable_size() call, also 'reallocate' the block so that the
compiler can see the larger size.  This is done through a dummy
reallocator whose only purpose is to tell the compiler about the larger
usable size, it doesn't do any actual reallocation.

Florian Weimer pointed out that this doesn't solve the problem of an
allocator potentially growing usable size at will, which will break the
implicit assumption in systemd use that the value returned remains
constant as long as the object is valid.  The safest way to fix that is
for systemd to step away from using malloc_usable_size() like this.

Resolves #22801.
2022-12-14 17:49:47 +01:00

109 lines
2.6 KiB
C

/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <malloc.h>
#include <stdint.h>
#include <string.h>
#include "alloc-util.h"
#include "macro.h"
#include "memory-util.h"
void* memdup(const void *p, size_t l) {
void *ret;
assert(l == 0 || p);
ret = malloc(l ?: 1);
if (!ret)
return NULL;
return memcpy_safe(ret, p, l);
}
void* memdup_suffix0(const void *p, size_t l) {
void *ret;
assert(l == 0 || p);
/* The same as memdup() but place a safety NUL byte after the allocated memory */
if (_unlikely_(l == SIZE_MAX)) /* prevent overflow */
return NULL;
ret = malloc(l + 1);
if (!ret)
return NULL;
((uint8_t*) ret)[l] = 0;
return memcpy_safe(ret, p, l);
}
void* greedy_realloc(
void **p,
size_t need,
size_t size) {
size_t a, newalloc;
void *q;
assert(p);
/* We use malloc_usable_size() for determining the current allocated size. On all systems we care
* about this should be safe to rely on. Should there ever arise the need to avoid relying on this we
* can instead locally fall back to realloc() on every call, rounded up to the next exponent of 2 or
* so. */
if (*p && (size == 0 || (MALLOC_SIZEOF_SAFE(*p) / size >= need)))
return *p;
if (_unlikely_(need > SIZE_MAX/2)) /* Overflow check */
return NULL;
newalloc = need * 2;
if (size_multiply_overflow(newalloc, size))
return NULL;
a = newalloc * size;
if (a < 64) /* Allocate at least 64 bytes */
a = 64;
q = realloc(*p, a);
if (!q)
return NULL;
return *p = q;
}
void* greedy_realloc0(
void **p,
size_t need,
size_t size) {
size_t before, after;
uint8_t *q;
assert(p);
before = MALLOC_SIZEOF_SAFE(*p); /* malloc_usable_size() will return 0 on NULL input, as per docs */
q = greedy_realloc(p, need, size);
if (!q)
return NULL;
after = MALLOC_SIZEOF_SAFE(q);
if (size == 0) /* avoid division by zero */
before = 0;
else
before = (before / size) * size; /* Round down */
if (after > before)
memzero(q + before, after - before);
return q;
}
void *expand_to_usable(void *ptr, size_t newsize _unused_) {
return ptr;
}