Skip to content

Commit f37ac50

Browse files
authored
Merge pull request #315 from microsoft/jb1/ap1-maturity
Jb1/ap1 maturity
2 parents d1d2417 + a3421d0 commit f37ac50

File tree

8 files changed

+1896
-227
lines changed

8 files changed

+1896
-227
lines changed

cpp/ql/lib/semmle/code/cpp/commons/DateTime.qll

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ class PackedTimeType extends Type {
1414
}
1515
}
1616

17-
private predicate timeType(string typeName) { typeName = ["_SYSTEMTIME", "SYSTEMTIME", "tm"] }
17+
private predicate timeType(string typeName) {
18+
typeName = ["_SYSTEMTIME", "SYSTEMTIME", "tm", "TIME_FIELDS", "_TIME_FIELDS", "PTIME_FIELDS"]
19+
}
1820

1921
/**
2022
* A type that is used to represent times and dates in an 'unpacked' form, that is,
@@ -95,3 +97,24 @@ class StructTmMonthFieldAccess extends MonthFieldAccess {
9597
class StructTmYearFieldAccess extends YearFieldAccess {
9698
StructTmYearFieldAccess() { this.getTarget().getName() = "tm_year" }
9799
}
100+
101+
/**
102+
* A `DayFieldAccess` for the `TIME_FIELDS` struct.
103+
*/
104+
class TimeFieldsDayFieldAccess extends DayFieldAccess {
105+
TimeFieldsDayFieldAccess() { this.getTarget().getName() = "Day" }
106+
}
107+
108+
/**
109+
* A `MonthFieldAccess` for the `TIME_FIELDS` struct.
110+
*/
111+
class TimeFieldsMonthFieldAccess extends MonthFieldAccess {
112+
TimeFieldsMonthFieldAccess() { this.getTarget().getName() = "Month" }
113+
}
114+
115+
/**
116+
* A `YearFieldAccess` for the `TIME_FIELDS` struct.
117+
*/
118+
class TimeFieldsYearFieldAccess extends YearFieldAccess {
119+
TimeFieldsYearFieldAccess() { this.getTarget().getName() = "Year" }
120+
}

cpp/ql/src/Likely Bugs/Leap Year/LeapYear.qll

Lines changed: 71 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ class CheckForLeapYearOperation extends Expr {
4141
}
4242
}
4343

