diff options
author | Ben Gamari <bgamari.foss@gmail.com> | 2017-09-26 15:09:13 -0400 |
---|---|---|
committer | Ben Gamari <ben@smart-cactus.org> | 2017-09-26 17:40:03 -0400 |
commit | 30a1eeea37e224e4ade9b8e7cdd30076cb716960 (patch) | |
tree | a686de6c4aa4b1dbb9622b1af0ba5b699d79fe49 | |
parent | 018c40fb1bb27853d0cefa5b90a44ce13e91a856 (diff) | |
download | haskell-30a1eeea37e224e4ade9b8e7cdd30076cb716960.tar.gz |
rts: Throw proper HeapOverflow exception on allocating large array
Test Plan: Validate, add tests
Reviewers: simonmar, austin, erikd
Reviewed By: simonmar
Subscribers: rwbarton, thomie
Differential Revision: https://phabricator.haskell.org/D4021
-rw-r--r-- | includes/rts/storage/GC.h | 5 | ||||
-rw-r--r-- | rts/PrimOps.cmm | 26 | ||||
-rw-r--r-- | rts/sm/Storage.c | 84 |
3 files changed, 79 insertions, 36 deletions
diff --git a/includes/rts/storage/GC.h b/includes/rts/storage/GC.h index 387bd260d3..2aed7c57ee 100644 --- a/includes/rts/storage/GC.h +++ b/includes/rts/storage/GC.h @@ -184,8 +184,9 @@ extern generation * oldest_gen; -------------------------------------------------------------------------- */ -StgPtr allocate ( Capability *cap, W_ n ); -StgPtr allocatePinned ( Capability *cap, W_ n ); +StgPtr allocate ( Capability *cap, W_ n ); +StgPtr allocateMightFail ( Capability *cap, W_ n ); +StgPtr allocatePinned ( Capability *cap, W_ n ); /* memory allocator for executable memory */ typedef void* AdjustorWritable; diff --git a/rts/PrimOps.cmm b/rts/PrimOps.cmm index 4d54ecf6dc..b43dfbf554 100644 --- a/rts/PrimOps.cmm +++ b/rts/PrimOps.cmm @@ -62,7 +62,10 @@ stg_newByteArrayzh ( W_ n ) payload_words = ROUNDUP_BYTES_TO_WDS(n); words = BYTES_TO_WDS(SIZEOF_StgArrBytes) + payload_words; - ("ptr" p) = ccall allocate(MyCapability() "ptr",words); + ("ptr" p) = ccall allocateMightFail(MyCapability() "ptr", words); + if (p == NULL) { + jump stg_raisezh(base_GHCziIOziException_heapOverflow_closure); + } TICK_ALLOC_PRIM(SIZEOF_StgArrBytes,WDS(payload_words),0); SET_HDR(p, stg_ARR_WORDS_info, CCCS); StgArrBytes_bytes(p) = n; @@ -92,6 +95,9 @@ stg_newPinnedByteArrayzh ( W_ n ) words = ROUNDUP_BYTES_TO_WDS(bytes); ("ptr" p) = ccall allocatePinned(MyCapability() "ptr", words); + if (p == NULL) { + jump stg_raisezh(base_GHCziIOziException_heapOverflow_closure); + } TICK_ALLOC_PRIM(SIZEOF_StgArrBytes,WDS(payload_words),0); /* Now we need to move p forward so that the payload is aligned @@ -130,6 +136,9 @@ stg_newAlignedPinnedByteArrayzh ( W_ n, W_ alignment ) words = ROUNDUP_BYTES_TO_WDS(bytes); ("ptr" p) = ccall allocatePinned(MyCapability() "ptr", words); + if (p == NULL) { + jump stg_raisezh(base_GHCziIOziException_heapOverflow_closure); + } TICK_ALLOC_PRIM(SIZEOF_StgArrBytes,WDS(payload_words),0); /* Now we need to move p forward so that the payload is aligned @@ -240,7 +249,10 @@ stg_newArrayzh ( W_ n /* words */, gcptr init ) // number of words. size = n + mutArrPtrsCardWords(n); words = BYTES_TO_WDS(SIZEOF_StgMutArrPtrs) + size; - ("ptr" arr) = ccall allocate(MyCapability() "ptr",words); + ("ptr" arr) = ccall allocateMightFail(MyCapability() "ptr",words); + if (arr == NULL) { + jump stg_raisezh(base_GHCziIOziException_heapOverflow_closure); + } TICK_ALLOC_PRIM(SIZEOF_StgMutArrPtrs, WDS(size), 0); SET_HDR(arr, stg_MUT_ARR_PTRS_DIRTY_info, CCCS); @@ -366,7 +378,10 @@ stg_newArrayArrayzh ( W_ n /* words */ ) // number of words. size = n + mutArrPtrsCardWords(n); words = BYTES_TO_WDS(SIZEOF_StgMutArrPtrs) + size; - ("ptr" arr) = ccall allocate(MyCapability() "ptr",words); + ("ptr" arr) = ccall allocateMightFail(MyCapability() "ptr",words); + if (arr == NULL) { + jump stg_raisezh(base_GHCziIOziException_heapOverflow_closure); + } TICK_ALLOC_PRIM(SIZEOF_StgMutArrPtrs, WDS(size), 0); SET_HDR(arr, stg_MUT_ARR_PTRS_DIRTY_info, W_[CCCS]); @@ -398,7 +413,10 @@ stg_newSmallArrayzh ( W_ n /* words */, gcptr init ) again: MAYBE_GC(again); words = BYTES_TO_WDS(SIZEOF_StgSmallMutArrPtrs) + n; - ("ptr" arr) = ccall allocate(MyCapability() "ptr",words); + ("ptr" arr) = ccall allocateMightFail(MyCapability() "ptr",words); + if (arr == NULL) { + jump stg_raisezh(base_GHCziIOziException_heapOverflow_closure); + } TICK_ALLOC_PRIM(SIZEOF_StgSmallMutArrPtrs, WDS(n), 0); SET_HDR(arr, stg_SMALL_MUT_ARR_PTRS_DIRTY_info, CCCS); diff --git a/rts/sm/Storage.c b/rts/sm/Storage.c index 6c5a73310c..e801c340f2 100644 --- a/rts/sm/Storage.c +++ b/rts/sm/Storage.c @@ -796,6 +796,20 @@ move_STACK (StgStack *src, StgStack *dest) dest->sp = (StgPtr)dest->sp + diff; } +STATIC_INLINE void +accountAllocation(Capability *cap, W_ n) +{ + TICK_ALLOC_HEAP_NOCTR(WDS(n)); + CCS_ALLOC(cap->r.rCCCS,n); + if (cap->r.rCurrentTSO != NULL) { + // cap->r.rCurrentTSO->alloc_limit -= n*sizeof(W_) + ASSIGN_Int64((W_*)&(cap->r.rCurrentTSO->alloc_limit), + (PK_Int64((W_*)&(cap->r.rCurrentTSO->alloc_limit)) + - n*sizeof(W_))); + } + +} + /* ----------------------------------------------------------------------------- StgPtr allocate (Capability *cap, W_ n) @@ -812,21 +826,37 @@ move_STACK (StgStack *src, StgStack *dest) that operation fails, then the whole process will be killed. -------------------------------------------------------------------------- */ +/* + * Allocate some n words of heap memory; terminating + * on heap overflow + */ StgPtr allocate (Capability *cap, W_ n) { + StgPtr p = allocateMightFail(cap, n); + if (p == NULL) { + reportHeapOverflow(); + // heapOverflow() doesn't exit (see #2592), but we aren't + // in a position to do a clean shutdown here: we + // either have to allocate the memory or exit now. + // Allocating the memory would be bad, because the user + // has requested that we not exceed maxHeapSize, so we + // just exit. + stg_exit(EXIT_HEAPOVERFLOW); + } + return p; +} + +/* + * Allocate some n words of heap memory; returning NULL + * on heap overflow + */ +StgPtr +allocateMightFail (Capability *cap, W_ n) +{ bdescr *bd; StgPtr p; - TICK_ALLOC_HEAP_NOCTR(WDS(n)); - CCS_ALLOC(cap->r.rCCCS,n); - if (cap->r.rCurrentTSO != NULL) { - // cap->r.rCurrentTSO->alloc_limit -= n*sizeof(W_) - ASSIGN_Int64((W_*)&(cap->r.rCurrentTSO->alloc_limit), - (PK_Int64((W_*)&(cap->r.rCurrentTSO->alloc_limit)) - - n*sizeof(W_))); - } - if (n >= LARGE_OBJECT_THRESHOLD/sizeof(W_)) { // The largest number of words such that // the computation of req_blocks will not overflow. @@ -845,16 +875,12 @@ allocate (Capability *cap, W_ n) req_blocks >= HS_INT32_MAX) // avoid overflow when // calling allocGroup() below { - reportHeapOverflow(); - // heapOverflow() doesn't exit (see #2592), but we aren't - // in a position to do a clean shutdown here: we - // either have to allocate the memory or exit now. - // Allocating the memory would be bad, because the user - // has requested that we not exceed maxHeapSize, so we - // just exit. - stg_exit(EXIT_HEAPOVERFLOW); + return NULL; } + // Only credit allocation after we've passed the size check above + accountAllocation(cap, n); + ACQUIRE_SM_LOCK bd = allocGroupOnNode(cap->node,req_blocks); dbl_link_onto(bd, &g0->large_objects); @@ -870,6 +896,7 @@ allocate (Capability *cap, W_ n) /* small allocation (<LARGE_OBJECT_THRESHOLD) */ + accountAllocation(cap, n); bd = cap->r.rCurrentAlloc; if (bd == NULL || bd->free + n > bd->start + BLOCK_SIZE_W) { @@ -955,7 +982,8 @@ allocate (Capability *cap, W_ n) to pinned ByteArrays, not scavenging is ok. This function is called by newPinnedByteArray# which immediately - fills the allocated memory with a MutableByteArray#. + fills the allocated memory with a MutableByteArray#. Note that + this returns NULL on heap overflow. ------------------------------------------------------------------------- */ StgPtr @@ -967,20 +995,16 @@ allocatePinned (Capability *cap, W_ n) // If the request is for a large object, then allocate() // will give us a pinned object anyway. if (n >= LARGE_OBJECT_THRESHOLD/sizeof(W_)) { - p = allocate(cap, n); - Bdescr(p)->flags |= BF_PINNED; - return p; - } - - TICK_ALLOC_HEAP_NOCTR(WDS(n)); - CCS_ALLOC(cap->r.rCCCS,n); - if (cap->r.rCurrentTSO != NULL) { - // cap->r.rCurrentTSO->alloc_limit -= n*sizeof(W_); - ASSIGN_Int64((W_*)&(cap->r.rCurrentTSO->alloc_limit), - (PK_Int64((W_*)&(cap->r.rCurrentTSO->alloc_limit)) - - n*sizeof(W_))); + p = allocateMightFail(cap, n); + if (p == NULL) { + return NULL; + } else { + Bdescr(p)->flags |= BF_PINNED; + return p; + } } + accountAllocation(cap, n); bd = cap->pinned_object_block; // If we don't have a block of pinned objects yet, or the current |