Skip to content

Commit f7231f4

Browse files
committed
C++: misc comment clean up per PR suggestions. Unified additional flow steps for two similar flows into a common additional step predicate.
1 parent e2ad1f6 commit f7231f4

File tree

2 files changed

+50
-34
lines changed

2 files changed

+50
-34
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,7 @@ module PossibleYearArithmeticOperationCheckFlow =
310310
TaintTracking::Global<PossibleYearArithmeticOperationCheckConfig>;
311311

312312
/**
313-
* Time conversion functions where either
313+
* A time conversion function where either
314314
* 1) an incorrect leap year date would result in an error that can be checked from the return value or
315315
* 2) an incorrect leap year date is auto corrected (no checks required)
316316
*/

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

Lines changed: 49 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ import semmle.code.cpp.controlflow.IRGuards
2727
*/
2828
class 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
*/
516535
module 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

Comments
 (0)