@@ -67,11 +67,6 @@ class IgnorableFunction extends Function {
6767 this .getName ( ) .toLowerCase ( ) .matches ( "%persian%" )
6868 or
6969 this .getFile ( ) .getBaseName ( ) .toLowerCase ( ) .matches ( "%persian%" )
70- // or
71- // // Functions with "convert" in the name are often conversion functions
72- // // This is a very broad heuristic to handle a few observed false positives
73- // // involving conversions with Hijri years.
74- // this.getName().toLowerCase().matches("%convert%")
7570 }
7671}
7772
@@ -265,9 +260,9 @@ predicate isOperationSourceCandidate(Expr e) {
265260 or
266261 e instanceof CrementOperation
267262 or
268- exists ( AssignSubExpr a | a . getRValue ( ) = e )
263+ e instanceof AssignSubExpr
269264 or
270- exists ( AssignAddExpr a | a . getRValue ( ) = e )
265+ e instanceof AssignAddExpr
271266 )
272267}
273268
@@ -313,64 +308,26 @@ class OperationSource extends Expr {
313308}
314309
315310class YearFieldAssignmentNode extends DataFlow:: Node {
311+ YearFieldAccess access ;
312+
316313 YearFieldAssignmentNode ( ) {
317314 not this .getEnclosingCallable ( ) .getUnderlyingCallable ( ) instanceof IgnorableFunction and
318315 (
319- this .asExpr ( ) instanceof YearFieldAssignment or
320- this .asDefiningArgument ( ) instanceof YearFieldOutArgAssignment
321- )
322- }
323- }
324-
325- /**
326- * An assignment to a year field, which will be a sink
327- */
328- abstract class YearFieldAssignment extends Expr {
329- abstract YearFieldAccess getYearFieldAccess ( ) ;
330- }
331-
332- class YearFieldOutArgAssignment extends YearFieldAssignment {
333- YearFieldAccess access ;
334-
335- YearFieldOutArgAssignment ( ) {
336- exists ( Call c | c .getAnArgument ( ) = access and this = access )
337- or
338- exists ( Call c , AddressOfExpr aoe |
339- c .getAnArgument ( ) = aoe and
340- aoe .getOperand ( ) = access and
341- this = aoe
342- )
343- }
344-
345- override YearFieldAccess getYearFieldAccess ( ) { result = access }
346- }
347-
348- /**
349- * An assignment to x ie `x = a`.
350- */
351- class YearFieldAssignmentAccess extends YearFieldAssignment {
352- YearFieldAccess access ;
353-
354- YearFieldAssignmentAccess ( ) {
355- // TODO: why can't I make this just the L value? Doesn't seem to work
356- exists ( Assignment a |
357- a .getLValue ( ) = access and
358- a .getRValue ( ) = this
316+ this .asDefinition ( ) .( Assignment ) .getLValue ( ) = access
317+ or
318+ this .asDefinition ( ) .( CrementOperation ) .getOperand ( ) = access
319+ or
320+ exists ( Call c | c .getAnArgument ( ) = access and this .asDefiningArgument ( ) = access )
321+ or
322+ exists ( Call c , AddressOfExpr aoe |
323+ c .getAnArgument ( ) = aoe and
324+ aoe .getOperand ( ) = access and
325+ this .asDefiningArgument ( ) = aoe
326+ )
359327 )
360328 }
361329
362- override YearFieldAccess getYearFieldAccess ( ) { result = access }
363- }
364-
365- /**
366- * A year field assignment, by a unary operator ie `x++`.
367- */
368- class YearFieldAssignmentUnary extends YearFieldAssignment instanceof CrementOperation {
369- YearFieldAccess access ;
370-
371- YearFieldAssignmentUnary ( ) { this .getOperand ( ) = access }
372-
373- override YearFieldAccess getYearFieldAccess ( ) { result = access }
330+ YearFieldAccess getYearFieldAccess ( ) { result = access }
374331}
375332
376333/**
@@ -423,7 +380,7 @@ predicate isLeapYearCheckSink(DataFlow::Node sink) {
423380/**
424381 * A flow configuration from a Year field access to some Leap year check or guard
425382 */
426- module YearFieldAccessToLeapYearCheckConfig implements DataFlow:: ConfigSig {
383+ module YearAssignmentToLeapYearCheckConfig implements DataFlow:: ConfigSig {
427384 predicate isSource ( DataFlow:: Node source ) { source instanceof YearFieldAssignmentNode }
428385
429386 predicate isSink ( DataFlow:: Node sink ) { isLeapYearCheckSink ( sink ) }
@@ -441,6 +398,13 @@ module YearFieldAccessToLeapYearCheckConfig implements DataFlow::ConfigSig {
441398 or
442399 // flow from a year access qualifier to a year field
443400 exists ( YearFieldAccess yfa | node2 .asExpr ( ) = yfa and node1 .asExpr ( ) = yfa .getQualifier ( ) )
401+ or
402+ // in cases of x.year = x and the x is checked, but the year x.year isn't directly
403+ // flow from a year assignment node to an RHS if it is an assignment
404+ exists ( YearFieldAssignmentNode yfan |
405+ node1 = yfan and
406+ node2 .asExpr ( ) = yfan .asDefinition ( ) .( Assignment ) .getRValue ( )
407+ )
444408 }
445409
446410 /**
@@ -450,36 +414,24 @@ module YearFieldAccessToLeapYearCheckConfig implements DataFlow::ConfigSig {
450414 DataFlow:: FlowFeature getAFeature ( ) { result instanceof DataFlow:: FeatureHasSourceCallContext }
451415}
452416
453- module YearFieldAccessToLeapYearCheckFlow =
454- TaintTracking:: Global< YearFieldAccessToLeapYearCheckConfig > ;
417+ module YearAssignmentToLeapYearCheckFlow =
418+ TaintTracking:: Global< YearAssignmentToLeapYearCheckConfig > ;
455419
456420/** Does there exist a flow from the given YearFieldAccess to a Leap Year check or guard? */
457- predicate isYearModifiedWithCheck ( YearFieldAccess fa ) {
458- exists ( YearFieldAccessToLeapYearCheckFlow :: PathNode src |
421+ predicate isYearModifiedWithCheck ( YearFieldAssignmentNode n ) {
422+ exists ( YearAssignmentToLeapYearCheckFlow :: PathNode src |
459423 src .isSource ( ) and
460- src .getNode ( ) . asExpr ( ) . ( YearFieldAssignment ) . getYearFieldAccess ( ) = fa
424+ src .getNode ( ) = n
461425 )
462426 or
463427 // If the time flows to a time conversion whose value/result is checked,
464428 // assume the leap year is being handled.
465- exists ( TimeStructToCheckedTimeConversionFlow :: PathNode timeQualSrc |
429+ exists ( YearAssignmentToCheckedTimeConversionFlow :: PathNode timeQualSrc |
466430 timeQualSrc .isSource ( ) and
467- timeQualSrc .getNode ( ) . asExpr ( ) = fa . getQualifier * ( )
431+ timeQualSrc .getNode ( ) = n
468432 )
469- // or
470- // isUsedInFeb29Check(fa)
471433}
472434
473- // /**
474- // * If there is a flow from a date struct year field access/assignment to a Feb 29 check
475- // */
476- // predicate isUsedInFeb29Check(YearFieldAccess fa) {
477- // exists(DateFebruary29Check check |
478- // DataFlow::localExprFlow(fa.getQualifier(), check.getDateQualifier())
479- // or
480- // DataFlow::localExprFlow(check.getDateQualifier(), fa.getQualifier())
481- // )
482- // }
483435/**
484436 * An expression which checks the value of a Month field `a->month == 1`.
485437 */
@@ -516,11 +468,11 @@ predicate isControlledByMonthEqualityCheckNonFebruary(Expr e) {
516468 * assume some sort of error handling is occurring that could be used
517469 * to detect bad dates due to leap year.
518470 */
519- module TimeStructToCheckedTimeConversionConfig implements DataFlow:: StateConfigSig {
471+ module YearAssignmentToCheckedTimeConversionConfig implements DataFlow:: StateConfigSig {
520472 class FlowState = boolean ;
521473
522474 predicate isSource ( DataFlow:: Node source , FlowState state ) {
523- exists ( YearFieldAccess yfa | source . asExpr ( ) = yfa . getQualifier ( ) ) and
475+ source instanceof YearFieldAssignmentNode and
524476 state = false
525477 }
526478
@@ -553,6 +505,8 @@ module TimeStructToCheckedTimeConversionConfig implements DataFlow::StateConfigS
553505 // flow from a YearFieldAccess to the qualifier
554506 node2 .asExpr ( ) = node1 .asExpr ( ) .( YearFieldAccess ) .getQualifier * ( )
555507 or
508+ node1 .( YearFieldAssignmentNode ) .getYearFieldAccess ( ) .getQualifier ( ) = node2 .asExpr ( )
509+ or
556510 // Pass through any intermediate struct
557511 exists ( Assignment a , DataFlow:: PostUpdateNode pun |
558512 a .getLValue ( ) .( YearFieldAccess ) .getQualifier * ( ) = pun .getPreUpdateNode ( ) .asExpr ( ) and
@@ -567,8 +521,8 @@ module TimeStructToCheckedTimeConversionConfig implements DataFlow::StateConfigS
567521 DataFlow:: FlowFeature getAFeature ( ) { result instanceof DataFlow:: FeatureHasSourceCallContext }
568522}
569523
570- module TimeStructToCheckedTimeConversionFlow =
571- DataFlow:: GlobalWithState< TimeStructToCheckedTimeConversionConfig > ;
524+ module YearAssignmentToCheckedTimeConversionFlow =
525+ DataFlow:: GlobalWithState< YearAssignmentToCheckedTimeConversionConfig > ;
572526
573527/**
574528 * Finds flow from a parameter of a function to a leap year check.
@@ -752,7 +706,7 @@ import OperationToYearAssignmentFlow::PathGraph
752706from OperationToYearAssignmentFlow:: PathNode src , OperationToYearAssignmentFlow:: PathNode sink
753707where
754708 OperationToYearAssignmentFlow:: flowPath ( src , sink ) and
755- not isYearModifiedWithCheck ( sink .getNode ( ) . asExpr ( ) . ( YearFieldAssignment ) . getYearFieldAccess ( ) ) and
709+ not isYearModifiedWithCheck ( sink .getNode ( ) ) and
756710 not isControlledByMonthEqualityCheckNonFebruary ( sink .getNode ( ) .asExpr ( ) ) and
757711 // Check if a month is set in the same block as the year operation source
758712 // and the month value would indicate its set to any other month than february.
@@ -761,8 +715,8 @@ where
761715 not exists ( DataFlow:: Node dayOrMonthValSrc , DataFlow:: Node dayOrMonthValSink , Assignment a |
762716 CandidateConstantToDayOrMonthAssignmentFlow:: flow ( dayOrMonthValSrc , dayOrMonthValSink ) and
763717 a .getRValue ( ) = dayOrMonthValSink .asExpr ( ) and
764- dayOrMonthValSrc .asExpr ( ) . getBasicBlock ( ) = src .getNode ( ) . asExpr ( ) .getBasicBlock ( ) and
765- dayOrMonthValSink .asExpr ( ) . getBasicBlock ( ) = sink .getNode ( ) . asExpr ( ) .getBasicBlock ( ) and
718+ dayOrMonthValSrc .getBasicBlock ( ) = src .getNode ( ) .getBasicBlock ( ) and
719+ dayOrMonthValSink .getBasicBlock ( ) = sink .getNode ( ) .getBasicBlock ( ) and
766720 (
767721 a .getLValue ( ) instanceof MonthFieldAccess and
768722 dayOrMonthValSrc .asExpr ( ) .getValue ( ) .toInt ( ) != 2
@@ -773,58 +727,3 @@ where
773727 )
774728select sink , src , sink ,
775729 "Year field has been modified, but no appropriate check for LeapYear was found."
776- // int limit() { result = 5 }
777- // module Partial = TimeStructToCheckedTimeConversionFlow::FlowExplorationFwd<limit/0>;
778- // import Partial::PartialPathGraph
779- // from Partial::PartialPathNode src, Partial::PartialPathNode sink
780- // where Partial::partialFlow(src, sink, _) and src.getLocation().getStartLine() = 1331
781- // select sink, src, sink, "RequestHandle use path"
782- // TODO: this is an older heuristic that should be reassessed.
783- // The heuristic stipulates that if a month or day is set to a constant in the local flow up to or after
784- // a modified year's sink, then assume the user is knowingly handling the conversion correctly.
785- // There is no interpretation of the assignment of the month/day or consideration for what branch the assignment is on.
786- // Merely if the assignment of a constant day/month occurs anywhere, the year modification can likely
787- // be ignored.
788- // not exists(FieldAccess mfa, AssignExpr ae, YearFieldAccess yfa, int val, Variable var |
789- // mfa instanceof MonthFieldAccess or mfa instanceof DayFieldAccess
790- // |
791- // yfa = sink.getNode().asExpr().(YearFieldAssignment).getYearFieldAccess() and
792- // var.getAnAccess() = yfa.getQualifier*() and
793- // mfa.getQualifier*() = var.getAnAccess() and
794- // mfa.isModified() and
795- // (
796- // mfa.getBasicBlock() = yfa.getBasicBlock().getASuccessor*() or
797- // yfa.getBasicBlock() = mfa.getBasicBlock().getASuccessor+()
798- // ) and
799- // ae = mfa.getEnclosingElement() and
800- // ae.getAnOperand().getValue().toInt() = val and
801- // // If the constant looks like it relates to february, then there still might be an error
802- // // so only look for any other constant.
803- // not val in [2, 28, 29]
804- // ) and
805- // // This is a quick huerstic to account for similar cases as the above heuristic for constant
806- // // months, but sometimes the month is set in a time struct inter-procedurally.
807- // // e.g.,
808- // // year++;
809- // // month = 1;
810- // // ... values passed to function that sets in a time struct
811- // // It is tricky to catch these situations with CodeQL. An AI should be able to detect it
812- // // however, in the meantime, a simple heuristic is to check within the same block as
813- // // the year assignment source, if there is another variable that looks like a month
814- // // set to a value that isn't 2, 28, or 29. Of course this is a workaround as it is
815- // // still possible for the user to incorrectly account for leap year (e.g., by not using
816- // // the month set in the block), but the rationale is it is more typically indicative
817- // // of a false positive in these cases.
818- // // TODO: can we remove this later an use an AI assessment to filter false positives?
819- // not exists(Variable v, VariableAccess va, BasicBlock bb, AssignExpr a, int val |
820- // v.getName().toLowerCase().matches("%month%") and
821- // va = v.getAnAccess() and
822- // va.getBasicBlock() = bb and
823- // bb = src.getNode().asExpr().getBasicBlock() and
824- // a.getBasicBlock() = bb and
825- // a.getLValue() = va and
826- // a.getRValue().getValue().toInt() = val and
827- // // If the constant looks like it relates to february, then there still might be an error
828- // // so only look for any other constant.
829- // val != 2
830- // )
0 commit comments