Skip to content

Commit a320f06

Browse files
committed
improve hardening for same-class realloc by calling memory_corruption_check_small
1 parent 212947c commit a320f06

4 files changed

Lines changed: 46 additions & 41 deletions

File tree

h_malloc.c

Lines changed: 42 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -800,6 +800,42 @@ static void enqueue_free_slab(struct size_class *c, struct slab_metadata *metada
800800
c->free_slabs_tail = substitute;
801801
}
802802

803+
static inline void memory_corruption_check_small(const void *p, const char *unaligned_msg, const char *unused_slot_msg,
804+
const char *quarantine_msg) {
805+
struct slab_size_class_info size_class_info = slab_size_class(p);
806+
size_t class = size_class_info.class;
807+
struct size_class *c = &ro.size_class_metadata[size_class_info.arena][class];
808+
size_t size = c->size;
809+
bool is_zero_size = class == 0;
810+
size_t slab_size = c->slab_size;
811+
812+
mutex_lock(&c->lock);
813+
814+
const struct slab_metadata *metadata = get_metadata(c, p);
815+
void *slab = get_slab(c, slab_size, metadata);
816+
size_t slot = libdivide_u32_do((const char *)p - (const char *)slab, &c->size_divisor);
817+
818+
if (unlikely(slot_pointer(size, slab, slot) != p)) {
819+
fatal_error(unaligned_msg);
820+
}
821+
822+
if (unlikely(!is_used_slot(metadata, slot))) {
823+
fatal_error(unused_slot_msg);
824+
}
825+
826+
if (likely(!is_zero_size)) {
827+
check_canary(metadata, p, size);
828+
}
829+
830+
#if SLAB_QUARANTINE
831+
if (unlikely(is_quarantine_slot(metadata, slot))) {
832+
fatal_error(quarantine_msg);
833+
}
834+
#endif
835+
836+
mutex_unlock(&c->lock);
837+
}
838+
803839
// preserves errno
804840
static inline void deallocate_small(void *p, const size_t *expected_size) {
805841
struct slab_size_class_info size_class_info = slab_size_class(p);
@@ -1558,10 +1594,13 @@ EXPORT void *h_realloc(void *old, size_t size) {
15581594
bool old_in_slab_region = old < get_slab_region_end() && old >= ro.slab_region_start;
15591595
if (old_in_slab_region) {
15601596
old_size = slab_usable_size(old);
1597+
thread_unseal_metadata();
15611598
if (size <= max_slab_size_class && get_size_info(size).size == old_size) {
1599+
memory_corruption_check_small(old, "invalid unaligned h_realloc",
1600+
"invalid h_realloc (unused)", "invalid h_realloc (quarantine)");
1601+
thread_seal_metadata();
15621602
return old_orig;
15631603
}
1564-
thread_unseal_metadata();
15651604
} else {
15661605
enforce_init();
15671606
thread_unseal_metadata();
@@ -1755,41 +1794,6 @@ EXPORT void h_free_sized(void *p, size_t expected_size) {
17551794
thread_seal_metadata();
17561795
}
17571796

1758-
static inline void memory_corruption_check_small(const void *p) {
1759-
struct slab_size_class_info size_class_info = slab_size_class(p);
1760-
size_t class = size_class_info.class;
1761-
struct size_class *c = &ro.size_class_metadata[size_class_info.arena][class];
1762-
size_t size = c->size;
1763-
bool is_zero_size = class == 0;
1764-
size_t slab_size = c->slab_size;
1765-
1766-
mutex_lock(&c->lock);
1767-
1768-
const struct slab_metadata *metadata = get_metadata(c, p);
1769-
void *slab = get_slab(c, slab_size, metadata);
1770-
size_t slot = libdivide_u32_do((const char *)p - (const char *)slab, &c->size_divisor);
1771-
1772-
if (unlikely(slot_pointer(size, slab, slot) != p)) {
1773-
fatal_error("invalid unaligned malloc_usable_size");
1774-
}
1775-
1776-
if (unlikely(!is_used_slot(metadata, slot))) {
1777-
fatal_error("invalid malloc_usable_size");
1778-
}
1779-
1780-
if (likely(!is_zero_size)) {
1781-
check_canary(metadata, p, size);
1782-
}
1783-
1784-
#if SLAB_QUARANTINE
1785-
if (unlikely(is_quarantine_slot(metadata, slot))) {
1786-
fatal_error("invalid malloc_usable_size (quarantine)");
1787-
}
1788-
#endif
1789-
1790-
mutex_unlock(&c->lock);
1791-
}
1792-
17931797
EXPORT size_t h_malloc_usable_size(H_MALLOC_USABLE_SIZE_CONST void *arg) {
17941798
if (arg == NULL) {
17951799
return 0;
@@ -1799,7 +1803,8 @@ EXPORT size_t h_malloc_usable_size(H_MALLOC_USABLE_SIZE_CONST void *arg) {
17991803

18001804
if (p < get_slab_region_end() && p >= ro.slab_region_start) {
18011805
thread_unseal_metadata();
1802-
memory_corruption_check_small(p);
1806+
memory_corruption_check_small(p, "invalid unaligned malloc_usable_size",
1807+
"invalid malloc_usable_size (unused)", "invalid malloc_usable_size (quarantine)");
18031808
thread_seal_metadata();
18041809

18051810
size_t size = slab_usable_size(p);

test/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ EXECUTABLES := \
6060
string_overflow \
6161
delete_type_size_mismatch \
6262
unaligned_malloc_usable_size_small \
63-
invalid_malloc_usable_size_small \
63+
invalid_malloc_usable_size_small_unused \
6464
invalid_malloc_usable_size_small_quarantine \
6565
malloc_object_size \
6666
malloc_object_size_offset \
File renamed without changes.

test/test_smc.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -105,12 +105,12 @@ def test_invalid_malloc_usable_size_small_quarantine(self):
105105
self.assertEqual(stderr.decode(
106106
"utf-8"), "fatal allocator error: invalid malloc_usable_size (quarantine)\n")
107107

108-
def test_invalid_malloc_usable_size_small(self):
108+
def test_invalid_malloc_usable_size_small_unused(self):
109109
_stdout, stderr, returncode = self.run_test(
110-
"invalid_malloc_usable_size_small")
110+
"invalid_malloc_usable_size_small_unused")
111111
self.assertEqual(returncode, -6)
112112
self.assertEqual(stderr.decode(
113-
"utf-8"), "fatal allocator error: invalid malloc_usable_size\n")
113+
"utf-8"), "fatal allocator error: invalid malloc_usable_size (unused)\n")
114114

115115
def test_read_after_free_large(self):
116116
_stdout, _stderr, returncode = self.run_test("read_after_free_large")

0 commit comments

Comments
 (0)