Compare commits

..

2 commits

Author SHA1 Message Date
SkewedZeppelin
a73585cc39
Merge b5a8849fc8 into 4fe9018b6f 2025-03-27 20:55:25 +00:00
Tavi
b5a8849fc8
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-27 16:54:51 -04:00
21 changed files with 268 additions and 55 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 and their wide variants: memcpy.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 wmemcpy.c wmemmove.c wmemset.c SOURCES += memcpy.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)
@ -146,10 +146,6 @@ $(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)
$(COMPILE.c) -Wno-cast-align $(OUTPUT_OPTION) $< $(COMPILE.c) -Wno-cast-align $(OUTPUT_OPTION) $<
$(OUT)/wmemcpy.o: wmemcpy.c musl.h $(CONFIG_FILE) | $(OUT)
$(COMPILE.c) $(OUTPUT_OPTION) $<
$(OUT)/wmemmove.o: wmemmove.c musl.h $(CONFIG_FILE) | $(OUT)
$(COMPILE.c) $(OUTPUT_OPTION) $<
$(OUT)/wmemset.o: wmemset.c musl.h $(CONFIG_FILE) | $(OUT) $(OUT)/wmemset.o: wmemset.c musl.h $(CONFIG_FILE) | $(OUT)
$(COMPILE.c) $(OUTPUT_OPTION) $< $(COMPILE.c) $(OUTPUT_OPTION) $<

View file

