blob: ed148f0e3366061c4fa5b61711c5370197f48077 [file] [log] [blame] [edit]
--
-- Copyright 2020, Data61, CSIRO (ABN 41 687 119 230)
--
-- SPDX-License-Identifier: BSD-2-Clause
--
module CapDL.STCC(
somefn
,transitiveClosure
,leakMatrix
) where
import CapDL.Matrix
import CapDL.Model
import Prelude ()
import Prelude.Compat
import Control.Monad.Compat
import Data.Map ( Map )
import qualified Data.Map as Map
import Data.Set ( Set )
import qualified Data.Set as Set
type ObjDictionary = Map ObjID Int
projectTCB capset =
Set.fromList [capObj | (TCBCap capObj) <- Set.toList capset]
projectCNode capset =
Set.fromList [capObj | (CNodeCap capObj _ _) <- Set.toList capset]
projectEndpoint capset =
Set.fromList $ concat [[(r, capObj) | r <- Set.toList rights] | (EndpointCap capObj _ rights) <- Set.toList capset]
projectNtfn capset =
Set.fromList $ concat [[(r, capObj) | r <- Set.toList rights] | (NotificationCap capObj _ rights) <- Set.toList capset]
projectFrame capset =
Set.fromList $ concat [[(r, capObj) | r <- Set.toList rights] | (FrameCap capObj rights Nothing True Nothing) <- Set.toList capset]
somefn :: Model Word -> IO ()
somefn (Model _ objects _ _ _) =
do matrix <- newEmptyMatrix $ length tcbIDs
leaksTos matrix objDict tcbDirectCaps
warshall matrix
_ <- saturateCSpaces matrix objDict cspaces
return ()
where
tcbIDs = [objID | (objID, (TCB {})) <- Map.toList objects]
cspaces = zip tcbIDs $ map (getCSpace objects) tcbIDs
tcbDirectCaps = directTCBConnections cspaces
objDict = Map.fromList (zip tcbIDs [0..])
transitiveClosure :: Model Word -> IO (Model Word)
transitiveClosure model@(Model _ objects _ _ _) =
do matrix <- newEmptyMatrix $ length tcbIDs
leaksTos matrix objDict tcbDirectCaps
warshall matrix
newcspaces <- saturateCSpaces matrix objDict cspaces
let cspaceUpdates = zipWith f newcspaces cspaces
newModel = updateModel model cspaceUpdates
return newModel
where
tcbIDs = [objID | (objID, (TCB {})) <- Map.toList objects]
cspaces = zip tcbIDs $ map (getCSpace objects) tcbIDs
tcbDirectCaps = directTCBConnections cspaces
objDict = Map.fromList (zip tcbIDs [0..])
f (objID, cspace) (_, cspace') = (objID, cspace' `Set.difference` cspace)
leakMatrix :: Model Word -> IO (String, String, Model Word)
leakMatrix model@(Model _ objects _ _ _) =
do authMatrix <- newEmptyMatrix $ mSize
leaksTos authMatrix objDict tcbDirectCaps
warshall authMatrix
newcspaces <- saturateCSpaces authMatrix objDict cspaces
let cspaceUpdates = zipWith f newcspaces cspaces
newModel = updateModel model cspaceUpdates
infoMatrix <- newEmptyMatrix $ mSize
authSharingFlows authMatrix infoMatrix
flowsTos infoMatrix objDict tcbDirectFlows
warshall infoMatrix
removeReflexive infoMatrix
leakDot <- showDotMatrix authMatrix objDict'
flowDot <- showDotMatrix infoMatrix objDict'
return (leakDot, flowDot, newModel)
where
f (objID, cspace) (_, cspace') = (objID, cspace' `Set.difference` cspace)
sndUnion (tcbID, set) (_, set') = (tcbID, Set.union set set')
tcbIDs = [objID | (objID, (TCB {})) <- Map.toList objects]
mSize = length tcbIDs
cspaces = [(tcbID, Set.fromList $ getCaps objects tcbID) | tcbID <- tcbIDs]
vspaces = [(tcbID, Set.fromList $ getFrames objects tcbID) | tcbID <- tcbIDs]
tcbDirectCaps = directTCBConnections cspaces
tcbDirectFlows = directTCBFlows (zipWith sndUnion vspaces cspaces)
objDict = Map.fromList (zip tcbIDs [0..])
objDict' = Map.fromList (zip [0..] tcbIDs)
updateModel :: Model Word -> [(ObjID, Set Cap)] -> Model Word
updateModel (Model arch objects irqNode cdt untypedCovers) tcbs =
Model arch (foldr f objects tcbs) irqNode cdt untypedCovers
where
f (tcb, cspace) objects =
let rootCNodeID = rootCNodeOf objects tcb
e = error "cannot find root CNode in updateModel"
(CNode slots x extraInfo) = Map.findWithDefault e rootCNodeID objects
maxKey = maximum $ Map.keys $ slots
newKeys = map (maxKey +) [1..]
newSlots = foldr (\(k,a) -> Map.insert k a) slots (zip newKeys $ Set.toList cspace)
in Map.insert rootCNodeID (CNode newSlots x extraInfo) objects
rootCNodeOf objects tcb =
let e = error "cannot find tcbID in updateModel" in
case (Map.findWithDefault e tcb objects) of
(TCB slots _ _ _ _) -> head [capObj | (CNodeCap capObj _ _) <- (map snd (Map.toList slots))]
_ -> error "non-tcb ID in updateModel"
saturateCSpaces :: Matrix -> ObjDictionary -> [(ObjID, Set Cap)] -> IO [(ObjID, Set Cap)]
saturateCSpaces _ _ [] = return []
saturateCSpaces matrix objDict (tcb : tcbs) =
do head <- foldM f tcb tcbs
newtail <- mapM ((flip f) tcb) tcbs
rest <- saturateCSpaces matrix objDict newtail
return $ head : rest
where
f (tcbID, cspace) (tcbID', cspace') =
do
b <- isLeak matrix objDict tcbID' tcbID
if b
then
let acc = cspace `Set.union` cspace' in acc `seq` return (tcbID, acc)
else
return (tcbID, cspace)
directTCBConnections :: [(ObjID, Set Cap)] -> [(ObjID, ObjID)]
directTCBConnections [] = []
directTCBConnections ((tcbID, cspace) : tcbs) =
foldr f [] tcbs ++ directTCBConnections tcbs
where
f (tcbID', cspace') acc =
acc
++ let endpointSet = projectEndpoint cspace
endpointSet' = projectEndpoint cspace'
shareCSpace = sharesCSpace (projectCNode cspace) (projectCNode cspace')
haveTCB = tcbID' `Set.member` projectTCB cspace
hadByTCB = tcbID `Set.member` projectTCB cspace'
outEndpointCon = grantReadOverlap endpointSet endpointSet'
inEndpointCon = grantReadOverlap endpointSet' endpointSet
outCon = shareCSpace || haveTCB || outEndpointCon
inCon = shareCSpace || hadByTCB || inEndpointCon
in case (inCon,outCon) of
(False, False) -> []
(False, True) -> [(tcbID, tcbID')]
(True, False) -> [(tcbID', tcbID)]
(True, True) -> [(tcbID, tcbID'), (tcbID', tcbID)]
directTCBFlows :: [(ObjID, Set Cap)] -> [(ObjID, ObjID)]
directTCBFlows [] = []
directTCBFlows ((tcbID, capSet) : tcbs) =
foldr f [] tcbs ++ directTCBFlows tcbs
where
f (tcbID', capSet') acc =
acc
++ let endpointSet = projectEndpoint capSet
endpointSet' = projectEndpoint capSet'
frameSet = projectFrame capSet
frameSet' = projectFrame capSet'
ntfnSet = projectNtfn capSet
ntfnSet' = projectNtfn capSet'
endpointFlow = writeReadOverlap endpointSet endpointSet'
|| writeReadOverlap endpointSet' endpointSet
frameFlowOut = writeReadOverlap frameSet frameSet'
frameFlowIn = writeReadOverlap frameSet' frameSet
ntfnFlowOut = writeReadOverlap ntfnSet ntfnSet'
ntfnFlowIn = writeReadOverlap ntfnSet' ntfnSet
outCon = endpointFlow || frameFlowOut || ntfnFlowOut
inCon = endpointFlow || frameFlowIn || ntfnFlowIn
in case (inCon,outCon) of
(False, False) -> []
(False, True) -> [(tcbID, tcbID')]
(True, False) -> [(tcbID', tcbID)]
(True, True) -> [(tcbID, tcbID'), (tcbID', tcbID)]
{- True if the first set of rights contains a grant to an object
and the second set of rights contains a read to that object -}
grantReadOverlap :: Set (Rights, ObjID) -> Set (Rights, ObjID) -> Bool
grantReadOverlap cSet cSet' =
let grants = Set.fromList [objID | (Grant, objID) <- Set.toList cSet]
reads = Set.fromList [objID | (Read, objID) <- Set.toList cSet']
in not $ Set.null $ grants `Set.intersection` reads
{- True if the first set of rights contains a write to an object
and the second set of rights contains a read to that object -}
writeReadOverlap :: Set (Rights, ObjID) -> Set (Rights, ObjID) -> Bool
writeReadOverlap cSet cSet' =
let writes = Set.fromList [objID | (Write, objID) <- Set.toList cSet]
reads = Set.fromList [objID | (Read, objID) <- Set.toList cSet']
in not $ Set.null $ writes `Set.intersection` reads
{- True if two sets of CNode objIDs intersect -}
sharesCSpace :: Set ObjID -> Set ObjID -> Bool
sharesCSpace cspace1 cspace2 = not $ Set.null $ cspace1 `Set.intersection` cspace2
warshall :: Matrix -> IO ()
warshall matrix =
do ((min, _), (max, _)) <- getSize matrix
forM_ [min..max] $ \k ->
forM_ [min..max] $ \i ->
forM_ [min..max] $ \j ->
do mij <- isConnected matrix i j
mik <- isConnected matrix i k
mkj <- isConnected matrix k j
when (mij || (mik && mkj)) $ connect matrix i j
authSharingFlows :: Matrix -> Matrix -> IO ()
authSharingFlows fromMatrix toMatrix =
do ((min, _), (max, _)) <- getSize fromMatrix
forM_ [min..max] $ \i ->
forM_ [min..max] $ \j ->
do x <- isConnected fromMatrix i j
when x $ connect toMatrix i j >> connect toMatrix j i
removeReflexive :: Matrix -> IO ()
removeReflexive matrix =
do ((min, _), (max, _)) <- getSize matrix
forM_ [min..max] $ \i ->
disconnect matrix i i
flowsTos = leaksTos
leaksTos matrix objDict leaks =
forM_ leaks $ \(objID, objID') ->
leaksTo matrix objDict objID objID'
{- Write to matrix that objID leaks to objID' -}
leaksTo matrix objDict objID objID' = connect matrix (indexOf objID) (indexOf objID')
where
indexOf x =
Map.findWithDefault
(error $ "died in leaksTo trying to find key " ++ show x)
x objDict
isLeak matrix objDict objID objID' = isConnected matrix (indexOf objID) (indexOf objID')
where
indexOf x = Map.findWithDefault
(error $ "died in isLeak trying to find key " ++ show x)
x objDict
getCSpace :: ObjMap Word -> ObjID -> Set Cap
getCSpace objects tcbID = Set.fromList $ getCaps objects tcbID
{- Returns all the caps in a TCB's CSpace -}
getCaps :: ObjMap Word -> ObjID -> [Cap]
getCaps objects tcbID =
case Map.findWithDefault e1 tcbID objects of
TCB slots _ _ _ _ ->
let cNodeCaps = [cap | (_, cap@(CNodeCap {})) <- Map.toList slots]
in concat $ map (getCaps' objects Set.empty) cNodeCaps
_ -> e2
where
e1 = error "cannot find tcbID in cnodesFrom"
e2 = error "objID given in cnodesFrom is not TCB"
getCaps' :: ObjMap Word -> Set ObjID -> Cap -> [Cap]
getCaps' objects visited cap =
case cap of
CNodeCap {}
| capObj cap `Set.member` visited -> []
| otherwise -> cap : getCaps'' objects visited (capObj cap)
_ -> [cap]
getCaps'' :: ObjMap Word -> Set ObjID -> ObjID -> [Cap]
getCaps'' objects visited cnodeID
| cnodeID `Set.member` visited = []
| otherwise =
case Map.findWithDefault e cnodeID objects of
CNode slots _ _ ->
let caps = map snd $ Map.toList slots
newVisited = cnodeID `Set.insert` visited
in concat $ map (getCaps' objects newVisited) caps
kObj@(_) -> error ("non-CNode objID passed into getCaps'': " ++ show kObj)
where
e = error "could not find rootID in objects map in cSpaceFrom'"
getFrames :: ObjMap Word -> ObjID -> [Cap]
getFrames objects tcbID =
case Map.findWithDefault e1 tcbID objects of
TCB slots _ _ _ _ ->
let pdCaps = [cap | (_, cap@(PDCap _ _)) <- Map.toList slots]
in concat $ map (getFrames' objects Set.empty) pdCaps
_ -> e2
where
e1 = error "cannot find tcbID in getFrames"
e2 = error "object ID given in getFrames is not TCB"
getFrames' :: ObjMap Word -> Set ObjID -> Cap -> [Cap]
getFrames' objects visited cap =
case cap of
PDCap capObj _
| capObj `Set.member` visited -> []
| otherwise -> cap : getFrames'' objects visited capObj
PTCap capObj _
| capObj `Set.member` visited -> []
| otherwise -> cap : getFrames'' objects visited capObj
FrameCap _ _ _ _ _ ->
[cap]
_ -> []
getFrames'' :: ObjMap Word -> Set ObjID -> ObjID -> [Cap]
getFrames'' objects visited objID
| objID `Set.member` visited = []
| otherwise =
case Map.findWithDefault e objID objects of
PD slots ->
let caps = map snd $ Map.toList slots
newVisited = objID `Set.insert` visited
in concat $ map (getFrames' objects newVisited) caps
PT slots ->
let caps = map snd $ Map.toList slots
newVisited = objID `Set.insert` visited
in concat $ map (getFrames' objects newVisited) caps
_ -> []
where
e = error "could not find objID in objects map in getFrames''"