| -- |
| -- 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''" |