packages feed

guarded-allocation (empty) → 0.0

raw patch · 9 files changed

+268/−0 lines, 9 filesdep +basesetup-changed

Dependencies added: base

Files

+ LICENSE view
@@ -0,0 +1,27 @@+Copyright (c) Henning Thielemann 2018++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions+are met:+1. Redistributions of source code must retain the above copyright+   notice, this list of conditions and the following disclaimer.+2. Redistributions in binary form must reproduce the above copyright+   notice, this list of conditions and the following disclaimer in the+   documentation and/or other materials provided with the distribution.+3. Neither the name of the author nor the names of his contributors+   may be used to endorse or promote products derived from this software+   without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE+ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF+SUCH DAMAGE.
+ Makefile view
@@ -0,0 +1,10 @@+test-build:+	runhaskell Setup configure --user -f-debug+	runhaskell Setup build+	runhaskell Setup haddock++	runhaskell Setup configure --user -fdebug+	runhaskell Setup build++%.html:	%.md+	pandoc $< --output=$@
+ README.md view
@@ -0,0 +1,29 @@+The overall idea of the package is to make programming mistakes+let low-level programs fail reproducibly.++What the routines do:++ *  After allocation fill the memory with the hex string 0xDEADF00D.+    This allows to check whether the caller+    properly initialises allocated buffers.++ *  Allocate some memory before and after the actual buffer+    and fill it with 0xABADCAFE.+    On deallocation it is checked that this pattern is still intact.+    If not, abort with an error.+    This allows to check for range violations.++ *  Before deallocation fill the memory with 0xDEADBEEF.+    This helps to detect when the program reads memory after its deallocation.++ *  The `create` routine additionally makes a copy of the initialized buffer.+    The finalizer compares the contents of the buffer and its copy.+    This way it can detect if an immutable array was altered after its creation.+++Range violations might alternatively be detected by range checking techniques.+Allocation problems might be solved using Regions.+The provided functions might overlook range violations+but they help detecting bugs+when you have not full control over the code that processes memory content,+e.g. when calling external routines via FFI.
+ Setup.lhs view
@@ -0,0 +1,3 @@+#! /usr/bin/env runhaskell+> import Distribution.Simple+> main = defaultMain
+ frontend/debug/Foreign/Marshal/Array/Guarded.hs view
@@ -0,0 +1,6 @@+module Foreign.Marshal.Array.Guarded (+   Array.create,+   Array.alloca,+   ) where++import qualified Foreign.Marshal.Array.Guarded.Debug as Array
+ frontend/plain/Foreign/Marshal/Array/Guarded.hs view
@@ -0,0 +1,6 @@+module Foreign.Marshal.Array.Guarded (+   Array.create,+   Array.alloca,+   ) where++import qualified Foreign.Marshal.Array.Guarded.Plain as Array
+ guarded-allocation.cabal view
@@ -0,0 +1,67 @@+Name:             guarded-allocation+Version:          0.0+License:          BSD3+License-File:     LICENSE+Author:           Henning Thielemann <haskell@henning-thielemann.de>+Maintainer:       Henning Thielemann <haskell@henning-thielemann.de>+Homepage:         http://hub.darcs.net/thielema/guarded-allocation/+Category:         Debug+Synopsis:         Memory allocation with added stress tests and integrity checks+Description:+  Provide adaptions of @mallocForeignPtrArray@ and @allocaArray@+  that add stress tests and integrity checks.+  .+  There are three modules:+  .+  * @Guarded.Plain@: exports the original allocation routines+  .+  * @Guarded.Debug@: exports allocation routines that add stress and checks+  .+  * @Guarded@: exports either @Guarded.Plain@ or @Guarded.Debug@+    depending on the Cabal @debug@ flag.+  .+  It is intended that you always import the @Guarded@ module in user code+  and install a package version with enabled debug flag+  to a custom package database for debugging.+  If you compile your user program you can choose production or debugging mode+  by choosing the default or the custom debugging package database,+  respectively.+  .+  This package is inspired by the famous Amiga debug tool MungWall.+  The Linux counterpart is Electric Fence.++Tested-With:      GHC==7.4.2, GHC==7.8.4, GHC==8.4.3+Cabal-Version:    >=1.6+Build-Type:       Simple+Extra-Source-Files:+  Makefile+  README.md++Source-Repository this+  Tag:         0.1+  Type:        darcs+  Location:    http://hub.darcs.net/thielema/guarded-allocation/++Source-Repository head+  Type:        darcs+  Location:    http://hub.darcs.net/thielema/guarded-allocation/++Flag debug+  Description: Add stress tests and integrity checks to allocations+  Default:     False+  Manual:      True++Library+  Build-Depends:+    base >=4.5 && <5++  GHC-Options:      -Wall+  Hs-Source-Dirs:   src+  If flag(debug)+    Hs-Source-Dirs: frontend/debug+  Else+    Hs-Source-Dirs: frontend/plain+  Exposed-Modules:+    Foreign.Marshal.Array.Guarded+    Foreign.Marshal.Array.Guarded.Plain+    Foreign.Marshal.Array.Guarded.Debug
+ src/Foreign/Marshal/Array/Guarded/Debug.hs view
@@ -0,0 +1,100 @@+module Foreign.Marshal.Array.Guarded.Debug (+   create,+   alloca,+   ) where++import qualified Foreign.Marshal.Array.Guarded.Plain as Plain++import Foreign.Marshal.Array+         (mallocArray, allocaArray, pokeArray, copyArray, advancePtr)+import Foreign.Marshal.Alloc (free)+import Foreign.Storable (Storable, peekByteOff, sizeOf)+import Foreign.Concurrent (newForeignPtr)+import Foreign.ForeignPtr (ForeignPtr)+import Foreign.Ptr (Ptr, castPtr)++import Control.Monad (when)++import Data.Foldable (for_)+import Data.Word (Word8)+++{- |+Array creation with additional immutability check, electrical fence+and pollution of uncleaned memory.++The function checks that the array is not altered anymore after creation.+-}+create :: (Storable a) => Int -> (Ptr a -> IO b) -> IO (ForeignPtr a, b)+create = flip asTypeOf Plain.create $ \size f -> do+   let border = 64+   let fullSize = size + 2*border+   ptrApre <- mallocArray fullSize+   ptrsA@(_ptrApre, ptrA, _ptrApost) <- fillAll border size ptrApre+   result <- f ptrA+   checkAll border ptrsA+   ptrB <- mallocArray size+   copyArray ptrB ptrA size+   fmap (flip (,) result) $ newForeignPtr ptrA $ do+      for_ (take (arraySize ptrA size) [0..]) $ \i -> do+         a <- peekByteOff ptrA i+         b <- peekByteOff ptrB i+         when (a/=(b::Word8)) $+            error $ "immutable array was altered at byte position " ++ show i+      trash fullSize ptrApre+      free ptrApre+      free ptrB+++alloca :: (Storable a) => Int -> (Ptr a -> IO b) -> IO b+alloca = flip asTypeOf Plain.alloca $ \size f -> do+   let border = 64+   let fullSize = size + 2*border+   allocaArray fullSize $ \ptrPre -> do+      ptrs@(_ptrPre, ptr, _ptrPost) <- fillAll border size ptrPre+      result <- f ptr+      checkAll border ptrs+      trash fullSize ptrPre+      return result+++fillAll :: (Storable a) => Int -> Int -> Ptr a -> IO (Ptr a, Ptr a, Ptr a)+fillAll border size ptrPre = do+   let ptr = advancePtr ptrPre border+   let ptrPost = advancePtr ptr size+   fill ptrPre border [0xAB,0xAD,0xCA,0xFE]+   fill ptr size [0xDE,0xAD,0xF0,0x0D]+   fill ptrPost border [0xAB,0xAD,0xCA,0xFE]+   return (ptrPre, ptr, ptrPost)++checkAll :: (Storable a) => Int -> (Ptr a, Ptr a, Ptr a) -> IO ()+checkAll border (ptrPre, _ptr, ptrPost) = do+   check "leading"  ptrPre  border [0xAB,0xAD,0xCA,0xFE]+   check "trailing" ptrPost border [0xAB,0xAD,0xCA,0xFE]++trash :: (Storable a) => Int -> Ptr a -> IO ()+trash fullSize ptrPre = fill ptrPre fullSize [0xDE,0xAD,0xBE,0xEF]+++{-# INLINE fill #-}+fill :: (Storable a) => Ptr a -> Int -> [Word8] -> IO ()+fill ptr n bytes =+   pokeArray (castPtr ptr) $ take (arraySize ptr n) $ cycle bytes++{-# INLINE check #-}+check :: (Storable a) => String -> Ptr a -> Int -> [Word8] -> IO ()+check name ptr n bytes =+   for_ (take (arraySize ptr n) $ zip [0..] $ cycle bytes) $ \(i,b) -> do+      a <- peekByteOff ptr i+      when (a/=(b::Word8)) $+         error $ "damaged " ++ name ++ " fence at position " ++ show i++arraySize :: (Storable a) => Ptr a -> Int -> Int+arraySize ptr n = arraySizeAux ptr n $ error "arraySize: undefined element"++{- |+Correct size computation should also respect padding caused by alignment.+However, mallocArray uses this simple arithmetic.+-}+arraySizeAux :: (Storable a) => Ptr a -> Int -> a -> Int+arraySizeAux _ n a = n * sizeOf a
+ src/Foreign/Marshal/Array/Guarded/Plain.hs view
@@ -0,0 +1,20 @@+module Foreign.Marshal.Array.Guarded.Plain (+   create,+   alloca,+   ) where++import Foreign.Marshal.Array (allocaArray)+import Foreign.Storable (Storable)+import Foreign.ForeignPtr (ForeignPtr, withForeignPtr, mallocForeignPtrArray)+import Foreign.Ptr (Ptr)++import Control.Applicative ((<$>))+++create :: (Storable a) => Int -> (Ptr a -> IO b) -> IO (ForeignPtr a, b)+create size f = do+   fptr <- mallocForeignPtrArray size+   (,) fptr <$> withForeignPtr fptr f++alloca :: (Storable a) => Int -> (Ptr a -> IO b) -> IO b+alloca = allocaArray