@@ -41,7 +41,6 @@ class CheckForLeapYearOperation extends Expr {
4141 }
4242}
4343
44- bindingset [ modVal]
4544Expr 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]
5452Expr 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 */
225181class 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 */
251204final 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 */
266218final 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 */
431415private 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 */
537521class 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