-
Notifications
You must be signed in to change notification settings - Fork 1.6k
/
Copy pathUninitializedLocal.ql
91 lines (85 loc) · 2.87 KB
/
UninitializedLocal.ql
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
/**
* @name Potentially uninitialized local variable
* @description Using a local variable before it is initialized gives the variable a default
* 'nil' value.
* @kind problem
* @problem.severity error
* @id rb/uninitialized-local-variable
* @tags quality
* reliability
* correctness
* @precision high
*/
import codeql.ruby.AST
import codeql.ruby.dataflow.SSA
private import codeql.ruby.dataflow.internal.DataFlowPublic
predicate factor(Expr e, Expr factor) {
factor = e
or
e.(BinaryOperation).getOperator() = ["||", "&&"] and
factor = e.(BinaryOperation).getAnOperand()
or
e.(BinaryOperation).getOperator() = ["||", "&&"] and
factor(e.(BinaryOperation).getAnOperand(), factor)
}
predicate previousConjunct(Expr e, Expr prev) {
exists(BinaryOperation b |
b.getOperator() = "&&" and
b.getRightOperand() = e
|
// 'prev' && 'e'
prev = b.getLeftOperand()
or
// (... && 'prev') && 'e'
b.getLeftOperand().(BinaryOperation).getOperator() = "&&" and
prev = b.getLeftOperand().(BinaryOperation).getRightOperand()
or
// (subtree['prev'] && _) && 'e'
b.getLeftOperand().(BinaryOperation).getOperator() = "&&" and
previousConjunct(b.getLeftOperand().(BinaryOperation).getRightOperand(), prev)
)
}
Expr evaluatingMention(LocalVariableReadAccess read) {
result = read
or
result.(AssignExpr).getLeftOperand() = read
or
result.(NotExpr).getOperand() = read
}
class RelevantLocalVariableReadAccess extends LocalVariableReadAccess {
RelevantLocalVariableReadAccess() {
not exists(MethodCall c |
c.getReceiver() = this and
c.getMethodName() = "nil?"
) and
// 'a' is fine to be uninitialised in 'a || ...'
not exists(BinaryOperation b |
b.getLeftOperand() = this and
b.getOperator() = "||"
) and
// The second 'a' cannot be uninitialised in 'a && (...a...)'
not exists(Expr parent |
parent.getAChild*() = this and
previousConjunct(parent, this.getVariable().getAnAccess())
) and
// Various guards
not exists(ConditionalExpr c | factor(c.getCondition(), evaluatingMention(this))) and
not exists(ConditionalExpr c | factor(c.getCondition(), this.getVariable().getAnAccess()) |
this = c.getBranch(true).getAChild*()
)
}
}
from RelevantLocalVariableReadAccess read, LocalVariable v, Node source
where
v = read.getVariable() and
exists(Ssa::Definition def, Ssa::UninitializedDefinition uninit, Node sink |
uninit = def.getAnUltimateDefinition() and
// def.getAnUltimateDefinition() instanceof Ssa::UninitializedDefinition and
read = def.getARead().getExpr() and
// localFlow(uninit, read)
source.asExpr() = uninit.getARead() and
sink.asExpr() = read.getAControlFlowNode() and
localFlow(source, sink)
)
select read, "Here local variable $@ is using $@.", v, v.getName(), source,
"this potentially uninitialized value"