Skip to content

Commit 4dcb6f1

Browse files
committed
Additional test cases and comments
1 parent acdbb8d commit 4dcb6f1

2 files changed

Lines changed: 161 additions & 4 deletions

File tree

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

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -535,7 +535,9 @@ class TimeConversionFunction extends Function {
535535
"SystemTimeToTzSpecificLocalTimeEx", "TzSpecificLocalTimeToSystemTime",
536536
"TzSpecificLocalTimeToSystemTimeEx", "RtlLocalTimeToSystemTime",
537537
"RtlTimeToSecondsSince1970", "_mkgmtime", "SetSystemTime", "SystemTimeToVariantTime",
538-
"VariantTimeToSystemTime", "mktime", "_mktime32", "_mktime64", "VarUdateFromDate"
538+
"VariantTimeToSystemTime",
539+
// NOTE: mktime will normalize a Feb 29 on a non-leap year to Mar 1 silently,
540+
"mktime", "_mktime32", "_mktime64", "VarUdateFromDate"
539541
]
540542
or
541543
this.getQualifiedName().matches("GetDateFormat%")

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

Lines changed: 158 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -851,10 +851,10 @@ bool tp_intermediaryVar(struct timespec now, struct logtime &timestamp_remote)
851851
timestamp_remote.tm = tm_parsed;
852852
timestamp_remote.tm.tm_isdst = -1;
853853
timestamp_remote.usec = now.tv_nsec * 0.001;
854-
for (year = tm_now.tm_year + 1;; --year) // $ Source
854+
for (year = tm_now.tm_year + 1;; --year)
855855
{
856856
// assert(year >= tm_now.tm_year - 1);
857-
timestamp_remote.tm.tm_year = year; // $ Alert[cpp/microsoft/public/leap-year/unchecked-after-arithmetic-year-modification]
857+
timestamp_remote.tm.tm_year = year;
858858
if (mktime(&timestamp_remote.tm) < t_now + 7 * 24 * 60 * 60)
859859
break;
860860
}
@@ -1381,4 +1381,159 @@ void constant_day_on_year_modification1(WORD year, WORD offset, WORD month){
13811381
// which is dangerous.
13821382
set_time(year+1, month, 31);// $ Source
13831383
}
1384-
}
1384+
}
1385+
1386+
1387+
void modification_after_conversion1(tm timeinfo){
1388+
// convert a tm year into a civil year, then modify after conversion
1389+
// This case shows a false negative where the year might be used and it is incorrectly modified,
1390+
// and never reassigned to another struct.
1391+
WORD year = timeinfo.tm_year + 1900;
1392+
1393+
year += 1; // $ MISSING: Alert[cpp/microsoft/public/leap-year/unchecked-after-arithmetic-year-modification]
1394+
}
1395+
1396+
WORD get_civil_year(tm timeinfo){
1397+
return timeinfo.tm_year + 1900;
1398+
}
1399+
1400+
void modification_after_conversion2(tm timeinfo){
1401+
// convert a tm year into a civil year, then modify after conversion
1402+
// This case shows a false negative where the year might be used and it is incorrectly modified,
1403+
// and never reassigned to another struct.
1404+
WORD year = get_civil_year(timeinfo);
1405+
year += 1; // $ MISSING: Alert[cpp/microsoft/public/leap-year/unchecked-after-arithmetic-year-modification]
1406+
}
1407+
1408+
void modification_after_conversion_saved_to_other_time_struct1(tm timeinfo){
1409+
// convert a tm year into a civil year, then modify after conversion
1410+
// This case shows a false negative where the year might be used and it is incorrectly modified,
1411+
// and never reassigned to another struct.
1412+
WORD year = timeinfo.tm_year + 1900;
1413+
1414+
year += 1; // $ Source
1415+
1416+
SYSTEMTIME s;
1417+
// FALSE NEGATIVE: missing this because the conversion happens locally before
1418+
// the year adjustment, which seems as though it is part of a conversion itself
1419+
s.wYear = year; // $ MISSING: Alert[cpp/microsoft/public/leap-year/unchecked-after-arithmetic-year-modification]
1420+
}
1421+
1422+
1423+
1424+
void modification_after_conversion_saved_to_other_time_struct2(tm timeinfo){
1425+
// convert a tm year into a civil year, then modify after conversion
1426+
// This case shows a false negative where the year might be used and it is incorrectly modified,
1427+
// and never reassigned to another struct.
1428+
WORD year = get_civil_year(timeinfo);
1429+
1430+
year += 1; // $ Source
1431+
1432+
SYSTEMTIME s;
1433+
s.wYear = year; // $ Alert[cpp/microsoft/public/leap-year/unchecked-after-arithmetic-year-modification]
1434+
}
1435+
1436+
void modification_after_conversion_saved_to_other_time_struct3(tm timeinfo){
1437+
// convert a tm year into a civil year, then modify after conversion
1438+
// This case shows a false negative where the year might be used and it is incorrectly modified,
1439+
// and never reassigned to another struct.
1440+
WORD year = timeinfo.tm_year + 1900;
1441+
1442+
year = year + 1; // $ Source
1443+
1444+
SYSTEMTIME s;
1445+
// FALSE NEGATIVE: missing this because the conversion happens locally before
1446+
// the year adjustment, which seems as though it is part of a conversion itself
1447+
s.wYear = year; // $ MISSING: Alert[cpp/microsoft/public/leap-year/unchecked-after-arithmetic-year-modification]
1448+
}
1449+
1450+
1451+
void year_saved_to_variable_then_modified1(tm timeinfo){
1452+
// A modified year is not directly assigned to the year, rather, the year is
1453+
// saved to a variable, modified, used, but never assigned back.
1454+
WORD year = timeinfo.tm_year;
1455+
1456+
// NOTE: should we even try to detect cases like this?
1457+
// Our current rationale is that a year in a struct is more dangerous than a year in isolation
1458+
// A year in isolation is harder to interpret
1459+
year += 1; // MISSING: $ Alert[cpp/microsoft/public/leap-year/unchecked-after-arithmetic-year-modification]
1460+
}
1461+
1462+
void modification_before_conversion1(tm timeinfo){
1463+
timeinfo.tm_year += 1; // $ Alert[cpp/microsoft/public/leap-year/unchecked-after-arithmetic-year-modification]
1464+
// convert a tm year into a civil year, then modify after conversion
1465+
// This case shows a false negative where the year might be used and it is incorrectly modified,
1466+
// and never reassigned to another struct.
1467+
WORD year = timeinfo.tm_year + 1900;
1468+
}
1469+
1470+
void modification_before_conversion2(tm timeinfo){
1471+
timeinfo.tm_year += 1; // $ Alert[cpp/microsoft/public/leap-year/unchecked-after-arithmetic-year-modification]
1472+
// convert a tm year into a civil year, then modify after conversion
1473+
// This case shows a false negative where the year might be used and it is incorrectly modified,
1474+
// and never reassigned to another struct.
1475+
WORD year = get_civil_year(timeinfo);
1476+
}
1477+
1478+
1479+
1480+
void year_saved_to_variable_then_modified_with_leap_check1(tm timeinfo){
1481+
// A modified year is not directly assigned to the year, rather, the year is
1482+
// saved to a variable, modified, used, but never assigned back.
1483+
WORD year = timeinfo.tm_year;
1484+
1485+
year += 1;
1486+
1487+
// performing a check is considered good enough, even if not used correctly
1488+
bool b = year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
1489+
}
1490+
1491+
1492+
void modification_after_conversion_with_leap_check1(tm timeinfo){
1493+
// convert a tm year into a civil year, then modify after conversion
1494+
// This case shows a false negative where the year might be used and it is incorrectly modified,
1495+
// and never reassigned to another struct.
1496+
WORD year = timeinfo.tm_year + 1900;
1497+
1498+
year += 1;
1499+
1500+
// performing a check is considered good enough, even if not used correctly
1501+
bool b = year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
1502+
}
1503+
1504+
void modification_after_conversion_with_leap_check2(tm timeinfo){
1505+
// convert a tm year into a civil year, then modify after conversion
1506+
// This case shows a false negative where the year might be used and it is incorrectly modified,
1507+
// and never reassigned to another struct.
1508+
WORD year = get_civil_year(timeinfo);
1509+
1510+
year += 1;
1511+
1512+
// performing a check is considered good enough, even if not used correctly
1513+
bool b = year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
1514+
}
1515+
1516+
void modification_before_conversion_with_leap_check1(tm timeinfo){
1517+
timeinfo.tm_year += 1;
1518+
// convert a tm year into a civil year, then modify after conversion
1519+
// This case shows a false negative where the year might be used and it is incorrectly modified,
1520+
// and never reassigned to another struct.
1521+
WORD year = timeinfo.tm_year + 1900;
1522+
1523+
// performing a check is considered good enough, even if not used correctly
1524+
bool b = year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
1525+
}
1526+
1527+
void modification_before_conversion_with_leap_check2(tm timeinfo){
1528+
timeinfo.tm_year += 1;
1529+
// convert a tm year into a civil year, then modify after conversion
1530+
// This case shows a false negative where the year might be used and it is incorrectly modified,
1531+
// and never reassigned to another struct.
1532+
WORD year = get_civil_year(timeinfo);
1533+
1534+
// performing a check is considered good enough, even if not used correctly
1535+
bool b = year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
1536+
}
1537+
1538+
1539+

0 commit comments

Comments
 (0)