packages feed

fb-util-0.1.0.1: Util/ASan.hs

{-
  Copyright (c) Meta Platforms, Inc. and affiliates.
  All rights reserved.

  This source code is licensed under the BSD-style license found in the
  LICENSE file in the root directory of this source tree.
-}

module Util.ASan
  ( alloca
  , allocaBytes
  , allocaArray
  , allocaArray0
  , allocaBytesAligned
  , with
  , byteStringWithCString
  , byteStringWithCStringLen
  , textWithCStringLen
  , textWithCString
  , textUseAsPtr
  ) where

import Control.Exception

import Foreign hiding
  (alloca, allocaBytes, allocaBytesAligned, allocaArray, allocaArray0, with)
import Foreign.C

import qualified Data.ByteString as BS
import qualified Data.ByteString.Internal as BI

import Data.Text.Internal (Text(..))
import Data.Text.Encoding (encodeUtf8)
import qualified Data.Text.Foreign as TF

import Util.FFI

-- The following functions already support ASan (they already allocate using
-- malloc):
-- newCStringFromText

alloca :: Storable a => (Ptr a -> IO b) -> IO b
alloca = doAlloca undefined
  where
    doAlloca :: Storable a' => a' -> (Ptr a' -> IO b') -> IO b'
    doAlloca dummy = allocaBytesAligned (sizeOf dummy) (alignment dummy)

allocaBytes :: Int -> (Ptr a -> IO b) -> IO b
allocaBytes size = bracket (mallocBytes size) free

-- alignedAlloc calls an aligned allocation function that is platform-dependent
-- void *alignedAlloc( size_t alignment, size_t size );
foreign import ccall unsafe "alignedAlloc"
  cAlignedAlloc :: CSize -> CSize -> IO (Ptr a)

allocaBytesAligned :: Int -> Int -> (Ptr a -> IO b) -> IO b
allocaBytesAligned size alignment =
  bracket (cAlignedAlloc (fromIntegral alignment) (fromIntegral size)) free

allocaArray :: Storable a => Int -> (Ptr a -> IO b) -> IO b
allocaArray  = doAlloca undefined
  where
    doAlloca :: Storable a' => a' -> Int -> (Ptr a' -> IO b') -> IO b'
    doAlloca dummy size = allocaBytesAligned (size * sizeOf dummy)
                                              (alignment dummy)

allocaArray0      :: Storable a => Int -> (Ptr a -> IO b) -> IO b
allocaArray0 size  = allocaArray (size + 1)

with :: Storable a => a -> (Ptr a -> IO b) -> IO b
with val f =
  alloca $ \ptr -> do
    poke ptr val
    f ptr

-- Text / ByteString marshalling

textWithCString :: Text -> (CString -> IO a) -> IO a
textWithCString = byteStringWithCString . encodeUtf8

byteStringWithCString :: BS.ByteString -> (CString -> IO a) -> IO a
byteStringWithCString (BI.PS fp off len) fun =
  allocaBytes (len+1) $ \ptr' -> do
    unsafeWithForeignPtr fp $ \ptr -> do
      copyBytes ptr' (ptr `plusPtr` off) len
      poke (ptr' `plusPtr` len) (0 :: Word8)
    fun ptr'


textWithCStringLen :: Text -> (CStringLen -> IO a) -> IO a
textWithCStringLen = byteStringWithCStringLen . encodeUtf8

byteStringWithCStringLen :: BS.ByteString -> (CStringLen -> IO a) -> IO a
byteStringWithCStringLen (BI.PS _ _ 0) fun = fun (nullPtr, 0)
byteStringWithCStringLen (BI.PS fp off len) fun =
  allocaBytes len $ \ptr' -> do
    unsafeWithForeignPtr fp $ \ptr -> copyBytes ptr' (ptr `plusPtr` off) len
    fun (ptr', len)

-- Text marshalling

textUseAsPtr :: Text -> (Ptr Word16 -> TF.I16 -> IO a) -> IO a
textUseAsPtr t@(Text _arr _off len) action =
    allocaBytes (len * 2) $ \buf -> do
      TF.unsafeCopyToPtr t buf
      action (castPtr buf) (fromIntegral len)