Files
codeql/cpp/ql/test/query-tests/Likely Bugs/Leap Year/UncheckedLeapYearAfterYearModification/test.cpp
2026-03-04 13:11:30 +00:00

1761 lines
45 KiB
C++

typedef unsigned short WORD;
typedef unsigned long DWORD, HANDLE;
typedef int BOOL, BOOLEAN, errno_t;
typedef char CHAR;
typedef short SHORT;
typedef long LONG;
typedef unsigned short WCHAR; // wc, 16-bit UNICODE character
typedef long __time64_t, time_t;
#define NULL 0
typedef struct _SYSTEMTIME {
WORD wYear;
WORD wMonth;
WORD wDayOfWeek;
WORD wDay;
WORD wHour;
WORD wMinute;
WORD wSecond;
WORD wMilliseconds;
} SYSTEMTIME, *PSYSTEMTIME, *LPSYSTEMTIME;
typedef struct _FILETIME {
DWORD dwLowDateTime;
DWORD dwHighDateTime;
} FILETIME, *PFILETIME, *LPFILETIME;
typedef struct _TIME_ZONE_INFORMATION {
LONG Bias;
WCHAR StandardName[32];
SYSTEMTIME StandardDate;
LONG StandardBias;
WCHAR DaylightName[32];
SYSTEMTIME DaylightDate;
LONG DaylightBias;
} TIME_ZONE_INFORMATION, *PTIME_ZONE_INFORMATION, *LPTIME_ZONE_INFORMATION;
typedef struct _TIME_DYNAMIC_ZONE_INFORMATION {
LONG Bias;
WCHAR StandardName[32];
SYSTEMTIME StandardDate;
LONG StandardBias;
WCHAR DaylightName[32];
SYSTEMTIME DaylightDate;
LONG DaylightBias;
WCHAR TimeZoneKeyName[128];
BOOLEAN DynamicDaylightTimeDisabled;
} DYNAMIC_TIME_ZONE_INFORMATION, *PDYNAMIC_TIME_ZONE_INFORMATION;
typedef const wchar_t* LPCWSTR;
struct tm
{
int tm_sec; // seconds after the minute - [0, 60] including leap second
int tm_min; // minutes after the hour - [0, 59]
int tm_hour; // hours since midnight - [0, 23]
int tm_mday; // day of the month - [1, 31]
int tm_mon; // months since January - [0, 11]
int tm_year; // years since 1900
int tm_wday; // days since Sunday - [0, 6]
int tm_yday; // days since January 1 - [0, 365]
int tm_isdst; // daylight savings time flag
};
struct timespec
{
time_t tv_sec;
long tv_nsec;
};
/* Timestamps of log entries. */
struct logtime {
struct tm tm;
long usec;
};
/*
* Data structure representing a broken-down timestamp.
*
* CAUTION: the IANA timezone library (src/timezone/) follows the POSIX
* convention that tm_mon counts from 0 and tm_year is relative to 1900.
* However, Postgres' datetime functions generally treat tm_mon as counting
* from 1 and tm_year as relative to 1 BC. Be sure to make the appropriate
* adjustments when moving from one code domain to the other.
*/
struct pg_tm
{
int tm_sec;
int tm_min;
int tm_hour;
int tm_mday;
int tm_mon; /* see above */
int tm_year; /* see above */
int tm_wday;
int tm_yday;
int tm_isdst;
long int tm_gmtoff;
const char *tm_zone;
};
BOOL
SystemTimeToFileTime(
const SYSTEMTIME* lpSystemTime,
LPFILETIME lpFileTime
);
BOOL
FileTimeToSystemTime(
const FILETIME* lpFileTime,
LPSYSTEMTIME lpSystemTime
);
BOOL
SystemTimeToTzSpecificLocalTime(
const TIME_ZONE_INFORMATION* lpTimeZoneInformation,
const SYSTEMTIME* lpUniversalTime,
LPSYSTEMTIME lpLocalTime
);
BOOL
SystemTimeToTzSpecificLocalTimeEx(
const DYNAMIC_TIME_ZONE_INFORMATION* lpTimeZoneInformation,
const SYSTEMTIME* lpUniversalTime,
LPSYSTEMTIME lpLocalTime
);
BOOL
TzSpecificLocalTimeToSystemTime(
const TIME_ZONE_INFORMATION* lpTimeZoneInformation,
const SYSTEMTIME* lpLocalTime,
LPSYSTEMTIME lpUniversalTime
);
BOOL
TzSpecificLocalTimeToSystemTimeEx(
const DYNAMIC_TIME_ZONE_INFORMATION* lpTimeZoneInformation,
const SYSTEMTIME* lpLocalTime,
LPSYSTEMTIME lpUniversalTime
);
int _wtoi(
const wchar_t *str
);
void GetSystemTime(
LPSYSTEMTIME lpSystemTime
);
void GetLocalTime(
LPSYSTEMTIME lpSystemTime
);
void GetSystemTimeAsFileTime(
LPFILETIME lpSystemTimeAsFileTime
);
__time64_t _mkgmtime64(
struct tm* _Tm
);
__time64_t _mkgmtime(
struct tm* const _Tm
)
{
return _mkgmtime64(_Tm);
}
__time64_t mktime(
struct tm* const _Tm
)
{
return _mkgmtime64(_Tm);
}
__time64_t _time64(
__time64_t* _Time
);
__time64_t time(
time_t* const _Time
)
{
return _time64(_Time);
}
int gmtime_s(
struct tm* _Tm,
__time64_t const* _Time
);
BOOL
GetFileTime(
HANDLE hFile,
LPFILETIME lpCreationTime,
LPFILETIME lpLastAccessTime,
LPFILETIME lpLastWriteTime
);
struct tm *localtime_r( const time_t *timer, struct tm *buf );
/**
* Negative Case
* FileTimeToSystemTime is called and the return value is checked
*/
void Correct_FileTimeToSystemTime(const FILETIME* lpFileTime)
{
SYSTEMTIME systemTime;
if (!FileTimeToSystemTime(lpFileTime, &systemTime))
{
/// handle error
return;
}
/// Normal usage
}
/**
* Positive (Out of Scope) Bug Case
* FileTimeToSystemTime is called but no check is conducted to verify the result of the operation
*/
void AntiPattern_FileTimeToSystemTime(const FILETIME* lpFileTime)
{
SYSTEMTIME systemTime;
// (out-of-scope) GeneralBug
FileTimeToSystemTime(lpFileTime, &systemTime);
}
/**
* Negative Case
* SystemTimeToTzSpecificLocalTime is called and the return value is verified
*/
void Correct_SystemTimeToTzSpecificLocalTime(const TIME_ZONE_INFORMATION *lpTimeZoneInformation, const SYSTEMTIME *lpUniversalTime)
{
SYSTEMTIME localTime;
if (!SystemTimeToTzSpecificLocalTime(lpTimeZoneInformation, lpUniversalTime, &localTime))
{
/// handle error
return;
}
/// Normal usage
}
/**
* Positive (Out of Scope) Case
* AntiPattern_SystemTimeToTzSpecificLocalTime is called but the return value is not validated
*/
void AntiPattern_SystemTimeToTzSpecificLocalTime(const TIME_ZONE_INFORMATION *lpTimeZoneInformation, const SYSTEMTIME *lpUniversalTime)
{
SYSTEMTIME localTime;
// (out-of-scope) GeneralBug
SystemTimeToTzSpecificLocalTime(lpTimeZoneInformation, lpUniversalTime, &localTime);
}
/**
* Negative Case
* SystemTimeToTzSpecificLocalTimeEx is called and the return value is validated
*/
void Correct_SystemTimeToTzSpecificLocalTimeEx(const DYNAMIC_TIME_ZONE_INFORMATION *lpTimeZoneInformation, const SYSTEMTIME *lpUniversalTime)
{
SYSTEMTIME localTime;
if (!SystemTimeToTzSpecificLocalTimeEx(lpTimeZoneInformation, lpUniversalTime, &localTime))
{
/// handle error
return;
}
/// Normal usage
}
/**
* Positive Case
* SystemTimeToTzSpecificLocalTimeEx is called but the return value is not validated
*/
void AntiPattern_SystemTimeToTzSpecificLocalTimeEx(const DYNAMIC_TIME_ZONE_INFORMATION *lpTimeZoneInformation, const SYSTEMTIME *lpUniversalTime)
{
SYSTEMTIME localTime;
// (out-of-scope) GeneralBug
SystemTimeToTzSpecificLocalTimeEx(lpTimeZoneInformation, lpUniversalTime, &localTime);
}
/**
* Negative Case
* Correct use of TzSpecificLocalTimeToSystemTime, function is called and the return value is validated.
*/
void Correct_TzSpecificLocalTimeToSystemTime(const TIME_ZONE_INFORMATION *lpTimeZoneInformation, const SYSTEMTIME *lpLocalTime)
{
SYSTEMTIME universalTime;
if (!TzSpecificLocalTimeToSystemTime(lpTimeZoneInformation, lpLocalTime, &universalTime))
{
/// handle error
return;
}
/// Normal usage
}
/**
* Positive (Out of Scope) Case
* TzSpecificLocalTimeToSystemTime is called however the return value is not validated
*/
void AntiPattern_TzSpecificLocalTimeToSystemTime(const TIME_ZONE_INFORMATION *lpTimeZoneInformation, const SYSTEMTIME *lpLocalTime)
{
SYSTEMTIME universalTime;
// (out-of-scope) GeneralBug
TzSpecificLocalTimeToSystemTime(lpTimeZoneInformation, lpLocalTime, &universalTime);
}
/**
* Negative Case
* TzSpecificLocalTimeToSystemTimeEx is called and the return value is correctly validated
*/
void Correct_TzSpecificLocalTimeToSystemTimeEx(const DYNAMIC_TIME_ZONE_INFORMATION *lpTimeZoneInformation, const SYSTEMTIME *lpLocalTime)
{
SYSTEMTIME universalTime;
if (!TzSpecificLocalTimeToSystemTimeEx(lpTimeZoneInformation, lpLocalTime, &universalTime))
{
/// handle error
return;
}
/// Normal usage
}
/**
* Positive (Out of Scope) Case
* TzSpecificLocalTimeToSystemTimeEx is called however the return value is not validated
*/
void AntiPattern_TzSpecificLocalTimeToSystemTimeEx(const DYNAMIC_TIME_ZONE_INFORMATION *lpTimeZoneInformation, const SYSTEMTIME *lpLocalTime)
{
SYSTEMTIME universalTime;
// (out-of-scope) GeneralBug
TzSpecificLocalTimeToSystemTimeEx(lpTimeZoneInformation, lpLocalTime, &universalTime);
}
/*************************************************
SYSTEMTIME Cases
*************************************************/
/**
* Negative Case
* SystemTimeToFileTime is called and the return value is validated in a guard
*/
void Correct_filetime_conversion_check(SYSTEMTIME& st)
{
FILETIME ft;
if (!SystemTimeToFileTime(&st, &ft))
{
/// Something failed, handle error
return;
}
/// SystemTimeToFileTime succeeded
}
//////////////////////////////////////////////
/**
* Positive (Out of Scope) Case
* SystemTimeToFileTime is called but the return value is not validated in a guard
*/
void AntiPattern_unchecked_filetime_conversion(SYSTEMTIME& st)
{
FILETIME ft;
// (out-of-scope) GeneralBug: Unchecked call to SystemTimeToFileTime. this may have failed, but we didn't check the return value!
SystemTimeToFileTime(&st, &ft);
}
/**
* Positive (Out of Scope) Case
* SystemTimeToFileTime is called but the return value is not validated in a guard
*/
void AntiPattern_unchecked_filetime_conversion2(SYSTEMTIME* st)
{
FILETIME ft;
if (st != NULL)
{
// (out-of-scope) GeneralBug: Unchecked call to SystemTimeToFileTime. this may have failed, but we didn't check the return value!
SystemTimeToFileTime(st, &ft);
}
}
/**
* Positive (Out of Scope)
* SYSTEMTIME.wDay is incremented by one (and no guard exists)
*/
void AntiPattern_unchecked_filetime_conversion2()
{
SYSTEMTIME st;
FILETIME ft;
GetSystemTime(&st);
st.wDay++;
// (out-of-scope) GeneralBug: Not checking is OK, no struct manipulation
SystemTimeToFileTime(&st, &ft);
}
/**
* Positive Cases
* - Anti-pattern 1: [year +-n, month, day]
* - Generic (Out of Scope) - UncheckedReturnValueForTimeFunctions
*/
void AntiPattern_unchecked_filetime_conversion2a()
{
SYSTEMTIME st;
FILETIME ft;
GetSystemTime(&st);
// BUG - UncheckedLeapYearAfterYearModification
st.wYear += 2; // $ Alert[cpp/leap-year/unchecked-after-arithmetic-year-modification]
// BUG - UncheckedReturnValueForTimeFunctions
SystemTimeToFileTime(&st, &ft);
}
/**
* Positive Cases
* - Anti-pattern 1: [year +-n, month, day]
* - Generic (Out of Scope) - UncheckedReturnValueForTimeFunctions
*/
void AntiPattern_unchecked_filetime_conversion2b()
{
SYSTEMTIME st;
FILETIME ft;
GetSystemTime(&st);
// BUG - UncheckedLeapYearAfterYearModification
st.wYear++; // $ Alert[cpp/leap-year/unchecked-after-arithmetic-year-modification]
// BUG - UncheckedReturnValueForTimeFunctions
SystemTimeToFileTime(&st, &ft);
}
/**
* Positive Cases
* - Anti-pattern 1: [year +-n, month, day]
* - Generic (Out of Scope) - UncheckedReturnValueForTimeFunctions
*/
void AntiPattern_unchecked_filetime_conversion2b(SYSTEMTIME* st)
{
FILETIME ft;
// BUG - UncheckedLeapYearAfterYearModification
st->wYear++; // $ Alert[cpp/leap-year/unchecked-after-arithmetic-year-modification]
// BUG - UncheckedReturnValueForTimeFunctions
SystemTimeToFileTime(st, &ft);
}
/**
* Positive Cases
* - Anti-pattern 3: datetime.AddDays(+-28)
* - Generic (Out of Scope) - UncheckedReturnValueForTimeFunctions
*/
void AntiPattern_unchecked_filetime_conversion3()
{
SYSTEMTIME st;
FILETIME ft;
GetSystemTime(&st);
if (st.wMonth < 12)
{
// Anti-pattern 3: datetime.AddDays(+-28)
st.wMonth++;
}
else
{
// No check for leap year is required here, as the month is statically set to January.
st.wMonth = 1;
st.wYear++;
}
// (out-of-scope) GeneralBug: Not checking if newly generated date is valid for conversion
SystemTimeToFileTime(&st, &ft);
}
//////////////////////////////////////////////
/**
* Negative Case - Anti-pattern 1: [year +-n, month, day]
* Year is incremented and if we are on Feb the 29th, set to the 28th if the new year is a common year.
*/
void CorrectPattern_check1()
{
SYSTEMTIME st;
GetSystemTime(&st);
st.wYear++;
// Guard against February the 29th
if (st.wMonth == 2 && st.wDay == 29)
{
// move back a day when landing on Feb 29 in an non-leap year
bool isLeapYear = st.wYear % 4 == 0 && (st.wYear % 100 != 0 || st.wYear % 400 == 0);
if (!isLeapYear)
{
st.wDay = 28;
}
}
// Safe to use
AntiPattern_unchecked_filetime_conversion(st);
}
/**
* Negative Case - Anti-pattern 1: [year +-n, month, day]
* Years is incremented by some integer and then the leap year case is correctly guarded and handled.
*/
void CorrectPattern_check2(int yearsToAdd)
{
SYSTEMTIME st;
GetSystemTime(&st);
st.wYear += yearsToAdd;
// Guard
bool isLeapYear = st.wYear % 4 == 0 && (st.wYear % 100 != 0 || st.wYear % 400 == 0);
st.wDay = st.wMonth == 2 && st.wDay == 29 && !isLeapYear ? 28 : st.wDay;
// Safe to use
AntiPattern_unchecked_filetime_conversion(st);
}
/**
* Could give rise to AntiPattern 7: IsLeapYear (Conditional Logic)
*/
bool isLeapYear(SYSTEMTIME& st)
{
return st.wYear % 4 == 0 && (st.wYear % 100 != 0 || st.wYear % 400 == 0);
}
/**
* Negative Case - Anti-pattern 1: [year +-n, month, day]
* Years is incremented by some integer and then the leap year case is correctly guarded and handled.
*/
void CorrectPattern_check3()
{
SYSTEMTIME st;
GetSystemTime(&st);
st.wYear++;
// Guard
/** Negative Case - Anti-pattern 7: IsLeapYear
* Body of conditional statement is safe recommended code
*/
if (st.wMonth == 2 && st.wDay == 29 && isLeapYear(st))
{
// move back a day when landing on Feb 29 in an non-leap year
st.wDay = 28;
}
// Safe to use
AntiPattern_unchecked_filetime_conversion(st);
}
/**
* Could give rise to AntiPattern 7: IsLeapYear (Conditional Logic)
*/
bool isLeapYear2(int year)
{
return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
}
bool fixDate(int day, int month, int year)
{
return (month == 2 && day == 29 && isLeapYear2(year));
}
/**
* Negative Case - Anti-pattern 1: [year +-n, month, day]
* Years is incremented by some integer and then the leap year case is correctly guarded and handled.
*/
void CorrectPattern_check4()
{
SYSTEMTIME st;
GetSystemTime(&st);
///// FP
st.wYear++;
// Guard
/** Negative Case - Anti-pattern 7: IsLeapYear
* Body of conditional statement is safe recommended code
*/
if (fixDate(st.wDay, st.wMonth, st.wYear))
{
// move back a day when landing on Feb 29 in an non-leap year
st.wDay = 28; // GOOD [FALSE POSITIVE] Anti-pattern 7
}
// Safe to use
AntiPattern_unchecked_filetime_conversion(st);
}
/**
* Negative Case - Generic
* No manipulation is conducted on struct populated from GetSystemTime.
*/
void CorrectPattern_NotManipulated_DateFromAPI_0()
{
SYSTEMTIME st;
GetSystemTime(&st);
FILETIME ft;
// Not checking is OK, no struct manipulation
SystemTimeToFileTime(&st, &ft);
}
/**
* Negative Case - Generic
* No manipulation is conducted on struct populated from GetFileTime.
*/
void CorrectPattern_NotManipulated_DateFromAPI_1(HANDLE hWatchdog)
{
SYSTEMTIME st;
FILETIME ft;
GetFileTime(hWatchdog, NULL, NULL, &ft);
FileTimeToSystemTime(&ft, &st);
}
/////////////////////////////////////////////////////////////////
/**
* Negative Case - Anti-pattern 1: [year +-n, month, day]
* Year is incremented by some integer and checked through a conversion through an inter procedural function check
*/
void AntiPattern_1_year_addition()
{
SYSTEMTIME st;
GetSystemTime(&st);
// Safe, checked interprocedurally through Correct_filetime_conversion_check
st.wYear++;
// Usage of potentially invalid date
Correct_filetime_conversion_check(st);
}
/**
* Negative Case - Anti-pattern 1: [year +-n, month, day]
* Years is incremented by some integer and checked through a conversion through an inter procedural function check
*/
void AntiPattern_simple_addition(int yearAddition)
{
SYSTEMTIME st;
GetSystemTime(&st);
st.wYear += yearAddition;
// Usage of potentially invalid date
Correct_filetime_conversion_check(st);
}
/**
* Positive Case - Anti-pattern 1: [year +-n, month, day]
* Years is incremented by some integer but a leap year is not handled *correctly*.
*/
void AntiPattern_IncorrectGuard(int yearsToAdd)
{
SYSTEMTIME st;
GetSystemTime(&st);
// BUG - UncheckedLeapYearAfterYearModification
st.wYear += yearsToAdd; // $ Alert[cpp/leap-year/unchecked-after-arithmetic-year-modification]
// Incorrect Guard
if (st.wMonth == 2 && st.wDay == 29)
{
// Part of a different anti-pattern (AntiPattern 5).
// Make sure the guard includes the proper check
bool isLeapYear = st.wYear % 4 == 0;
if (!isLeapYear)
{
st.wDay = 28;
}
}
}
/*************************************************
struct tm Cases
*************************************************/
void CorrectUsageOf_mkgmtime(struct tm& timeinfo)
{
if (_mkgmtime(&timeinfo) == -1)
{
/// Something failed, handle error
return;
}
/// _mkgmtime succeeded
}
/**
* Positive Case - General (Out of Scope)
* Must Check for return value of _mkgmtime
*/
void AntiPattern_uncheckedUsageOf_mkgmtime(struct tm& timeinfo)
{
// (out-of-scope) GeneralBug: Must check return value for _mkgmtime
// QLNOTE: Include other related _mkgmtime* functions in the function name pattern
_mkgmtime(&timeinfo);
// _mktime64(&timeinfo);
// _mktime32(&timeinfo);
}
//////////////////////////////////////////////////////////
/**
* Negative Case - Anti-pattern 1: [year +-n, month, day]
* Years is incremented by some integer and leap year is not handled correctly.
*/
void Correct_year_addition_struct_tm()
{
time_t rawtime;
struct tm timeinfo;
time(&rawtime);
// NOTE: Should ideally check return value for this function (not in scope)
errno_t err = gmtime_s(&timeinfo, &rawtime);
if (err)
{
/// handle error
return;
}
// this is the potentially dangerous part, when not followed up with leap year checks
timeinfo.tm_year++;
// Guard
// move back a day when landing on Feb 29 in an non-leap year
bool isLeapYear = timeinfo.tm_year % 4 == 0 && (timeinfo.tm_year % 100 != 0 || (timeinfo.tm_year + 1900) % 400 == 0);
timeinfo.tm_mday = timeinfo.tm_mon == 1 && timeinfo.tm_mday == 29 && !isLeapYear ? 28 : timeinfo.tm_mday;
// safe to use
AntiPattern_uncheckedUsageOf_mkgmtime(timeinfo);
}
/**
* Positive Case - Anti-pattern 1: [year +-n, month, day]
* Years is incremented by some integer and leap year is not handled correctly.
*/
void Incorrect_LinuxPattern()
{
time_t rawtime;
struct tm timeinfo;
time(&rawtime);
// NOTE: Should ideally check return value for this function (not in scope)
errno_t err = gmtime_s(&timeinfo, &rawtime);
/* from 1900 -> from 1980 */
// BUG - UncheckedLeapYearAfterYearModification
timeinfo.tm_year -= 80;
/* 0~11 -> 1~12 */
timeinfo.tm_mon++;
/* 0~59 -> 0~29(2sec counts) */
timeinfo.tm_sec >>= 1;
// safe to use
AntiPattern_uncheckedUsageOf_mkgmtime(timeinfo);
}
//////////////////////////////////////////
/**
* Negative Case - Anti-pattern 1: [year +-n, month, day]
* Years is incremented by some integer and leap year is assumed checked through
* check of a conversion functions return value.
*/
void AntiPattern_year_addition_struct_tm()
{
time_t rawtime;
struct tm timeinfo;
time(&rawtime);
gmtime_s(&timeinfo, &rawtime);
timeinfo.tm_year++;
// mkgmtime result checked in nested call here, assume leap year conversion is potentially handled
CorrectUsageOf_mkgmtime(timeinfo);
}
/////////////////////////////////////////////////////////
/**
* Positive Case - Anti-pattern 1: [year +-n, month, day]
*/
void test(int x)
{
struct tm timeinfo;
SYSTEMTIME st;
timeinfo.tm_year = x;
// BUG - UncheckedLeapYearAfterYearModification
// Positive Case - Anti-pattern 1: [year +-n, month, day]
timeinfo.tm_year = x + timeinfo.tm_year; // $ Alert[cpp/leap-year/unchecked-after-arithmetic-year-modification]
st.wYear = x;
// BUG - UncheckedLeapYearAfterYearModification
// Positive Case - Anti-pattern 1: [year +-n, month, day]
st.wYear = x + st.wYear; // $ Alert[cpp/leap-year/unchecked-after-arithmetic-year-modification]
}
/**
* Positive AntiPattern 1 NOTE: historically considered positive but mktime checks year validity, needs re-assessment
* Year field is modified but via an intermediary variable.
*/
void tp_intermediaryVar(struct timespec now, struct logtime &timestamp_remote)
{
struct tm tm_parsed;
struct tm tm_now;
time_t t_now;
int year;
/*
* As the timestamp does not contain the year
* number, daylight saving time information, nor
* a time zone, attempt to infer it. Due to
* clock skews, the timestamp may even be part
* of the next year. Use the last year for which
* the timestamp is at most one week in the
* future.
*
* This loop can only run for at most three
* iterations before terminating.
*/
t_now = now.tv_sec;
localtime_r(&t_now, &tm_now);
timestamp_remote.tm = tm_parsed;
timestamp_remote.tm.tm_isdst = -1;
timestamp_remote.usec = now.tv_nsec * 0.001;
for (year = tm_now.tm_year + 1;; --year)
{
// assert(year >= tm_now.tm_year - 1);
timestamp_remote.tm.tm_year = year;
if (mktime(&timestamp_remote.tm) < t_now + 7 * 24 * 60 * 60)
break;
}
}
// False positive
inline void
IncrementMonth(LPSYSTEMTIME pst)
{
if (pst->wMonth < 12)
{
pst->wMonth++;
}
else
{
pst->wMonth = 1;
pst->wYear++;
}
}
/////////////////////////////////////////////////////////
void mkDateTest(int year)
{
struct tm t;
t.tm_sec = 0;
t.tm_min = 0;
t.tm_hour = 0;
t.tm_mday = 1; // day of the month - [1, 31]
t.tm_mon = 0; // months since January - [0, 11]
if (year >= 1900)
{
// 4-digit year
t.tm_year = year - 1900; // GOOD
}
else if ((year >= 0) && (year < 100))
{
// 2-digit year assumed in the range 2000 - 2099
t.tm_year = year + 100; // GOOD [FALSE POSITIVE]
}
else
{
// fail
}
// ...
}
/**
* Negative Case - Anti-pattern 1a: [a.year, b.month, b.day]
* False positive: No modification of SYSTEMTIME struct.
*/
void unmodified1()
{
SYSTEMTIME st;
FILETIME ft;
WORD w;
GetSystemTime(&st);
w = st.wYear;
SystemTimeToFileTime(&st, &ft); // GOOD - no modification
}
/**
* Negative Case - Anti-pattern 1a: [a.year, b.month, b.day]
* False positive: No modification of SYSTEMTIME struct.
*/
void unmodified2()
{
SYSTEMTIME st;
FILETIME ft;
WORD *w_ptr;
GetSystemTime(&st);
w_ptr = &(st.wYear);
SystemTimeToFileTime(&st, &ft); // GOOD - no modification
}
/**
* Positive Case - Anti-pattern 1: [year +-n, month, day]
* Modification of SYSTEMTIME struct adding to year but no leap year guard is conducted.
*/
void modified3()
{
SYSTEMTIME st;
FILETIME ft;
WORD *w_ptr;
GetSystemTime(&st);
// BUG - UncheckedLeapYearAfterYearModification
st.wYear = st.wYear + 1; // $ Alert[cpp/leap-year/unchecked-after-arithmetic-year-modification]
SystemTimeToFileTime(&st, &ft);
}
/**
* Positive Case - Anti-pattern 1: [year +-n, month, day]
* Modification of SYSTEMTIME struct adding to year but no leap year guard is conducted.
*/
void modified4()
{
SYSTEMTIME st;
FILETIME ft;
WORD *w_ptr;
GetSystemTime(&st);
// BUG - UncheckedLeapYearAfterYearModification
st.wYear++; // $ Alert[cpp/leap-year/unchecked-after-arithmetic-year-modification]
SystemTimeToFileTime(&st, &ft);
}
/**
* Negative Case - Anti-pattern 1: [year +-n, month, day]
* Modification of SYSTEMTIME struct adding to year but value passed to a
* conversion function that can be checked for success, and the result is checked.
*/
void modified5()
{
SYSTEMTIME st;
FILETIME ft;
WORD *w_ptr;
GetSystemTime(&st);
st.wYear++;
// Presumed safe usage, as if the conversion is incorrect, a user can handle the error.
// NOTE: it doesn't mean the user actually does the correct conversion and it it also
// doesn't mean it will error our in all cases that may be invalid.
// For example, if a leap year and the date is 28, we may want 29 if the time is meant
// to capture the end of the month, but 28 is still valid and will not error out.
if (SystemTimeToFileTime(&st, &ft))
{
///...
}
}
/**
* Negative Case - Anti-pattern 1: [year +-n, month, day]
* Modification of SYSTEMTIME struct by copying from another struct, but no arithmetic is performed.
*/
bool
FMAPITimeToSysTimeW(LPCWSTR wszTime, SYSTEMTIME *psystime)
{
// if (!wszTime || SafeIsBadReadPtr(wszTime, 1) || lstrlenW(wszTime) != cchMAPITime)
// return false;
// AssertTag(!SafeIsBadWritePtr(psystime, sizeof(SYSTEMTIME)), 0x0004289a /* tag_abc80 */);
// memset(psystime, 0, sizeof(SYSTEMTIME));
psystime->wYear = (WORD)_wtoi(wszTime);
psystime->wMonth = (WORD)_wtoi(wszTime+5);
psystime->wDay = (WORD)_wtoi(wszTime+8);
psystime->wHour = (WORD)_wtoi(wszTime+11);
psystime->wMinute = (WORD)_wtoi(wszTime+14);
return true;
}
/**
* Negative Case - Anti-pattern 1: [year +-n, month, day]
* Modification of SYSTEMTIME struct by copying from another struct, but no arithmetic is performed.
*/
void fp_daymonth_guard(){
SYSTEMTIME st;
FILETIME ft;
GetSystemTime(&st);
// FALSE POSITIVE: year is incremented but month is checked and day corrected
// in a ternary operation. It may be possible to fix this with a more sophisticated
// data flow analysis.
st.wYear++; // $ SPURIOUS: Alert[cpp/leap-year/unchecked-after-arithmetic-year-modification]
st.wDay = st.wMonth == 2 && st.wDay == 29 ? 28 : st.wDay;
SystemTimeToFileTime(&st, &ft);
}
void increment_arg(WORD &x){
x++; // $ Source
}
void increment_arg_by_pointer(WORD *x){
(*x)++; // $ Source
}
void fn_year_set_through_out_arg(){
SYSTEMTIME st;
GetSystemTime(&st);
// BAD, year incremented without check
increment_arg(st.wYear); // $ Alert[cpp/leap-year/unchecked-after-arithmetic-year-modification]
// GetSystemTime(&st);
// Bad, year incremented without check
increment_arg_by_pointer(&st.wYear); // $ Alert[cpp/leap-year/unchecked-after-arithmetic-year-modification]
}
/* TODO: don't alert on simple copies from another struct where all three {year,month,day} are copied
void
GetEpochTime(struct pg_tm *tm)
{
struct pg_tm *t0;
pg_time_t epoch = 0;
t0 = pg_gmtime(&epoch);
tm->tm_year = t0->tm_year;
tm->tm_mon = t0->tm_mon;
tm->tm_mday = t0->tm_mday;
tm->tm_hour = t0->tm_hour;
tm->tm_min = t0->tm_min;
tm->tm_sec = t0->tm_sec;
tm->tm_year += 1900;
tm->tm_mon++;
} */
void
fp_guarded_by_month(struct pg_tm *tm){
int woy = 52;
int MONTHS_PER_YEAR = 12;
/*
* If it is week 52/53 and the month is January, then the
* week must belong to the previous year. Also, some
* December dates belong to the next year.
*/
if (woy >= 52 && tm->tm_mon == 1)
--tm->tm_year; // Negative Test Case
if (woy <= 1 && tm->tm_mon == MONTHS_PER_YEAR)
++tm->tm_year; // Negative Test Case
}
typedef unsigned short CSHORT;
typedef struct _TIME_FIELDS {
CSHORT Year;
CSHORT Month;
CSHORT Day;
CSHORT Hour;
CSHORT Minute;
CSHORT Second;
CSHORT Milliseconds;
CSHORT Weekday;
} TIME_FIELDS, *PTIME_FIELDS;
void
tp_ptime(PTIME_FIELDS ptm){
ptm->Year = ptm->Year - 1; // $ Alert[cpp/leap-year/unchecked-after-arithmetic-year-modification]
}
bool isLeapYearRaw(WORD year)
{
return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
}
void leap_year_checked_raw_false_positive1(WORD year, WORD offset, WORD day){
struct tm tmp;
year += offset;
if (isLeapYearRaw(year)){
// in this simplified example, assume the logic of this function
// can assume a day is 28 by default
// this check is more to establish the leap year guard is present
day += 1;
}
// Assume the check handled leap year correctly
tmp.tm_year = year; // GOOD
tmp.tm_mday = day;
}
void leap_year_checked_raw_false_positive2(WORD year, WORD offset, WORD day){
struct tm tmp;
year += offset;
tmp.tm_year = year; // GOOD, check performed immediately after on raw year
// Adding some additional checks to resemble cases observed in the wild
if ( day > 0)
{
if (isLeapYearRaw(year)){
// Assume logic that would adjust the day correctly
}
}
else{
if (isLeapYearRaw(year)){
// Assume logic that would adjust the day correctly
}
}
tmp.tm_mday = day;
year += offset; // $ Source
tmp.tm_year = year; // $ Alert[cpp/leap-year/unchecked-after-arithmetic-year-modification]
}
bool isNotLeapYear(struct tm tm)
{
return !(tm.tm_year % 4 == 0 && (tm.tm_year % 100 != 0 || tm.tm_year % 400 == 0));
}
bool isNotLeapYear2(struct tm tm)
{
return (tm.tm_year % 4 != 0 || (tm.tm_year % 100 == 0 && tm.tm_year % 400 != 0));
}
void inverted_leap_year_check(WORD year, WORD offset, WORD day){
struct tm tmp;
tmp.tm_year = year + offset;
if (isNotLeapYear(tmp)){
day = 28;
}
tmp.tm_year = year + offset;
if(isNotLeapYear2(tmp)){
day = 28;
}
tmp.tm_year = year + offset;
bool isNotLeapYear = (tmp.tm_year % 4 != 0 || (tmp.tm_year % 100 == 0 && tmp.tm_year % 400 != 0));
if(isNotLeapYear){
day = 28;
}
tmp.tm_year = year + offset; // $ Alert[cpp/leap-year/unchecked-after-arithmetic-year-modification]
}
void simplified_leap_year_check1(WORD year, WORD offset){
struct tm tmp;
tmp.tm_year = year + offset; // OK
bool isLeap = (!((tmp.tm_year + 1900) % 4)) && ((tmp.tm_year + 1900) % 100 || !((tmp.tm_year + 1900) % 400));
if(isLeap){
// do something
}
// Modified after check, could be dangerous
tmp.tm_year = year + offset; // $ Alert[cpp/leap-year/unchecked-after-arithmetic-year-modification]
}
void simplified_leap_year_check2(WORD year, WORD offset){
struct tm tmp;
tmp.tm_year = year + offset; // OK
bool isNotLeap = ((tmp.tm_year + 1900) % 4) || (!((tmp.tm_year + 1900) % 100) && ((tmp.tm_year + 1900) % 400));
if(isNotLeap){
// do something
}
// Modified after check, could be dangerous
tmp.tm_year = year + offset; // $ Alert[cpp/leap-year/unchecked-after-arithmetic-year-modification]
}
void simplified_leap_year_check3(WORD year, WORD offset){
SYSTEMTIME tmp;
tmp.wYear = year + offset; // OK
bool isLeap = (!(tmp.wYear % 4)) && (tmp.wYear % 100 || !(tmp.wYear% 400));
if(isLeap){
// do something
}
// Modified after check, could be dangerous
tmp.wYear = year + offset; // $ Alert[cpp/leap-year/unchecked-after-arithmetic-year-modification]
}
void simplified_leap_year_check4(WORD year, WORD offset){
SYSTEMTIME tmp;
tmp.wYear = year + offset; // OK
bool isNotLeap = (tmp.wYear % 4) || (!(tmp.wYear % 100) && (tmp.wYear % 400));
if(isNotLeap){
// do something
}
// Modified after check, could be dangerous
tmp.wYear = year + offset; // $ Alert[cpp/leap-year/unchecked-after-arithmetic-year-modification]
}
void bad_simplified_leap_year_check1(WORD year, WORD offset){
struct tm tmp;
tmp.tm_year = year + offset; // $ Alert[cpp/leap-year/unchecked-after-arithmetic-year-modification]
// incorrect logic, should negate the %4 result
bool isLeap = ((tmp.tm_year + 1900) % 4) && ((tmp.tm_year + 1900) % 100 || !((tmp.tm_year + 1900) % 400));
if(isLeap){
// do something
}
}
void bad_simplified_leap_year_check2(WORD year, WORD offset){
struct tm tmp;
tmp.tm_year = year + offset; // $ Alert[cpp/leap-year/unchecked-after-arithmetic-year-modification]
// incorrect logic, should not negate the %4 result
bool isNotLeap = (!((tmp.tm_year + 1900) % 4)) || (!((tmp.tm_year + 1900) % 100) && ((tmp.tm_year + 1900) % 400));
if(isNotLeap){
// do something
}
}
void bad_simplified_leap_year_check3(WORD year, WORD offset){
SYSTEMTIME tmp;
tmp.wYear = year + offset; // $ Alert[cpp/leap-year/unchecked-after-arithmetic-year-modification]
// incorrect logic, should negate the %4 result
bool isLeap = (tmp.wYear % 4) && (tmp.wYear % 100 || !(tmp.wYear % 400));
if(isLeap){
// do something
}
}
void bad_simplified_leap_year_check4(WORD year, WORD offset){
SYSTEMTIME tmp;
tmp.wYear = year + offset; // $ Alert[cpp/leap-year/unchecked-after-arithmetic-year-modification]
// incorrect logic, should not negate the %4 result
bool isNotLeap = (!(tmp.wYear % 4)) || (!(tmp.wYear % 100) && (tmp.wYear % 400));
if(isNotLeap){
// do something
}
}
void compound_leap_year_check(WORD year, WORD offset, WORD month, WORD day){
struct tm tmp;
tmp.tm_year = year + offset;
bool isLeap = tmp.tm_year % 4 == 0 && (tmp.tm_year % 100 != 0 || tmp.tm_year % 400 == 0) && (month == 2 && day == 29);
if(isLeap){
// do something
}
tmp.tm_mday = day;
tmp.tm_mon = month;
}
void indirect_time_conversion_check(WORD year, WORD offset){
SYSTEMTIME tmp;
tmp.wYear = year + offset;
FILETIME ft;
// (out-of-scope) GeneralBug: Unchecked call to SystemTimeToFileTime. this may have failed, but we didn't check the return value!
BOOL res = SystemTimeToFileTime(&tmp, &ft);
// Assume this check of the result is sufficient as an implicit leap year check.
bool x = (res == 0) ? true : false;
}
void set_time(WORD year, WORD month, WORD day){
SYSTEMTIME tmp;
tmp.wYear = year; // $ Alert[cpp/leap-year/unchecked-after-arithmetic-year-modification]
tmp.wMonth = month;
tmp.wDay = day;
}
void constant_month_on_year_modification1(WORD year, WORD offset, WORD month){
SYSTEMTIME tmp;
if(month++ > 12){
tmp.wMonth = 1;
tmp.wYear = year + 1;// OK since the year is incremented with a known non-leap year month change
}
if(month++ > 12){
set_time(year+1, 1, 31);// OK since the year is incremented with a known non-leap year month change
}
}
void constant_month_on_year_modification2(WORD year, WORD offset, WORD month){
SYSTEMTIME tmp;
if(month++ > 12){
tmp.wMonth = 1;
tmp.wYear = year + 1;// OK since the year is incremented with a known non-leap year month change
}
if(month++ > 12){
// some heuristics to detect a false positive here rely on variable names
// which is often consistent in the wild.
// This variant uses the variable names yeartmp and monthtmp
WORD yeartmp;
WORD monthtmp;
yeartmp = year + 1;
monthtmp = 1;
set_time(yeartmp, monthtmp, 31);// OK since the year is incremented with a known non-leap year month change
}
}
typedef struct parent_struct {
SYSTEMTIME t;
} PARENT_STRUCT;
void nested_time_struct(WORD year, WORD offset){
PARENT_STRUCT ps;
ps.t.wYear = year + offset; // OK, checked below
bool isLeap = isLeapYearRaw(ps.t.wYear);
if(isLeap){
// do something
}
}
void intermediate_time_struct(WORD year, WORD offset){
SYSTEMTIME tm, tm2;
FILETIME ftTime;
tm.wYear = year + offset;
tm2.wYear = tm.wYear;
while ( !SystemTimeToFileTime( &tm2, &ftTime ) )
{
/// handle error
}
}
void constant_day_on_year_modification1(WORD year, WORD offset, WORD month){
SYSTEMTIME tmp;
if(month++ > 12){
tmp.wDay = 1;
tmp.wYear = year + 1;// OK since the year is incremented with a known non-leap year day
}
if(month++ > 12){
set_time(year+1, month, 1);// OK since the year is incremented with a known non-leap year day
}
if(month++ > 12){
// BAD, year incremented, month unknown in block, and date is set to 31
// which is dangerous.
set_time(year+1, month, 31);// $ Source
}
}
void constant_day_on_year_modification2(WORD year, WORD month){
SYSTEMTIME tmp;
// FLASE POSITIVE SOURCE:
// flowing into set_time, the set time does pass a constant day
// but the source here and the source of that constant month don't align
// Current heuristics require the source of the constant day align with the
// source and/or the sink of the year modification.
// We could potentially improve this by checking the paths of both the year and day
// flows, but this may be more complex than is warranted for now.
year = year + 1; // $ SPURIOUS: Source
if(month++ > 12){
tmp.wDay = 1;
tmp.wYear = year;// OK since the year is incremented with a known non-leap year day
}
if(month++ > 12){
set_time(year, month, 1);// OK since the year is incremented with a known non-leap year day
}
year = year + 1; // $ Source
if(month++ > 12){
// BAD, year incremented, month unknown in block, and date is set to 31
// which is dangerous.
set_time(year, month, 31);
}
}
void modification_after_conversion1(tm timeinfo){
// convert a tm year into a civil year, then modify after conversion
// This case shows a false negative where the year might be used and it is incorrectly modified,
// and never reassigned to another struct.
WORD year = timeinfo.tm_year + 1900;
year += 1; // $ MISSING: Alert[cpp/leap-year/unchecked-after-arithmetic-year-modification]
}
WORD get_civil_year(tm timeinfo){
return timeinfo.tm_year + 1900;
}
void modification_after_conversion2(tm timeinfo){
// convert a tm year into a civil year, then modify after conversion
// This case shows a false negative where the year might be used and it is incorrectly modified,
// and never reassigned to another struct.
WORD year = get_civil_year(timeinfo);
year += 1; // $ MISSING: Alert[cpp/leap-year/unchecked-after-arithmetic-year-modification]
}
void modification_after_conversion_saved_to_other_time_struct1(tm timeinfo){
// convert a tm year into a civil year, then modify after conversion
// This case shows a false negative where the year might be used and it is incorrectly modified,
// and never reassigned to another struct.
WORD year = timeinfo.tm_year + 1900;
year += 1; // $ MISSING: Source
SYSTEMTIME s;
// FALSE NEGATIVE: missing this because the conversion happens locally before
// the year adjustment, which seems as though it is part of a conversion itself
s.wYear = year; // $ MISSING: Alert[cpp/leap-year/unchecked-after-arithmetic-year-modification]
}
void modification_after_conversion_saved_to_other_time_struct2(tm timeinfo){
// convert a tm year into a civil year, then modify after conversion
// This case shows a false negative where the year might be used and it is incorrectly modified,
// and never reassigned to another struct.
WORD year = get_civil_year(timeinfo);
year += 1; // $ Source
SYSTEMTIME s;
s.wYear = year; // $ Alert[cpp/leap-year/unchecked-after-arithmetic-year-modification]
}
void modification_after_conversion_saved_to_other_time_struct3(tm timeinfo){
// convert a tm year into a civil year, then modify after conversion
// This case shows a false negative where the year might be used and it is incorrectly modified,
// and never reassigned to another struct.
WORD year = timeinfo.tm_year + 1900;
year = year + 1; // $ MISSING: Source
SYSTEMTIME s;
// FALSE NEGATIVE: missing this because the conversion happens locally before
// the year adjustment, which seems as though it is part of a conversion itself
s.wYear = year; // $ MISSING: Alert[cpp/leap-year/unchecked-after-arithmetic-year-modification]
}
void year_saved_to_variable_then_modified1(tm timeinfo){
// A modified year is not directly assigned to the year, rather, the year is
// saved to a variable, modified, used, but never assigned back.
WORD year = timeinfo.tm_year;
// NOTE: should we even try to detect cases like this?
// Our current rationale is that a year in a struct is more dangerous than a year in isolation
// A year in isolation is harder to interpret
year += 1; // MISSING: $ Alert[cpp/leap-year/unchecked-after-arithmetic-year-modification]
}
void modification_before_conversion1(tm timeinfo){
timeinfo.tm_year += 1; // $ Alert[cpp/leap-year/unchecked-after-arithmetic-year-modification]
// convert a tm year into a civil year, then modify after conversion
// This case shows a false negative where the year might be used and it is incorrectly modified,
// and never reassigned to another struct.
WORD year = timeinfo.tm_year + 1900;
}
void modification_before_conversion2(tm timeinfo){
timeinfo.tm_year += 1; // $ Alert[cpp/leap-year/unchecked-after-arithmetic-year-modification]
// convert a tm year into a civil year, then modify after conversion
// This case shows a false negative where the year might be used and it is incorrectly modified,
// and never reassigned to another struct.
WORD year = get_civil_year(timeinfo);
}
void year_saved_to_variable_then_modified_with_leap_check1(tm timeinfo){
// A modified year is not directly assigned to the year, rather, the year is
// saved to a variable, modified, used, but never assigned back.
WORD year = timeinfo.tm_year;
year += 1;
// performing a check is considered good enough, even if not used correctly
bool b = (year+1900) % 4 == 0 && ((year+1900) % 100 != 0 || (year+1900) % 400 == 0);
}
void modification_after_conversion_with_leap_check1(tm timeinfo){
// convert a tm year into a civil year, then modify after conversion
// This case shows a false negative where the year might be used and it is incorrectly modified,
// and never reassigned to another struct.
WORD year = timeinfo.tm_year + 1900;
year += 1;
// performing a check is considered good enough, even if not used correctly
bool b = year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
}
void modification_after_conversion_with_leap_check2(tm timeinfo){
// convert a tm year into a civil year, then modify after conversion
// This case shows a false negative where the year might be used and it is incorrectly modified,
// and never reassigned to another struct.
WORD year = get_civil_year(timeinfo);
year += 1;
// performing a check is considered good enough, even if not used correctly
bool b = year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
}
void modification_before_conversion_with_leap_check1(tm timeinfo){
timeinfo.tm_year += 1;
// convert a tm year into a civil year, then modify after conversion
// This case shows a false negative where the year might be used and it is incorrectly modified,
// and never reassigned to another struct.
WORD year = timeinfo.tm_year + 1900;
// performing a check is considered good enough, even if not used correctly
bool b = year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
}
void modification_before_conversion_with_leap_check2(tm timeinfo){
timeinfo.tm_year += 1;
// convert a tm year into a civil year, then modify after conversion
// This case shows a false negative where the year might be used and it is incorrectly modified,
// and never reassigned to another struct.
WORD year = get_civil_year(timeinfo);
// performing a check is considered good enough, even if not used correctly
bool b = (year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0);
}
void odd_leap_year_check1(tm timeinfo){
timeinfo.tm_year += 1;
// Using an odd sytle of checking divisible by 4 presumably as an optimization trick
if(((timeinfo.tm_year+1900) & 3) == 0 && ((timeinfo.tm_year+1900) % 100 != 0 || (timeinfo.tm_year+1900) % 400 == 0))
{
// do something
}
}
void odd_leap_year_check2(tm timeinfo){
timeinfo.tm_year += 1; // $ SPURIOUS: Alert[cpp/leap-year/unchecked-after-arithmetic-year-modification]
// Using an odd sytle of checking divisible by 4 presumably as an optimization trick
// but also check unrelated conditions on the year as an optimization to rule out irrelevant years
// for gregorian leap years
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))
{
// do something
}
}
void odd_leap_year_check3(tm timeinfo){
timeinfo.tm_year += 1; // $ SPURIOUS: Alert[cpp/leap-year/unchecked-after-arithmetic-year-modification]
// Using an odd sytle of checking divisible by 4 presumably as an optimization trick
// but also check unrelated conditions on the year as an optimization to rule out irrelevant years
// for gregorian leap years
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))
{
// do something
}
}
void odd_leap_year_check4(tm timeinfo){
timeinfo.tm_year += 1;
WORD year = timeinfo.tm_year + 1900;
if( (year % 4 == 0) && (year % 100 > 0 || (year % 400 == 0)))
{
// do something
}
}
void odd_leap_year_check5(tm timeinfo){
timeinfo.tm_year += 1;
WORD year = timeinfo.tm_year + 1900;
if( (year % 4 > 0) || (year % 100 == 0 && (year % 400 > 0)))
{
// do something
}
}
void date_adjusted_through_mkgmtime(tm timeinfo){
timeinfo.tm_year += 1; // $ SPURIOUS: Alert[cpp/leap-year/unchecked-after-arithmetic-year-modification]
// Using an odd sytle of checking divisible by 4 presumably as an optimization trick
// but also check unrelated conditions on the year as an optimization to rule out irrelevant years
// for gregorian leap years
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))
{
// do something
}
}
bool data_killer(WORD *d){
(*d) = 1;
return true;
}
void interproc_data_killer1(tm timeinfo, WORD delta){
WORD year = delta + 1;
if(data_killer(&year)){
timeinfo.tm_year = year;
}
}
void leap_year_check_after_normalization(tm timeinfo, WORD delta){
WORD year = delta + 1;
if(data_killer(&year)){
timeinfo.tm_year = year;
}
}
void leap_year_check_call_on_conversion1(tm timeinfo){
timeinfo.tm_year += 1;
isLeapYearRaw(timeinfo.tm_year + 1900);
}
void leap_year_check_call_on_conversion2(tm timeinfo){
timeinfo.tm_year += 1;
WORD year = get_civil_year(timeinfo);
isLeapYearRaw(year);
}
WORD getDaysInMonth(WORD year, WORD month){
// simplified
if(month == 2){
return isLeapYearRaw(year) ? 29 : 28;
}
// else assume logic for every other month,
// returning 30 for simplicity
return 30;
}
WORD get_civil_year_raw(WORD year){
return year + 1900;
}
void leap_year_check_call_on_conversion3(tm timeinfo, WORD year, WORD month, WORD delta){
year += delta;
WORD days = getDaysInMonth(get_civil_year_raw(year), month);
timeinfo.tm_year = year;
}
void assumed_maketime_conversion1(tm timeinfo)
{
//the docs of mktime suggest feb29 is handled, and conversion will occur automatically
//no check required.
timeinfo.tm_year += 1;
mktime(&timeinfo);
}
void bad_leap_year_check_logic1(tm timeinfo){
timeinfo.tm_year += 1; // $ Alert[cpp/leap-year/unchecked-after-arithmetic-year-modification]
WORD year = get_civil_year(timeinfo);
// expected logic:
//(year % 4) && ((year % 100) || !(year % 400 )))
WORD days = (!(year % 4) && (!(year % 100) || (year % 400))) ? 366 : 365;
}