-
Notifications
You must be signed in to change notification settings - Fork 2k
Expand file tree
/
Copy pathFlowVar.qll
More file actions
881 lines (800 loc) · 30.6 KB
/
FlowVar.qll
File metadata and controls
881 lines (800 loc) · 30.6 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
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
/**
* DEPRECATED: Use `semmle.code.cpp.dataflow.new.DataFlow` instead.
*
* Provides a class for handling variables in the data flow analysis.
*/
import cpp
private import semmle.code.cpp.controlflow.SSA
private import SubBasicBlocks
private import AddressFlow
private import semmle.code.cpp.models.implementations.Iterator
private import semmle.code.cpp.models.interfaces.PointerWrapper
/**
* A conceptual variable that is assigned only once, like an SSA variable. This
* class is used for tracking data flow through variables, where the desired
* semantics is sometimes different from what the SSA library provides. Unlike
* SSA, there are no _phi_ nodes; instead, each `VariableAccess` may be
* associated with more than one `FlowVar`.
*
* Each instance of this class corresponds to a modification or an initial
* value of a variable. A `FlowVar` has exactly one result for either
* `definedByExpr` or `definedByInitialValue`. The documentation on those two
* member predicates explains how a `FlowVar` relates to syntactic constructs of
* the language.
*/
cached
class FlowVar extends TFlowVar {
/**
* Gets a `VariableAccess` that _may_ take its value from `this`. Consider
* the following snippet.
*
* ```
* int x = 0;
* if (b) { f(x); x = 1; }
* g(x)
* ```
*
* The access to `x` in `f(x)` may take its value from the `FlowVar`
* corresponding to `int x = 0`, while the access to `x` in `g(x)` may take
* its value from the `FlowVar` corresponding to `int x = 0` or the `FlowVar`
* corresponding to `x = 1`. The `x` in `x = 1` is not considered to be an
* access.
*/
cached
abstract VariableAccess getAnAccess();
/**
* Holds if this `FlowVar` corresponds to a modification occurring when `node` is
* evaluated, receiving a value best described by `e`. The following is an
* exhaustive list of cases where this may happen.
*
* - `node` is an `Initializer` and `e` is its contained expression.
* - `node` is an `AssignExpr`, and `e` is its right-hand side.
* - `node` is an `AssignOperation`, and `e` is `node`.
* - `node` is a `CrementOperation`, and `e` is `node`. The case where
* `node instanceof PostCrementOperation` is an exception to the rule that
* `this` contains the value of `e` after the evaluation of `node`.
*/
cached
abstract predicate definedByExpr(Expr e, ControlFlowNode node);
/**
* Holds if this `FlowVar` is a `PartialDefinition` whose outer defined
* expression is `e`. For example, in `f(&x)`, the outer defined expression
* is `&x`.
*/
cached
abstract predicate definedPartiallyAt(Expr e);
/**
* Holds if this `FlowVar` is a definition of a reference parameter `p` that
* persists until the function returns.
*/
cached
abstract predicate reachesRefParameter(Parameter p);
/**
* Holds if this `FlowVar` corresponds to the initial value of `v`. The following
* is an exhaustive list of cases where this may happen.
*
* - `v` is a parameter, and `this` contains the value of the parameter at
* the entry point of its function body.
* - `v` is an uninitialized local variable, and `v` contains its (arbitrary)
* value before it is reassigned. If it can be statically determined that a
* local variable is always overwritten before it is used, there is no
* `FlowVar` instance for the uninitialized value of that variable.
*/
cached
abstract predicate definedByInitialValue(StackVariable v);
/** Gets a textual representation of this element. */
cached
abstract string toString();
/** Gets the location of this element. */
cached
abstract Location getLocation();
}
/**
* Provides classes and predicates dealing with "partial definitions".
*
* In contrast to a normal "definition", which provides a new value for
* something, a partial definition is an expression that may affect a
* value, but does not necessarily replace it entirely. For example:
* ```
* x.y = 1; // a partial definition of the object `x`.
* x.y.z = 1; // a partial definition of the objects `x` and `x.y`.
* x.setY(1); // a partial definition of the object `x`.
* setY(&x); // a partial definition of the object `x`.
* ```
*/
private module PartialDefinitions {
abstract class PartialDefinition extends Expr {
ControlFlowNode node;
/**
* Gets the subBasicBlock where this `PartialDefinition` is defined.
*/
ControlFlowNode getSubBasicBlockStart() { result = node }
/**
* Holds if this `PartialDefinition` defines variable `v` at control-flow
* node `cfn`.
*/
// does this work with a dispred?
pragma[noinline]
abstract predicate partiallyDefinesVariableAt(Variable v, ControlFlowNode cfn);
/**
* Holds if this partial definition may modify `inner` (or what it points
* to) through `outer`. These expressions will never be `Conversion`s.
*
* For example, in `f(& (*a).x)`, there are two results:
* - `inner` = `... .x`, `outer` = `&...`
* - `inner` = `a`, `outer` = `*`
*/
abstract predicate definesExpressions(Expr inner, Expr outer);
/**
* Gets the location of this element, adjusted to avoid unknown locations
* on compiler-generated `ThisExpr`s.
*/
Location getActualLocation() {
not exists(this.getLocation()) and result = this.getParent().getLocation()
or
this.getLocation() instanceof UnknownLocation and
result = this.getParent().getLocation()
or
result = this.getLocation() and not result instanceof UnknownLocation
}
}
class IteratorPartialDefinition extends PartialDefinition {
Variable collection;
Expr innerDefinedExpr;
IteratorPartialDefinition() {
innerDefinedExpr = getInnerDefinedExpr(this, node) and
(
innerDefinedExpr.(Call).getQualifier() = getAnIteratorAccess(collection)
or
innerDefinedExpr.(Call).getQualifier() = collection.getAnAccess() and
collection instanceof IteratorParameter
) and
innerDefinedExpr.(Call).getTarget() instanceof IteratorPointerDereferenceMemberOperator
or
// iterators passed by value without a copy constructor
exists(Call call |
call = node and
call.getAnArgument() = innerDefinedExpr and
innerDefinedExpr = this and
this = getAnIteratorAccess(collection) and
not call.getTarget() instanceof IteratorPointerDereferenceMemberOperator
)
or
// iterators passed by value with a copy constructor
exists(Call call, ConstructorCall copy |
copy.getTarget() instanceof CopyConstructor and
call = node and
call.getAnArgument() = copy and
copy.getArgument(0) = getAnIteratorAccess(collection) and
innerDefinedExpr = this and
this = copy and
not call.getTarget() instanceof IteratorPointerDereferenceMemberOperator
)
}
override predicate definesExpressions(Expr inner, Expr outer) {
inner = innerDefinedExpr and
outer = this
}
override predicate partiallyDefinesVariableAt(Variable v, ControlFlowNode cfn) {
v = collection and
cfn = node
}
}
private Expr getInnerDefinedExpr(Expr e, ControlFlowNode node) {
not e instanceof Conversion and
exists(Expr convertedInner |
valueToUpdate(convertedInner, e.getFullyConverted(), node) and
result = convertedInner.getUnconverted()
)
}
class VariablePartialDefinition extends PartialDefinition {
Expr innerDefinedExpr;
VariablePartialDefinition() { innerDefinedExpr = getInnerDefinedExpr(this, node) }
/**
* Holds if this partial definition may modify `inner` (or what it points
* to) through `outer`. These expressions will never be `Conversion`s.
*
* For example, in `f(& (*a).x)`, there are two results:
* - `inner` = `... .x`, `outer` = `&...`
* - `inner` = `a`, `outer` = `*`
*/
override predicate definesExpressions(Expr inner, Expr outer) {
inner = innerDefinedExpr and
outer = this
}
override predicate partiallyDefinesVariableAt(Variable v, ControlFlowNode cfn) {
innerDefinedExpr = v.getAnAccess() and
cfn = node
}
}
/**
* A partial definition that's a definition via an output iterator.
*/
class DefinitionByIterator extends IteratorPartialDefinition {
DefinitionByIterator() { exists(Call c | this = c.getAnArgument() or this = c.getQualifier()) }
}
/**
* A partial definition that's a definition by reference.
*/
class DefinitionByReference extends VariablePartialDefinition {
DefinitionByReference() { exists(Call c | this = c.getAnArgument() or this = c.getQualifier()) }
}
}
import PartialDefinitions
private import FlowVar_internal
/**
* Provides classes and predicates that ought to be private but cannot use the
* `private` annotation because they may be referred to by unit tests.
*/
module FlowVar_internal {
/**
* For various reasons, not all variables handled perfectly by the SSA library.
* Ideally, this predicate should become larger as the SSA library improves.
* Before we can remove the `BlockVar` class completely, the SSA library needs
* the following improvements.
* - Considering uninitialized local variables to be definitions.
* - Supporting fields, globals and statics like the Java SSA library does.
* - Supporting all local variables, even if their address is taken by
* address-of, reference assignments, or lambdas.
* - Understanding that assignment to a field of a local struct is a
* definition of the struct but not a complete overwrite. This is what the
* IR library uses chi nodes for.
*/
predicate fullySupportedSsaVariable(Variable v) {
v = any(SsaDefinition def).getAVariable() and
// A partially-defined variable is handled using the partial definitions logic.
not any(PartialDefinition p).partiallyDefinesVariableAt(v, _) and
// SSA variables do not exist before their first assignment, but one
// feature of this data flow library is to track where uninitialized data
// ends up.
not mayBeUsedUninitialized(v, _) and
// If `v` may be a variable that is always overwritten in a loop that
// always executes at least once, we give it special treatment in
// `BlockVar`, somewhat analogous to unrolling the first iteration of the
// loop.
not exists(AlwaysTrueUponEntryLoop loop | loop.alwaysAssignsBeforeLeavingCondition(_, _, v)) and
// The SSA library has a theoretically accurate treatment of reference types,
// treating them as immutable, but for data flow it gives better results in
// practice to make the variable synonymous with its contents.
not v.getUnspecifiedType() instanceof ReferenceType and
not v instanceof IteratorParameter and
not v instanceof PointerWrapperParameter
}
/**
* Holds if `sbb` is the `SubBasicBlock` where `v` receives its initial value.
* See the documentation for `FlowVar.definedByInitialValue`.
*/
predicate blockVarDefinedByVariable(SubBasicBlock sbb, StackVariable v) {
sbb = v.(Parameter).getFunction().getEntryPoint()
or
exists(DeclStmt declStmt |
declStmt.getDeclaration(_) = v.(LocalVariable) and
sbb.contains(declStmt) and
mayBeUsedUninitialized(v, _)
)
}
newtype TFlowVar =
TSsaVar(SsaDefinition def, StackVariable v) {
fullySupportedSsaVariable(v) and
v = def.getAVariable()
} or
TBlockVar(SubBasicBlock sbb, Variable v) {
not fullySupportedSsaVariable(v) and
not v instanceof Field and // Fields are interprocedural data flow, not local
reachable(sbb) and
(
initializer(v, sbb.getANode())
or
assignmentLikeOperation(sbb, v, _)
or
exists(PartialDefinition p | p.partiallyDefinesVariableAt(v, sbb))
or
blockVarDefinedByVariable(sbb, v)
)
}
/**
* A variable whose analysis is backed by the SSA library.
*/
class SsaVar extends TSsaVar, FlowVar {
SsaDefinition def;
StackVariable v;
SsaVar() { this = TSsaVar(def, v) }
override VariableAccess getAnAccess() {
// There is no need to know about direct accesses to phi nodes because
// the data flow library will never see those, and the `FlowVar` library
// is only meant to be used by the data flow library.
this.isNonPhi() and
(
// This base case could be included in the transitive case by changing
// `+` to `*`, but that's slower because it goes through the `TSsaVar`
// indirection.
result = def.getAUse(v)
or
exists(SsaDefinition descendantDef |
this.getASuccessorSsaVar+() = TSsaVar(descendantDef, _) and
result = descendantDef.getAUse(v)
)
)
or
parameterUsedInConstructorFieldInit(v, result) and
def.definedByParameter(v)
}
override predicate definedByExpr(Expr e, ControlFlowNode node) {
e = def.getDefiningValue(v) and
(
if def.getDefinition() = v.getInitializer().getExpr()
then node = v.getInitializer()
else node = def.getDefinition()
)
}
override predicate definedPartiallyAt(Expr e) { none() }
override predicate definedByInitialValue(StackVariable param) {
def.definedByParameter(param) and
param = v
}
// `fullySupportedSsaVariable` excludes reference types
override predicate reachesRefParameter(Parameter p) { none() }
/**
* Holds if this `SsaVar` corresponds to a non-phi definition. Users of this
* library will never directly use an `SsaVar` that comes from a phi node,
* so such values are purely for implementation use.
*/
private predicate isNonPhi() {
// This implementation is positively phrased in terms of these two helper
// predicates because it performs better than phrasing it negatively in
// terms of `def.isPhiNode`.
this.definedByExpr(_, _)
or
this.definedByInitialValue(_)
}
/**
* Holds if `result` might have the same value as `this` because `result` is
* a phi node with `this` as input.
*/
private SsaVar getASuccessorSsaVar() {
exists(SsaDefinition succDef |
result = TSsaVar(succDef, v) and
def = succDef.getAPhiInput(v)
)
}
override string toString() { result = def.toString(v) }
override Location getLocation() { result = def.getLocation() }
}
/**
* A variable whose analysis is backed by a simple control flow analysis that
* does not perform as well as the SSA library but gives better results for
* data flow purposes in some cases.
*/
class BlockVar extends TBlockVar, FlowVar {
SubBasicBlock sbb;
Variable v;
BlockVar() { this = TBlockVar(sbb, v) }
override VariableAccess getAnAccess() {
variableAccessInSBB(v, getAReachedBlockVarSBB(this), result) and
result != sbb
or
parameterUsedInConstructorFieldInit(v, result) and
sbb = v.(Parameter).getFunction().getEntryPoint()
}
override predicate reachesRefParameter(Parameter p) {
parameterIsNonConstReference(p) and
p = v and
// This definition reaches the exit node of the function CFG
getAReachedBlockVarSBB(this).getEnd() = p.getFunction()
}
override predicate definedByInitialValue(StackVariable lsv) {
blockVarDefinedByVariable(sbb, lsv) and
lsv = v
}
override predicate definedByExpr(Expr e, ControlFlowNode node) {
assignmentLikeOperation(node, v, e) and
node = sbb
or
// We pick the defining `ControlFlowNode` of an `Initializer` to be its
// expression rather than the `Initializer` itself. That's because the
// `Initializer` of a `ConditionDeclExpr` is for historical reasons not
// part of the CFG and therefore ends up in the wrong basic block.
initializer(v, e) and
node = e and
node = sbb.getANode()
}
override predicate definedPartiallyAt(Expr e) {
exists(PartialDefinition p |
p.partiallyDefinesVariableAt(v, sbb) and
p.definesExpressions(_, e)
)
}
override string toString() {
this.definedByExpr(_, _) and
result = "assignment to " + v
or
this.definedByInitialValue(_) and
result = "initial value of " + v
or
exists(Expr partialDef |
this.definedPartiallyAt(partialDef) and
result = "partial definition at " + partialDef
)
or
// impossible case
not this.definedByExpr(_, _) and
not this.definedByInitialValue(_) and
not this.definedPartiallyAt(_) and
result = "undefined " + v
}
override Location getLocation() { result = sbb.getStart().getLocation() }
}
/** Type-specialized version of `getEnclosingElement`. */
private ControlFlowNode getCfnParent(ControlFlowNode node) { result = node.getEnclosingElement() }
/**
* A for-loop or while-loop whose condition is always true upon entry but not
* always true after the first iteration.
*/
class AlwaysTrueUponEntryLoop extends Stmt {
AlwaysTrueUponEntryLoop() {
this.(WhileStmt).conditionAlwaysTrueUponEntry() and
not this.(WhileStmt).conditionAlwaysTrue()
or
this.(ForStmt).conditionAlwaysTrueUponEntry() and
not this.(ForStmt).conditionAlwaysTrue()
}
/**
* Holds if this loop always assigns to `v` before leaving through an edge
* from `bbInside` in its condition to `bbOutside` outside the loop. Also,
* `v` must be used outside the loop.
*/
predicate alwaysAssignsBeforeLeavingCondition(
BasicBlock bbInside, BasicBlock bbOutside, Variable v
) {
v = this.getARelevantVariable() and
this.bbInLoopCondition(bbInside) and
not this.bbInLoop(bbOutside) and
bbOutside = bbInside.getASuccessor() and
not this.reachesWithoutAssignment(bbInside, v)
}
/**
* Gets a variable that is assigned in this loop and read outside the loop.
*/
Variable getARelevantVariable() {
result = this.getAVariableAssignedInLoop() and
exists(VariableAccess va |
va.getTarget() = result and
readAccess(va) and
exists(BasicBlock bb | bb = va.getBasicBlock() | not this.bbInLoop(bb))
)
}
/** Gets a variable that is assigned in this loop. */
pragma[noinline]
private Variable getAVariableAssignedInLoop() {
exists(BasicBlock bbAssign |
assignmentLikeOperation(bbAssign.getANode(), result, _) and
this.bbInLoop(bbAssign)
)
}
private predicate bbInLoopCondition(BasicBlock bb) {
getCfnParent*(bb.getANode()) = this.(Loop).getCondition()
}
private predicate bbInLoop(BasicBlock bb) {
bbDominates(this.(Loop).getStmt(), bb)
or
this.bbInLoopCondition(bb)
}
/** Holds if `sbb` is inside this loop. */
predicate sbbInLoop(SubBasicBlock sbb) { this.bbInLoop(sbb.getBasicBlock()) }
/**
* Holds if `bb` is a basic block inside this loop where `v` has not been
* overwritten at the end of `bb`.
*/
private predicate reachesWithoutAssignment(BasicBlock bb, Variable v) {
(
// For the type of loop we are interested in, the body is always a
// basic block.
bb = this.(Loop).getStmt() and
v = this.getARelevantVariable()
or
this.reachesWithoutAssignment(pragma[only_bind_out](bb.getAPredecessor()), v) and
this.bbInLoop(bb)
) and
not assignsToVar(bb, v)
}
}
pragma[noinline]
private predicate assignsToVar(BasicBlock bb, Variable v) {
assignmentLikeOperation(bb.getANode(), v, _) and
exists(AlwaysTrueUponEntryLoop loop | v = loop.getARelevantVariable())
}
/**
* Holds if `loop` always assigns to `v` before leaving through an edge
* from `bbInside` in its condition to `bbOutside` outside the loop. Also,
* `v` must be used outside the loop.
*/
predicate skipLoop(
SubBasicBlock sbbInside, SubBasicBlock sbbOutside, Variable v, AlwaysTrueUponEntryLoop loop
) {
exists(BasicBlock bbInside, BasicBlock bbOutside |
loop.alwaysAssignsBeforeLeavingCondition(bbInside, bbOutside, v) and
bbInside = sbbInside.getBasicBlock() and
bbOutside = sbbOutside.getBasicBlock() and
sbbInside.lastInBB() and
sbbOutside.firstInBB()
)
}
// The noopt is needed to avoid getting `variableLiveInSBB` joined in before
// `getASuccessor`.
pragma[noopt]
private SubBasicBlock getAReachedBlockVarSBB(TBlockVar start) {
exists(Variable v |
start = TBlockVar(result, v) and
variableLiveInSBB(result, v) and
not largeVariable(v, _, _)
)
or
exists(SubBasicBlock mid, SubBasicBlock sbbDef, Variable v |
mid = getAReachedBlockVarSBB(start) and
start = TBlockVar(sbbDef, v) and
result = mid.getASuccessor() and
variableLiveInSBB(result, v) and
forall(AlwaysTrueUponEntryLoop loop | skipLoop(mid, result, v, loop) | loop.sbbInLoop(sbbDef)) and
not assignmentLikeOperation(result, v, _)
)
}
/**
* Holds if `v` may have too many combinations of definitions and reached
* blocks for us the feasibly compute its def-use relation.
*/
private predicate largeVariable(Variable v, int liveBlocks, int defs) {
liveBlocks = strictcount(SubBasicBlock sbb | variableLiveInSBB(sbb, v)) and
defs = strictcount(SubBasicBlock sbb | exists(TBlockVar(sbb, v))) and
// Convert to float to avoid int overflow (32-bit two's complement)
liveBlocks.(float) * defs.(float) > 100000.0
}
/**
* Holds if a value held in `v` at the start of `sbb` (or after the first
* assignment, if that assignment is to `v`) might later be read.
*/
private predicate variableLiveInSBB(SubBasicBlock sbb, Variable v) {
variableAccessInSBB(v, sbb, _)
or
// Non-const reference parameters are live at the end of the function
parameterIsNonConstReference(v) and
sbb.contains(v.(Parameter).getFunction())
or
exists(SubBasicBlock succ | succ = sbb.getASuccessor() |
variableLiveInSBB(succ, v) and
not variableNotLiveBefore(succ, v)
)
}
predicate parameterIsNonConstReference(Parameter p) {
exists(ReferenceType refType |
refType = p.getUnderlyingType() and
(
not refType.getBaseType().isConst()
or
// A field of a parameter of type `const std::shared_ptr<A>& p` can still be changed even though
// the base type of the reference is `const`.
refType.getBaseType().getUnspecifiedType() =
any(PointerWrapper wrapper | not wrapper.pointsToConst())
)
)
or
p instanceof IteratorParameter
or
p instanceof PointerWrapperParameter
}
/**
* Holds if liveness of `v` should stop propagating backwards from `sbb`.
*/
private predicate variableNotLiveBefore(SubBasicBlock sbb, Variable v) {
assignmentLikeOperation(sbb, v, _)
or
// Liveness of `v` is killed when going backwards from a block that declares it
exists(DeclStmt ds | ds.getADeclaration().(LocalVariable) = v and sbb.contains(ds))
}
/** Holds if `va` is a read access to `v` in `sbb`, where `v` is modeled by `BlockVar`. */
pragma[noinline]
private predicate variableAccessInSBB(Variable v, SubBasicBlock sbb, VariableAccess va) {
exists(TBlockVar(_, v)) and
va.getTarget() = v and
va = sbb.getANode() and
not overwrite(va, _)
}
/**
* A local variable that is uninitialized immediately after its declaration.
*/
class UninitializedLocalVariable extends LocalVariable, StackVariable {
UninitializedLocalVariable() { not this.hasInitializer() }
}
/** Holds if `va` may be an uninitialized access to `v`. */
predicate mayBeUsedUninitialized(UninitializedLocalVariable v, VariableAccess va) {
exists(BasicBlock bb, int vaIndex |
va.getTarget() = v and
readAccess(va) and
va = bb.getNode(vaIndex) and
notAccessedAtStartOfBB(v, bb) and
(
vaIndex < indexOfFirstOverwriteInBB(v, bb)
or
not exists(indexOfFirstOverwriteInBB(v, bb))
)
)
}
/**
* Holds if `v` has not been accessed at the start of `bb`, for a variable
* `v` where `allReadsDominatedByOverwrite(v)` does not hold.
*/
predicate notAccessedAtStartOfBB(UninitializedLocalVariable v, BasicBlock bb) {
// Start from a BB containing an uninitialized variable
bb.getANode().(DeclStmt).getDeclaration(_) = v and
// Only consider variables for which initialization before reading cannot
// be proved by simpler means. This predicate is expensive in time and
// space, whereas `allReadsDominatedByOverwrite` is cheap.
not allReadsDominatedByOverwrite(v)
or
exists(BasicBlock pred |
pred = bb.getAPredecessor() and
notAccessedAtStartOfBB(v, pred) and
// Stop searching when `v` is accessed.
not pred.getANode() = v.getAnAccess()
)
}
/**
* Holds if all read accesses of `v` are dominated by an overwrite of `v`.
*/
predicate allReadsDominatedByOverwrite(UninitializedLocalVariable v) {
forall(VariableAccess va |
va.getTarget() = v and
readAccess(va)
|
dominatedByOverwrite(v, va)
)
}
/** Holds if `va` accesses `v` and is dominated by an overwrite of `v`. */
predicate dominatedByOverwrite(UninitializedLocalVariable v, VariableAccess va) {
exists(BasicBlock bb, int vaIndex |
va = bb.getNode(vaIndex) and
va.getTarget() = v and
vaIndex > indexOfFirstOverwriteInBB(v, bb)
or
va = bb.getNode(vaIndex) and
va.getTarget() = v and
bbStrictlyDominates(getAnOverwritingBB(v), bb)
)
}
/** Gets a basic block in which `v` is overwritten. */
BasicBlock getAnOverwritingBB(UninitializedLocalVariable v) {
exists(indexOfFirstOverwriteInBB(v, result))
}
int indexOfFirstOverwriteInBB(LocalVariable v, BasicBlock bb) {
result = min(int i | overwrite(v.getAnAccess(), bb.getNode(i)))
}
/**
* Holds if the value of the variable accessed at `va` may affect the execution
* of the program.
*/
predicate readAccess(VariableAccess va) {
reachable(va) and
not overwrite(va, _) and
not va = any(SizeofExprOperator so).getAChild+() and
not va = any(AlignofExprOperator ao).getAChild+()
}
/**
* Holds if the value of the variable accessed at `va` is completely
* overwritten at `node`, where `va` is uniquely determined by `node`.
*/
predicate overwrite(VariableAccess va, ControlFlowNode node) {
va = node.(AssignExpr).getLValue()
}
/**
* Holds if `v` is modified through `va` as a side effect of evaluating
* `node`, receiving a value best described by `assignedExpr`.
* Assignment-like operations are those that desugar to a non-overloaded `=`
* assignment: `=`, `+=`, `++`, `--`, etc.
*
* This corresponds to `FlowVar::definedByExpr`, except that the case where
* `node instanceof Initializer` is covered by `initializer` instead of this
* predicate.
*/
predicate assignmentLikeOperation(ControlFlowNode node, Variable v, Expr assignedExpr) {
// Together, the two following cases cover `Assignment`
node =
any(AssignExpr ae |
v.getAnAccess() = ae.getLValue() and
assignedExpr = ae.getRValue()
)
or
node =
any(AssignOperation ao |
v.getAnAccess() = ao.getLValue() and
// Here and in the `PrefixCrementOperation` case, we say that the assigned
// expression is the operation itself. For example, we say that `x += 1`
// assigns `x += 1` to `x`. The justification is that after this operation,
// `x` will contain the same value that `x += 1` evaluated to.
assignedExpr = ao
)
or
// This case does not add further data flow paths, except if a
// `PrefixCrementOperation` is itself a source
node =
any(CrementOperation op |
v.getAnAccess() = op.getOperand() and
assignedExpr = op
)
}
Expr getAnIteratorAccess(Variable collection) {
exists(
Call c, SsaDefinition def, Variable iterator, FunctionInput input, FunctionOutput output
|
c.getTarget().(GetIteratorFunction).getsIterator(input, output) and
(
(
input.isQualifierObject() or
input.isQualifierAddress()
) and
c.getQualifier() = collection.getAnAccess()
or
exists(int index |
input.isParameter(index) or
input.isParameterDeref(index)
|
c.getArgument(index) = collection.getAnAccess()
)
) and
output.isReturnValue() and
def.getAnUltimateDefiningValue(iterator) = c and
result = def.getAUse(iterator)
)
or
exists(Call crement |
crement = result and
[crement.getQualifier(), crement.getArgument(0)] = getAnIteratorAccess(collection) and
crement.getTarget().getName() = ["operator++", "operator--"]
)
}
class IteratorParameter extends Parameter {
IteratorParameter() { this.getUnspecifiedType() instanceof Iterator }
}
class PointerWrapperParameter extends Parameter {
PointerWrapperParameter() { this.getUnspecifiedType() instanceof PointerWrapper }
}
/**
* Holds if `v` is initialized to have value `assignedExpr`.
*/
predicate initializer(LocalVariable v, Expr assignedExpr) {
assignedExpr = v.getInitializer().getExpr()
}
/**
* Holds if `p` is a parameter to a constructor that is used in a
* `ConstructorFieldInit` at `va`. This ignores the corner case that `p`
* might have been overwritten to have a different value before this happens.
*/
predicate parameterUsedInConstructorFieldInit(Parameter p, VariableAccess va) {
exists(Constructor constructor |
constructor.getInitializer(_).(ConstructorFieldInit).getExpr().getAChild*() = va and
va = p.getAnAccess()
// We don't require that `constructor` has `p` as a parameter because
// that follows from the two other conditions and would likely just
// confuse the join orderer.
)
}
/**
* A point in a basic block where a non-SSA variable may have a different value
* than it did elsewhere in the same basic block. Extending this class
* configures the `SubBasicBlocks` library as needed for the implementation of
* this library.
*/
class DataFlowSubBasicBlockCutNode extends SubBasicBlockCutNode {
DataFlowSubBasicBlockCutNode() {
exists(Variable v | not fullySupportedSsaVariable(v) |
assignmentLikeOperation(this, v, _)
or
exists(PartialDefinition p | p.partiallyDefinesVariableAt(v, this))
// It is not necessary to cut the basic blocks at `Initializer` nodes
// because the affected variable can have no _other_ value before its
// initializer. It is not necessary to cut basic blocks at procedure
// entries for the sake of `Parameter`s since we are already guaranteed
// to have a `SubBasicBlock` starting at procedure entry.
)
}
}
}
/* module FlowVar_internal */