packages feed

crucible-llvm-0.6: src/Lang/Crucible/LLVM/Errors/MemoryError.hs

-- |
-- Module           : Lang.Crucible.LLVM.Errors.MemoryError
-- Description      : Errors that arise when reading and writing memory
-- Copyright        : (c) Galois, Inc 2018
-- License          : BSD3
-- Maintainer       : Langston Barrett <lbarrett@galois.com>
-- Stability        : provisional
--
--------------------------------------------------------------------------

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeOperators #-}
module Lang.Crucible.LLVM.Errors.MemoryError
( MemoryError(..)
, MemErrContext
, explain
, details
, ppMemoryError
, MemoryOp(..)
, memOpMem
, ppMemoryOp
, MemoryErrorReason(..)
, ppMemoryErrorReason
, FuncLookupError(..)
, ppFuncLookupError

, concMemoryError
, concMemoryOp
) where

import           Prelude hiding (pred)

import           Data.Text (Text)
import qualified Text.LLVM.AST as L
import           Type.Reflection (SomeTypeRep(SomeTypeRep))
import           Prettyprinter

import           What4.Interface
import           What4.Expr (GroundValue)

import           Lang.Crucible.LLVM.MemModel.Pointer (LLVMPtr, concBV)
import           Lang.Crucible.LLVM.MemModel.Common
import           Lang.Crucible.LLVM.MemModel.Type
import           Lang.Crucible.LLVM.MemModel.MemLog
import qualified Lang.Crucible.LLVM.PrettyPrint as LPP

data MemoryOp sym w
  = MemLoadOp  StorageType (Maybe String) (LLVMPtr sym w) (Mem sym)
  | MemStoreOp StorageType (Maybe String) (LLVMPtr sym w) (Mem sym)
  | MemStoreBytesOp (Maybe String) (LLVMPtr sym w) (Maybe (SymBV sym w)) (Mem sym)
  | forall wlen. (1 <= wlen) => MemCopyOp
       (Maybe String, LLVMPtr sym w) -- dest
       (Maybe String, LLVMPtr sym w) -- src
       (SymBV sym wlen) -- length
       (Mem sym)
  | MemLoadHandleOp (Maybe L.Type) (Maybe String) (LLVMPtr sym w) (Mem sym)
  | forall wlen. (1 <= wlen) => MemInvalidateOp
       Text (Maybe String) (LLVMPtr sym w) (SymBV sym wlen) (Mem sym)

memOpMem :: MemoryOp sym w -> Mem sym
memOpMem =
  \case
    MemLoadOp _ _ _ mem -> mem
    MemStoreOp _ _ _ mem -> mem
    MemStoreBytesOp _ _ _ mem -> mem
    MemCopyOp _ _ _ mem -> mem
    MemLoadHandleOp _ _ _ mem -> mem
    MemInvalidateOp _ _ _ _ mem -> mem

data MemoryError sym where
  MemoryError :: (1 <= w) =>
    MemoryOp sym w ->
    MemoryErrorReason ->
    MemoryError sym

-- | The kinds of type errors that arise while reading memory/constructing LLVM
-- values
data MemoryErrorReason =
    TypeMismatch StorageType StorageType
  | UnexpectedArgumentType Text [StorageType]
  | ApplyViewFail ValueView
  | Invalid StorageType
  | Invalidated Text
  | NoSatisfyingWrite StorageType
  | UnwritableRegion
  | UnreadableRegion
  | BadFunctionPointer FuncLookupError
  | OverlappingRegions
  deriving (Eq, Ord)

-- | Reasons that looking up a function handle associated with an LLVM pointer
-- may fail
data FuncLookupError
  = SymbolicPointer
  | RawBitvector
  | NoOverride
  | Uncallable SomeTypeRep
  deriving (Eq, Ord)

ppFuncLookupError :: FuncLookupError -> Doc ann
ppFuncLookupError =
  \case
    SymbolicPointer -> "Cannot resolve a symbolic pointer to a function handle"
    RawBitvector -> "Cannot treat raw bitvector as function pointer"
    NoOverride -> "No implementation or override found for pointer"
    Uncallable (SomeTypeRep typeRep) ->
      vsep [ "Data associated with the pointer found, but was not a callable function:"
           , hang 2 (viaShow typeRep)
           ]

type MemErrContext sym w = MemoryOp sym w

ppGSym :: Maybe String -> [Doc ann]
ppGSym Nothing = []
ppGSym (Just nm) = [ "Global symbol", viaShow nm ]

ppMemoryOp :: IsExpr (SymExpr sym) => MemoryOp sym w -> Doc ann
ppMemoryOp (MemLoadOp tp gsym ptr mem)  =
  vsep [ "Performing overall load at type:" <+> ppType tp
       , indent 2 (hsep ([ "Via pointer:" ] ++ ppGSym gsym ++ [ ppPtr ptr ]))
       , "In memory state:"
       , indent 2 (ppMem mem)
       ]

ppMemoryOp (MemStoreOp tp gsym ptr mem) =
  vsep [ "Performing store at type:" <+> ppType tp
       , indent 2 (hsep ([ "Via pointer:" ] ++ ppGSym gsym ++ [ ppPtr ptr ]))
       , "In memory state:"
       , indent 2 (ppMem mem)
       ]

ppMemoryOp (MemStoreBytesOp gsym ptr Nothing mem) =
  vsep [ "Performing byte array store for entire address space"
       , indent 2 (hsep ([ "Via pointer:" ] ++ ppGSym gsym ++ [ ppPtr ptr ]))
       , "In memory state:"
       , indent 2 (ppMem mem)
       ]

