Skip to content

Commit 6c103a9

Browse files
committed
Time conversion results that are checked in a ternary operator conditions are now assumed to be a check of the result, i.e., a valid leap year check.
1 parent 8e7ff2d commit 6c103a9

File tree

3 files changed

+43
-1
lines changed

3 files changed

+43
-1
lines changed

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

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,13 @@ predicate isControlledByMonthEqualityCheckNonFebruary(Expr e) {
473473
)
474474
}
475475

476+
/**
477+
* Flow from a year field access through a time conversion function
478+
* where the call's result is used to check error. The result must
479+
* be used as a guard for an if or ternary operator. If so,
480+
* assume some sort of error handling is occurring that could be used
481+
* to detect bad dates due to leap year.
482+
*/
476483
module TimeStructToCheckedTimeConversionConfig implements DataFlow::StateConfigSig {
477484
class FlowState = boolean;
478485

@@ -483,7 +490,11 @@ module TimeStructToCheckedTimeConversionConfig implements DataFlow::StateConfigS
483490

484491
predicate isSink(DataFlow::Node sink, FlowState state) {
485492
state = true and
486-
exists(IfStmt ifs | ifs.getCondition().getAChild*() = sink.asExpr())
493+
(
494+
exists(IfStmt ifs | ifs.getCondition().getAChild*() = sink.asExpr())
495+
or
496+
exists(ConditionalExpr ce | ce.getCondition().getAChild*() = sink.asExpr())
497+
)
487498
}
488499

489500
predicate isAdditionalFlowStep(

cpp/ql/test/query-tests/Likely Bugs/Leap Year/UncheckedLeapYearAfterYearModification/UncheckedLeapYearAfterYearModification.expected

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ nodes
7575
| test.cpp:1243:16:1243:28 | ... + ... | semmle.label | ... + ... |
7676
| test.cpp:1250:16:1250:28 | ... + ... | semmle.label | ... + ... |
7777
| test.cpp:1257:16:1257:28 | ... + ... | semmle.label | ... + ... |
78+
| test.cpp:1263:16:1263:28 | ... + ... | semmle.label | ... + ... |
79+
| test.cpp:1277:14:1277:26 | ... + ... | semmle.label | ... + ... |
7880
subpaths
7981
testFailures
8082
| test.cpp:1075:2:1075:11 | ... ++ | Unexpected result: Alert |

cpp/ql/test/query-tests/Likely Bugs/Leap Year/UncheckedLeapYearAfterYearModification/test.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1257,3 +1257,32 @@ void simplified_leap_year_check(WORD year, WORD offset){
12571257
tmp.tm_year = year + offset; // $ Alert[cpp/microsoft/public/leap-year/unchecked-after-arithmetic-year-modification]
12581258
}
12591259

1260+
void compound_leap_year_check(WORD year, WORD offset, WORD month, WORD day){
1261+
struct tm tmp;
1262+
1263+
tmp.tm_year = year + offset;
1264+
1265+
bool isLeap = tmp.tm_year % 4 == 0 && (tmp.tm_year % 100 != 0 || tmp.tm_year % 400 == 0) && (month == 2 && day == 29);
1266+
1267+
if(isLeap){
1268+
// do something
1269+
}
1270+
tmp.tm_mday = day;
1271+
tmp.tm_mon = month;
1272+
}
1273+
1274+
void indirect_time_conversion_check(WORD year, WORD offset){
1275+
SYSTEMTIME tmp;
1276+
1277+
tmp.wYear = year + offset;
1278+
1279+
FILETIME ft;
1280+
1281+
// (out-of-scope) GeneralBug: Unchecked call to SystemTimeToFileTime. this may have failed, but we didn't check the return value!
1282+
BOOL res = SystemTimeToFileTime(&tmp, &ft);
1283+
1284+
// Assume this check of the result is sufficient as an implicit leap year check.
1285+
bool x = (res == 0) ? true : false;
1286+
}
1287+
1288+

0 commit comments

Comments
 (0)