-
Notifications
You must be signed in to change notification settings - Fork 36
Expand file tree
/
Copy pathCILState.fs
More file actions
383 lines (324 loc) · 15.7 KB
/
CILState.fs
File metadata and controls
383 lines (324 loc) · 15.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
namespace VSharp.Interpreter.IL
open FSharpx.Collections
open VSharp
open System.Text
open System.Collections.Generic
open VSharp.Core
open VSharp.Interpreter.IL
open ipOperations
type CallStackEvent =
| Push of callFrom: codeLocation * callTo: codeLocation
| Pop
[<ReferenceEquality>]
type cilState =
{ mutable ipStack : ipStack
// TODO: get rid of currentLoc!
mutable currentLoc : codeLocation // This field stores only approximate information and can't be used for getting the precise location. Instead, use ipStack.Head
state : state
mutable filterResult : term option
//TODO: #mb frames list #mb transfer to Core.State
mutable iie : InsufficientInformationException option
mutable level : level
mutable startingIP : ip
mutable initialEvaluationStackSize : uint32
mutable stepsNumber : uint
mutable suspended : bool
mutable targets : Set<codeLocation> option
mutable lastPushInfo : term option
mutable lastCallStackEvents : CallStackEvent FSharpx.Collections.Queue
/// <summary>
/// All basic blocks visited by the state.
/// </summary>
mutable history : Set<codeLocation>
/// <summary>
/// If the state is not isolated (produced during forward execution), Some of it's entry point method, else None.
/// </summary>
entryMethod : Method option
/// <summary>
/// Deterministic state id.
/// </summary>
id : uint
}
with
member x.Result with get() =
// assert(Memory.CallStackSize x.state = 1)
match EvaluationStack.Length x.state.evaluationStack with
| _ when Memory.CallStackSize x.state > 2 -> internalfail "Finished state has many frames on stack! (possibly unhandled exception)"
| 0 -> Nop
| 1 ->
let result = EvaluationStack.Pop x.state.evaluationStack |> fst
match x.ipStack with
| [Exit m] -> Types.Cast result m.ReturnType
| _ when x.state.exceptionsRegister.UnhandledError -> Nop
| _ -> internalfailf "Method is not finished! IpStack = %O" x.ipStack
| _ -> internalfail "EvaluationStack size was bigger than 1"
interface IGraphTrackableState with
override this.CodeLocation = this.currentLoc
override this.CallStack = Memory.StackTrace this.state.stack |> List.map (fun m -> m :?> Method)
type cilStateComparer(comparer) =
interface IComparer<cilState> with
override _.Compare(x : cilState, y : cilState) =
comparer x y
module internal CilStateOperations =
let mutable currentStateId = 0u
let getNextStateId() =
let nextId = currentStateId
currentStateId <- currentStateId + 1u
nextId
let makeCilState entryMethod curV initialEvaluationStackSize state =
{ ipStack = [curV]
currentLoc = ip2codeLocation curV |> Option.get
state = state
filterResult = None
iie = None
level = PersistentDict.empty
startingIP = curV
initialEvaluationStackSize = initialEvaluationStackSize
stepsNumber = 0u
suspended = false
targets = None
lastPushInfo = None
lastCallStackEvents = Queue.empty
history = Set.empty
entryMethod = Some entryMethod
id = getNextStateId()
}
let makeInitialState m state = makeCilState m (instruction m 0<offsets>) 0u state
let mkCilStateHashComparer = cilStateComparer (fun a b -> a.GetHashCode().CompareTo(b.GetHashCode()))
let isIsolated state = state.entryMethod.IsNone
let entryMethodOf state =
if isIsolated state then
invalidOp "Isolated state doesn't have an entry method"
state.entryMethod.Value
let isIIEState (s : cilState) = Option.isSome s.iie
let isExecutable (s : cilState) =
match s.ipStack with
| [] -> __unreachable__()
| Exit _ :: [] -> false
| _ -> true
let isError (s : cilState) =
match s.state.exceptionsRegister with
| NoException -> false
| _ -> true
let isUnhandledError (s : cilState) =
match s.state.exceptionsRegister with
| Unhandled _ -> true
| _ -> false
let levelToUnsignedInt (lvl : level) = PersistentDict.fold (fun acc _ v -> max acc v) 0u lvl //TODO: remove it when ``level'' subtraction would be generalized
let currentIp (s : cilState) =
match s.ipStack with
| [] -> __unreachable__()
| h::_ -> h
// List.head s.ipStack
let stoppedByException (s : cilState) =
match currentIp s with
| SearchingForHandler([], []) -> true
| _ -> false
let hasRuntimeException (s : cilState) =
match s.state.exceptionsRegister with
| Unhandled(_, isRuntime) -> isRuntime
| _ -> false
let isStopped s = isIIEState s || stoppedByException s || not(isExecutable(s))
let tryCurrentLoc = currentIp >> ip2codeLocation
let currentLoc = tryCurrentLoc >> Option.get
let startingLoc (s : cilState) = s.startingIP |> ip2codeLocation |> Option.get
let violatesLevel (s : cilState) maxBound =
match tryCurrentLoc s with
| Some currLoc when PersistentDict.contains currLoc s.level ->
s.level.[currLoc] >= maxBound
| _ -> false
let methodOf = function
| Exit m
| Instruction(_, m)
| Leave(_, _, _, m) -> m
| _ -> __notImplemented__()
let offsetOf = function
| Instruction(offset, _) -> Some offset
| Exit _
| Leave _
| SearchingForHandler _ -> None
| ip -> internalfailf "offsetOf: unexpected ip %O" ip
// [NOTE] Obtaining exploring method
let currentMethod = currentIp >> methodOf
let currentOffset = currentIp >> offsetOf
let startsFromMethodBeginning (s : cilState) =
match s.startingIP with
| Instruction (0<offsets>, _) -> true
| _ -> false
let private moveCodeLoc (cilState : cilState) (ip : ip) =
match ip2codeLocation ip with
| Some loc when loc.method.HasBody -> cilState.currentLoc <- loc
| _ -> ()
let pushToIp (ip : ip) (cilState : cilState) =
let loc = cilState.currentLoc
match ip2codeLocation ip with
| Some loc' when loc'.method.HasBody ->
cilState.currentLoc <- loc'
Application.addCallEdge loc loc'
cilState.lastCallStackEvents <- Queue.conj (Push(loc, loc')) cilState.lastCallStackEvents
| _ -> ()
cilState.ipStack <- ip :: cilState.ipStack
let setCurrentIp (ip : ip) (cilState : cilState) =
moveCodeLoc cilState ip
cilState.ipStack <- ip :: List.tail cilState.ipStack
let setIpStack (ipStack : ipStack) (cilState : cilState) = cilState.ipStack <- ipStack
let startingIpOf (cilState : cilState) = cilState.startingIP
let composeIps (oldIpStack : ipStack) (newIpStack : ipStack) = newIpStack @ oldIpStack
let composeLevel (lvl1 : level) (lvl2 : level) =
let composeOne (lvl : level) k v =
let oldValue = PersistentDict.tryFind lvl k |> Option.defaultValue 0u
PersistentDict.add k (v + oldValue) lvl
PersistentDict.fold composeOne lvl1 lvl2
let compose (cilState1 : cilState) (cilState2 : cilState) =
assert(currentIp cilState1 = cilState2.startingIP)
let level =
PersistentDict.fold (fun (acc : level) k v ->
let oldValue = if PersistentDict.contains k acc then PersistentDict.find acc k else 0u
PersistentDict.add k (v + oldValue) acc
) cilState1.level cilState2.level
let iie = None // we might concretize state, so we should try executed instructions again
let ip = composeIps (List.tail cilState1.ipStack) cilState2.ipStack
let states = Memory.ComposeStates cilState1.state cilState2.state
let _, leftEvaluationStack = EvaluationStack.PopMany (int cilState2.initialEvaluationStackSize) cilState1.state.evaluationStack
let makeResultState (state : state) =
let state' = { state with evaluationStack = EvaluationStack.Union leftEvaluationStack state.evaluationStack }
{cilState2 with state = state'; ipStack = ip; level = level; initialEvaluationStackSize = cilState1.initialEvaluationStackSize
startingIP = cilState1.startingIP; iie = iie; id = getNextStateId()}
List.map makeResultState states
let incrementLevel (cilState : cilState) k =
let lvl = cilState.level
let oldValue = PersistentDict.tryFind lvl k |> Option.defaultValue 0u
cilState.level <- PersistentDict.add k (oldValue + 1u) lvl
let decrementLevel (cilState : cilState) k =
let lvl = cilState.level
let oldValue = PersistentDict.tryFind lvl k
match oldValue with
| Some value when value > 0u -> cilState.level <- PersistentDict.add k (value - 1u) lvl
| _ -> ()
let clearLastCallStackEvents (cilState : cilState) =
cilState.lastCallStackEvents <- Queue.empty
let setBasicBlockIsVisited (cilState : cilState) (loc : codeLocation) =
cilState.history <- Set.add loc cilState.history
// ------------------------------- Helper functions for cilState and state interaction -------------------------------
let stateOf (cilState : cilState) = cilState.state
let popFrameOf (cilState : cilState) =
Memory.PopFrame cilState.state
let ip = List.tail cilState.ipStack
cilState.ipStack <- ip
cilState.lastCallStackEvents <- Queue.conj Pop cilState.lastCallStackEvents
match ip with
| ip::_ -> moveCodeLoc cilState ip
| [] -> ()
let setCurrentTime time (cilState : cilState) = cilState.state.currentTime <- time
let setEvaluationStack evaluationStack (cilState : cilState) = cilState.state.evaluationStack <- evaluationStack
let clearEvaluationStackLastFrame (cilState : cilState) =
cilState.state.evaluationStack <- EvaluationStack.ClearActiveFrame cilState.state.evaluationStack
// TODO: Not mutable -- copies cilState #do
let changeState (cilState : cilState) state =
if LanguagePrimitives.PhysicalEquality state cilState.state then cilState
else {cilState with state = state; id = getNextStateId()}
let setException exc (cilState : cilState) = cilState.state.exceptionsRegister <- exc
let push v (cilState : cilState) =
cilState.state.evaluationStack <- EvaluationStack.Push v cilState.state.evaluationStack
cilState.lastPushInfo <- Some v
let pushMany vs (cilState : cilState) = cilState.state.evaluationStack <- EvaluationStack.PushMany vs cilState.state.evaluationStack
let peek (cilState : cilState) =
EvaluationStack.Pop cilState.state.evaluationStack |> fst
let peek2 (cilState : cilState) =
let stack = cilState.state.evaluationStack
let arg2, stack = EvaluationStack.Pop stack
let arg1, _ = EvaluationStack.Pop stack
arg2, arg1
let pop (cilState : cilState) =
let v, evaluationStack = EvaluationStack.Pop cilState.state.evaluationStack
cilState.state.evaluationStack <- evaluationStack
v
let pop2 (cilState : cilState) =
let arg2 = pop cilState
let arg1 = pop cilState
arg2, arg1
let pop3 (cilState : cilState) =
let arg3 = pop cilState
let arg2 = pop cilState
let arg1 = pop cilState
arg3, arg2, arg1
let pushNewObjForValueTypes (afterCall : cilState) =
let ref = pop afterCall
let value = Memory.Read afterCall.state ref
push value afterCall
let addTarget (state : cilState) target =
match state.targets with
| Some targets -> state.targets <- Some <| Set.add target targets
| None -> state.targets <- Some (Set.add target Set.empty)
let removeTarget (state : cilState) target =
match state.targets with
| Some targets ->
let newTargets = Set.remove target targets
if newTargets.Count = 0 then
state.targets <- None
else
state.targets <- Some <| Set.remove target targets
| None -> ()
let checkTargets (state : cilState) =
match state.targets with
| Some targets -> targets.Count <> 0
| None -> true
// ------------------------------- Helper functions for cilState -------------------------------
let moveIp offset (m : Method) cilState =
assert m.HasBody
let opCode = MethodBody.parseInstruction m offset
let newIps =
let nextTargets = MethodBody.findNextInstructionOffsetAndEdges opCode m.ILBytes offset
match nextTargets with
| UnconditionalBranch nextInstruction
| FallThrough nextInstruction -> instruction m nextInstruction :: []
| Return -> exit m :: []
| ExceptionMechanism ->
// TODO: use ExceptionMechanism? #do
// let toObserve = __notImplemented__()
// searchingForHandler toObserve 0 :: []
__notImplemented__()
| ConditionalBranch (fall, targets) -> fall :: targets |> List.map (instruction m)
List.map (fun ip -> setCurrentIp ip cilState) newIps
let GuardedApplyCIL (cilState : cilState) term (f : cilState -> term -> ('a list -> 'b) -> 'b) (k : 'a list -> 'b) =
let mkCilState state =
if LanguagePrimitives.PhysicalEquality state cilState.state then cilState
else {cilState with state = state; id = getNextStateId()}
GuardedStatedApplyk
(fun state term k -> f (mkCilState state) term k)
cilState.state term id (List.concat >> k)
let StatedConditionalExecutionCIL (cilState : cilState) conditionInvocation thenBranch elseBranch k =
let origCilState = {cilState with state = cilState.state}
let mkCilState state =
if LanguagePrimitives.PhysicalEquality state cilState.state then cilState
else {origCilState with state = state; id = getNextStateId()}
StatedConditionalExecution cilState.state conditionInvocation
(fun state k -> thenBranch (mkCilState state) k)
(fun state k -> elseBranch (mkCilState state) k)
(fun x y -> [x; y])
(List.concat >> k)
let BranchOnNullCIL (cilState : cilState) term thenBranch elseBranch k =
StatedConditionalExecutionCIL cilState
(fun state k -> k (IsNullReference term, state))
thenBranch
elseBranch
k
// ------------------------------- Pretty printing for cilState -------------------------------
let private dumpSectionValue section value (sb : StringBuilder) =
let sb = Utils.PrettyPrinting.dumpSection section sb
Utils.PrettyPrinting.appendLine sb value
let private dumpIp (ipStack : ipStack) =
List.fold (fun acc entry -> sprintf "%s\n%O" acc entry) "" ipStack
let ipAndMethodBase2String (codeLocation : codeLocation) =
sprintf "Method: %O, offset = %d" codeLocation.method codeLocation.offset
// TODO: print filterResult and IIE ?
let dump (cilState : cilState) : string =
let sb = (StringBuilder())
let sb = dumpSectionValue "Starting ip" (sprintf "%O" cilState.startingIP) sb
let sb = dumpSectionValue "IP" (dumpIp cilState.ipStack) sb
let sb = dumpSectionValue "IIE" (sprintf "%O" cilState.iie) sb
let sb = dumpSectionValue "Initial EvaluationStack Size" (sprintf "%O" cilState.initialEvaluationStackSize) sb
let sb = Utils.PrettyPrinting.dumpDict "Level" id ipAndMethodBase2String id sb cilState.level
let stateDump = Print.Dump cilState.state
let sb = dumpSectionValue "State" stateDump sb
if sb.Length = 0 then "<EmptyCilState>" else sb.ToString()