Skip to content

Commit 3b5cfb9

Browse files
perform size checks on memcpy/memmove/memset
- underlying functions were copied from musl, licensed MIT Signed-off-by: Tavi <tavi@divested.dev>
1 parent 4fe9018 commit 3b5cfb9

22 files changed

+457
-8
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+
*_musl functions extracted from musl and macros removed:
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: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,10 @@ ifeq (,$(filter $(CONFIG_SELF_INIT),true false))
8989
$(error CONFIG_SELF_INIT must be true or false)
9090
endif
9191

92+
ifeq (,$(filter $(CONFIG_BLOCK_OPS_CHECK_SIZE),true false))
93+
$(error CONFIG_BLOCK_OPS_CHECK_SIZE must be true or false)
94+
endif
95+
9296
CPPFLAGS += \
9397
-DCONFIG_SEAL_METADATA=$(CONFIG_SEAL_METADATA) \
9498
-DZERO_ON_FREE=$(CONFIG_ZERO_ON_FREE) \
@@ -108,7 +112,8 @@ CPPFLAGS += \
108112
-DCONFIG_CLASS_REGION_SIZE=$(CONFIG_CLASS_REGION_SIZE) \
109113
-DN_ARENA=$(CONFIG_N_ARENA) \
110114
-DCONFIG_STATS=$(CONFIG_STATS) \
111-
-DCONFIG_SELF_INIT=$(CONFIG_SELF_INIT)
115+
-DCONFIG_SELF_INIT=$(CONFIG_SELF_INIT) \
116+
-DCONFIG_BLOCK_OPS_CHECK_SIZE=$(CONFIG_BLOCK_OPS_CHECK_SIZE)
112117

113118
$(OUT)/libhardened_malloc$(SUFFIX).so: $(OBJECTS) | $(OUT)
114119
$(CC) $(CFLAGS) $(LDFLAGS) -shared $^ $(LDLIBS) -o $@

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,10 @@ 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.
282+
Note, memset override is currently disabled due to being broken.
279283

280284
The following integer configuration options are available:
281285

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 := true

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: 144 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -528,7 +528,7 @@ static void set_canary(UNUSED const struct slab_metadata *metadata, UNUSED void
528528
}
529529
#endif
530530

531-
memcpy((char *)p + size - canary_size, &metadata->canary_value, canary_size);
531+
h_memcpy_internal((char *)p + size - canary_size, &metadata->canary_value, canary_size);
532532
#endif
533533
}
534534

