Skip to content

Commit c7141d1

Browse files
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>
1 parent 4fe9018 commit c7141d1

38 files changed

+948
-9
lines changed

Android.bp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ common_cflags = [
2828
"-DN_ARENA=1",
2929
"-DCONFIG_STATS=true",
3030
"-DCONFIG_SELF_INIT=false",
31+
"-DCONFIG_BLOCK_OPS_CHECK_SIZE=false",
3132
]
3233

3334
cc_defaults {

CREDITS

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,30 @@ h_malloc.c open-addressed hash table (regions_grow, regions_insert, regions_find
2323
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
2424
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
2525

26+
memcpy.c, memmove.c, memset.c, wmemset.c:
27+
Copyright © 2005-2020 Rich Felker, et al.
28+
29+
Permission is hereby granted, free of charge, to any person obtaining
30+
a copy of this software and associated documentation files (the
31+
"Software"), to deal in the Software without restriction, including
32+
without limitation the rights to use, copy, modify, merge, publish,
33+
distribute, sublicense, and/or sell copies of the Software, and to
34+
permit persons to whom the Software is furnished to do so, subject to
35+
the following conditions:
36+
37+
The above copyright notice and this permission notice shall be
38+
included in all copies or substantial portions of the Software.
39+
40+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
41+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
42+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
43+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
44+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
45+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
46+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
47+
48+
Contributor list: https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT
49+
2650
libdivide:
2751

2852
Copyright (C) 2010 - 2019 ridiculous_fish, <libdivide@ridiculousfish.com>

Makefile

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ CXXFLAGS := $(CXXFLAGS) -std=c++17 -fsized-deallocation $(SHARED_FLAGS)
4040
LDFLAGS := $(LDFLAGS) -Wl,-O1,--as-needed,-z,defs,-z,relro,-z,now,-z,nodlopen,-z,text
4141

4242
SOURCES := chacha.c h_malloc.c memory.c pages.c random.c util.c
43+
ifeq ($(CONFIG_BLOCK_OPS_CHECK_SIZE),true)
44+
SOURCES += memcpy.c memmove.c memset.c wmemset.c
45+
BOSC_EXTRAS := musl.h
46+
endif
4347
OBJECTS := $(SOURCES:.c=.o)
4448

4549
ifeq ($(CONFIG_CXX_ALLOCATOR),true)
@@ -89,6 +93,10 @@ ifeq (,$(filter $(CONFIG_SELF_INIT),true false))
8993
$(error CONFIG_SELF_INIT must be true or false)
9094
endif
9195

96+
ifeq (,$(filter $(CONFIG_BLOCK_OPS_CHECK_SIZE),true false))
97+
$(error CONFIG_BLOCK_OPS_CHECK_SIZE must be true or false)
98+
endif
99+
92100
CPPFLAGS += \
93101
-DCONFIG_SEAL_METADATA=$(CONFIG_SEAL_METADATA) \
94102
-DZERO_ON_FREE=$(CONFIG_ZERO_ON_FREE) \
@@ -108,7 +116,8 @@ CPPFLAGS += \
108116
-DCONFIG_CLASS_REGION_SIZE=$(CONFIG_CLASS_REGION_SIZE) \
109117
-DN_ARENA=$(CONFIG_N_ARENA) \
110118
-DCONFIG_STATS=$(CONFIG_STATS) \
111-
-DCONFIG_SELF_INIT=$(CONFIG_SELF_INIT)
119+
-DCONFIG_SELF_INIT=$(CONFIG_SELF_INIT) \
120+
-DCONFIG_BLOCK_OPS_CHECK_SIZE=$(CONFIG_BLOCK_OPS_CHECK_SIZE)
112121

113122
$(OUT)/libhardened_malloc$(SUFFIX).so: $(OBJECTS) | $(OUT)
114123
$(CC) $(CFLAGS) $(LDFLAGS) -shared $^ $(LDLIBS) -o $@
@@ -118,7 +127,7 @@ $(OUT):
118127

119128
$(OUT)/chacha.o: chacha.c chacha.h util.h $(CONFIG_FILE) | $(OUT)
120129
$(COMPILE.c) $(OUTPUT_OPTION) $<
121-
$(OUT)/h_malloc.o: h_malloc.c include/h_malloc.h mutex.h memory.h pages.h random.h util.h $(CONFIG_FILE) | $(OUT)
130+
$(OUT)/h_malloc.o: h_malloc.c include/h_malloc.h mutex.h memory.h $(BOSC_EXTRAS) pages.h random.h util.h $(CONFIG_FILE) | $(OUT)
122131
$(COMPILE.c) $(OUTPUT_OPTION) $<
123132
$(OUT)/memory.o: memory.c memory.h util.h $(CONFIG_FILE) | $(OUT)
124133
$(COMPILE.c) $(OUTPUT_OPTION) $<
@@ -131,6 +140,15 @@ $(OUT)/random.o: random.c random.h chacha.h util.h $(CONFIG_FILE) | $(OUT)
131140
$(OUT)/util.o: util.c util.h $(CONFIG_FILE) | $(OUT)
132141
$(COMPILE.c) $(OUTPUT_OPTION) $<
133142

143+
$(OUT)/memcpy.o: memcpy.c musl.h $(CONFIG_FILE) | $(OUT)
144+
$(COMPILE.c) -Wno-cast-align $(OUTPUT_OPTION) $<
145+
$(OUT)/memmove.o: memmove.c musl.h $(CONFIG_FILE) | $(OUT)
146+
$(COMPILE.c) -Wno-cast-align $(OUTPUT_OPTION) $<
147+
$(OUT)/memset.o: memset.c musl.h $(CONFIG_FILE) | $(OUT)
148+
$(COMPILE.c) -Wno-cast-align $(OUTPUT_OPTION) $<
149+
$(OUT)/wmemset.o: wmemset.c musl.h $(CONFIG_FILE) | $(OUT)
150+
$(COMPILE.c) $(OUTPUT_OPTION) $<
151+
134152
check: tidy
135153

136154
tidy:

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,9 @@ The following boolean configuration options are available:
276276
hardware, which may become drastically lower in the future. Whether or not
277277
this feature is enabled, the metadata is all contained within an isolated
278278
memory region with high entropy random guard regions around it.
279+
* `CONFIG_BLOCK_OPS_CHECK_SIZE`: `true` or `false` (default) to ensure length
280+
parameter of the memcpy/memmove/memset block operations and their wide
281+
variants are within approximate bounds to minimize buffer overflows.
279282

280283
The following integer configuration options are available:
281284

config/default.mk

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,4 @@ CONFIG_CLASS_REGION_SIZE := 34359738368 # 32GiB
2121
CONFIG_N_ARENA := 4
2222
CONFIG_STATS := false
2323
CONFIG_SELF_INIT := true
24+
CONFIG_BLOCK_OPS_CHECK_SIZE := false

config/light.mk

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,4 @@ CONFIG_CLASS_REGION_SIZE := 34359738368 # 32GiB
2121
CONFIG_N_ARENA := 4
2222
CONFIG_STATS := false
2323
CONFIG_SELF_INIT := true
24+
CONFIG_BLOCK_OPS_CHECK_SIZE := false

h_malloc.c

Lines changed: 93 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@
2020
#include "random.h"
2121
#include "util.h"
2222

23+
#if CONFIG_BLOCK_OPS_CHECK_SIZE && !defined(HAS_ARM_MTE)
24+
#include "musl.h"
25+
#endif
26+
2327
#ifdef USE_PKEY
2428
#include <sys/mman.h>
2529
#endif
@@ -528,7 +532,7 @@ static void set_canary(UNUSED const struct slab_metadata *metadata, UNUSED void
528532
}
529533
#endif
530534

531-
memcpy((char *)p + size - canary_size, &metadata->canary_value, canary_size);
535+
h_memcpy_internal((char *)p + size - canary_size, &metadata->canary_value, canary_size);
532536
#endif
533537
}
534538

@@ -541,7 +545,7 @@ static void check_canary(UNUSED const struct slab_metadata *metadata, UNUSED con
541545
#endif
542546

543547
u64 canary_value;
544-
memcpy(&canary_value, (const char *)p + size - canary_size, canary_size);
548+
h_memcpy_internal(&canary_value, (const char *)p + size - canary_size, canary_size);
545549

546550
#ifdef HAS_ARM_MTE
547551
if (unlikely(canary_value == 0)) {
@@ -831,7 +835,7 @@ static inline void deallocate_small(void *p, const size_t *expected_size) {
831835
#endif
832836

833837
if (ZERO_ON_FREE && !skip_zero) {
834-
memset(p, 0, size - canary_size);
838+
h_memset_internal(p, 0, size - canary_size);
835839
}
836840
}
837841

@@ -1502,7 +1506,7 @@ EXPORT void *h_calloc(size_t nmemb, size_t size) {
15021506
total_size = adjust_size_for_canary(total_size);
15031507
void *p = alloc(total_size);
15041508
if (!ZERO_ON_FREE && likely(p != NULL) && total_size && total_size <= max_slab_size_class) {
1505-
memset(p, 0, total_size - canary_size);
1509+
h_memset_internal(p, 0, total_size - canary_size);
15061510
}
15071511
#ifdef HAS_ARM_MTE
15081512
// use an assert instead of adding a conditional to memset() above (freed memory is always
@@ -1624,7 +1628,7 @@ EXPORT void *h_realloc(void *old, size_t size) {
16241628
mutex_unlock(&ra->lock);
16251629

16261630
if (memory_remap_fixed(old, old_size, new, size)) {
1627-
memcpy(new, old, copy_size);
1631+
h_memcpy_internal(new, old, copy_size);
16281632
deallocate_pages(old, old_size, old_guard_size);
16291633
} else {
16301634
memory_unmap((char *)old - old_guard_size, old_guard_size);
@@ -1646,7 +1650,7 @@ EXPORT void *h_realloc(void *old, size_t size) {
16461650
if (copy_size > 0 && copy_size <= max_slab_size_class) {
16471651
copy_size -= canary_size;
16481652
}
1649-
memcpy(new, old_orig, copy_size);
1653+
h_memcpy_internal(new, old_orig, copy_size);
16501654
if (old_size <= max_slab_size_class) {
16511655
deallocate_small(old, NULL);
16521656
} else {
@@ -1874,6 +1878,89 @@ EXPORT size_t h_malloc_object_size_fast(const void *p) {
18741878
return SIZE_MAX;
18751879
}
18761880

1881+
#if CONFIG_BLOCK_OPS_CHECK_SIZE && !defined(HAS_ARM_MTE)
1882+
EXPORT void *memcpy(void *restrict dst, const void *restrict src, size_t len) {
1883+
if (unlikely(dst == src || len == 0)) {
1884+
return dst;
1885+
}
1886+
if (unlikely(dst < src + len && dst + len > src)) {
1887+
fatal_error("memcpy overlap");
1888+
}
1889+
if (unlikely(len > malloc_object_size(src))) {
1890+
fatal_error("memcpy read overflow");
1891+
}
1892+
if (unlikely(len > malloc_object_size(dst))) {
1893+
fatal_error("memcpy buffer overflow");
1894+
}
1895+
return musl_memcpy(dst, src, len);
1896+
}
1897+
1898+
EXPORT void *memmove(void *dst, const void *src, size_t len) {
1899+
if (unlikely(dst == src || len == 0)) {
1900+
return dst;
1901+
}
1902+
if (unlikely(len > malloc_object_size(src))) {
1903+
fatal_error("memmove read overflow");
1904+
}
1905+
if (unlikely(len > malloc_object_size(dst))) {
1906+
fatal_error("memmove buffer overflow");
1907+
}
1908+
return musl_memmove(dst, src, len);
1909+
}
1910+
1911+
EXPORT void *memset(void *dst, int value, size_t len) {
1912+
if (unlikely(len == 0)) {
1913+
return dst;
1914+
}
1915+
if (unlikely(len > malloc_object_size(dst))) {
1916+
fatal_error("memset buffer overflow");
1917+
}
1918+
return musl_memset(dst, value, len);
1919+
}
1920+
1921+
EXPORT wchar_t *wmemcpy(wchar_t *restrict dst, const wchar_t *restrict src, size_t len) {
1922+
if (unlikely(dst == src || len == 0)) {
1923+
return dst;
1924+
}
1925+
size_t lenAdj = len * sizeof(wchar_t);
1926+
if (unlikely(dst < src + lenAdj && dst + lenAdj > src)) {
1927+
fatal_error("wmemcpy overlap");
1928+
}
1929+
if (unlikely(lenAdj > malloc_object_size(src))) {
1930+
fatal_error("wmemcpy read overflow");
1931+
}
1932+
if (unlikely(lenAdj > malloc_object_size(dst))) {
1933+
fatal_error("wmemcpy buffer overflow");
1934+
}
1935+
return (wchar_t *)musl_memcpy((char *)dst, (const char *)src, lenAdj);
1936+
}
1937+
1938+
EXPORT wchar_t *wmemmove(wchar_t *dst, const wchar_t *src, size_t len) {
1939+
if (unlikely(dst == src || len == 0)) {
1940+
return dst;
1941+
}
1942+
size_t lenAdj = len * sizeof(wchar_t);
1943+
if (unlikely(lenAdj > malloc_object_size(src))) {
1944+
fatal_error("wmemmove read overflow");
1945+
}
1946+
if (unlikely(lenAdj > malloc_object_size(dst))) {
1947+
fatal_error("wmemmove buffer overflow");
1948+
}
1949+
return (wchar_t *)musl_memmove((char *)dst, (const char *)src, lenAdj);
1950+
}
1951+
1952+
EXPORT wchar_t *wmemset(wchar_t *dst, wchar_t value, size_t len) {
1953+
if (unlikely(len == 0)) {
1954+
return dst;
1955+
}
1956+
size_t lenAdj = len * sizeof(wchar_t);
1957+
if (unlikely(lenAdj > malloc_object_size(dst))) {
1958+
fatal_error("wmemset buffer overflow");
1959+
}
1960+
return musl_wmemset(dst, value, len);
1961+
}
1962+
#endif /* CONFIG_BLOCK_OPS_CHECK_SIZE && !defined(HAS_ARM_MTE) */
1963+
18771964
EXPORT int h_mallopt(UNUSED int param, UNUSED int value) {
18781965
#ifdef __ANDROID__
18791966
if (param == M_PURGE) {

include/h_malloc.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,22 @@ __attribute__((malloc)) __attribute__((alloc_size(2))) __attribute__((alloc_alig
5555
void *h_aligned_alloc(size_t alignment, size_t size);
5656
void h_free(void *ptr);
5757

58+
#if CONFIG_BLOCK_OPS_CHECK_SIZE && !defined(HAS_ARM_MTE)
59+
void *memcpy(void *dst, const void *src, size_t len);
60+
void *memmove(void *dst, const void *src, size_t len);
61+
void *memset(void *dst, int value, size_t len);
62+
wchar_t *wmemcpy(wchar_t *dst, const wchar_t *src, size_t len);
63+
wchar_t *wmemmove(wchar_t *dst, const wchar_t *src, size_t len);
64+
wchar_t *wmemset(wchar_t *dst, wchar_t value, size_t len);
65+
#define h_memcpy_internal musl_memcpy
66+
#define h_memmove_internal musl_memmove
67+
#define h_memset_internal musl_memset
68+
#else
69+
#define h_memcpy_internal memcpy
70+
#define h_memmove_internal memmove
71+
#define h_memset_internal memset
72+
#endif
73+
5874
// POSIX
5975
int h_posix_memalign(void **memptr, size_t alignment, size_t size);
6076

0 commit comments

Comments
 (0)