packages feed

crucible-symio-0.1: src/Lang/Crucible/SymIO/Types.hs

-----------------------------------------------------------------------
-- |
-- Module           : Lang.Crucible.SymIO.Types
-- Description      : Crucible type definitions related to VFS
-- Copyright        : (c) Galois, Inc 2020
-- License          : BSD3
-- Maintainer       : Daniel Matichuk <dmatichuk@galois.com>
-- Stability        : provisional
------------------------------------------------------------------------

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ViewPatterns #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TypeApplications #-}

{-# OPTIONS_GHC -fno-warn-orphans #-}
module Lang.Crucible.SymIO.Types
  ( symIOIntrinsicTypes
  , FilePointer(..)
  , FilePointerType
  , pattern FilePointerRepr
  , FileHandle
  , FileHandleType
  , pattern FileHandleRepr
  , FileIdent
  , FileIdentType
  , FileSystem(..)
  , muxFileSystem
  , FileSystemType
  , FileSystemIndex
  , pattern FileSystemRepr
  , File(..)
  , pattern FileRepr
  , FileType
  , muxFile
  , DataChunk
  , SizedDataChunk
  , SizedDataChunkType
  )
where

import           Data.Typeable
import           GHC.TypeNats

import qualified Data.Parameterized.Map as MapF
import           Data.Parameterized.Context
import           Data.Parameterized.Classes
import           Data.Parameterized.NatRepr

import           Lang.Crucible.Backend
import           Lang.Crucible.Simulator.RegValue
import           Lang.Crucible.Types
import           Lang.Crucible.Simulator.Intrinsics

import           What4.Interface
import qualified What4.CachedArray as CA

-- | The intrinsic types used in the symbolic filesystem
symIOIntrinsicTypes :: IsSymInterface sym => IntrinsicTypes sym
symIOIntrinsicTypes = id
  . MapF.insert (knownSymbol :: SymbolRepr "VFS_filesystem") IntrinsicMuxFn
  . MapF.insert (knownSymbol :: SymbolRepr "VFS_file") IntrinsicMuxFn
  . MapF.insert (knownSymbol :: SymbolRepr "VFS_filepointer") IntrinsicMuxFn
  $ MapF.empty

-- | An identifier for a file, which must be resolved into a 'File' to access
-- the underlying filesystem.
--
-- This is a file path
type FileIdent sym = RegValue sym FileIdentType

-- | The crucible-level type of 'FileIdent'
type FileIdentType = StringType Char8

-- | The crucible-level type of 'FileSystem'
type FileSystemType w = IntrinsicType "VFS_filesystem" (EmptyCtx ::> BVType w)

-- | Defines the current state of a symbolic filesystem.
data FileSystem sym w =
  FileSystem
    {
      fsPtrSize :: NatRepr w
    , fsFileNames :: RegValue sym (StringMapType (FileType w))
    -- ^ map from concrete file identifiers to files
    , fsFileSizes :: CA.CachedArray sym (EmptyCtx ::> BaseIntegerType) (BaseBVType w)
    -- ^ a symbolic map from files to their size
    , fsSymData :: CA.CachedArray sym (EmptyCtx ::> BaseBVType w ::> BaseIntegerType) (BaseBVType 8)
    -- ^ array representing symbolic file contents
    , fsConstraints :: forall a. ((IsSymInterface sym, 1 <= w) => a) -> a
    }

-- | A base index into the filesystem, consistent of a file identifier and an offset into that file.
type FileSystemIndex sym w = Assignment (SymExpr sym) (EmptyCtx ::> BaseBVType w ::> BaseIntegerType)

muxFileSystem ::
  IsSymInterface sym =>
  sym ->
  Pred sym ->
  FileSystem sym w ->
  FileSystem sym w ->
  IO (FileSystem sym w)
muxFileSystem sym p fsT fsF = do
  symData <- CA.muxArrays sym p (fsSymData fsT) (fsSymData fsF)
  symFiles <- muxStringMap sym (muxFile sym) p (fsFileNames fsT) (fsFileNames fsF)
  symFileSizes <- CA.muxArrays sym p (fsFileSizes fsT) (fsFileSizes fsF)
  return $ fsT { fsSymData  = symData, fsFileNames = symFiles, fsFileSizes = symFileSizes }

instance (IsSymInterface sym) => IntrinsicClass sym "VFS_filesystem" where
  type Intrinsic sym "VFS_filesystem" (EmptyCtx ::> BVType w) = FileSystem sym w

  muxIntrinsic sym _iTypes _nm (Empty :> (BVRepr _w)) = muxFileSystem sym
  muxIntrinsic _ _ nm ctx = \_ _ _ -> typeError nm ctx

pattern FileSystemRepr :: () => (1 <= w, ty ~ FileSystemType w) => NatRepr w -> TypeRepr ty
pattern FileSystemRepr w <- IntrinsicRepr (testEquality (knownSymbol :: SymbolRepr "VFS_filesystem") -> Just Refl)
                                           (Empty :> BVRepr w)
  where
    FileSystemRepr w = IntrinsicRepr knownSymbol (Empty :> BVRepr w)

-- | The crucible type of file handles.
type FileHandleType w = ReferenceType (MaybeType (FilePointerType w))

-- |  A file handle is a mutable file pointer that increments every time it is read.
type FileHandle sym w = RegValue sym (FileHandleType w)

-- | A 'File' represents a file in the filesystem independent
-- of any open handles to it
--
-- The 'NatRepr' records the size of file pointers (in bits)
--
-- The 'SymInteger' is an index into the underlying array of arrays that represents file contents
data File sym w = File (NatRepr w) (SymInteger sym)

pattern FileRepr :: () => (1 <= w, ty ~ FileType w) => NatRepr w -> TypeRepr ty
pattern FileRepr w <- IntrinsicRepr (testEquality (knownSymbol :: SymbolRepr "VFS_file") -> Just Refl)
                                           (Empty :> BVRepr w)
  where
    FileRepr w = IntrinsicRepr knownSymbol (Empty :> BVRepr w)

-- | The crucible-level type of 'File'
type FileType w = IntrinsicType "VFS_file" (EmptyCtx ::> BVType w)

instance (IsSymInterface sym) => IntrinsicClass sym "VFS_file" where
  type Intrinsic sym "VFS_file" (EmptyCtx ::> BVType w) = File sym w

  muxIntrinsic sym _iTypes _nm (Empty :> BVRepr _w) = muxFile sym
  muxIntrinsic _ _ nm ctx = typeError nm ctx

muxFile ::
  IsSymInterface sym =>
  sym ->
  Pred sym ->
  File sym w ->
  File sym w ->
  IO (File sym w)
muxFile sym p (File w f1) (File _w f2) = File w <$> baseTypeIte sym p f1 f2

-- | A file pointer represents an index into a particular file.
--
-- The 'File' is similar to an inode, and uniquely identifies a file (as an
-- index into the array of all files).  The 'SymBV' is the offset into the file
-- that the file pointer is currently at (i.e., where the next read or write
-- will be from).
data FilePointer sym w =
  FilePointer (File sym w) (SymBV sym w) 

-- | The crucible type of 'FilePointer'
type FilePointerType w = IntrinsicType "VFS_filepointer" (EmptyCtx ::> BVType w)

instance (IsSymInterface sym) => IntrinsicClass sym "VFS_filepointer" where
  type Intrinsic sym "VFS_filepointer" (EmptyCtx ::> BVType w) = FilePointer sym w

  muxIntrinsic sym _iTypes _nm (Empty :> (BVRepr _w)) = muxFilePointer sym
  muxIntrinsic _ _ nm ctx = typeError nm ctx

-- | Mux on 'FilePointer'
muxFilePointer ::
  (1 <= w) =>
  IsSymInterface sym =>
  sym ->
  Pred sym ->
  FilePointer sym w ->
  FilePointer sym w ->
  IO (FilePointer sym w)
muxFilePointer sym p (FilePointer f1 off1) (FilePointer f2 off2) =
  do b   <- muxFile sym p f1 f2
     off <- bvIte sym p off1 off2
     return $ FilePointer b off


type DataChunk sym w = CA.ArrayChunk sym (BaseBVType w) (BaseBVType 8)

type SizedDataChunkType w = SymbolicStructType (EmptyCtx ::> BaseArrayType (EmptyCtx ::> BaseBVType w) (BaseBVType 8) ::> BaseBVType w)
type SizedDataChunk sym w = SymStruct sym (EmptyCtx ::> BaseArrayType (EmptyCtx ::> BaseBVType w) (BaseBVType 8) ::> BaseBVType w)

-- | A file handle is a reference to an optional file pointer
--
-- If the file pointer is not present, the file handle is closed. Otherwise, the
-- file pointer is the current pointer into the file (i.e., that will be read
-- from or written to next).
--
-- Note that this is just the repr and the real file handle value is symbolic
-- and stored in a Crucible reference.
pattern FileHandleRepr :: () => (1 <= w, ty ~ FileHandleType w) => NatRepr w -> TypeRepr ty
pattern FileHandleRepr w = ReferenceRepr (MaybeRepr (FilePointerRepr w))

pattern FilePointerRepr :: () => (1 <= w, ty ~ FilePointerType w) => NatRepr w -> TypeRepr ty
pattern FilePointerRepr w <- IntrinsicRepr (testEquality (knownSymbol :: SymbolRepr "VFS_filepointer") -> Just Refl)
                                           (Empty :> BVRepr w)
  where
    FilePointerRepr w = IntrinsicRepr knownSymbol (Empty :> BVRepr w)