From c1038faf8c0f8310c485008615486e8359dea528 Mon Sep 17 00:00:00 2001 From: Dejice Jacob Date: Mon, 25 Oct 2021 17:21:32 +0100 Subject: [PATCH] Prevent memory coalescing if cap-bounds do not span full length on CHERI (a cherry-pick of commit d8ecfa397 partly from capablevms/bdwgc) Issue #627 (bdwgc). During a GC sweep, coalescing contiguous memory that spans two separate system OS allocations (e.g. using `mmap()`), causes memory leakage in CHERI systems. This is because the capability used to represent such coalesced (expanded) memory regions has memory bounds derived from the original allocation syscall (smaller than the size of the coalesced memory). To prevent a client being given a capability with invalid bounds, such coalescing is prevented. * allchblk.c [CHERI_PURECAP] (GC_freehblk): Call `CAPABILITY_COVERS_RANGE()` for `hbp` and `next` to check if coalescing with the successor is possible; add comment and FIXME item; evaluate `cheri_base_get(hbp)<=ADDR(prev)` to check if coalescing with the predecessor is possible. * include/private/gc_priv.h [CHERI_PURECAP] (CAPABILITY_COVERS_RANGE): New macro. * include/private/gc_priv.h [CHERI_PURECAP] (SPANNING_CAPABILITY): Use `CAPABILITY_COVERS_RANGE()`. --- allchblk.c | 17 +++++++++++++++-- include/private/gc_priv.h | 6 ++++-- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/allchblk.c b/allchblk.c index 041a52afd..483459aa1 100644 --- a/allchblk.c +++ b/allchblk.c @@ -1095,7 +1095,15 @@ GC_freehblk(struct hblk *hbp) GET_HDR(next, nexthdr); prev = GC_free_block_ending_at(hbp); /* Coalesce with successor, if possible. */ - if (nexthdr != NULL && HBLK_IS_FREE(nexthdr) && IS_MAPPED(nexthdr) + if (nexthdr != NULL && HBLK_IS_FREE(nexthdr) + && IS_MAPPED(nexthdr) +#ifdef CHERI_PURECAP + /* FIXME: Coalesce with super-capability. */ + /* Bounds of capability should span the entire coalesced memory; */ + /* bounds being larger than the block size is OK; bounded by the */ + /* imprecision of original capability obtained from system memory. */ + && CAPABILITY_COVERS_RANGE(hbp, ADDR(next), ADDR(next) + nexthdr->hb_sz) +#endif && !BLOCKS_MERGE_OVERFLOW(hhdr, nexthdr)) { GC_remove_from_fl(nexthdr); hhdr->hb_sz += nexthdr->hb_sz; @@ -1105,7 +1113,12 @@ GC_freehblk(struct hblk *hbp) /* Coalesce with predecessor, if possible. */ if (prev /* != NULL */) { /* CPPCHECK */ prevhdr = HDR(prev); - if (IS_MAPPED(prevhdr) && !BLOCKS_MERGE_OVERFLOW(prevhdr, hhdr)) { + if (IS_MAPPED(prevhdr) +#ifdef CHERI_PURECAP + /* FIXME: Coalesce with super-capability. */ + && cheri_base_get(hbp) <= ADDR(prev) +#endif + && !BLOCKS_MERGE_OVERFLOW(prevhdr, hhdr)) { GC_remove_from_fl(prevhdr); prevhdr->hb_sz += hhdr->hb_sz; #ifdef USE_MUNMAP diff --git a/include/private/gc_priv.h b/include/private/gc_priv.h index de6884483..b31170da2 100644 --- a/include/private/gc_priv.h +++ b/include/private/gc_priv.h @@ -2178,9 +2178,11 @@ ptr_t GC_save_regs_in_stack(void); #endif /* !E2K */ #ifdef CHERI_PURECAP +# define CAPABILITY_COVERS_RANGE(cap, b_addr, e_addr) \ + (cheri_base_get(cap) <= (b_addr) \ + && cheri_base_get(cap) + cheri_length_get(cap) >= (e_addr)) # define SPANNING_CAPABILITY(cap, b_addr, e_addr) \ - (cheri_tag_get(cap) && cheri_base_get(cap) <= (b_addr) \ - && cheri_base_get(cap) + cheri_length_get(cap) >= (e_addr) \ + (cheri_tag_get(cap) && CAPABILITY_COVERS_RANGE(cap, b_addr, e_addr) \ && (cheri_perms_get(cap) & (CHERI_PERM_LOAD | CHERI_PERM_LOAD_CAP)) \ != 0) #endif