ppMemoryOp (MemStoreBytesOp gsym ptr (Just len) mem) =
  vsep [ "Performing byte array store of length:" <+> printSymExpr len
       , indent 2 (hsep ([ "Via pointer:" ] ++ ppGSym gsym ++ [ ppPtr ptr ]))
       , "In memory state:"
       , indent 2 (ppMem mem)
       ]

ppMemoryOp (MemCopyOp (gsym_dest, dest) (gsym_src, src) len mem) =
  vsep [ "Performing a memory copy of" <+> printSymExpr len <+> "bytes"
       , indent 2 (hsep ([ "Destination:" ] ++ ppGSym gsym_dest ++ [ ppPtr dest ]))
       , indent 2 (hsep ([ "Source:     " ] ++ ppGSym gsym_src ++ [ ppPtr src ]))
       , "In memory state:"
       , indent 2 (ppMem mem)
       ]

ppMemoryOp (MemLoadHandleOp sig gsym ptr mem) =
  vsep [ case sig of
           Just s ->
             hsep ["Attempting to load callable function with type:", viaShow (LPP.ppType s)]
           Nothing ->
             hsep ["Attempting to load callable function:"]
       , indent 2 (hsep ([ "Via pointer:" ] ++ ppGSym gsym ++ [ ppPtr ptr ]))
       , "In memory state:"
       , indent 2 (ppMem mem)
       ]

ppMemoryOp (MemInvalidateOp msg gsym ptr len mem) =
  vsep [ "Performing explicit memory invalidation of" <+> printSymExpr len <+> "bytes"
       , pretty msg
       , indent 2 (hsep ([ "Via pointer:" ] ++ ppGSym gsym ++ [ ppPtr ptr ]))
       , "In memory state:"
       , indent 2 (ppMem mem)
       ]

explain :: IsExpr (SymExpr sym) => MemoryError sym -> Doc ann
explain (MemoryError _mop rsn) = ppMemoryErrorReason rsn

details :: IsExpr (SymExpr sym) => MemoryError sym -> Doc ann
details (MemoryError mop _rsn) = ppMemoryOp mop

ppMemoryError :: IsExpr (SymExpr sym) => MemoryError sym -> Doc ann
ppMemoryError (MemoryError mop rsn) = vcat [ppMemoryErrorReason rsn, ppMemoryOp mop]

ppMemoryErrorReason :: MemoryErrorReason -> Doc ann
ppMemoryErrorReason =
  \case
    TypeMismatch ty1 ty2 ->
      vcat
      [ "Type mismatch: "
      , indent 2 (vcat [ ppType ty1
                       , ppType ty2
                       ])
      ]
    UnexpectedArgumentType txt vals ->
      vcat
      [ "Unexpected argument type:"
      , pretty txt
      , indent 2 (vcat (map viaShow vals))
      ]
    ApplyViewFail vw ->
      "Failure when applying value view" <+> viaShow vw
    Invalid ty ->
      "Load from invalid memory at type" <+> ppType ty
    Invalidated msg ->
      "Load from explicitly invalidated memory:" <+> pretty msg
    NoSatisfyingWrite tp ->
      vcat
       [ "No previous write to this location was found"
       , indent 2 ("Attempting load at type:" <+> ppType tp)
       ]
    UnwritableRegion ->
      "The region wasn't allocated, wasn't large enough, or was marked as readonly"
    UnreadableRegion ->
      "The region wasn't allocated or wasn't large enough"
    BadFunctionPointer err ->
      vcat
       [ "The given pointer could not be resolved to a callable function"
       , ppFuncLookupError err
       ]
    OverlappingRegions ->
      "Memory regions required to be disjoint"

concMemoryError ::
  IsExprBuilder sym =>
  sym ->
  (forall tp. SymExpr sym tp -> IO (GroundValue tp)) ->
  MemoryError sym -> IO (MemoryError sym)
concMemoryError sym conc (MemoryError mop rsn) =
  MemoryError <$> concMemoryOp sym conc mop <*> pure rsn

concMemoryOp ::
  (1 <= w, IsExprBuilder sym) =>
  sym ->
  (forall tp. SymExpr sym tp -> IO (GroundValue tp)) ->
  MemoryOp sym w -> IO (MemoryOp sym w)
concMemoryOp sym conc (MemLoadOp tp gsym ptr mem) =
  MemLoadOp tp gsym <$> concPtr sym conc ptr <*> concMem sym conc mem
concMemoryOp sym conc (MemStoreOp tp gsym ptr mem) =
  MemStoreOp tp gsym <$> concPtr sym conc ptr <*> concMem sym conc mem
concMemoryOp sym conc (MemStoreBytesOp gsym ptr len mem) =
  MemStoreBytesOp gsym <$>
    concPtr sym conc ptr <*>
    traverse (concBV sym conc) len <*>
    concMem sym conc mem
concMemoryOp sym conc (MemLoadHandleOp tp gsym ptr mem) =
  MemLoadHandleOp tp gsym <$> concPtr sym conc ptr <*> concMem sym conc mem
concMemoryOp sym conc (MemCopyOp (gsym_dest, dest) (gsym_src, src) len mem) =
  do dest' <- concPtr sym conc dest
     src'  <- concPtr sym conc src
     len'  <- concBV sym conc len
     mem'  <- concMem sym conc mem
     pure (MemCopyOp (gsym_dest, dest') (gsym_src, src') len' mem')
concMemoryOp sym conc (MemInvalidateOp msg gsym ptr len mem) =
  MemInvalidateOp msg gsym <$>
    concPtr sym conc ptr <*>
    concBV sym conc len <*>
    concMem sym conc mem