Compare commits

..

2 commits

Author SHA1 Message Date
SkewedZeppelin
5d22d91964
Merge dd6eb962de into 4fe9018b6f 2025-03-23 08:51:54 +00:00
Tavi
dd6eb962de
perform size checks on memcpy/memmove/memset
- memset is disabled for now as it causes hangs
- underlying functions were copied from isoalloc, licensed Apache-2.0
	- credit Chris Rohlf for memcpy/memset
	- credit David Carlier for memmove

Signed-off-by: Tavi <tavi@divested.dev>
2025-03-23 04:51:44 -04:00
8 changed files with 67 additions and 53 deletions

View file

@ -279,7 +279,7 @@ The following boolean configuration options are available:
* `CONFIG_BLOCK_OPS_CHECK_SIZE`: `true` or `false` (default) to ensure length * `CONFIG_BLOCK_OPS_CHECK_SIZE`: `true` or `false` (default) to ensure length
parameter of the memcpy/memmove/memset block operations are within parameter of the memcpy/memmove/memset block operations are within
approximate bounds to minimize buffer overflows. Note, memset override is approximate bounds to minimize buffer overflows. Note, memset override is
currently disabled due to improper behavior. currently disabled due to being broken.
The following integer configuration options are available: The following integer configuration options are available:

View file

