diff --git a/source/cell.c b/source/cell.c index e356b35e..622d35da 100644 --- a/source/cell.c +++ b/source/cell.c @@ -377,7 +377,7 @@ int cell_init(int argc, char **argv) free(boot_data); return 1; } - JSContext *ctx = JS_NewContextWithHeapSize(g_runtime, 16 * 1024 * 1024); + JSContext *ctx = JS_NewContextWithHeapSize(g_runtime, 1024 * 1024); if (!ctx) { printf("Failed to create JS context\n"); free(boot_data); JS_FreeRuntime(g_runtime); diff --git a/source/quickjs-internal.h b/source/quickjs-internal.h index e5922f25..afb5bb8a 100644 --- a/source/quickjs-internal.h +++ b/source/quickjs-internal.h @@ -78,7 +78,7 @@ */ // #define DUMP_BYTECODE (1) /* dump GC summary: old/new heap, recovery %, heap growth */ -// #define DUMP_GC +#define DUMP_GC /* dump detailed GC: roots, scanning, object traversal (implies DUMP_GC) */ // #define DUMP_GC_DETAIL #ifdef DUMP_GC_DETAIL @@ -115,6 +115,14 @@ #define gc_poison_region(addr, size) ((void)0) #define gc_unpoison_region(addr, size) ((void)0) #endif + +#include +#include + +static inline size_t poison_page_align(size_t size) { + size_t ps = (size_t)sysconf(_SC_PAGESIZE); + return (size + ps - 1) & ~(ps - 1); +} #endif /* POISON_HEAP */ #ifdef HAVE_ASAN diff --git a/source/runtime.c b/source/runtime.c index 5f0d69ad..069bb0f6 100644 --- a/source/runtime.c +++ b/source/runtime.c @@ -935,7 +935,10 @@ void buddy_destroy (BuddyAllocator *b) { static void *heap_block_alloc(JSRuntime *rt, size_t size) { #ifdef POISON_HEAP (void)rt; - return malloc(size); + size = poison_page_align(size); + void *p = mmap(NULL, size, PROT_READ | PROT_WRITE, + MAP_ANON | MAP_PRIVATE, -1, 0); + return (p == MAP_FAILED) ? NULL : p; #else return buddy_alloc(&rt->buddy, size); #endif @@ -944,8 +947,9 @@ static void *heap_block_alloc(JSRuntime *rt, size_t size) { static void heap_block_free(JSRuntime *rt, void *ptr, size_t size) { #ifdef POISON_HEAP (void)rt; - (void)size; - /* Don't free - leave it poisoned to catch stale accesses */ + /* mmap'd memory is intentionally never munmap'd so virtual addresses + are never reused (preventing stale pointer aliasing). Pages stay + resident because chase() reads forwarding pointers from old blocks. */ gc_poison_region(ptr, size); #else buddy_free(&rt->buddy, ptr, size); @@ -1174,10 +1178,16 @@ int ctx_gc (JSContext *ctx, int allow_grow, size_t alloc_size) { while (new_size < need && new_size < (1ULL << BUDDY_MAX_ORDER)) new_size *= 2; } +#ifdef POISON_HEAP + new_size = poison_page_align(new_size); +#endif uint8_t *new_block = heap_block_alloc (rt, new_size); if (!new_block) { /* Try with same size */ new_size = ctx->current_block_size; +#ifdef POISON_HEAP + new_size = poison_page_align(new_size); +#endif new_block = heap_block_alloc (rt, new_size); if (!new_block) return -1; } @@ -1432,6 +1442,9 @@ JSContext *JS_NewContextRawWithHeapSize (JSRuntime *rt, size_t heap_size) { } /* Allocate initial heap block for bump allocation */ +#ifdef POISON_HEAP + heap_size = poison_page_align(heap_size); +#endif ctx->current_block_size = heap_size; ctx->next_block_size = ctx->current_block_size; ctx->heap_base = heap_block_alloc (rt, ctx->current_block_size);