Skip to content

Commit c7a6543

Browse files
committed
Use Bens version + Autoformat
1 parent 7649370 commit c7a6543

1 file changed

Lines changed: 198 additions & 85 deletions

File tree

cpp/ql/src/Likely Bugs/Leap Year/UncheckedLeapYearAfterYearModificationPrecise.ql

Lines changed: 198 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -16,137 +16,215 @@ import LeapYear
1616
* The set of expressions which are ignorable; either because they seem to not be part of a year mutation,
1717
* or because they seem to be a conversion pattern of mapping date scalars.
1818
*/
19-
abstract class IgnorableOperationSource extends Expr {}
19+
abstract class IgnorableOperation extends Expr { }
2020

21-
class IgnorableExprAssignRem extends IgnorableOperationSource instanceof AssignRemExpr{}
22-
class IgnorableExprRem extends IgnorableOperationSource instanceof RemExpr{}
23-
class IgnorableExprUnaryMinus extends IgnorableOperationSource instanceof UnaryMinusExpr{}
24-
/**
25-
* Ignore any operation that is nested inside a larger operation, assume the larger operation is the real source
26-
* */
27-
class IgnorableExprNestedExpr extends IgnorableOperationSource instanceof BinaryArithmeticOperation{
28-
IgnorableExprNestedExpr(){
29-
exists(BinaryArithmeticOperation parentOp |
30-
parentOp.getAChild+() = this
31-
)
32-
}
21+
class IgnorableExprAssignRem extends IgnorableOperation instanceof AssignRemExpr { }
22+
23+
class IgnorableExprRem extends IgnorableOperation instanceof RemExpr { }
24+
25+
class IgnorableExprUnaryMinus extends IgnorableOperation instanceof UnaryMinusExpr { }
26+
27+
class IgnorableNonPlusMinusOperation extends IgnorableOperation instanceof Operation {
28+
IgnorableNonPlusMinusOperation() { not isOperationSourceCandidate(this) }
29+
}
30+
31+
class IgnorableNonPlusMinusAssignment extends IgnorableOperation instanceof AssignArithmeticOperation
32+
{
33+
IgnorableNonPlusMinusAssignment() { not isOperationSourceCandidate(this) }
3334
}
3435

3536
/**
3637
* Anything involving a sub expression with char literal 48, ignore as a likely string conversion
3738
*/
38-
class IgnorableExpr48Mapping extends IgnorableOperationSource{
39-
IgnorableExpr48Mapping(){
40-
exists(SubExpr child | this.(Operation).getAChild*() = child |
41-
child.getRightOperand().(Literal).getValue().toInt() = 48
42-
or
43-
child.getRightOperand().(CharLiteral).getValue().toInt() = 48
44-
)
45-
}
39+
class IgnorableExpr48Mapping extends IgnorableOperation {
40+
IgnorableExpr48Mapping() {
41+
exists(SubExpr child | this.(Operation).getAChild*() = child |
42+
child.getRightOperand().(Literal).getValue().toInt() = 48
43+
or
44+
child.getRightOperand().(CharLiteral).getValue().toInt() = 48
45+
)
46+
}
4647
}
4748

4849
/**
4950
* if doing any kind of operation involving multiplying 10, 100, 1000, etc., likely some kind of conversion
5051
* and ignorable
5152
*/
52-
class IgnorableExpr10MulipleComponent extends IgnorableOperationSource{
53-
IgnorableExpr10MulipleComponent(){
54-
this.(Operation).getAChild*().(MulExpr).getAnOperand().(Literal).getValue().toInt() % 10 = 0
55-
or
56-
this.(AssignMulExpr).getRValue().(Literal).getValue().toInt() % 10 = 0
57-
}
53+
class IgnorableExpr10MulipleComponent extends IgnorableOperation {
54+
IgnorableExpr10MulipleComponent() {
55+
this.(Operation).getAChild*().(MulExpr).getAnOperand().(Literal).getValue().toInt() in [
56+
10, 100, 100
57+
]
58+
or
59+
exists(AssignMulExpr a | this = a or a.getRValue() = this |
60+
a.getRValue().getAChild*().(Literal).getValue().toInt() in [10, 100, 100]
61+
)
62+
}
5863
}
5964

6065
/*
6166
* linux time conversions expect the year to start from 1900, so subtracting or
6267
* adding 1900 or anything involving 1900 as a generalization is probably
6368
* a conversion that is ignorable
64-
* */
65-
class IgnorableExprExpr1900Mapping extends IgnorableOperationSource instanceof Operation{
69+
*/
70+
71+
class IgnorableExprExpr1900Mapping extends IgnorableOperation {
6672
IgnorableExprExpr1900Mapping() {
6773
this.(Operation).getAnOperand().getAChild*().(Literal).getValue().toInt() = 1900
74+
or
75+
exists(AssignArithmeticOperation a | this = a or this = a.getRValue() |
76+
a.getRValue().getAChild*().(Literal).getValue().toInt() = 1900
77+
)
78+
// or
79+
// this.(Literal).getValue().toInt() = 1900
6880
}
6981
}
7082

83+
predicate isOperationSourceCandidate(Expr e) {
84+
e instanceof SubExpr
85+
or
86+
e instanceof AddExpr
87+
or
88+
e instanceof CrementOperation
89+
or
90+
exists(AssignSubExpr a | a.getRValue() = e)
91+
or
92+
exists(AssignAddExpr a | a.getRValue() = e)
93+
}
94+
7195
class OperationSource extends Expr {
7296
OperationSource() {
73-
(
74-
this instanceof BinaryArithmeticOperation
75-
or
76-
this instanceof UnaryArithmeticOperation
77-
or
78-
exists(AssignArithmeticOperation a | a.getRValue() = this)
79-
) and
80-
not this instanceof IgnorableOperationSource
97+
// operation source candidates that aren't nested inside other operation source candidates
98+
isOperationSourceCandidate(this) and
99+
not exists(Expr parent | isOperationSourceCandidate(parent) | parent.getAChild+() = this) and
100+
not this instanceof IgnorableOperation and
101+
not this.getEnclosingFunction() instanceof IgnorableFunction
81102
}
82103
}
83104

84105
/**
85106
* An assignment to a year field, which will be a sink
107+
* NOTE: could not make this abstract, it was binding to all expressions
86108
*/
87-
abstract class YearFieldAssignment extends Expr{}
109+
abstract class YearFieldAssignment extends Expr {
110+
abstract YearFieldAccess getYearFieldAccess();
111+
}
88112

89113
/**
90114
* An assignment to x ie `x = a`.
91115
*/
92-
class YearFieldAssignmentAccess extends YearFieldAssignment{
93-
YearFieldAssignmentAccess(){
94-
// TODO: why can't I make this just the L value? Doesn't seem to work
95-
exists(Assignment a |
96-
a.getLValue() instanceof YearFieldAccess and
97-
a.getRValue() = this
98-
)
99-
}
116+
class YearFieldAssignmentAccess extends YearFieldAssignment {
117+
YearFieldAccess access;
118+
119+
YearFieldAssignmentAccess() {
120+
// TODO: why can't I make this just the L value? Doesn't seem to work
121+
exists(Assignment a |
122+
a.getLValue() = access and
123+
a.getRValue() = this
124+
)
125+
}
126+
127+
override YearFieldAccess getYearFieldAccess() { result = access }
100128
}
101129

102130
/**
103131
* A year field assignment, by a unary operator ie `x++`.
104132
*/
105-
class YearFieldAssignmentUnary extends YearFieldAssignment instanceof UnaryArithmeticOperation{
106-
YearFieldAssignmentUnary(){
107-
this.getOperand() instanceof YearFieldAccess
108-
}
133+
class YearFieldAssignmentUnary extends YearFieldAssignment instanceof CrementOperation {
134+
YearFieldAccess access;
135+
136+
YearFieldAssignmentUnary() { this.getOperand() = access }
137+
138+
override YearFieldAccess getYearFieldAccess() { result = access }
109139
}
110140

111-
class MonthOrDayFieldAccess extends FieldAccess{
112-
MonthOrDayFieldAccess(){
113-
this instanceof MonthFieldAccess
114-
or
115-
this instanceof DayFieldAccess
116-
}
141+
/**
142+
* An access to either a Month or Day.
143+
*/
144+
class MonthOrDayFieldAccess extends FieldAccess {
145+
MonthOrDayFieldAccess() {
146+
this instanceof MonthFieldAccess
147+
or
148+
this instanceof DayFieldAccess
149+
}
117150
}
118151

119152
/**
120-
* A static assignment to the day or month access
121-
*
122-
* ```
123-
* a.day = 28;
124-
* ```
153+
* Flow for non operation candidate sources to year assignments to detect
154+
* ignorable functions.
155+
* If the ignorable operation flows to a year assignment and the source and sink
156+
* are in the same function, we will consider that function ignorable.
125157
*/
126-
class GoodExpr extends YearFieldAssignment{
127-
GoodExpr(){
128-
exists(Variable var, VariableAccess va, YearFieldAccess yfa |
129-
// yfa = this.getYearAccess() and
130-
yfa.getQualifier() = va and
131-
var.getAnAccess() = va and
132-
exists(MonthOrDayFieldAccess mfa, AssignExpr ae, int val |
133-
mfa.getQualifier() = var.getAnAccess() and
134-
mfa.isModified() and
135-
(
136-
mfa.getBasicBlock() = yfa.getBasicBlock().getASuccessor*() or
137-
yfa.getBasicBlock() = mfa.getBasicBlock().getASuccessor+()
138-
) and
139-
ae = mfa.getEnclosingElement() and
140-
ae.getAnOperand().getValue().toInt() = val
141-
)
142-
)
143-
}
158+
module IgnorableOperationToYearConfig implements DataFlow::ConfigSig {
159+
predicate isSource(DataFlow::Node n) {
160+
(
161+
n.asExpr() instanceof Operation or
162+
n.asExpr() instanceof AssignArithmeticOperation
163+
) and
164+
not isOperationSourceCandidate(n.asExpr())
165+
}
166+
167+
predicate isSink(DataFlow::Node n) {
168+
n.asExpr() instanceof YearFieldAssignment
169+
or
170+
n.asExpr() instanceof YearFieldAccess
171+
}
172+
173+
// looking for sources and sinks in the same function
174+
DataFlow::FlowFeature getAFeature() {
175+
result instanceof DataFlow::FeatureEqualSourceSinkCallContext
176+
}
177+
178+
predicate isBarrierOut(DataFlow::Node n) { isSink(n) }
179+
}
180+
181+
module IgnorableOperationToYearFlow = TaintTracking::Global<IgnorableOperationToYearConfig>;
182+
183+
module YearToIgnorableOperationConfig implements DataFlow::ConfigSig {
184+
predicate isSink(DataFlow::Node n) {
185+
(
186+
n.asExpr() instanceof Operation or
187+
n.asExpr() instanceof AssignArithmeticOperation
188+
) and
189+
not isOperationSourceCandidate(n.asExpr())
190+
}
191+
192+
predicate isSource(DataFlow::Node n) {
193+
n.asExpr() instanceof YearFieldAssignment
194+
or
195+
n.asExpr() instanceof YearFieldAccess
196+
}
197+
198+
// looking for sources and sinks in the same function
199+
DataFlow::FlowFeature getAFeature() {
200+
result instanceof DataFlow::FeatureEqualSourceSinkCallContext
201+
}
202+
203+
predicate isBarrierOut(DataFlow::Node n) { isSink(n) }
204+
}
205+
206+
module YearToIgnorableOperationFlow = TaintTracking::Global<YearToIgnorableOperationConfig>;
207+
208+
class IgnorableFunction extends Function {
209+
IgnorableFunction() { isIgnorableFunction(this) }
210+
}
211+
212+
predicate isIgnorableFunction(Function f) {
213+
exists(IgnorableOperationToYearFlow::PathNode sink |
214+
sink.getNode().asExpr().getEnclosingFunction() = f and
215+
sink.isSink()
216+
)
217+
or
218+
exists(YearToIgnorableOperationFlow::PathNode sink |
219+
sink.getNode().asExpr().getEnclosingFunction() = f and
220+
sink.isSink()
221+
)
144222
}
145223

146224
/**
147-
* A DataFlow configuration for identifying flows from some non trivial access or literal
148-
* to the Year field of a date object.
149-
*/
225+
* A DataFlow configuration for identifying flows from some non trivial access or literal
226+
* to the Year field of a date object.
227+
*/
150228
module OperationToYearAssignmentConfig implements DataFlow::ConfigSig {
151229
predicate isSource(DataFlow::Node n) { n.asExpr() instanceof OperationSource }
152230

@@ -157,15 +235,50 @@ module OperationToYearAssignmentConfig implements DataFlow::ConfigSig {
157235
or
158236
n.asExpr().getUnspecifiedType() instanceof PointerType
159237
or
160-
n.asExpr() instanceof IgnorableOperationSource
238+
n.asExpr() instanceof IgnorableOperation
161239
or
162240
exists(ChecksForLeapYearFunctionCall fc | fc.getAnArgument() = n.asExpr())
241+
or
242+
n.asExpr().getEnclosingFunction() instanceof IgnorableFunction
163243
}
164244
}
165245

166246
module OperationToYearAssignmentFlow = TaintTracking::Global<OperationToYearAssignmentConfig>;
247+
248+
predicate isYearModifiedWithCheck(YearFieldAccess fa) {
249+
// If there is a local check for leap year after the modification
250+
exists(LeapYearFieldAccess yfacheck, Variable var, YearFieldAccess yfa |
251+
// TODO: cleanup
252+
yfa = fa and
253+
var.getAnAccess() = fa.getQualifier() and
254+
yfacheck.getQualifier() = var.getAnAccess() and
255+
yfacheck.isUsedInCorrectLeapYearCheck() and
256+
yfacheck.getBasicBlock() = yfa.getBasicBlock().getASuccessor*()
257+
)
258+
or
259+
// If there is a data flow from the variable that was modified to a function that seems to check for leap year
260+
exists(VariableAccess source, ChecksForLeapYearFunctionCall fc, Variable var |
261+
var.getAnAccess() = fa.getQualifier() and
262+
source = var.getAnAccess() and
263+
LeapYearCheckFlow::flow(DataFlow::exprNode(source), DataFlow::exprNode(fc.getAnArgument()))
264+
)
265+
or
266+
// If there is a data flow from the field that was modified to a function that seems to check for leap year
267+
exists(
268+
VariableAccess vacheck, YearFieldAccess yfacheck, ChecksForLeapYearFunctionCall fc, Variable var
269+
|
270+
// TODO: cleanup
271+
var.getAnAccess() = fa.getQualifier() and
272+
vacheck = var.getAnAccess() and
273+
yfacheck.getQualifier() = vacheck and
274+
LeapYearCheckFlow::flow(DataFlow::exprNode(yfacheck), DataFlow::exprNode(fc.getAnArgument()))
275+
)
276+
}
277+
167278
import OperationToYearAssignmentFlow::PathGraph
168279

169280
from OperationToYearAssignmentFlow::PathNode src, OperationToYearAssignmentFlow::PathNode sink
170-
where OperationToYearAssignmentFlow::flowPath(src, sink)
171-
select sink, src, sink, "TEST"
281+
where
282+
OperationToYearAssignmentFlow::flowPath(src, sink) and
283+
not isYearModifiedWithCheck(sink.getNode().asExpr().(YearFieldAssignment).getYearFieldAccess())
284+
select sink, src, sink, "TEST"

0 commit comments

Comments
 (0)