@@ -541,7 +541,7 @@ static void check_canary(UNUSED const struct slab_metadata *metadata, UNUSED con
541541
#endif
542542

543543
u64 canary_value;
544-
memcpy(&canary_value, (const char *)p + size - canary_size, canary_size);
544+
h_memcpy_internal(&canary_value, (const char *)p + size - canary_size, canary_size);
545545

546546
#ifdef HAS_ARM_MTE
547547
if (unlikely(canary_value == 0)) {
@@ -831,7 +831,7 @@ static inline void deallocate_small(void *p, const size_t *expected_size) {
831831
#endif
832832

833833
if (ZERO_ON_FREE && !skip_zero) {
834-
memset(p, 0, size - canary_size);
834+
h_memset_internal(p, 0, size - canary_size);
835835
}
836836
}
837837

@@ -1502,7 +1502,7 @@ EXPORT void *h_calloc(size_t nmemb, size_t size) {
15021502
total_size = adjust_size_for_canary(total_size);
15031503
void *p = alloc(total_size);
15041504
if (!ZERO_ON_FREE && likely(p != NULL) && total_size && total_size <= max_slab_size_class) {
1505-
memset(p, 0, total_size - canary_size);
1505+
h_memset_internal(p, 0, total_size - canary_size);
15061506
}
15071507
#ifdef HAS_ARM_MTE
15081508
// 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) {
16241624
mutex_unlock(&ra->lock);
16251625

16261626
if (memory_remap_fixed(old, old_size, new, size)) {
1627-
memcpy(new, old, copy_size);
1627+
h_memcpy_internal(new, old, copy_size);
16281628
deallocate_pages(old, old_size, old_guard_size);
16291629
} else {
16301630
memory_unmap((char *)old - old_guard_size, old_guard_size);
@@ -1646,7 +1646,7 @@ EXPORT void *h_realloc(void *old, size_t size) {
16461646
if (copy_size > 0 && copy_size <= max_slab_size_class) {
16471647
copy_size -= canary_size;
16481648
}
1649-
memcpy(new, old_orig, copy_size);
1649+
h_memcpy_internal(new, old_orig, copy_size);
16501650
if (old_size <= max_slab_size_class) {
16511651
deallocate_small(old, NULL);
16521652
} else {
@@ -1874,6 +1874,144 @@ EXPORT size_t h_malloc_object_size_fast(const void *p) {
18741874
return SIZE_MAX;
18751875
}
18761876

1877+
#if CONFIG_BLOCK_OPS_CHECK_SIZE && !defined(HAS_ARM_MTE)
1878+
inline void __attribute__((optimize("O1"))) *h_memcpy_musl(void *restrict dst, const void *restrict src, size_t len) {
1879+
unsigned char *d = dst;
1880+
const unsigned char *s = src;
1881+
1882+
for (; len; len--) *d++ = *s++;
1883+
1884+
return dst;
1885+
}
1886+
1887+
EXPORT void *h_memcpy_wrapped(void *restrict dst, const void *restrict src, size_t len) {
1888+
if(dst == src || len == 0) {
1889+
return dst;
1890+
}
1891+
if (dst < src + len && dst + len > src) {
1892+
fatal_error("memcpy overlap");
1893+
}
1894+
if (len > malloc_object_size(src)) {
1895+
fatal_error("memcpy read overflow");
1896+
}
1897+
if (len > malloc_object_size(dst)) {
1898+
fatal_error("memcpy buffer overflow");
1899+
}
1900+
return h_memcpy_musl(dst, src, len);
1901+
}
1902+
1903+
inline void *h_memmove_musl(void *dst, const void *src, size_t len) {
1904+
char *d = dst;
1905+
const char *s = src;
1906+
1907+
if (d < s) {
1908+
for (; len; len--) *d++ = *s++;
1909+
} else {
1910+
while (len) len--, d[len] = s[len];
1911+
}
1912+
1913+
return dst;
1914+
}
1915+
1916+
EXPORT void *h_memmove_wrapped(void *dst, const void *src, size_t len) {
1917+
if(dst == src || len == 0) {
1918+
return dst;
1919+
}
1920+
if (len > malloc_object_size(src)) {
1921+
fatal_error("memmove read overflow");
1922+
}
1923+
if (len > malloc_object_size(dst)) {
1924+
fatal_error("memmove buffer overflow");
1925+
}
1926+
return h_memmove_musl(dst, src, len);
1927+
}
1928+
1929+
inline void __attribute__((optimize("O1"))) *h_memset_musl(void *dst, int value, size_t len) {
1930+
unsigned char *s = dst;
1931+
1932+
for (; len; len--, s++) *s = value;
1933+
1934+
return dst;
1935+
}
1936+
1937+
EXPORT void *h_memset_wrapped(void *dst, int value, size_t len) {
1938+
if(len == 0) {
1939+
return dst;
1940+
}
1941+
if (len > malloc_object_size(dst)) {
1942+
fatal_error("memset buffer overflow");
1943+
}
1944+
return h_memset_musl(dst, value, len);
1945+
}
1946+
1947+
inline wchar_t *h_wmemcpy_musl(wchar_t *restrict dst, const wchar_t *restrict src, size_t len) {
1948+
wchar_t *ret = dst;
1949+
1950+
while (len--) *dst++ = *src++;
1951+
1952+
return ret;
1953+
}
1954+
1955+
EXPORT wchar_t *h_wmemcpy_wrapped(wchar_t *restrict dst, const wchar_t *restrict src, size_t len) {
1956+
if(dst == src || len == 0) {
1957+
return dst;
1958+
}
1959+
if (dst < src + len && dst + len > src) {
1960+
fatal_error("wmemcpy overlap");
1961+
}
1962+
if (len > malloc_object_size(src)) {
1963+
fatal_error("wmemcpy read overflow");
1964+
}
1965+
if (len > malloc_object_size(dst)) {
1966+
fatal_error("wmemcpy buffer overflow");
1967+
}
1968+
return h_wmemcpy_musl(dst, src, len);
1969+
}
1970+
1971+
inline wchar_t *h_wmemmove_musl(wchar_t *dst, const wchar_t *src, size_t len) {
1972+
wchar_t *ret = dst;
1973+
1974+
if ((uintptr_t)dst-(uintptr_t)src < len * sizeof *dst) {
1975+
while (len--) dst[len] = src[len];
1976+
} else {
1977+
while (len--) *dst++ = *src++;
1978+
}
1979+
1980+
return ret;
1981+
}
1982+
1983+
EXPORT wchar_t *h_wmemmove_wrapped(wchar_t *dst, const wchar_t *src, size_t len) {
1984+
if(dst == src || len == 0) {
1985+
return dst;
1986+
}
1987+
if (len > malloc_object_size(src)) {
1988+
fatal_error("wmemmove read overflow");
1989+
}
1990+
if (len > malloc_object_size(dst)) {
1991+
fatal_error("wmemmove buffer overflow");
1992+
}
1993+
return h_wmemmove_musl(dst, src, len);
1994+
}
1995+
1996+
inline wchar_t *h_wmemset_musl(wchar_t *dst, wchar_t value, size_t len) {
1997+
wchar_t *ret = dst;
1998+
1999+
while (len--) *dst++ = value;
2000+
2001+
return ret;
2002+
}
2003+
2004+
EXPORT wchar_t *h_wmemset_wrapped(wchar_t *dst, wchar_t value, size_t len) {
2005+
if(len == 0) {
2006+
return dst;
2007+
}
2008+
if (len > malloc_object_size(dst)) {
2009+
fatal_error("wmemset buffer overflow");
2010+
}
2011+
return h_wmemset_musl(dst, value, len);
2012+
}
2013+
#endif
2014+
18772015
EXPORT int h_mallopt(UNUSED int param, UNUSED int value) {
18782016
#ifdef __ANDROID__
18792017
if (param == M_PURGE) {

include/h_malloc.h

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,14 @@ extern "C" {
1515
#define h_realloc realloc
1616
#define h_aligned_alloc aligned_alloc
1717
#define h_free free
18+
#if CONFIG_BLOCK_OPS_CHECK_SIZE && !defined(HAS_ARM_MTE)
19+
#define h_memcpy_wrapped memcpy
20+
#define h_memmove_wrapped memmove
21+
#define h_memset_wrapped memset
22+
#define h_wmemcpy_wrapped wmemcpy
23+
#define h_wmemmove_wrapped wmemmove
24+
#define h_wmemset_wrapped wmemset
25+
#endif
1826

1927
#define h_posix_memalign posix_memalign
2028

@@ -54,6 +62,27 @@ __attribute__((alloc_size(2))) void *h_realloc(void *ptr, size_t size);
5462
__attribute__((malloc)) __attribute__((alloc_size(2))) __attribute__((alloc_align(1)))
5563
void *h_aligned_alloc(size_t alignment, size_t size);
5664
void h_free(void *ptr);
65+
#if CONFIG_BLOCK_OPS_CHECK_SIZE && !defined(HAS_ARM_MTE)
66+
void *h_memcpy_musl(void *dst, const void *src, size_t len);
67+
void *h_memcpy_wrapped(void *dst, const void *src, size_t len);
68+
void *h_memmove_musl(void *dst, const void *src, size_t len);
69+
void *h_memmove_wrapped(void *dst, const void *src, size_t len);
70+
void *h_memset_musl(void *dst, int value, size_t len);
71+
void *h_memset_wrapped(void *dst, int value, size_t len);
72+
wchar_t *h_wmemcpy_musl(wchar_t *dst, const wchar_t *src, size_t len);
73+
wchar_t *h_wmemcpy_wrapped(wchar_t *dst, const wchar_t *src, size_t len);
74+
wchar_t *h_wmemmove_musl(wchar_t *dst, const wchar_t *src, size_t len);
75+
wchar_t *h_wmemmove_wrapped(wchar_t *dst, const wchar_t *src, size_t len);
76+
wchar_t *h_wmemset_musl(wchar_t *dst, wchar_t value, size_t len);
77+
wchar_t *h_wmemset_wrapped(wchar_t *dst, wchar_t value, size_t len);
78+
#define h_memcpy_internal h_memcpy_musl
79+
#define h_memove_internal h_memmove_musl
80+
#define h_memset_internal h_memset_musl
81+
#else
82+
#define h_memcpy_internal __builtin_memcpy
83+
#define h_memove_internal __builtin_memmove
84+
#define h_memset_internal __builtin_memset
85+
#endif
5786

5887
// POSIX
5988
int h_posix_memalign(void **memptr, size_t alignment, size_t size);

test/.gitignore

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,15 @@ overflow_small_8_byte
4141
uninitialized_read_large
4242
uninitialized_read_small
4343
realloc_init
44+
memcpy_buffer_overflow
45+
memcpy_read_overflow
46+
memcpy_valid_same
47+
memcpy_valid_mismatched
48+
memmove_buffer_overflow
49+
memmove_read_overflow
50+
memmove_valid_same
51+
memmove_valid_mismatched
52+
memset_buffer_overflow
53+
memset_valid_same
54+
memset_valid_mismatched
4455
__pycache__/

test/Makefile

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,18 @@ EXECUTABLES := \
6767
invalid_malloc_object_size_small \
6868
invalid_malloc_object_size_small_quarantine \
6969
impossibly_large_malloc \
70-
realloc_init
70+
realloc_init \
71+
memcpy_buffer_overflow \
72+
memcpy_read_overflow \
73+
memcpy_valid_same \
74+
memcpy_valid_mismatched \
75+
memmove_buffer_overflow \
76+
memmove_read_overflow \
77+
memmove_valid_same \
78+
memmove_valid_mismatched \
79+
memset_buffer_overflow \
80+
memset_valid_same \
81+
memset_valid_mismatched
7182

7283
all: $(EXECUTABLES)
7384

test/memcpy_buffer_overflow.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#include <stdlib.h>
2+
#include <string.h>
3+
4+
#include "test_util.h"
5+
6+
OPTNONE int main(void) {
7+
char *firstbuffer = malloc(16);
8+
char *secondbuffer = malloc(32);
9+
if (!firstbuffer && !secondbuffer) {
10+
return 1;
11+
}
12+
memset(secondbuffer, 'a', 32);
13+
memcpy(firstbuffer, secondbuffer, 32);
14+
return 1;
15+
}

test/memcpy_read_overflow.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#include <stdlib.h>
2+
#include <string.h>
3+
4+
#include "test_util.h"
5+
6+
OPTNONE int main(void) {
7+
char *firstbuffer = malloc(32);
8+
char *secondbuffer = malloc(16);
9+
if (!firstbuffer && !secondbuffer) {
10+
return 1;
11+
}
12+
memset(secondbuffer, 'a', 16);
13+
memcpy(firstbuffer, secondbuffer, 32);
14+
return 1;
15+
}

0 commit comments

Comments
 (0)