44-
bindingset[modVal]
4544
Expr moduloCheckEQ_0(EQExpr eq, int modVal) {
4645
exists(RemExpr rem | rem = eq.getLeftOperand() |
4746
result = rem.getLeftOperand() and
@@ -50,7 +49,6 @@ Expr moduloCheckEQ_0(EQExpr eq, int modVal) {
5049
eq.getRightOperand().getValue().toInt() = 0
5150
}
5251

53-
bindingset[modVal]
5452
Expr moduloCheckNEQ_0(NEExpr neq, int modVal) {
5553
exists(RemExpr rem | rem = neq.getLeftOperand() |
5654
result = rem.getLeftOperand() and
@@ -59,48 +57,6 @@ Expr moduloCheckNEQ_0(NEExpr neq, int modVal) {
5957
neq.getRightOperand().getValue().toInt() = 0
6058
}
6159

62-
/**
63-
* Returns if the two expressions resolve to the same value, albeit it is a fuzzy attempt.
64-
* SSA is not fit for purpose here as calls break SSA equivalence.
65-
*/
66-
predicate exprEq_propertyPermissive(Expr e1, Expr e2) {
67-
not e1 = e2 and
68-
(
69-
DataFlow::localFlow(DataFlow::exprNode(e1), DataFlow::exprNode(e2))
70-
or
71-
if e1 instanceof ThisExpr and e2 instanceof ThisExpr
72-
then any()
73-
else
74-
/* If it's a direct Access, check that the target is the same. */
75-
if e1 instanceof Access
76-
then e1.(Access).getTarget() = e2.(Access).getTarget()
77-
else
78-
/* If it's a Call, compare qualifiers and only permit no-argument Calls. */
79-
if e1 instanceof Call
80-
then
81-
e1.(Call).getTarget() = e2.(Call).getTarget() and
82-
e1.(Call).getNumberOfArguments() = 0 and
83-
e2.(Call).getNumberOfArguments() = 0 and
84-
if e1.(Call).hasQualifier()
85-
then exprEq_propertyPermissive(e1.(Call).getQualifier(), e2.(Call).getQualifier())
86-
else any()
87-
else
88-
/* If it's a binaryOperation, compare op and recruse */
89-
if e1 instanceof BinaryOperation
90-
then
91-
e1.(BinaryOperation).getOperator() = e2.(BinaryOperation).getOperator() and
92-
exprEq_propertyPermissive(e1.(BinaryOperation).getLeftOperand(),
93-
e2.(BinaryOperation).getLeftOperand()) and
94-
exprEq_propertyPermissive(e1.(BinaryOperation).getRightOperand(),
95-
e2.(BinaryOperation).getRightOperand())
96-
else
97-
// Otherwise fail (and permit the raising of a finding)
98-
if e1 instanceof Literal
99-
then e1.(Literal).getValue() = e2.(Literal).getValue()
100-
else none()
101-
)
102-
}
103-
10460
/**
10561
* An expression that is the subject of a mod-4 check.
10662
* ie `expr % 4 == 0`
@@ -224,11 +180,8 @@ final class ExprCheckCenturyComponentDiv400Inverted extends ExprCheckCenturyComp
224180
*/
225181
class ExprCheckCenturyComponent extends LogicalOrExpr {
226182
ExprCheckCenturyComponent() {
227-
exists(ExprCheckCenturyComponentDiv400 exprDiv400, ExprCheckCenturyComponentDiv100 exprDiv100 |
228-
this.getAnOperand() = exprDiv100 and
229-
this.getAnOperand() = exprDiv400 and
230-
exprEq_propertyPermissive(exprDiv100.getYearExpr(), exprDiv400.getYearExpr())
231-
)
183+
this.getAnOperand() instanceof ExprCheckCenturyComponentDiv100 and
184+
this.getAnOperand() instanceof ExprCheckCenturyComponentDiv400
232185
}
233186

234187
Expr getYearExpr() {
@@ -250,10 +203,9 @@ abstract class ExprCheckLeapYear extends Expr { }
250203
*/
251204
final class ExprCheckLeapYearFormA extends ExprCheckLeapYear, LogicalAndExpr {
252205
ExprCheckLeapYearFormA() {
253-
exists(Expr e, ExprCheckCenturyComponent centuryCheck |
254-
e = moduloCheckEQ_0(this.getLeftOperand(), 4) and
255-
centuryCheck = this.getAnOperand().getAChild*() and
256-
exprEq_propertyPermissive(e, centuryCheck.getYearExpr())
206+
exists(ExprCheckCenturyComponent centuryCheck |
207+
exists(moduloCheckEQ_0(this.getLeftOperand(), 4)) and
208+
centuryCheck = this.getAnOperand().getAChild*()
257209
)
258210
}
259211
}
@@ -265,15 +217,11 @@ final class ExprCheckLeapYearFormA extends ExprCheckLeapYear, LogicalAndExpr {
265217
*/
266218
final class ExprCheckLeapYearFormB extends ExprCheckLeapYear, LogicalOrExpr {
267219
ExprCheckLeapYearFormB() {
268-
exists(VariableAccess va1, VariableAccess va2, VariableAccess va3 |
269-
va1 = moduloCheckEQ_0(this.getAnOperand(), 400) and
270-
va2 = moduloCheckNEQ_0(this.getAnOperand().(LogicalAndExpr).getAnOperand(), 100) and
271-
va3 = moduloCheckEQ_0(this.getAnOperand().(LogicalAndExpr).getAnOperand(), 4) and
272-
// The 400-leap year check may be offset by [1900,1970,2000].
273-
exists(Expr va1_subExpr | va1_subExpr = va1.getAChild*() |
274-
exprEq_propertyPermissive(va1_subExpr, va2) and
275-
exprEq_propertyPermissive(va2, va3)
276-
)
220+
exists(LogicalAndExpr land |
221+
exists(moduloCheckEQ_0(this.getAnOperand(), 400)) and
222+
land = this.getAnOperand() and
223+
exists(moduloCheckNEQ_0(land.getAnOperand(), 100)) and
224+
exists(moduloCheckEQ_0(land.getAnOperand(), 4))
277225
)
278226
}
279227
}
@@ -410,6 +358,42 @@ class StructTmLeapYearFieldAccess extends LeapYearFieldAccess {
410358
}
411359
}
412360

361+
/**
362+
* `stDate.wMonth == 2`
363+
*/
364+
private class DateCheckMonthFebruary extends EQExpr {
365+
MonthFieldAccess mfa;
366+
367+
DateCheckMonthFebruary() {
368+
this.hasOperands(mfa, any(Literal lit | lit.getValue() = "2"))
369+
}
370+
371+
Expr getDateQualifier() { result = mfa.getQualifier() }
372+
}
373+
374+
/**
375+
* `stDate.wDay == 29`
376+
*/
377+
class DateCheckDay29 extends EQExpr {
378+
DayFieldAccess dfa;
379+
380+
DateCheckDay29() { this.hasOperands(dfa, any(Literal lit | lit.getValue() = "29")) }
381+
382+
Expr getDateQualifier() { result = dfa.getQualifier() }
383+
}
384+
385+
/**
386+
* The combination of a February and Day 29 verification
387+
* `stDate.wMonth == 2 && stDate.wDay == 29`
388+
*/
389+
class DateFebruary29Check extends LogicalAndExpr {
390+
DateCheckMonthFebruary checkFeb;
391+
392+
DateFebruary29Check() { this.hasOperands(checkFeb, any(DateCheckDay29 check29)) }
393+
394+
Expr getDateQualifier() { result = checkFeb.getDateQualifier() }
395+
}
396+
413397
/**
414398
* `Function` that includes an operation that is checking for leap year.
415399
*/
@@ -425,7 +409,7 @@ class ChecksForLeapYearFunctionCall extends FunctionCall {
425409
}
426410

427411
/**
428-
* A `DataFlow` configuraiton for finding a variable access that would flow into
412+
* A `DataFlow` configuration for finding a variable access that would flow into
429413
* a function call that includes an operation to check for leap year.
430414
*/
431415
private module LeapYearCheckFlowConfig implements DataFlow::ConfigSig {
@@ -535,13 +519,30 @@ class SafeTimeGatheringFunction extends Function {
535519
* This list of APIs should check for the return value to detect problems during the conversion.
536520
*/
537521
class TimeConversionFunction extends Function {
522+
boolean autoLeapYearCorrecting;
523+
538524
TimeConversionFunction() {
539-
this.getQualifiedName() =
540-
[
541-
"FileTimeToSystemTime", "SystemTimeToFileTime", "SystemTimeToTzSpecificLocalTime",
542-
"SystemTimeToTzSpecificLocalTimeEx", "TzSpecificLocalTimeToSystemTime",
543-
"TzSpecificLocalTimeToSystemTimeEx", "RtlLocalTimeToSystemTime",
544-
"RtlTimeToSecondsSince1970", "_mkgmtime"
545-
]
525+
autoLeapYearCorrecting = false and
526+
(
527+
this.getName() =
528+
[
529+
"FileTimeToSystemTime", "SystemTimeToFileTime", "SystemTimeToTzSpecificLocalTime",
530+
"SystemTimeToTzSpecificLocalTimeEx", "TzSpecificLocalTimeToSystemTime",
531+
"TzSpecificLocalTimeToSystemTimeEx", "RtlLocalTimeToSystemTime",
532+
"RtlTimeToSecondsSince1970", "_mkgmtime", "SetSystemTime", "VarUdateFromDate", "from_tm"
533+
]
534+
or
535+
// Matches all forms of GetDateFormat, e.g. GetDateFormatA/W/Ex
536+
this.getName().matches("GetDateFormat%")
537+
)
538+
or
539+
autoLeapYearCorrecting = true and
540+
this.getName() =
541+
["mktime", "_mktime32", "_mktime64", "SystemTimeToVariantTime", "VariantTimeToSystemTime"]
546542
}
543+
544+
/**
545+
* Holds if the function is expected to auto convert a bad leap year date.
546+
*/
547+
predicate isAutoLeapYearCorrecting() { autoLeapYearCorrecting = true }
547548
}

0 commit comments

Comments
 (0)