Skip to content

Commit 662119a

Browse files
committed
Adding a test for setting a year field through a return arg. Misc. tweaks.
1 parent 6d7bd97 commit 662119a

2 files changed

Lines changed: 115 additions & 10 deletions

File tree

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

Lines changed: 95 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -45,18 +45,52 @@ class IgnorableExpr48Mapping extends IgnorableOperation {
4545
}
4646
}
4747

48+
class IgnorableCharLiteralArithmetic extends IgnorableOperation {
49+
IgnorableCharLiteralArithmetic() {
50+
exists(this.(BinaryArithmeticOperation).getAnOperand().(TextLiteral).getValue())
51+
or
52+
exists(AssignArithmeticOperation e | e.getRValue() = this |
53+
exists(this.(TextLiteral).getValue())
54+
)
55+
}
56+
}
57+
4858
/*
4959
* linux time conversions expect the year to start from 1900, so subtracting or
5060
* adding 1900 or anything involving 1900 as a generalization is probably
5161
* a conversion that is ignorable
5262
*/
5363

54-
class IgnorableExprExpr1900Mapping extends IgnorableOperation {
55-
IgnorableExprExpr1900Mapping() {
56-
this.(Operation).getAnOperand().(Literal).getValue().toInt() = 1900
57-
or
58-
exists(AssignArithmeticOperation a | this = a.getRValue() |
59-
a.getRValue().(Literal).getValue().toInt() = 1900
64+
predicate isLikelyConversionConstant(int c) {
65+
c = 146097 or // days in 400-year Gregorian cycle
66+
c = 36524 or // days in 100-year Gregorian subcycle
67+
c = 1461 or // days in 4-year cycle (incl. 1 leap)
68+
c = 32044 or // Fliegel–van Flandern JDN epoch shift
69+
c = 1721425 or // JDN of 0001‑01‑01 (Gregorian)
70+
c = 1721119 or // alt epoch offset
71+
c = 2400000 or // MJD → JDN conversion
72+
c = 2400001 or
73+
c = 2400000 or
74+
c = 2141 or // fixed‑point month/day extraction
75+
c = 65536 or
76+
c = 7834 or
77+
c = 256 or
78+
c = 1900 // struct tm base‑year offset; harmless
79+
}
80+
81+
/**
82+
* Some constants indicate conversion that are ignorable, e.g.,
83+
* julian to gregorian conversion or conversions from linux time structs
84+
* that start at 1900, etc.
85+
*/
86+
class IgnorableConstantArithmetic extends IgnorableOperation {
87+
IgnorableConstantArithmetic() {
88+
exists(int i | isLikelyConversionConstant(i) |
89+
this.(Operation).getAnOperand().(Literal).getValue().toInt() = i
90+
or
91+
exists(AssignArithmeticOperation a | this = a.getRValue() |
92+
a.getRValue().(Literal).getValue().toInt() = i
93+
)
6094
)
6195
}
6296
}
@@ -69,6 +103,26 @@ class IgnorableUnaryBitwiseOperation extends IgnorableOperation instanceof Unary
69103
class IgnorableAssignmentBitwiseOperation extends IgnorableOperation instanceof AssignBitwiseOperation
70104
{ }
71105

106+
/**
107+
* Any arithmetic operation where one of the operands is a pointer or char type, ignore it
108+
*/
109+
class IgnorablePointerOrCharArithmetic extends IgnorableOperation {
110+
IgnorablePointerOrCharArithmetic() {
111+
this instanceof BinaryArithmeticOperation and
112+
(
113+
this.(BinaryArithmeticOperation).getAnOperand().getUnspecifiedType() instanceof PointerType
114+
or
115+
this.(BinaryArithmeticOperation).getAnOperand().getUnspecifiedType() instanceof CharType
116+
)
117+
or
118+
exists(AssignArithmeticOperation a | a.getRValue() = this |
119+
a.getAnOperand().getUnspecifiedType() instanceof PointerType
120+
or
121+
a.getAnOperand().getUnspecifiedType() instanceof CharType
122+
)
123+
}
124+
}
125+
72126
/**
73127
* An expression that is a candidate source for an dataflow configuration for an Operation that could flow to a Year field.
74128
*/
@@ -147,6 +201,27 @@ class OperationSource extends Expr {
147201
}
148202
}
149203

204+
class YearFieldAssignmentNode extends DataFlow::Node {
205+
YearFieldAssignmentNode() {
206+
this.asExpr() instanceof YearFieldAssignment
207+
or
208+
this.asDefiningArgument() instanceof YearFieldAccess
209+
or
210+
// TODO: is there a better way to do this?
211+
// i.e., without having to be cognizant of the addressof
212+
// occurring, especially if this occurs on a dataflow
213+
exists(AddressOfExpr aoe |
214+
aoe = this.asDefiningArgument() and
215+
aoe.getOperand() instanceof YearFieldAccess
216+
)
217+
}
218+
219+
YearFieldAccess getYearFieldAccess() {
220+
result = this.asDefiningArgument() or
221+
result = this.asExpr().(YearFieldAssignment).getYearFieldAccess()
222+
}
223+
}
224+
150225
/**
151226
* An assignment to a year field, which will be a sink
152227
* NOTE: could not make this abstract, it was binding to all expressions
@@ -190,15 +265,20 @@ class YearFieldAssignmentUnary extends YearFieldAssignment instanceof CrementOpe
190265
module OperationToYearAssignmentConfig implements DataFlow::ConfigSig {
191266
predicate isSource(DataFlow::Node n) { n.asExpr() instanceof OperationSource }
192267

193-
predicate isSink(DataFlow::Node n) { n.asExpr() instanceof YearFieldAssignment }
268+
predicate isSink(DataFlow::Node n) { n instanceof YearFieldAssignmentNode }
194269

195270
predicate isBarrier(DataFlow::Node n) {
196271
exists(ArrayExpr arr | arr.getArrayOffset() = n.asExpr())
197272
or
198273
n.asExpr().getUnspecifiedType() instanceof PointerType
199274
or
275+
n.asExpr().getUnspecifiedType() instanceof CharType
276+
or
200277
n.asExpr() instanceof IgnorableOperation
201278
}
279+
280+
predicate isBarrierOut(DataFlow::Node n) { isSink(n) }
281+
// DataFlow::FlowFeature getAFeature() { result instanceof DataFlow::FeatureHasSourceCallContext }
202282
}
203283

204284
module OperationToYearAssignmentFlow = TaintTracking::Global<OperationToYearAssignmentConfig>;
@@ -240,7 +320,7 @@ predicate isRootOperationSource(OperationSource e) {
240320
* A flow configuration from a Year field access to some Leap year check or guard
241321
*/
242322
module YearFieldAccessToLeapYearCheckConfig implements DataFlow::ConfigSig {
243-
predicate isSource(DataFlow::Node source) { source.asExpr() instanceof YearFieldAccess }
323+
predicate isSource(DataFlow::Node source) { source instanceof YearFieldAssignmentNode }
244324

245325
predicate isSink(DataFlow::Node sink) {
246326
// Local leap year check
@@ -261,14 +341,19 @@ module YearFieldAccessToLeapYearCheckConfig implements DataFlow::ConfigSig {
261341

262342
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
263343
// flow from a YearFieldAccess to the qualifier
264-
node1.asExpr() instanceof YearFieldAccess and
265344
node2.asExpr() = node1.asExpr().(YearFieldAccess).getQualifier()
266345
}
346+
267347
// Use this to make the sink for leap year check intra-proc only
268348
// i.e., the sink must be in the same scope (function) as the source.
269349
// DataFlow::FlowFeature getAFeature() {
270350
// result instanceof DataFlow::FeatureEqualSourceSinkCallContext
271351
// }
352+
/**
353+
* Enforcing the check must occur in the same call context as the source,
354+
* i.e., do not return from the source function and check in a caller.
355+
*/
356+
DataFlow::FlowFeature getAFeature() { result instanceof DataFlow::FeatureHasSourceCallContext }
272357
}
273358

274359
module YearFieldAccessToLeapYearCheckFlow =
@@ -287,7 +372,7 @@ predicate isYearModifiedWithCheck(YearFieldAccess fa) {
287372
/**
288373
* If there is a flow from a date struct year field access/assignment to a Feb 29 check
289374
*/
290-
predicate isUsedInFeb29Check(YearFieldAccess fa){
375+
predicate isUsedInFeb29Check(YearFieldAccess fa) {
291376
exists(DateFebruary29Check check |
292377
DataFlow::localExprFlow(fa.getQualifier(), check.getDateQualifier())
293378
or

cpp/ql/test/query-tests/Likely Bugs/Leap Year/UncheckedLeapYearAfterYearModification/test.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1068,4 +1068,24 @@ void fp_daymonth_guard(){
10681068
st.wDay = st.wMonth == 2 && st.wDay == 29 ? 28 : st.wDay;
10691069

10701070
SystemTimeToFileTime(&st, &ft);
1071+
}
1072+
1073+
void increment_arg(WORD &x){
1074+
x++;
1075+
}
1076+
1077+
void increment_arg_by_pointer(WORD *x){
1078+
(*x)++;
1079+
}
1080+
1081+
1082+
void fn_year_set_through_out_arg(){
1083+
SYSTEMTIME st;
1084+
GetSystemTime(&st);
1085+
// BAD, year incremented without check
1086+
increment_arg(st.wYear);
1087+
1088+
// GetSystemTime(&st);
1089+
// Bad, year incremented without check
1090+
increment_arg_by_pointer(&st.wYear);
10711091
}

0 commit comments

Comments
 (0)