Skip to content

Commit 7ff7006

Browse files
committed
Adding more false positive cases, and fixing prior test cases to be more realistic.
1 parent 86dfe52 commit 7ff7006

2 files changed

Lines changed: 61 additions & 6 deletions

File tree

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,18 @@ module OperationToYearAssignmentConfig implements DataFlow::ConfigSig {
365365
or
366366
isLeapYearCheckSink(n)
367367
or
368+
// this is a bit of a hack to address cases where a year is normalized and checked, but the
369+
// normalized year is never itself assigned to the final year struct
370+
// isLeapYear(getCivilYear(year))
371+
// struct.year = year
372+
// This is assuming a user would have done this all on one line though.
373+
// setting a variable for the conversion and passing that separately would be more difficult to track
374+
// considering this approach good enough for current observed false positives
375+
// TODO: can this approach be made better?
376+
exists(Call c, Expr arg |
377+
isLeapYearCheckCall(c, arg) and arg.getAChild*() = [n.asExpr(), n.asIndirectExpr()]
378+
)
379+
or
368380
// If as the flow progresses, the value holding a dangerous operation result
369381
// is apparently being passed by address to some function, it is more than likely
370382
// intended to be modified, and therefore, the definition is killed.

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

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1519,7 +1519,7 @@ void year_saved_to_variable_then_modified_with_leap_check1(tm timeinfo){
15191519
year += 1;
15201520

15211521
// performing a check is considered good enough, even if not used correctly
1522-
bool b = year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
1522+
bool b = (year+1900) % 4 == 0 && ((year+1900) % 100 != 0 || (year+1900) % 400 == 0);
15231523
}
15241524

15251525

@@ -1566,15 +1566,15 @@ void modification_before_conversion_with_leap_check2(tm timeinfo){
15661566
WORD year = get_civil_year(timeinfo);
15671567

15681568
// performing a check is considered good enough, even if not used correctly
1569-
bool b = year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
1569+
bool b = (year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0);
15701570
}
15711571

15721572
void odd_leap_year_check1(tm timeinfo){
15731573
timeinfo.tm_year += 1;
15741574

15751575

15761576
// Using an odd sytle of checking divisible by 4 presumably as an optimization trick
1577-
if(((timeinfo.tm_year & 3) == 0) && (timeinfo.tm_year % 100 != 0 || timeinfo.tm_year % 400 == 0))
1577+
if(((timeinfo.tm_year+1900) & 3) == 0 && ((timeinfo.tm_year+1900) % 100 != 0 || (timeinfo.tm_year+1900) % 400 == 0))
15781578
{
15791579
// do something
15801580
}
@@ -1586,7 +1586,7 @@ void odd_leap_year_check2(tm timeinfo){
15861586
// Using an odd sytle of checking divisible by 4 presumably as an optimization trick
15871587
// but also check unrelated conditions on the year as an optimization to rule out irrelevant years
15881588
// for gregorian leap years
1589-
if(timeinfo.tm_mon == 2 && ((timeinfo.tm_year & 3) == 0) && (timeinfo.tm_year <= 1582 || timeinfo.tm_year % 100 != 0 || timeinfo.tm_year % 400 == 0))
1589+
if(timeinfo.tm_mon == 2 && ((timeinfo.tm_year+1900) & 3) == 0 && ((timeinfo.tm_year+1900) <= 1582 || (timeinfo.tm_year+1900) % 100 != 0 || (timeinfo.tm_year+1900) % 400 == 0))
15901590
{
15911591
// do something
15921592
}
@@ -1598,7 +1598,7 @@ void odd_leap_year_check3(tm timeinfo){
15981598
// Using an odd sytle of checking divisible by 4 presumably as an optimization trick
15991599
// but also check unrelated conditions on the year as an optimization to rule out irrelevant years
16001600
// for gregorian leap years
1601-
if(timeinfo.tm_mon == 2 && (timeinfo.tm_year % 4) == 0 && (timeinfo.tm_year <= 1582 || timeinfo.tm_year % 100 != 0 || timeinfo.tm_year % 400 == 0))
1601+
if(timeinfo.tm_mon == 2 && ((timeinfo.tm_year+1900) % 4) == 0 && ((timeinfo.tm_year+1900) <= 1582 || (timeinfo.tm_year+1900) % 100 != 0 || (timeinfo.tm_year+1900) % 400 == 0))
16021602
{
16031603
// do something
16041604
}
@@ -1611,7 +1611,7 @@ void date_adjusted_through_mkgmtime(tm timeinfo){
16111611
// Using an odd sytle of checking divisible by 4 presumably as an optimization trick
16121612
// but also check unrelated conditions on the year as an optimization to rule out irrelevant years
16131613
// for gregorian leap years
1614-
if(timeinfo.tm_mon == 2 && (timeinfo.tm_year % 4) == 0 && (timeinfo.tm_year <= 1582 || timeinfo.tm_year % 100 != 0 || timeinfo.tm_year % 400 == 0))
1614+
if(timeinfo.tm_mon == 2 && ((timeinfo.tm_year+1900) % 4) == 0 && ((timeinfo.tm_year+1900) <= 1582 || (timeinfo.tm_year+1900) % 100 != 0 || (timeinfo.tm_year+1900) % 400 == 0))
16151615
{
16161616
// do something
16171617
}
@@ -1630,3 +1630,46 @@ void interproc_data_killer1(tm timeinfo, WORD delta){
16301630
}
16311631
}
16321632

1633+
1634+
void leap_year_check_after_normalization(tm timeinfo, WORD delta){
1635+
WORD year = delta + 1;
1636+
1637+
if(data_killer(&year)){
1638+
timeinfo.tm_year = year;
1639+
}
1640+
}
1641+
1642+
1643+
void leap_year_check_call_on_conversion1(tm timeinfo){
1644+
timeinfo.tm_year += 1;
1645+
isLeapYearRaw(timeinfo.tm_year + 1900);
1646+
}
1647+
1648+
void leap_year_check_call_on_conversion2(tm timeinfo){
1649+
timeinfo.tm_year += 1;
1650+
WORD year = get_civil_year(timeinfo);
1651+
isLeapYearRaw(year);
1652+
}
1653+
1654+
WORD getDaysInMonth(WORD year, WORD month){
1655+
// simplified
1656+
if(month == 2){
1657+
return isLeapYearRaw(year) ? 29 : 28;
1658+
}
1659+
// else assume logic for every other month,
1660+
// returning 30 for simplicity
1661+
return 30;
1662+
}
1663+
1664+
WORD get_civil_year_raw(WORD year){
1665+
return year + 1900;
1666+
}
1667+
1668+
void leap_year_check_call_on_conversion3(tm timeinfo, WORD year, WORD month, WORD delta){
1669+
year += delta;
1670+
WORD days = getDaysInMonth(get_civil_year_raw(year), month);
1671+
timeinfo.tm_year = year;
1672+
}
1673+
1674+
1675+

0 commit comments

Comments
 (0)