mirror of
https://github.com/github/codeql.git
synced 2026-05-02 04:05:14 +02:00
1761 lines
45 KiB
C++
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 ×tamp_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(×tamp_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;
|
|
}
|