avoid write-barrier memory protection for 'atomic-interior allocation

Split 'atomic-interior allocation to separate pages from other 'interior
allocation, and do not page-protect 'atomic-interior memory. Otherwise,
atomic-interior memory passed from one place to another --- especially
via the `#:in-original-place?` option on foreign functions --- can crash
due to triggering a write barrier in the wrong place.

Commit c18f6e8d6d, which changed some cross-place arguments to Pango and
Cairo to be 'atomic-interior, exposed the problem.

Merge to v6.0.1
This commit is contained in:
Matthew Flatt 2014-04-22 07:47:19 -06:00
parent d47c85be22
commit 4fde0e9901
4 changed files with 222 additions and 159 deletions

View File

@ -41,7 +41,7 @@ The basic collector allocation functions are:
that does not contain pointers to collectable objects. If the memory that does not contain pointers to collectable objects. If the memory
does contain pointers, they are invisible to the collector and will does contain pointers, they are invisible to the collector and will
not prevent an object from being collected. Newly allocated atomic not prevent an object from being collected. Newly allocated atomic
memory is not necessary zeroed. memory is not necessarily zeroed.
Atomic memory is used for strings or other blocks of memory which do Atomic memory is used for strings or other blocks of memory which do
not contain pointers. Atomic memory can also be used to store not contain pointers. Atomic memory can also be used to store
@ -117,6 +117,17 @@ allocator, on anytime that Racket has control, except during functions
that are documented otherwise. The predicate and accessor macros that are documented otherwise. The predicate and accessor macros
listed in @secref["im:stdtypes"] never trigger a collection. listed in @secref["im:stdtypes"] never trigger a collection.
As described in @secref["im:3m:places"], different @|tech-place|s
manage allocation separately. Movable memory should not be
communicated from one place to another, since the source place might
move the memory before it is used in the destination place.
Furthermore, allocated memory that contains pointers must not be
written in a @|tech-place| other than the one where it is allocated,
due to the place-specific implementation of a write barrier for
generational garbage collection. No write barrier is used for memory
that is allocated by @cppi{scheme_malloc_atomic_allow_interior} to
contain no pointers.
@; ---------------------------------------------------------------------- @; ----------------------------------------------------------------------
@section[#:tag "im:3m"]{Cooperating with 3m} @section[#:tag "im:3m"]{Cooperating with 3m}

View File

@ -0,0 +1,26 @@
#lang racket/base
(require ffi/unsafe
racket/place)
;; Check that atomic memory allocated in one place can be written
;; in another place without triggering a write barrier (and crashing)
(define c-memset
(get-ffi-obj 'memset #f
(_fun #:in-original-place? #t _pointer _int _size -> _void)))
(define N 50)
(define (go)
(place
ch
(define s (malloc N 'atomic-interior))
(collect-garbage)
(c-memset s 65 N)
(place-channel-put ch (ptr-ref s _byte (quotient N 2)))))
(module+ main
(define p (go))
(unless (equal? 65 (place-channel-get p))
(error "crash"))
'ok)

View File

@ -97,10 +97,17 @@ enum {
PAGE_TARRAY = 3, PAGE_TARRAY = 3,
PAGE_PAIR = 4, PAGE_PAIR = 4,
PAGE_BIG = 5, PAGE_BIG = 5,
/* the number of page types. */ /* the number of page types: */
PAGE_TYPES = 6, PAGE_TYPES = 6,
}; };
enum {
MED_PAGE_NONATOMIC = 0,
MED_PAGE_ATOMIC = 1,
/* the number of medium-page types: */
MED_PAGE_TYPES = 2
};
enum { enum {
SIZE_CLASS_SMALL_PAGE = 0, SIZE_CLASS_SMALL_PAGE = 0,
SIZE_CLASS_MED_PAGE = 1, SIZE_CLASS_MED_PAGE = 1,
@ -1041,12 +1048,16 @@ static void *allocate_big(const size_t request_size_bytes, int type)
} }
} }
#define MED_NEXT_SEARCH_SLOT(page) ((page)->previous_size) #define MED_NEXT_SEARCH_SLOT(page) ((page)->previous_size)
inline static mpage *create_new_medium_page(NewGC *gc, const int sz, const int pos) { inline static mpage *create_new_medium_page(NewGC *gc, const int sz, const int pos, int type) {
mpage *page; mpage *page;
int n; int n, ty;
ty = ((type == PAGE_ATOMIC) ? MED_PAGE_ATOMIC : MED_PAGE_NONATOMIC);
page = malloc_mpage(); page = malloc_mpage();
page->addr = malloc_pages(gc, APAGE_SIZE, APAGE_SIZE, MMU_ZEROED, MMU_BIG_MED, MMU_PROTECTABLE, &page->mmu_src_block); page->addr = malloc_pages(gc, APAGE_SIZE, APAGE_SIZE, MMU_ZEROED, MMU_BIG_MED,
(type == MED_PAGE_NONATOMIC) ? MMU_PROTECTABLE : MMU_NON_PROTECTABLE,
&page->mmu_src_block);
page->size = sz; page->size = sz;
page->size_class = 1; page->size_class = 1;
page->page_type = PAGE_BIG; page->page_type = PAGE_BIG;
@ -1061,11 +1072,11 @@ inline static mpage *create_new_medium_page(NewGC *gc, const int sz, const int p
} }
/* push page onto linked list */ /* push page onto linked list */
page->next = gc->med_pages[pos]; page->next = gc->med_pages[ty][pos];
if (page->next) if (page->next)
page->next->prev = page; page->next->prev = page;
gc->med_pages[pos] = page; gc->med_pages[ty][pos] = page;
gc->med_freelist_pages[pos] = page; gc->med_freelist_pages[ty][pos] = page;
if (gc->saved_allocator) { /* see MESSAGE ALLOCATION above */ if (gc->saved_allocator) { /* see MESSAGE ALLOCATION above */
orphan_page_accounting(gc, APAGE_SIZE); orphan_page_accounting(gc, APAGE_SIZE);
@ -1079,10 +1090,12 @@ inline static mpage *create_new_medium_page(NewGC *gc, const int sz, const int p
} }
inline static void *medium_page_realloc_dead_slot(NewGC *gc, const int sz, const int pos, const int type) { inline static void *medium_page_realloc_dead_slot(NewGC *gc, const int sz, const int pos, const int type) {
int n; int n, ty;
mpage *page; mpage *page;
for (page = gc->med_freelist_pages[pos]; page; page = gc->med_freelist_pages[pos] = page->prev) { ty = ((type == PAGE_ATOMIC) ? MED_PAGE_ATOMIC : MED_PAGE_NONATOMIC);
for (page = gc->med_freelist_pages[ty][pos]; page; page = gc->med_freelist_pages[ty][pos] = page->prev) {
for (n = MED_NEXT_SEARCH_SLOT(page); ((n + sz) <= APAGE_SIZE); n += sz) { for (n = MED_NEXT_SEARCH_SLOT(page); ((n + sz) <= APAGE_SIZE); n += sz) {
objhead * info = (objhead *)PTR(NUM(page->addr) + n); objhead * info = (objhead *)PTR(NUM(page->addr) + n);
if (info->dead) { if (info->dead) {
@ -1152,7 +1165,7 @@ static void *allocate_medium(const size_t request_size_bytes, const int type)
mpage *page; mpage *page;
objhead *info; objhead *info;
page = create_new_medium_page(gc, sz, pos); page = create_new_medium_page(gc, sz, pos, type);
info = (objhead *)PTR(NUM(page->addr) + MED_NEXT_SEARCH_SLOT(page)); info = (objhead *)PTR(NUM(page->addr) + MED_NEXT_SEARCH_SLOT(page));
info->dead = 0; info->dead = 0;
@ -1160,6 +1173,7 @@ static void *allocate_medium(const size_t request_size_bytes, const int type)
objptr = OBJHEAD_TO_OBJPTR(info); objptr = OBJHEAD_TO_OBJPTR(info);
} }
#if defined(GC_DEBUG_PAGES) && defined(MASTER_ALLOC_DEBUG) #if defined(GC_DEBUG_PAGES) && defined(MASTER_ALLOC_DEBUG)
if (postmaster_and_master_gc(gc)) { if (postmaster_and_master_gc(gc)) {
GCVERBOSEprintf(gc, "MASTERGC_allocate_medium %zi %i %i %i %i %p\n", request_size_bytes, type, sz, pos, 1 << (pos +3), objptr); GCVERBOSEprintf(gc, "MASTERGC_allocate_medium %zi %i %i %i %i %p\n", request_size_bytes, type, sz, pos, 1 << (pos +3), objptr);
@ -3622,7 +3636,7 @@ void GC_dump_with_traces(int flags,
{ {
NewGC *gc = GC_get_GC(); NewGC *gc = GC_get_GC();
mpage *page; mpage *page;
int i, num_immobiles; int i, ty, num_immobiles;
GC_Immobile_Box *ib; GC_Immobile_Box *ib;
static uintptr_t counts[MAX_DUMP_TAG], sizes[MAX_DUMP_TAG]; static uintptr_t counts[MAX_DUMP_TAG], sizes[MAX_DUMP_TAG];
@ -3683,8 +3697,9 @@ void GC_dump_with_traces(int flags,
} }
} }
} }
for (ty = 0; ty < MED_PAGE_TYPES; ty++) {
for (i = 0; i < NUM_MED_PAGE_SIZES; i++) { for (i = 0; i < NUM_MED_PAGE_SIZES; i++) {
for (page = gc->med_pages[i]; page; page = page->next) { for (page = gc->med_pages[ty][i]; page; page = page->next) {
void **start = PPTR(NUM(page->addr) + PREFIX_SIZE); void **start = PPTR(NUM(page->addr) + PREFIX_SIZE);
void **end = PPTR(NUM(page->addr) + APAGE_SIZE - page->size); void **end = PPTR(NUM(page->addr) + APAGE_SIZE - page->size);
@ -3713,6 +3728,7 @@ void GC_dump_with_traces(int flags,
} }
} }
} }
}
num_immobiles = 0; num_immobiles = 0;
for (ib = gc->immobile_boxes; ib; ib = ib->next) for (ib = gc->immobile_boxes; ib; ib = ib->next)
@ -3751,11 +3767,12 @@ void GC_dump_with_traces(int flags,
type_name[i], total_use, count)); type_name[i], total_use, count));
} }
GCWARN((GCOUTF, "Generation 1 [medium]:")); for (ty = 0; ty < MED_PAGE_TYPES; ty++) {
GCWARN((GCOUTF, "Generation 1 [medium%s]:", ty ? " atomic" : ""));
for (i = 0; i < NUM_MED_PAGE_SIZES; i++) { for (i = 0; i < NUM_MED_PAGE_SIZES; i++) {
if (gc->med_pages[i]) { if (gc->med_pages[ty][i]) {
intptr_t count = 0, page_count = 0; intptr_t count = 0, page_count = 0;
for (page = gc->med_pages[i]; page; page = page->next) { for (page = gc->med_pages[ty][i]; page; page = page->next) {
void **start = PPTR(NUM(page->addr) + PREFIX_SIZE); void **start = PPTR(NUM(page->addr) + PREFIX_SIZE);
void **end = PPTR(NUM(page->addr) + APAGE_SIZE - page->size); void **end = PPTR(NUM(page->addr) + APAGE_SIZE - page->size);
@ -3770,11 +3787,11 @@ void GC_dump_with_traces(int flags,
} }
} }
GCWARN((GCOUTF, " %" PRIdPTR " [%" PRIdPTR "/%" PRIdPTR "]", GCWARN((GCOUTF, " %" PRIdPTR " [%" PRIdPTR "/%" PRIdPTR "]",
count, page_count, gc->med_pages[i]->size)); count, page_count, gc->med_pages[ty][i]->size));
} }
} }
GCWARN((GCOUTF, "\n")); GCWARN((GCOUTF, "\n"));
}
GCWARN((GCOUTF,"\n")); GCWARN((GCOUTF,"\n"));
GCWARN((GCOUTF,"Current memory use: %" PRIdPTR "\n", GC_get_memory_use(NULL))); GCWARN((GCOUTF,"Current memory use: %" PRIdPTR "\n", GC_get_memory_use(NULL)));
@ -3866,7 +3883,7 @@ static void reset_gen1_pages_live_and_previous_sizes(NewGC *gc)
} }
for (i = 0; i < NUM_MED_PAGE_SIZES; i++) { for (i = 0; i < NUM_MED_PAGE_SIZES; i++) {
for (work = gc->med_pages[i]; work; work = work->next) { for (work = gc->med_pages[MED_PAGE_NONATOMIC][i]; work; work = work->next) {
if (work->generation) { if (work->generation) {
reset_gen1_page(gc, work); reset_gen1_page(gc, work);
} }
@ -3891,7 +3908,7 @@ static void mark_backpointers(NewGC *gc)
{ {
if(!gc->gc_full) { if(!gc->gc_full) {
mpage *work; mpage *work;
int i; int i, ty;
PageMap pagemap = gc->page_maps; PageMap pagemap = gc->page_maps;
/* if this is not a full collection, then we need to mark any pointers /* if this is not a full collection, then we need to mark any pointers
@ -3936,8 +3953,9 @@ static void mark_backpointers(NewGC *gc)
} }
} }
for (ty = 0; ty < MED_PAGE_TYPES; ty++) {
for (i = 0; i < NUM_MED_PAGE_SIZES; i++) { for (i = 0; i < NUM_MED_PAGE_SIZES; i++) {
for (work = gc->med_pages[i]; work; work = work->next) { for (work = gc->med_pages[ty][i]; work; work = work->next) {
if(work->back_pointers) { if(work->back_pointers) {
void **start = PPTR(NUM(work->addr) + PREFIX_SIZE); void **start = PPTR(NUM(work->addr) + PREFIX_SIZE);
void **end = PPTR(NUM(work->addr) + APAGE_SIZE - work->size); void **end = PPTR(NUM(work->addr) + APAGE_SIZE - work->size);
@ -3945,6 +3963,7 @@ static void mark_backpointers(NewGC *gc)
work->marked_on = 1; work->marked_on = 1;
pagemap_add(pagemap, work); pagemap_add(pagemap, work);
if (!ty) {
while(start <= end) { while(start <= end) {
objhead *info = (objhead *)start; objhead *info = (objhead *)start;
if(!info->dead) { if(!info->dead) {
@ -3958,6 +3977,8 @@ static void mark_backpointers(NewGC *gc)
} }
} }
} }
}
}
} }
mpage *allocate_compact_target(NewGC *gc, mpage *work) mpage *allocate_compact_target(NewGC *gc, mpage *work)
@ -4142,7 +4163,7 @@ static void killing_debug(NewGC *gc, mpage *page, objhead *info) {
static void repair_heap(NewGC *gc) static void repair_heap(NewGC *gc)
{ {
mpage *page; mpage *page;
int i; int i, ty;
Fixup2_Proc *fixup_table = gc->fixup_table; Fixup2_Proc *fixup_table = gc->fixup_table;
#ifdef MZ_USE_PLACES #ifdef MZ_USE_PLACES
int master_has_switched = postmaster_and_master_gc(gc); int master_has_switched = postmaster_and_master_gc(gc);
@ -4306,8 +4327,9 @@ static void repair_heap(NewGC *gc)
} }
} }
for (ty = 0; ty < MED_PAGE_TYPES; ty++) {
for (i = 0; i < NUM_MED_PAGE_SIZES; i++) { for (i = 0; i < NUM_MED_PAGE_SIZES; i++) {
for (page = gc->med_pages[i]; page; page = page->next) { for (page = gc->med_pages[ty][i]; page; page = page->next) {
#ifdef MZ_USE_PLACES #ifdef MZ_USE_PLACES
if (master_has_switched || page->marked_on) if (master_has_switched || page->marked_on)
#else #else
@ -4359,6 +4381,7 @@ static void repair_heap(NewGC *gc)
} }
} }
} }
}
} }
static inline void gen1_free_mpage(PageMap pagemap, mpage *page) { static inline void gen1_free_mpage(PageMap pagemap, mpage *page) {
@ -4411,7 +4434,7 @@ inline static void gen0_free_big_pages(NewGC *gc) {
static void clean_up_heap(NewGC *gc) static void clean_up_heap(NewGC *gc)
{ {
int i; int i, ty;
uintptr_t memory_in_use = 0; uintptr_t memory_in_use = 0;
PageMap pagemap = gc->page_maps; PageMap pagemap = gc->page_maps;
@ -4448,11 +4471,12 @@ static void clean_up_heap(NewGC *gc)
} }
} }
for (ty = 0; ty < MED_PAGE_TYPES; ty++) {
for (i = 0; i < NUM_MED_PAGE_SIZES; i++) { for (i = 0; i < NUM_MED_PAGE_SIZES; i++) {
mpage *work; mpage *work;
mpage *prev = NULL, *next; mpage *prev = NULL, *next;
for (work = gc->med_pages[i]; work; work = next) { for (work = gc->med_pages[ty][i]; work; work = next) {
if (work->marked_on) { if (work->marked_on) {
void **start = PPTR(NUM(work->addr) + PREFIX_SIZE); void **start = PPTR(NUM(work->addr) + PREFIX_SIZE);
void **end = PPTR(NUM(work->addr) + APAGE_SIZE - work->size); void **end = PPTR(NUM(work->addr) + APAGE_SIZE - work->size);
@ -4477,7 +4501,7 @@ static void clean_up_heap(NewGC *gc)
prev = work; prev = work;
} else { } else {
/* free the page */ /* free the page */
if(prev) prev->next = next; else gc->med_pages[i] = next; if(prev) prev->next = next; else gc->med_pages[ty][i] = next;
if(next) work->next->prev = prev; if(next) work->next->prev = prev;
GCVERBOSEPAGE(gc, "Cleaning up MED PAGE NO OBJ", work); GCVERBOSEPAGE(gc, "Cleaning up MED PAGE NO OBJ", work);
gen1_free_mpage(pagemap, work); gen1_free_mpage(pagemap, work);
@ -4486,7 +4510,7 @@ static void clean_up_heap(NewGC *gc)
/* Page wasn't touched in full GC, or gen-0 not touched, /* Page wasn't touched in full GC, or gen-0 not touched,
so we can free it. */ so we can free it. */
next = work->next; next = work->next;
if(prev) prev->next = next; else gc->med_pages[i] = next; if(prev) prev->next = next; else gc->med_pages[ty][i] = next;
if(next) work->next->prev = prev; if(next) work->next->prev = prev;
GCVERBOSEPAGE(gc, "Cleaning up MED NO MARKEDON", work); GCVERBOSEPAGE(gc, "Cleaning up MED NO MARKEDON", work);
gen1_free_mpage(pagemap, work); gen1_free_mpage(pagemap, work);
@ -4500,7 +4524,8 @@ static void clean_up_heap(NewGC *gc)
pagemap_add(pagemap, work); pagemap_add(pagemap, work);
} }
} }
gc->med_freelist_pages[i] = prev; gc->med_freelist_pages[ty][i] = prev;
}
} }
memory_in_use = add_no_overflow(memory_in_use, gc->phantom_count); memory_in_use = add_no_overflow(memory_in_use, gc->phantom_count);
@ -4530,7 +4555,7 @@ static void unprotect_old_pages(NewGC *gc)
} }
for (i = 0; i < NUM_MED_PAGE_SIZES; i++) { for (i = 0; i < NUM_MED_PAGE_SIZES; i++) {
for (page = gc->med_pages[i]; page; page = page->next) { for (page = gc->med_pages[MED_PAGE_NONATOMIC][i]; page; page = page->next) {
if (page->mprotected) { if (page->mprotected) {
page->mprotected = 0; page->mprotected = 0;
mmu_queue_write_unprotect_range(mmu, page->addr, real_page_size(page), page_mmu_type(page), &page->mmu_src_block); mmu_queue_write_unprotect_range(mmu, page->addr, real_page_size(page), page_mmu_type(page), &page->mmu_src_block);
@ -4566,7 +4591,7 @@ static void protect_old_pages(NewGC *gc)
} }
for (i = 0; i < NUM_MED_PAGE_SIZES; i++) { for (i = 0; i < NUM_MED_PAGE_SIZES; i++) {
for (page = gc->med_pages[i]; page; page = page->next) { for (page = gc->med_pages[MED_PAGE_NONATOMIC][i]; page; page = page->next) {
if (!page->mprotected) { if (!page->mprotected) {
page->back_pointers = 0; page->back_pointers = 0;
page->mprotected = 1; page->mprotected = 1;

View File

@ -142,8 +142,9 @@ typedef struct NewGC {
/* All non-gen0 pages are held in the following structure. */ /* All non-gen0 pages are held in the following structure. */
struct mpage *gen1_pages[PAGE_TYPES]; struct mpage *gen1_pages[PAGE_TYPES];
struct mpage *med_pages[NUM_MED_PAGE_SIZES]; /* 0 is non-atomic, 1 is atomic: */
struct mpage *med_freelist_pages[NUM_MED_PAGE_SIZES]; struct mpage *med_pages[2][NUM_MED_PAGE_SIZES];
struct mpage *med_freelist_pages[2][NUM_MED_PAGE_SIZES];
MarkSegment *mark_stack; MarkSegment *mark_stack;