Compare commits

..

2 commits

Author SHA1 Message Date
SkewedZeppelin
fc042d5daa
Merge 0cada13b78 into 4fe9018b6f 2025-03-30 14:11:07 +00:00
Tavi
0cada13b78
perform size checks on memcpy/memmove/memset
Signed-off-by: Tavi <tavi@divested.dev>
Co-authored-by: =?UTF-8?q?Christian=20G=C3=B6ttsche?= <cgzones@googlemail.com>
2025-03-30 10:11:00 -04:00
16 changed files with 169 additions and 8 deletions

View file

@ -23,7 +23,7 @@ h_malloc.c open-addressed hash table (regions_grow, regions_insert, regions_find
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
memcpy.c, memmove.c, memset.c, wmemset.c:
memcpy.c, memccpy.c, memmove.c, memset.c, wmemset.c:
Copyright © 2005-2020 Rich Felker, et al.
Permission is hereby granted, free of charge, to any person obtaining

View file

@ -41,7 +41,7 @@ LDFLAGS := $(LDFLAGS) -Wl,-O1,--as-needed,-z,defs,-z,relro,-z,now,-z,nodlopen,-z
SOURCES := chacha.c h_malloc.c memory.c pages.c random.c util.c
ifeq ($(CONFIG_BLOCK_OPS_CHECK_SIZE),true)
SOURCES += memcpy.c memmove.c memset.c wmemset.c
SOURCES += memcpy.c memccpy.c memmove.c memset.c wmemset.c
BOSC_EXTRAS := musl.h
endif
OBJECTS := $(SOURCES:.c=.o)
@ -135,13 +135,15 @@ $(OUT)/new.o: new.cc include/h_malloc.h util.h $(CONFIG_FILE) | $(OUT)
$(COMPILE.cc) $(OUTPUT_OPTION) $<
$(OUT)/pages.o: pages.c pages.h memory.h util.h $(CONFIG_FILE) | $(OUT)
$(COMPILE.c) $(OUTPUT_OPTION) $<
$(OUT)/random.o: random.c random.h chacha.h util.h $(CONFIG_FILE) | $(OUT)
$(OUT)/random.o: random.c random.h chacha.h $(BOSC_EXTRAS) util.h $(CONFIG_FILE) | $(OUT)
$(COMPILE.c) $(OUTPUT_OPTION) $<
$(OUT)/util.o: util.c util.h $(CONFIG_FILE) | $(OUT)
$(COMPILE.c) $(OUTPUT_OPTION) $<
$(OUT)/memcpy.o: memcpy.c musl.h $(CONFIG_FILE) | $(OUT)
$(COMPILE.c) -Wno-cast-align $(OUTPUT_OPTION) $<
$(OUT)/memccpy.o: memccpy.c musl.h $(CONFIG_FILE) | $(OUT)
$(COMPILE.c) -Wno-cast-align $(OUTPUT_OPTION) $<
$(OUT)/memmove.o: memmove.c musl.h $(CONFIG_FILE) | $(OUT)
$(COMPILE.c) -Wno-cast-align $(OUTPUT_OPTION) $<
$(OUT)/memset.o: memset.c musl.h $(CONFIG_FILE) | $(OUT)

View file

@ -277,8 +277,8 @@ The following boolean configuration options are available:
this feature is enabled, the metadata is all contained within an isolated
memory region with high entropy random guard regions around it.
* `CONFIG_BLOCK_OPS_CHECK_SIZE`: `true` or `false` (default) to ensure length
parameter of the memcpy/memmove/memset block operations and their wide
variants are within approximate bounds to minimize buffer overflows.
parameter of the memcpy/memccpy/memmove/memset block operations and their
wide variants are within approximate bounds to minimize buffer overflows.
The following integer configuration options are available:

View file

@ -1895,6 +1895,22 @@ EXPORT void *memcpy(void *restrict dst, const void *restrict src, size_t len) {
return musl_memcpy(dst, src, len);
}
EXPORT void *memccpy(void *restrict dst, const void *restrict src, int value, size_t len) {
if (unlikely(dst == src || len == 0)) {
return dst;
}
if (unlikely(dst < (src + len) && (dst + len) > src)) {
fatal_error("memccpy overlap");
}
if (unlikely(len > malloc_object_size(src))) {
fatal_error("memccpy read overflow");
}
if (unlikely(len > malloc_object_size(dst))) {
fatal_error("memccpy buffer overflow");
}
return musl_memccpy(dst, src, value, len);
}
EXPORT void *memmove(void *dst, const void *src, size_t len) {
if (unlikely(dst == src || len == 0)) {
return dst;

View file

@ -57,6 +57,7 @@ void h_free(void *ptr);
#if CONFIG_BLOCK_OPS_CHECK_SIZE && !defined(HAS_ARM_MTE)
void *memcpy(void *dst, const void *src, size_t len);
void *memccpy(void *dst, const void *src, int value, size_t len);
void *memmove(void *dst, const void *src, size_t len);
void *memset(void *dst, int value, size_t len);
wchar_t *wmemcpy(wchar_t *dst, const wchar_t *src, size_t len);

38
memccpy.c Normal file
View file

@ -0,0 +1,38 @@
#include "musl.h"
/* Copied from musl libc version 1.2.5 licensed under the MIT license */
#include <string.h>
#include <stdint.h>
#include <limits.h>
#define ALIGN (sizeof(size_t)-1)
#define ONES ((size_t)-1/UCHAR_MAX)
#define HIGHS (ONES * (UCHAR_MAX/2+1))
#define HASZERO(x) (((x)-ONES) & ~(x) & HIGHS)
void *musl_memccpy(void *restrict dest, const void *restrict src, int c, size_t n)
{
unsigned char *d = dest;
const unsigned char *s = src;
c = (unsigned char)c;
#ifdef __GNUC__
typedef size_t __attribute__((__may_alias__)) word;
word *wd;
const word *ws;
if (((uintptr_t)s & ALIGN) == ((uintptr_t)d & ALIGN)) {
for (; ((uintptr_t)s & ALIGN) && n && (*d=*s)!=c; n--, s++, d++);
if ((uintptr_t)s & ALIGN) goto tail;
size_t k = ONES * c;
wd=(void *)d; ws=(const void *)s;
for (; n>=sizeof(size_t) && !HASZERO(*ws^k);
n-=sizeof(size_t), ws++, wd++) *wd = *ws;
d=(void *)wd; s=(const void *)ws;
}
#endif
for (; n && (*d=*s)!=c; n--, s++, d++);
tail:
if (n) return d+1;
return 0;
}

1
musl.h
View file

@ -3,6 +3,7 @@
#include <stddef.h>
void *musl_memcpy(void *dst, const void *src, size_t len);
void *musl_memccpy(void *restrict dest, const void *restrict src, int c, size_t n);
void *musl_memmove(void *dst, const void *src, size_t len);
void *musl_memset(void *dst, int value, size_t len);
wchar_t *musl_wmemset(wchar_t *dst, wchar_t value, size_t len);

View file

@ -5,6 +5,10 @@
#include "random.h"
#include "util.h"
#if CONFIG_BLOCK_OPS_CHECK_SIZE && !defined(HAS_ARM_MTE)
#include "musl.h"
#endif
#include <sys/random.h>
static void get_random_seed(void *buf, size_t size) {
@ -65,7 +69,7 @@ void get_random_bytes(struct random_state *state, void *buf, size_t size) {
size_t remaining = RANDOM_CACHE_SIZE - state->index;
size_t copy_size = min(size, remaining);
memcpy(buf, state->cache + state->index, copy_size);
h_memcpy_internal(buf, state->cache + state->index, copy_size);
state->index += copy_size;
buf = (char *)buf + copy_size;
@ -79,7 +83,7 @@ u16 get_random_u16(struct random_state *state) {
if (remaining < sizeof(value)) {
refill(state);
}
memcpy(&value, state->cache + state->index, sizeof(value));
h_memcpy_internal(&value, state->cache + state->index, sizeof(value));
state->index += sizeof(value);
return value;
}
@ -106,7 +110,7 @@ u64 get_random_u64(struct random_state *state) {
if (remaining < sizeof(value)) {
refill(state);
}
memcpy(&value, state->cache + state->index, sizeof(value));
h_memcpy_internal(&value, state->cache + state->index, sizeof(value));
state->index += sizeof(value);
return value;
}

View file

@ -22,4 +22,10 @@ u16 get_random_u16_uniform(struct random_state *state, u16 bound);
u64 get_random_u64(struct random_state *state);
u64 get_random_u64_uniform(struct random_state *state, u64 bound);
#if CONFIG_BLOCK_OPS_CHECK_SIZE && !defined(HAS_ARM_MTE)
#define h_memcpy_internal musl_memcpy
#else
#define h_memcpy_internal memcpy
#endif
#endif

4
test/.gitignore vendored
View file

@ -45,6 +45,10 @@ memcpy_buffer_overflow
memcpy_read_overflow
memcpy_valid_same
memcpy_valid_mismatched
memccpy_buffer_overflow
memccpy_read_overflow
memccpy_valid_same
memccpy_valid_mismatched
memmove_buffer_overflow
memmove_read_overflow
memmove_valid_same

View file

@ -72,6 +72,10 @@ EXECUTABLES := \
memcpy_read_overflow \
memcpy_valid_same \
memcpy_valid_mismatched \
memccpy_buffer_overflow \
memccpy_read_overflow \
memccpy_valid_same \
memccpy_valid_mismatched \
memmove_buffer_overflow \
memmove_read_overflow \
memmove_valid_same \

View file

@ -0,0 +1,15 @@
#include <stdlib.h>
#include <string.h>
#include "test_util.h"
OPTNONE int main(void) {
char *firstbuffer = malloc(16);
char *secondbuffer = malloc(32);
if (!firstbuffer && !secondbuffer) {
return 1;
}
memset(secondbuffer, 'a', 32);
memccpy(firstbuffer, secondbuffer, 'b', 32);
return 1;
}

View file

@ -0,0 +1,15 @@
#include <stdlib.h>
#include <string.h>
#include "test_util.h"
OPTNONE int main(void) {
char *firstbuffer = malloc(32);
char *secondbuffer = malloc(16);
if (!firstbuffer && !secondbuffer) {
return 1;
}
memset(secondbuffer, 'a', 16);
memccpy(firstbuffer, secondbuffer, 'b', 32);
return 1;
}

View file

@ -0,0 +1,15 @@
#include <stdlib.h>
#include <string.h>
#include "test_util.h"
OPTNONE int main(void) {
char *firstbuffer = malloc(32);
char *secondbuffer = malloc(16);
if (!firstbuffer && !secondbuffer) {
return 1;
}
memset(secondbuffer, 'a', 16);
memccpy(firstbuffer, secondbuffer, 'b', 16);
return 0;
}

15
test/memccpy_valid_same.c Normal file
View file

@ -0,0 +1,15 @@
#include <stdlib.h>
#include <string.h>
#include "test_util.h"
OPTNONE int main(void) {
char *firstbuffer = malloc(16);
char *secondbuffer = malloc(16);
if (!firstbuffer && !secondbuffer) {
return 1;
}
memset(secondbuffer, 'a', 16);
memccpy(firstbuffer, secondbuffer, 'b', 16);
return 0;
}

View file

@ -262,6 +262,31 @@ class TestSimpleMemoryCorruption(unittest.TestCase):
"memcpy_valid_mismatched")
self.assertEqual(returncode, 0)
#def test_memccpy_buffer_overflow(self):
# _stdout, stderr, returncode = self.run_test(
# "memccpy_buffer_overflow")
# self.assertEqual(returncode, -6)
# self.assertEqual(stderr.decode(
# "utf-8"), "fatal allocator error: memccpy buffer overflow\n")
#def test_memccpy_read_overflow(self):
# _stdout, stderr, returncode = self.run_test(
# "memccpy_read_overflow")
# self.assertEqual(returncode, -6)
# self.assertEqual(stderr.decode(
# "utf-8"), "fatal allocator error: memccpy read overflow\n")
def test_memccpy_valid_same(self):
_stdout, _stderr, returncode = self.run_test(
"memccpy_valid_same")
self.assertEqual(returncode, 0)
def test_memccpy_valid_mismatched(self):
_stdout, _stderr, returncode = self.run_test(
"memccpy_valid_mismatched")
self.assertEqual(returncode, 0)
#def test_memmove_buffer_overflow(self):
# _stdout, stderr, returncode = self.run_test(
# "memmove_buffer_overflow")