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 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 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. Copyright © 2005-2020 Rich Felker, et al.
Permission is hereby granted, free of charge, to any person obtaining 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 SOURCES := chacha.c h_malloc.c memory.c pages.c random.c util.c
ifeq ($(CONFIG_BLOCK_OPS_CHECK_SIZE),true) 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 BOSC_EXTRAS := musl.h
endif endif
OBJECTS := $(SOURCES:.c=.o) 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) $< $(COMPILE.cc) $(OUTPUT_OPTION) $<
$(OUT)/pages.o: pages.c pages.h memory.h util.h $(CONFIG_FILE) | $(OUT) $(OUT)/pages.o: pages.c pages.h memory.h util.h $(CONFIG_FILE) | $(OUT)
$(COMPILE.c) $(OUTPUT_OPTION) $< $(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) $< $(COMPILE.c) $(OUTPUT_OPTION) $<
$(OUT)/util.o: util.c util.h $(CONFIG_FILE) | $(OUT) $(OUT)/util.o: util.c util.h $(CONFIG_FILE) | $(OUT)
$(COMPILE.c) $(OUTPUT_OPTION) $< $(COMPILE.c) $(OUTPUT_OPTION) $<
$(OUT)/memcpy.o: memcpy.c musl.h $(CONFIG_FILE) | $(OUT) $(OUT)/memcpy.o: memcpy.c musl.h $(CONFIG_FILE) | $(OUT)
$(COMPILE.c) -Wno-cast-align $(OUTPUT_OPTION) $< $(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) $(OUT)/memmove.o: memmove.c musl.h $(CONFIG_FILE) | $(OUT)
$(COMPILE.c) -Wno-cast-align $(OUTPUT_OPTION) $< $(COMPILE.c) -Wno-cast-align $(OUTPUT_OPTION) $<
$(OUT)/memset.o: memset.c musl.h $(CONFIG_FILE) | $(OUT) $(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 this feature is enabled, the metadata is all contained within an isolated
memory region with high entropy random guard regions around it. memory region with high entropy random guard regions around it.
* `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 and their wide parameter of the memcpy/memccpy/memmove/memset block operations and their
variants are within approximate bounds to minimize buffer overflows. wide variants are within approximate bounds to minimize buffer overflows.
The following integer configuration options are available: 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); 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) { EXPORT void *memmove(void *dst, const void *src, size_t len) {
if (unlikely(dst == src || len == 0)) { if (unlikely(dst == src || len == 0)) {
return dst; return dst;

View file

@ -57,6 +57,7 @@ void h_free(void *ptr);
#if CONFIG_BLOCK_OPS_CHECK_SIZE && !defined(HAS_ARM_MTE) #if CONFIG_BLOCK_OPS_CHECK_SIZE && !defined(HAS_ARM_MTE)
void *memcpy(void *dst, const void *src, size_t len); 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 *memmove(void *dst, const void *src, size_t len);
void *memset(void *dst, int value, 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); 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> #include <stddef.h>
void *musl_memcpy(void *dst, const void *src, size_t len); 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_memmove(void *dst, const void *src, size_t len);
void *musl_memset(void *dst, int value, 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); wchar_t *musl_wmemset(wchar_t *dst, wchar_t value, size_t len);

View file

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

4
test/.gitignore vendored
View file

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

View file

@ -72,6 +72,10 @@ EXECUTABLES := \
memcpy_read_overflow \ memcpy_read_overflow \
memcpy_valid_same \ memcpy_valid_same \
memcpy_valid_mismatched \ memcpy_valid_mismatched \
memccpy_buffer_overflow \
memccpy_read_overflow \
memccpy_valid_same \
memccpy_valid_mismatched \
memmove_buffer_overflow \ memmove_buffer_overflow \
memmove_read_overflow \ memmove_read_overflow \
memmove_valid_same \ 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") "memcpy_valid_mismatched")
self.assertEqual(returncode, 0) 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): #def test_memmove_buffer_overflow(self):
# _stdout, stderr, returncode = self.run_test( # _stdout, stderr, returncode = self.run_test(
# "memmove_buffer_overflow") # "memmove_buffer_overflow")