@ -1880,10 +1880,10 @@ EXPORT size_t h_malloc_object_size_fast(const void *p) {
#if CONFIG_BLOCK_OPS_CHECK_SIZE && !defined(HAS_ARM_MTE) #if CONFIG_BLOCK_OPS_CHECK_SIZE && !defined(HAS_ARM_MTE)
EXPORT void *memcpy(void *restrict dst, const void *restrict src, size_t len) { EXPORT void *memcpy(void *restrict dst, const void *restrict src, size_t len) {
if(dst == src || len == 0) { if (unlikely(dst == src || len == 0)) {
return dst; return dst;
} }
if (unlikely(dst < src + len && dst + len > src)) { if (unlikely(dst < (src + len) && (dst + len) > src)) {
fatal_error("memcpy overlap"); fatal_error("memcpy overlap");
} }
if (unlikely(len > malloc_object_size(src))) { if (unlikely(len > malloc_object_size(src))) {
@ -1896,7 +1896,7 @@ EXPORT void *memcpy(void *restrict dst, const void *restrict src, size_t len) {
} }
EXPORT void *memmove(void *dst, const void *src, size_t len) { EXPORT void *memmove(void *dst, const void *src, size_t len) {
if(dst == src || len == 0) { if (unlikely(dst == src || len == 0)) {
return dst; return dst;
} }
if (unlikely(len > malloc_object_size(src))) { if (unlikely(len > malloc_object_size(src))) {
@ -1909,7 +1909,7 @@ EXPORT void *memmove(void *dst, const void *src, size_t len) {
} }
EXPORT void *memset(void *dst, int value, size_t len) { EXPORT void *memset(void *dst, int value, size_t len) {
if(len == 0) { if (unlikely(len == 0)) {
return dst; return dst;
} }
if (unlikely(len > malloc_object_size(dst))) { if (unlikely(len > malloc_object_size(dst))) {
@ -1919,44 +1919,46 @@ EXPORT void *memset(void *dst, int value, size_t len) {
} }
EXPORT wchar_t *wmemcpy(wchar_t *restrict dst, const wchar_t *restrict src, size_t len) { EXPORT wchar_t *wmemcpy(wchar_t *restrict dst, const wchar_t *restrict src, size_t len) {
if(dst == src || len == 0) { if (unlikely(dst == src || len == 0)) {
return dst; return dst;
} }
if (dst < src + len && dst + len > src) { if (unlikely(dst < (src + len) && (dst + len) > src)) {
fatal_error("wmemcpy overlap"); fatal_error("wmemcpy overlap");
} }
if (len > malloc_object_size(src)) { size_t lenAdj = len * sizeof(wchar_t);
if (unlikely(lenAdj > malloc_object_size(src))) {
fatal_error("wmemcpy read overflow"); fatal_error("wmemcpy read overflow");
} }
if (len > malloc_object_size(dst)) { if (unlikely(lenAdj > malloc_object_size(dst))) {
fatal_error("wmemcpy buffer overflow"); fatal_error("wmemcpy buffer overflow");
} }
return musl_wmemcpy(dst, src, len); return (wchar_t *)musl_memcpy((char *)dst, (const char *)src, lenAdj);
} }
EXPORT wchar_t *wmemmove(wchar_t *dst, const wchar_t *src, size_t len) { EXPORT wchar_t *wmemmove(wchar_t *dst, const wchar_t *src, size_t len) {
if(dst == src || len == 0) { if (unlikely(dst == src || len == 0)) {
return dst; return dst;
} }
if (len > malloc_object_size(src)) { size_t lenAdj = len * sizeof(wchar_t);
if (unlikely(lenAdj > malloc_object_size(src))) {
fatal_error("wmemmove read overflow"); fatal_error("wmemmove read overflow");
} }
if (len > malloc_object_size(dst)) { if (unlikely(lenAdj > malloc_object_size(dst))) {
fatal_error("wmemmove buffer overflow"); fatal_error("wmemmove buffer overflow");
} }
return musl_wmemmove(dst, src, len); return (wchar_t *)musl_memmove((char *)dst, (const char *)src, lenAdj);
} }
EXPORT wchar_t *wmemset(wchar_t *dst, wchar_t value, size_t len) { EXPORT wchar_t *wmemset(wchar_t *dst, wchar_t value, size_t len) {
if(len == 0) { if (unlikely(len == 0)) {
return dst; return dst;
} }
if (len > malloc_object_size(dst)) { if (unlikely((len * sizeof(wchar_t)) > malloc_object_size(dst))) {
fatal_error("wmemset buffer overflow"); fatal_error("wmemset buffer overflow");
} }
return musl_wmemset(dst, value, len); return musl_wmemset(dst, value, len);
} }
#endif #endif /* CONFIG_BLOCK_OPS_CHECK_SIZE && !defined(HAS_ARM_MTE) */
EXPORT int h_mallopt(UNUSED int param, UNUSED int value) { EXPORT int h_mallopt(UNUSED int param, UNUSED int value) {
#ifdef __ANDROID__ #ifdef __ANDROID__

View file

@ -20,7 +20,7 @@ void *musl_memmove(void *dest, const void *src, size_t n)
const char *s = src; const char *s = src;
if (d==s) return d; if (d==s) return d;
if ((uintptr_t)s-(uintptr_t)d-n <= -2*n) return memcpy(d, s, n); if ((uintptr_t)s-(uintptr_t)d-n <= -2*n) return musl_memcpy(d, s, n);
if (d<s) { if (d<s) {
#ifdef __GNUC__ #ifdef __GNUC__

2
musl.h
View file

@ -5,6 +5,4 @@
void *musl_memcpy(void *dst, const void *src, size_t len); void *musl_memcpy(void *dst, const void *src, size_t len);
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_wmemcpy(wchar_t *dst, const wchar_t *src, size_t len);
wchar_t *musl_wmemmove(wchar_t *dst, const wchar_t *src, 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);

11
test/.gitignore vendored
View file

@ -52,4 +52,15 @@ memmove_valid_mismatched
memset_buffer_overflow memset_buffer_overflow
memset_valid_same memset_valid_same
memset_valid_mismatched memset_valid_mismatched
wmemcpy_buffer_overflow
wmemcpy_read_overflow
wmemcpy_valid_same
wmemcpy_valid_mismatched
wmemmove_buffer_overflow
wmemmove_read_overflow
wmemmove_valid_same
wmemmove_valid_mismatched
wmemset_buffer_overflow
wmemset_valid_same
wmemset_valid_mismatched
__pycache__/ __pycache__/

View file

@ -78,7 +78,18 @@ EXECUTABLES := \
memmove_valid_mismatched \ memmove_valid_mismatched \
memset_buffer_overflow \ memset_buffer_overflow \
memset_valid_same \ memset_valid_same \
memset_valid_mismatched memset_valid_mismatched \
wmemcpy_buffer_overflow \
wmemcpy_read_overflow \
wmemcpy_valid_same \
wmemcpy_valid_mismatched \
wmemmove_buffer_overflow \
wmemmove_read_overflow \
wmemmove_valid_same \
wmemmove_valid_mismatched \
wmemset_buffer_overflow \
wmemset_valid_same \
wmemset_valid_mismatched
all: $(EXECUTABLES) all: $(EXECUTABLES)

View file

@ -293,6 +293,71 @@ class TestSimpleMemoryCorruption(unittest.TestCase):
# self.assertEqual(stderr.decode( # self.assertEqual(stderr.decode(
# "utf-8"), "fatal allocator error: memset buffer overflow\n") # "utf-8"), "fatal allocator error: memset buffer overflow\n")
#def test_wmemcpy_buffer_overflow(self):
# _stdout, stderr, returncode = self.run_test(
# "wmemcpy_buffer_overflow")
# self.assertEqual(returncode, -6)
# self.assertEqual(stderr.decode(
# "utf-8"), "fatal allocator error: wmemcpy buffer overflow\n")
#def test_wmemcpy_read_overflow(self):
# _stdout, stderr, returncode = self.run_test(
# "wmemcpy_read_overflow")
# self.assertEqual(returncode, -6)
# self.assertEqual(stderr.decode(
# "utf-8"), "fatal allocator error: wmemcpy read overflow\n")
def test_wmemcpy_valid_same(self):
_stdout, _stderr, returncode = self.run_test(
"wmemcpy_valid_same")
self.assertEqual(returncode, 0)
def test_wmemcpy_valid_mismatched(self):
_stdout, _stderr, returncode = self.run_test(
"wmemcpy_valid_mismatched")
self.assertEqual(returncode, 0)
#def test_wmemmove_buffer_overflow(self):
# _stdout, stderr, returncode = self.run_test(
# "wmemmove_buffer_overflow")
# self.assertEqual(returncode, -6)
# self.assertEqual(stderr.decode(
# "utf-8"), "fatal allocator error: wmemmove buffer overflow\n")
#def test_wmemmove_read_overflow(self):
# _stdout, stderr, returncode = self.run_test(
# "wmemmove_read_overflow")
# self.assertEqual(returncode, -6)
# self.assertEqual(stderr.decode(
# "utf-8"), "fatal allocator error: wmemmove read overflow\n")
def test_wmemmove_valid_same(self):
_stdout, _stderr, returncode = self.run_test(
"wmemmove_valid_same")
self.assertEqual(returncode, 0)
def test_wmemmove_valid_mismatched(self):
_stdout, _stderr, returncode = self.run_test(
"wmemmove_valid_mismatched")
self.assertEqual(returncode, 0)
#def test_wmemset_buffer_overflow(self):
# _stdout, stderr, returncode = self.run_test(
# "wmemset_buffer_overflow")
# self.assertEqual(returncode, -6)
# self.assertEqual(stderr.decode(
# "utf-8"), "fatal allocator error: wmemset buffer overflow\n")
def test_wmemset_valid_same(self):
_stdout, _stderr, returncode = self.run_test(
"wmemset_valid_same")
self.assertEqual(returncode, 0)
def test_wmemset_valid_mismatched(self):
_stdout, _stderr, returncode = self.run_test(
"wmemset_valid_mismatched")
self.assertEqual(returncode, 0)
def test_memset_valid_same(self): def test_memset_valid_same(self):
_stdout, _stderr, returncode = self.run_test( _stdout, _stderr, returncode = self.run_test(
"memset_valid_same") "memset_valid_same")

View file

@ -0,0 +1,15 @@
#include <stdlib.h>
#include <wchar.h>
#include "test_util.h"
OPTNONE int main(void) {
wchar_t *firstbuffer = malloc(16 * sizeof(wchar_t));
wchar_t *secondbuffer = malloc(32 * sizeof(wchar_t));
if (!firstbuffer && !secondbuffer) {
return 1;
}
wmemset(secondbuffer, L'\U0001F642', 32);
wmemcpy(firstbuffer, secondbuffer, 32);
return 1;
}

View file

@ -0,0 +1,15 @@
#include <stdlib.h>
#include <wchar.h>
#include "test_util.h"
OPTNONE int main(void) {
wchar_t *firstbuffer = malloc(32 * sizeof(wchar_t));
wchar_t *secondbuffer = malloc(16 * sizeof(wchar_t));
if (!firstbuffer && !secondbuffer) {
return 1;
}
wmemset(secondbuffer, L'\U0001F642', 16);
wmemcpy(firstbuffer, secondbuffer, 32);
return 1;
}

View file

@ -0,0 +1,15 @@
#include <stdlib.h>
#include <wchar.h>
#include "test_util.h"
OPTNONE int main(void) {
wchar_t *firstbuffer = malloc(32 * sizeof(wchar_t));
wchar_t *secondbuffer = malloc(16 * sizeof(wchar_t));
if (!firstbuffer && !secondbuffer) {
return 1;
}
wmemset(secondbuffer, L'\U0001F642', 16);
wmemcpy(firstbuffer, secondbuffer, 16);
return 0;
}

15
test/wmemcpy_valid_same.c Normal file
View file

@ -0,0 +1,15 @@
#include <stdlib.h>
#include <wchar.h>
#include "test_util.h"
OPTNONE int main(void) {
wchar_t *firstbuffer = malloc(16 * sizeof(wchar_t));
wchar_t *secondbuffer = malloc(16 * sizeof(wchar_t));
if (!firstbuffer && !secondbuffer) {
return 1;
}
wmemset(secondbuffer, L'\U0001F642', 16);
wmemcpy(firstbuffer, secondbuffer, 16);
return 0;
}

View file

@ -0,0 +1,15 @@
#include <stdlib.h>
#include <wchar.h>
#include "test_util.h"
OPTNONE int main(void) {
wchar_t *firstbuffer = malloc(16 * sizeof(wchar_t));
wchar_t *secondbuffer = malloc(32 * sizeof(wchar_t));
if (!firstbuffer && !secondbuffer) {
return 1;
}
wmemset(secondbuffer, L'\U0001F642', 32);
wmemmove(firstbuffer, secondbuffer, 32);
return 1;
}

View file

@ -0,0 +1,15 @@
#include <stdlib.h>
#include <wchar.h>
#include "test_util.h"
OPTNONE int main(void) {
wchar_t *firstbuffer = malloc(32 * sizeof(wchar_t));
wchar_t *secondbuffer = malloc(16 * sizeof(wchar_t));
if (!firstbuffer && !secondbuffer) {
return 1;
}
wmemset(secondbuffer, L'\U0001F642', 16);
wmemmove(firstbuffer, secondbuffer, 32);
return 1;
}

View file

@ -0,0 +1,15 @@
#include <stdlib.h>
#include <wchar.h>
#include "test_util.h"
OPTNONE int main(void) {
wchar_t *firstbuffer = malloc(32 * sizeof(wchar_t));
wchar_t *secondbuffer = malloc(16 * sizeof(wchar_t));
if (!firstbuffer && !secondbuffer) {
return 1;
}
wmemset(secondbuffer, L'\U0001F642', 16);
wmemmove(firstbuffer, secondbuffer, 16);
return 0;
}

View file

@ -0,0 +1,15 @@
#include <stdlib.h>
#include <wchar.h>
#include "test_util.h"
OPTNONE int main(void) {
wchar_t *firstbuffer = malloc(16 * sizeof(wchar_t));
wchar_t *secondbuffer = malloc(16 * sizeof(wchar_t));
if (!firstbuffer && !secondbuffer) {
return 1;
}
wmemset(secondbuffer, L'\U0001F642', 16);
wmemmove(firstbuffer, secondbuffer, 16);
return 0;
}

View file

@ -0,0 +1,13 @@
#include <stdlib.h>
#include <wchar.h>
#include "test_util.h"
OPTNONE int main(void) {
wchar_t *buffer = malloc(16 * sizeof(wchar_t));
if (!buffer) {
return 1;
}
wmemset(buffer, L'\U0001F642', 32);
return 1;
}

View file

@ -0,0 +1,13 @@
#include <stdlib.h>
#include <wchar.h>
#include "test_util.h"
OPTNONE int main(void) {
wchar_t *buffer = malloc(32 * sizeof(wchar_t));
if (!buffer) {
return 1;
}
wmemset(buffer, L'\U0001F642', 16);
return 0;
}

13
test/wmemset_valid_same.c Normal file
View file

@ -0,0 +1,13 @@
#include <stdlib.h>
#include <wchar.h>
#include "test_util.h"
OPTNONE int main(void) {
wchar_t *buffer = malloc(16 * sizeof(wchar_t));
if (!buffer) {
return 1;
}
wmemset(buffer, L'\U0001F642', 16);
return 0;
}

View file

@ -1,12 +0,0 @@
#include "musl.h"
/* Copied from musl libc version 1.2.5 licensed under the MIT license */
#include <wchar.h>
wchar_t *musl_wmemcpy(wchar_t *restrict d, const wchar_t *restrict s, size_t n)
{
wchar_t *a = d;
while (n--) *d++ = *s++;
return a;
}

View file

@ -1,17 +0,0 @@
#include "musl.h"
/* Copied from musl libc version 1.2.5 licensed under the MIT license */
#include <wchar.h>
#include <stdint.h>
wchar_t *musl_wmemmove(wchar_t *d, const wchar_t *s, size_t n)
{
wchar_t *d0 = d;
if (d == s) return d;
if ((uintptr_t)d-(uintptr_t)s < n * sizeof *d)
while (n--) d[n] = s[n];
else
while (n--) *d++ = *s++;
return d0;
}