@@ -27,6 +27,8 @@ import semmle.code.cpp.controlflow.IRGuards
2727 */
2828class IgnorableFunction extends Function {
2929 IgnorableFunction ( ) {
30+ // arithmetic in known time conversion functions may look like dangerous operations
31+ // we assume all known time conversion functions are safe.
3032 this instanceof TimeConversionFunction
3133 or
3234 // Helper utility in postgres with string time conversions
@@ -421,6 +423,41 @@ predicate isLeapYearCheckSink(DataFlow::Node sink) {
421423 isLeapYearCheckCall ( _, [ sink .asExpr ( ) , sink .asIndirectExpr ( ) ] )
422424}
423425
426+ predicate yearAssignmentToCheckCommonSteps ( DataFlow:: Node node1 , DataFlow:: Node node2 ) {
427+ // flow from a YearFieldAccess to the qualifier
428+ node2 .asExpr ( ) = node1 .asExpr ( ) .( YearFieldAccess ) .getQualifier * ( )
429+ or
430+ // getting the 'access' can be tricky at definitions (assignments especially)
431+ // as dataflow uses asDefinition not asExpr.
432+ // the YearFieldAssignmentNode holds the access in these cases
433+ node1 .( YearFieldAssignmentNode ) .getYearFieldAccess ( ) .getQualifier ( ) = node2 .asExpr ( )
434+ or
435+ // flow from a year access qualifier to a year field
436+ exists ( YearFieldAccess yfa | node2 .asExpr ( ) = yfa and node1 .asExpr ( ) = yfa .getQualifier ( ) )
437+ or
438+ node1 .( YearFieldAssignmentNode ) .getYearFieldAccess ( ) .getQualifier ( ) = node2 .asExpr ( )
439+ or
440+ // Pass through any intermediate struct
441+ exists ( Assignment a , DataFlow:: PostUpdateNode pun |
442+ a .getLValue ( ) .( YearFieldAccess ) .getQualifier * ( ) = pun .getPreUpdateNode ( ) .asExpr ( ) and
443+ a .getRValue ( ) = node1 .asExpr ( ) and
444+ node2 .asExpr ( ) = a .getLValue ( ) .( YearFieldAccess ) .getQualifier * ( )
445+ )
446+ or
447+ // in cases of t.year = x and the value of x is checked, but the year t.year isn't directly checked
448+ // flow from a year assignment node to an RHS if it is an assignment
449+ // e.g.,
450+ // t.year = x;
451+ // isLeapYear(x);
452+ // --> at this point there is no flow of t.year to a check, but only its raw value
453+ // To detect the flow of 'x' to the isLeapYear check,
454+ // flow from t.year to 'x' (at assignment, t.year = x, flow to the RHS to track use-use flow of x)
455+ exists ( YearFieldAssignmentNode yfan |
456+ node1 = yfan and
457+ node2 .asExpr ( ) = yfan .asDefinition ( ) .( Assignment ) .getRValue ( )
458+ )
459+ }
460+
424461/**
425462 * A flow configuration from a Year field access to some Leap year check or guard
426463 */
@@ -430,25 +467,7 @@ module YearAssignmentToLeapYearCheckConfig implements DataFlow::ConfigSig {
430467 predicate isSink ( DataFlow:: Node sink ) { isLeapYearCheckSink ( sink ) }
431468
432469 predicate isAdditionalFlowStep ( DataFlow:: Node node1 , DataFlow:: Node node2 ) {
433- // flow from a YearFieldAccess to the qualifier
434- node2 .asExpr ( ) = node1 .asExpr ( ) .( YearFieldAccess ) .getQualifier * ( )
435- or
436- // Pass through any intermediate struct
437- exists ( Assignment a , DataFlow:: PostUpdateNode pun |
438- a .getLValue ( ) .( YearFieldAccess ) .getQualifier * ( ) = pun .getPreUpdateNode ( ) .asExpr ( ) and
439- a .getRValue ( ) = node1 .asExpr ( ) and
440- node2 .asExpr ( ) = a .getLValue ( ) .( YearFieldAccess ) .getQualifier * ( )
441- )
442- or
443- // flow from a year access qualifier to a year field
444- exists ( YearFieldAccess yfa | node2 .asExpr ( ) = yfa and node1 .asExpr ( ) = yfa .getQualifier ( ) )
445- or
446- // in cases of x.year = x and the x is checked, but the year x.year isn't directly
447- // flow from a year assignment node to an RHS if it is an assignment
448- exists ( YearFieldAssignmentNode yfan |
449- node1 = yfan and
450- node2 .asExpr ( ) = yfan .asDefinition ( ) .( Assignment ) .getRValue ( )
451- )
470+ yearAssignmentToCheckCommonSteps ( node1 , node2 )
452471 }
453472
454473 /**
@@ -514,6 +533,11 @@ predicate isControlledByMonthEqualityCheckNonFebruary(Expr e) {
514533 * auto convert to a sanity check guard of the result for error conditions.
515534 */
516535module YearAssignmentToCheckedTimeConversionConfig implements DataFlow:: StateConfigSig {
536+ // Flow state tracks if flow goes through a known time conversion function
537+ // see `TimeConversionFunction`.
538+ // A valid check with a time conversion function is either the case:
539+ // 1) the year flows into a time conversion function, and the time conversion function's result is checked or
540+ // 2) the year flows into a time conversion function that auto corrects for leap year, so no check is necessary.
517541 class FlowState = boolean ;
518542
519543 predicate isSource ( DataFlow:: Node source , FlowState state ) {
@@ -522,6 +546,9 @@ module YearAssignmentToCheckedTimeConversionConfig implements DataFlow::StateCon
522546 }
523547
524548 predicate isSink ( DataFlow:: Node sink , FlowState state ) {
549+ // Case 1: Flow through a time conversion function that requires a check,
550+ // and we have arrived at a guard, implying the result was checked for possible error, including leap year error.
551+ // state = true indicates the flow went through a time conversion function
525552 state = true and
526553 (
527554 exists ( IfStmt ifs | ifs .getCondition ( ) .getAChild * ( ) = [ sink .asExpr ( ) , sink .asIndirectExpr ( ) ] )
@@ -533,6 +560,8 @@ module YearAssignmentToCheckedTimeConversionConfig implements DataFlow::StateCon
533560 exists ( Loop l | l .getCondition ( ) .getAChild * ( ) = [ sink .asExpr ( ) , sink .asIndirectExpr ( ) ] )
534561 )
535562 or
563+ // Case 2: Flow through a time conversion function that auto corrects for leap year, so no check is necessary.
564+ // state true or false, as flowing through a time conversion function is not necessary in this instance.
536565 state in [ true , false ] and
537566 exists ( Call c , TimeConversionFunction f |
538567 f .isAutoLeapYearCorrecting ( ) and
@@ -554,20 +583,7 @@ module YearAssignmentToCheckedTimeConversionConfig implements DataFlow::StateCon
554583 }
555584
556585 predicate isAdditionalFlowStep ( DataFlow:: Node node1 , DataFlow:: Node node2 ) {
557- // flow from a YearFieldAccess to the qualifier
558- node2 .asExpr ( ) = node1 .asExpr ( ) .( YearFieldAccess ) .getQualifier * ( )
559- or
560- node1 .( YearFieldAssignmentNode ) .getYearFieldAccess ( ) .getQualifier ( ) = node2 .asExpr ( )
561- or
562- // Pass through any intermediate struct
563- exists ( Assignment a , DataFlow:: PostUpdateNode pun |
564- a .getLValue ( ) .( YearFieldAccess ) .getQualifier * ( ) = pun .getPreUpdateNode ( ) .asExpr ( ) and
565- a .getRValue ( ) = node1 .asExpr ( ) and
566- node2 .asExpr ( ) = a .getLValue ( ) .( YearFieldAccess ) .getQualifier * ( )
567- )
568- or
569- // flow from a year access qualifier to a year field
570- exists ( YearFieldAccess yfa | node2 .asExpr ( ) = yfa and node1 .asExpr ( ) = yfa .getQualifier ( ) )
586+ yearAssignmentToCheckCommonSteps ( node1 , node2 )
571587 }
572588
573589 DataFlow:: FlowFeature getAFeature ( ) { result instanceof DataFlow:: FeatureHasSourceCallContext }
0 commit comments