@ -528,7 +528,7 @@ static void set_canary(UNUSED const struct slab_metadata *metadata, UNUSED void
} }
#endif #endif
memcpy((char *)p + size - canary_size, &metadata->canary_value, canary_size); h_memcpy_real((char *)p + size - canary_size, &metadata->canary_value, canary_size);
#endif #endif
} }
@ -541,7 +541,7 @@ static void check_canary(UNUSED const struct slab_metadata *metadata, UNUSED con
#endif #endif
u64 canary_value; u64 canary_value;
memcpy(&canary_value, (const char *)p + size - canary_size, canary_size); h_memcpy_real(&canary_value, (const char *)p + size - canary_size, canary_size);
#ifdef HAS_ARM_MTE #ifdef HAS_ARM_MTE
if (unlikely(canary_value == 0)) { if (unlikely(canary_value == 0)) {
@ -831,7 +831,7 @@ static inline void deallocate_small(void *p, const size_t *expected_size) {
#endif #endif
if (ZERO_ON_FREE && !skip_zero) { if (ZERO_ON_FREE && !skip_zero) {
memset(p, 0, size - canary_size); h_memset_real(p, 0, size - canary_size);
} }
} }
@ -1502,7 +1502,7 @@ EXPORT void *h_calloc(size_t nmemb, size_t size) {
total_size = adjust_size_for_canary(total_size); total_size = adjust_size_for_canary(total_size);
void *p = alloc(total_size); void *p = alloc(total_size);
if (!ZERO_ON_FREE && likely(p != NULL) && total_size && total_size <= max_slab_size_class) { if (!ZERO_ON_FREE && likely(p != NULL) && total_size && total_size <= max_slab_size_class) {
memset(p, 0, total_size - canary_size); h_memset_real(p, 0, total_size - canary_size);
} }
#ifdef HAS_ARM_MTE #ifdef HAS_ARM_MTE
// use an assert instead of adding a conditional to memset() above (freed memory is always // use an assert instead of adding a conditional to memset() above (freed memory is always
@ -1624,7 +1624,7 @@ EXPORT void *h_realloc(void *old, size_t size) {
mutex_unlock(&ra->lock); mutex_unlock(&ra->lock);
if (memory_remap_fixed(old, old_size, new, size)) { if (memory_remap_fixed(old, old_size, new, size)) {
memcpy(new, old, copy_size); h_memcpy_real(new, old, copy_size);
deallocate_pages(old, old_size, old_guard_size); deallocate_pages(old, old_size, old_guard_size);
} else { } else {
memory_unmap((char *)old - old_guard_size, old_guard_size); memory_unmap((char *)old - old_guard_size, old_guard_size);
@ -1646,7 +1646,7 @@ EXPORT void *h_realloc(void *old, size_t size) {
if (copy_size > 0 && copy_size <= max_slab_size_class) { if (copy_size > 0 && copy_size <= max_slab_size_class) {
copy_size -= canary_size; copy_size -= canary_size;
} }
memcpy(new, old_orig, copy_size); h_memcpy_real(new, old_orig, copy_size);
if (old_size <= max_slab_size_class) { if (old_size <= max_slab_size_class) {
deallocate_small(old, NULL); deallocate_small(old, NULL);
} else { } else {
@ -1874,8 +1874,8 @@ EXPORT size_t h_malloc_object_size_fast(const void *p) {
return SIZE_MAX; return SIZE_MAX;
} }
#if CONFIG_BLOCK_OPS_CHECK_SIZE
inline void *h_memcpy_real(void *dst, const void *src, size_t len) { inline void *h_memcpy_real(void *dst, const void *src, size_t len) {
#if CONFIG_BLOCK_OPS_CHECK_SIZE && !defined(HAS_ARM_MTE)
char *p_dst = (char *)dst; char *p_dst = (char *)dst;
char const *p_src = (char const *)src; char const *p_src = (char const *)src;
@ -1884,27 +1884,16 @@ inline void *h_memcpy_real(void *dst, const void *src, size_t len) {
} }
return dst; return dst;
} #else
return memcpy(dst, src, len);
EXPORT void *h_memcpy(void *dst, const void *src, size_t len) { #endif
if (len > malloc_object_size_fast(src)) {
fatal_error("memcpy read overflow");
}
if (len > malloc_object_size_fast(dst)) {
fatal_error("memcpy buffer overflow");
}
return h_memcpy_real(dst, src, len);
} }
inline void *h_memmove_real(void *dst, const void *src, size_t len) { inline void *h_memmove_real(void *dst, const void *src, size_t len) {
#if CONFIG_BLOCK_OPS_CHECK_SIZE && !defined(HAS_ARM_MTE)
char *p_dst = (char *)dst; char *p_dst = (char *)dst;
char const *p_src = (char const *)src; char const *p_src = (char const *)src;
if(dst == src) {
return dst;
}
if(p_src < p_dst) { if(p_src < p_dst) {
p_dst += len; p_dst += len;
p_src += len; p_src += len;
@ -1912,24 +1901,17 @@ inline void *h_memmove_real(void *dst, const void *src, size_t len) {
*--p_dst = *--p_src; *--p_dst = *--p_src;
} }
} else { } else {
dst = h_memcpy(dst, src, len); dst = h_memcpy_real(dst, src, len);
} }
return dst; return dst;
} #else
return memmove(dst, src, len);
EXPORT void *h_memmove(void *dst, const void *src, size_t len) { #endif
if (len > malloc_object_size_fast(src)) {
fatal_error("memmove read overflow");
}
if (len > malloc_object_size_fast(dst)) {
fatal_error("memmove buffer overflow");
}
return h_memmove_real(dst, src, len);
} }
inline void *h_memset_real(void *dst, int value, size_t len) { inline void *h_memset_real(void *dst, int value, size_t len) {
#if CONFIG_BLOCK_OPS_CHECK_SIZE && !defined(HAS_ARM_MTE)
char *p_dst = (char *)dst; char *p_dst = (char *)dst;
while(len--) { while(len--) {
@ -1937,13 +1919,45 @@ inline void *h_memset_real(void *dst, int value, size_t len) {
} }
return dst; return dst;
#else
return memset(dst, value, len);
#endif
}
#if CONFIG_BLOCK_OPS_CHECK_SIZE && !defined(HAS_ARM_MTE)
EXPORT void *h_memcpy(void *dst, const void *src, size_t len) {
if(dst == src || len == 0) {
return dst;
}
if (len > malloc_object_size(src)) {
fatal_error("memcpy read overflow");
}
if (len > malloc_object_size(dst)) {
fatal_error("memcpy buffer overflow");
}
return h_memcpy_real(dst, src, len);
}
EXPORT void *h_memmove(void *dst, const void *src, size_t len) {
if(dst == src || len == 0) {
return dst;
}
if (len > malloc_object_size(src)) {
fatal_error("memmove read overflow");
}
if (len > malloc_object_size(dst)) {
fatal_error("memmove buffer overflow");
}
return h_memmove_real(dst, src, len);
} }
EXPORT void *h_memset(void *dst, int value, size_t len) { EXPORT void *h_memset(void *dst, int value, size_t len) {
if (len > malloc_object_size_fast(dst)) { if(len == 0) {
return dst;
}
if (len > malloc_object_size(dst)) {
fatal_error("memset buffer overflow"); fatal_error("memset buffer overflow");
} }
return h_memset_real(dst, value, len); return h_memset_real(dst, value, len);
} }
#endif #endif

View file

@ -15,7 +15,7 @@ extern "C" {
#define h_realloc realloc #define h_realloc realloc
#define h_aligned_alloc aligned_alloc #define h_aligned_alloc aligned_alloc
#define h_free free #define h_free free
#if CONFIG_BLOCK_OPS_CHECK_SIZE #if CONFIG_BLOCK_OPS_CHECK_SIZE && !defined(HAS_ARM_MTE)
#define h_memcpy memcpy #define h_memcpy memcpy
#define h_memmove memmove #define h_memmove memmove
//#define h_memset memset //#define h_memset memset
@ -59,12 +59,12 @@ __attribute__((alloc_size(2))) void *h_realloc(void *ptr, size_t size);
__attribute__((malloc)) __attribute__((alloc_size(2))) __attribute__((alloc_align(1))) __attribute__((malloc)) __attribute__((alloc_size(2))) __attribute__((alloc_align(1)))
void *h_aligned_alloc(size_t alignment, size_t size); void *h_aligned_alloc(size_t alignment, size_t size);
void h_free(void *ptr); void h_free(void *ptr);
#if CONFIG_BLOCK_OPS_CHECK_SIZE
void *h_memcpy_real(void *dst, const void *src, size_t len); void *h_memcpy_real(void *dst, const void *src, size_t len);
void *h_memcpy(void *dst, const void *src, size_t len);
void *h_memmove_real(void *dst, const void *src, size_t len); void *h_memmove_real(void *dst, const void *src, size_t len);
void *h_memmove(void *dst, const void *src, size_t len);
void *h_memset_real(void *dst, int value, size_t len); void *h_memset_real(void *dst, int value, size_t len);
#if CONFIG_BLOCK_OPS_CHECK_SIZE && !defined(HAS_ARM_MTE)
void *h_memcpy(void *dst, const void *src, size_t len);
void *h_memmove(void *dst, const void *src, size_t len);
void *h_memset(void *dst, int value, size_t len); void *h_memset(void *dst, int value, size_t len);
#endif #endif

View file

@ -9,7 +9,7 @@ OPTNONE int main(void) {
if (!firstbuffer && !secondbuffer) { if (!firstbuffer && !secondbuffer) {
return 1; return 1;
} }
memset(secondbuffer, 'a', 16); memset(secondbuffer, 'a', 32);
memcpy(firstbuffer, secondbuffer, 32); memcpy(firstbuffer, secondbuffer, 32);
return 1; return 1;
} }

View file

@ -4,12 +4,12 @@
#include "test_util.h" #include "test_util.h"
OPTNONE int main(void) { OPTNONE int main(void) {
char *firstbuffer = malloc(16); char *firstbuffer = malloc(32);
char *secondbuffer = malloc(8); char *secondbuffer = malloc(16);
if (!firstbuffer && !secondbuffer) { if (!firstbuffer && !secondbuffer) {
return 1; return 1;
} }
memset(secondbuffer, 'a', 8); memset(secondbuffer, 'a', 16);
memcpy(firstbuffer, secondbuffer, 8); memcpy(firstbuffer, secondbuffer, 16);
return 0; return 0;
} }

View file

@ -9,7 +9,7 @@ OPTNONE int main(void) {
if (!firstbuffer && !secondbuffer) { if (!firstbuffer && !secondbuffer) {
return 1; return 1;
} }
memset(secondbuffer, 'a', 16); memset(secondbuffer, 'a', 32);
memmove(firstbuffer, secondbuffer, 32); memmove(firstbuffer, secondbuffer, 32);
return 1; return 1;
} }

View file

@ -4,12 +4,12 @@
#include "test_util.h" #include "test_util.h"
OPTNONE int main(void) { OPTNONE int main(void) {
char *firstbuffer = malloc(16); char *firstbuffer = malloc(32);
char *secondbuffer = malloc(8); char *secondbuffer = malloc(16);
if (!firstbuffer && !secondbuffer) { if (!firstbuffer && !secondbuffer) {
return 1; return 1;
} }
memset(secondbuffer, 'a', 8); memset(secondbuffer, 'a', 16);
memmove(firstbuffer, secondbuffer, 8); memmove(firstbuffer, secondbuffer, 16);
return 0; return 0;
} }

View file

@ -4,10 +4,10 @@
#include "test_util.h" #include "test_util.h"
OPTNONE int main(void) { OPTNONE int main(void) {
char *buffer = malloc(16); char *buffer = malloc(32);
if (!buffer) { if (!buffer) {
return 1; return 1;
} }
memset(buffer, 'a', 8); memset(buffer, 'a', 16);
return 0; return 0;
} }