Skip to content

Commit

Permalink
Fix #2316, CFE_TIME_Print() calls strftime()
Browse files Browse the repository at this point in the history
  • Loading branch information
CDKnightNASA committed May 26, 2023
1 parent b429d91 commit 5100004
Show file tree
Hide file tree
Showing 6 changed files with 47 additions and 167 deletions.
8 changes: 7 additions & 1 deletion modules/core_api/fsw/inc/cfe_time.h
Original file line number Diff line number Diff line change
Expand Up @@ -691,6 +691,8 @@ CFE_Status_t CFE_TIME_UnregisterSynchCallback(CFE_TIME_SynchCallbackPtr_t Callba
** - \c \\0 = trailing null
**
** \par Assumptions, External Events, and Notes:
** - This function calls strftime to format the output. Systems without this
** C99-standard function will have to write their own implementation.
** - The value of the time argument is simply added to the configuration
** definitions for the ground epoch and converted into a fixed length
** string in the buffer provided by the caller.
Expand All @@ -706,8 +708,12 @@ CFE_Status_t CFE_TIME_UnregisterSynchCallback(CFE_TIME_SynchCallbackPtr_t Callba
**
** \param[in] TimeToPrint The time to print into the character array.
**
** \return Execution status, see \ref CFEReturnCodes
** \retval #CFE_SUCCESS \copybrief CFE_SUCCESS
** \retval #CFE_TIME_BAD_ARGUMENT \copybrief CFE_TIME_BAD_ARGUMENT
**
******************************************************************************/
void CFE_TIME_Print(char *PrintBuffer, CFE_TIME_SysTime_t TimeToPrint);
CFE_Status_t CFE_TIME_Print(char *PrintBuffer, CFE_TIME_SysTime_t TimeToPrint);

/*****************************************************************************/
/**
Expand Down
3 changes: 2 additions & 1 deletion modules/core_api/ut-stubs/src/cfe_time_handlers.c
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,14 @@
* Default handler for CFE_TIME_Print coverage stub function
*
*------------------------------------------------------------*/
void UT_DefaultHandler_CFE_TIME_Print(void *UserObj, UT_EntryKey_t FuncKey, const UT_StubContext_t *Context)
CFE_Status_t UT_DefaultHandler_CFE_TIME_Print(void *UserObj, UT_EntryKey_t FuncKey, const UT_StubContext_t *Context)
{
char * PrintBuffer = UT_Hook_GetArgValueByName(Context, "PrintBuffer", char *);
CFE_TIME_SysTime_t TimeToPrint = UT_Hook_GetArgValueByName(Context, "TimeToPrint", CFE_TIME_SysTime_t);

snprintf(PrintBuffer, CFE_TIME_PRINTED_STRING_SIZE, "UT %u.%u -", (unsigned int)TimeToPrint.Seconds,
(unsigned int)TimeToPrint.Subseconds);
return CFE_SUCCESS;
}

/*------------------------------------------------------------
Expand Down
6 changes: 5 additions & 1 deletion modules/core_api/ut-stubs/src/cfe_time_stubs.c
Original file line number Diff line number Diff line change
Expand Up @@ -305,12 +305,16 @@ uint32 CFE_TIME_Micro2SubSecs(uint32 MicroSeconds)
* Generated stub function for CFE_TIME_Print()
* ----------------------------------------------------
*/
void CFE_TIME_Print(char *PrintBuffer, CFE_TIME_SysTime_t TimeToPrint)
CFE_Status_t CFE_TIME_Print(char *PrintBuffer, CFE_TIME_SysTime_t TimeToPrint)

Check notice

Code scanning / CodeQL

Long function without assertion Note

All functions of more than 10 lines should have at least one assertion.
{
UT_GenStub_SetupReturnBuffer(CFE_TIME_Print, CFE_Status_t);

Check warning

Code scanning / CodeQL

Uses of recursion Warning

The function CFE_TIME_Print is indirectly recursive via this call to
UT_Stub_RegisterReturnType
.

UT_GenStub_AddParam(CFE_TIME_Print, char *, PrintBuffer);
UT_GenStub_AddParam(CFE_TIME_Print, CFE_TIME_SysTime_t, TimeToPrint);

UT_GenStub_Execute(CFE_TIME_Print, Basic, UT_DefaultHandler_CFE_TIME_Print);

return UT_GenStub_GetReturnValue(CFE_TIME_Print, CFE_Status_t);

Check warning

Code scanning / CodeQL

Uses of recursion Warning

The function CFE_TIME_Print is indirectly recursive via this call to
UT_Stub_GetReturnValuePtr
.
}

/*
Expand Down
15 changes: 3 additions & 12 deletions modules/time/config/default_cfe_time_interface_cfg.h
Original file line number Diff line number Diff line change
Expand Up @@ -151,23 +151,14 @@
** \cfetimecfg Default EPOCH Values
**
** \par Description:
** Default ground time epoch values
** Default ground time epoch values (from Jan. 1, 1970 00:00:00)
** Note: these values are used only by the CFE_TIME_Print() API function
**
** \par Limits
** Year - must be within 136 years
** Day - Jan 1 = 1, Feb 1 = 32, etc.
** Hour - 0 to 23
** Minute - 0 to 59
** Second - 0 to 59
** Micros - 0 to 999999
*/
#define CFE_MISSION_TIME_EPOCH_YEAR 1980
#define CFE_MISSION_TIME_EPOCH_DAY 1
#define CFE_MISSION_TIME_EPOCH_HOUR 0
#define CFE_MISSION_TIME_EPOCH_MINUTE 0
#define CFE_MISSION_TIME_EPOCH_SECOND 0
#define CFE_MISSION_TIME_EPOCH_MICROS 0
#define CFE_MISSION_TIME_EPOCH_SECONDS 315532800 /* Jan. 1, 1980 00:00:00 */
#define CFE_MISSION_TIME_EPOCH_MICROS 0

/**
** \cfetimecfg Time File System Factor
Expand Down
156 changes: 18 additions & 138 deletions modules/time/fsw/src/cfe_time_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@

#include <string.h>

#include <time.h>

/*----------------------------------------------------------------
*
* Implemented per public API
Expand Down Expand Up @@ -563,150 +565,28 @@ uint32 CFE_TIME_Micro2SubSecs(uint32 MicroSeconds)
* See description in header file for argument/return detail
*
*-----------------------------------------------------------------*/
void CFE_TIME_Print(char *PrintBuffer, CFE_TIME_SysTime_t TimeToPrint)
CFE_Status_t CFE_TIME_Print(char *PrintBuffer, CFE_TIME_SysTime_t TimeToPrint)

Check notice

Code scanning / CodeQL

Long function without assertion Note

All functions of more than 10 lines should have at least one assertion.
{
uint32 NumberOfYears;
uint32 NumberOfDays;
uint32 NumberOfHours;
uint32 NumberOfMinutes;
uint32 NumberOfSeconds;
uint32 NumberOfMicros;
uint32 DaysInThisYear;

bool StillCountingYears = true;
size_t FmtLen = 0;
uint32 Micros = (CFE_TIME_Sub2MicroSecs(TimeToPrint.Subseconds) + CFE_MISSION_TIME_EPOCH_MICROS) / 10;

if (PrintBuffer == NULL)
{
CFE_ES_WriteToSysLog("%s: Failed invalid arguments\n", __func__);
return;
}

/*
** Convert the cFE time (offset from epoch) into calendar time...
*/
NumberOfMicros = CFE_TIME_Sub2MicroSecs(TimeToPrint.Subseconds) + CFE_MISSION_TIME_EPOCH_MICROS;

NumberOfMinutes = (NumberOfMicros / 60000000) + (TimeToPrint.Seconds / 60) + CFE_MISSION_TIME_EPOCH_MINUTE;
NumberOfMicros = NumberOfMicros % 60000000;

NumberOfSeconds = (NumberOfMicros / 1000000) + (TimeToPrint.Seconds % 60) + CFE_MISSION_TIME_EPOCH_SECOND;
NumberOfMicros = NumberOfMicros % 1000000;
/*
** Adding the epoch "seconds" after computing the minutes avoids
** overflow problems when the input time value (seconds) is
** at, or near, 0xFFFFFFFF...
*/
while (NumberOfSeconds >= 60)
{
NumberOfMinutes++;
NumberOfSeconds -= 60;
}

/*
** Compute the years/days/hours/minutes...
*/
NumberOfHours = (NumberOfMinutes / 60) + CFE_MISSION_TIME_EPOCH_HOUR;
NumberOfMinutes = (NumberOfMinutes % 60);

/*
** Unlike hours and minutes, epoch days are counted as Jan 1 = day 1...
*/
NumberOfDays = (NumberOfHours / 24) + (CFE_MISSION_TIME_EPOCH_DAY - 1);
NumberOfHours = (NumberOfHours % 24);

NumberOfYears = CFE_MISSION_TIME_EPOCH_YEAR;

/*
** Convert total number of days into years and remainder days...
*/
while (StillCountingYears)
{
/*
** Set number of days in this year (leap year?)...
*/
DaysInThisYear = 365;

if ((NumberOfYears % 4) == 0)
{
if ((NumberOfYears % 100) != 0)
{
DaysInThisYear = 366;
}
else if ((NumberOfYears % 400) == 0)
{
DaysInThisYear = 366;
}
else
{
/* Do Nothing. Non-leap year. */
}
}

/*
** When we have less than a years worth of days, we're done...
*/
if (NumberOfDays < DaysInThisYear)
{
StillCountingYears = false;
}
else
{
/*
** Add a year and remove the number of days in that year...
*/
NumberOfYears++;
NumberOfDays -= DaysInThisYear;
}
return CFE_TIME_BAD_ARGUMENT;
}

/*
** Unlike hours and minutes, days are displayed as Jan 1 = day 1...
*/
NumberOfDays++;

/*
** After computing microseconds, convert to 5 digits from 6 digits...
*/
NumberOfMicros = NumberOfMicros / 10;

/*
** Build formatted output string (yyyy-ddd-hh:mm:ss.xxxxx)...
*/
*PrintBuffer++ = '0' + (char)(NumberOfYears / 1000);
NumberOfYears = NumberOfYears % 1000;
*PrintBuffer++ = '0' + (char)(NumberOfYears / 100);
NumberOfYears = NumberOfYears % 100;
*PrintBuffer++ = '0' + (char)(NumberOfYears / 10);
*PrintBuffer++ = '0' + (char)(NumberOfYears % 10);
*PrintBuffer++ = '-';

*PrintBuffer++ = '0' + (char)(NumberOfDays / 100);
NumberOfDays = NumberOfDays % 100;
*PrintBuffer++ = '0' + (char)(NumberOfDays / 10);
*PrintBuffer++ = '0' + (char)(NumberOfDays % 10);
*PrintBuffer++ = '-';

*PrintBuffer++ = '0' + (char)(NumberOfHours / 10);
*PrintBuffer++ = '0' + (char)(NumberOfHours % 10);
*PrintBuffer++ = ':';

*PrintBuffer++ = '0' + (char)(NumberOfMinutes / 10);
*PrintBuffer++ = '0' + (char)(NumberOfMinutes % 10);
*PrintBuffer++ = ':';

*PrintBuffer++ = '0' + (char)(NumberOfSeconds / 10);
*PrintBuffer++ = '0' + (char)(NumberOfSeconds % 10);
*PrintBuffer++ = '.';

*PrintBuffer++ = '0' + (char)(NumberOfMicros / 10000);
NumberOfMicros = NumberOfMicros % 10000;
*PrintBuffer++ = '0' + (char)(NumberOfMicros / 1000);
NumberOfMicros = NumberOfMicros % 1000;
*PrintBuffer++ = '0' + (char)(NumberOfMicros / 100);
NumberOfMicros = NumberOfMicros % 100;
*PrintBuffer++ = '0' + (char)(NumberOfMicros / 10);
*PrintBuffer++ = '0' + (char)(NumberOfMicros % 10);
*PrintBuffer++ = '\0';
time_t sec = TimeToPrint.Seconds + CFE_MISSION_TIME_EPOCH_SECONDS; // epoch is Jan 1, 1980
FmtLen = strftime(PrintBuffer, CFE_TIME_PRINTED_STRING_SIZE - 6, "%Y-%j-%H:%M:%S", gmtime(&sec));

Check failure

Code scanning / CodeQL

Use of potentially dangerous function Critical

Call to 'gmtime' is potentially dangerous.
PrintBuffer += FmtLen;
*(PrintBuffer++) = '.';

*(PrintBuffer++) = '0' + (char)((Micros % 100000) / 10000);
*(PrintBuffer++) = '0' + (char)((Micros % 10000) / 1000);
*(PrintBuffer++) = '0' + (char)((Micros % 1000) / 100);
*(PrintBuffer++) = '0' + (char)((Micros % 100) / 10);
*(PrintBuffer++) = '0' + (char)(Micros % 10);
*PrintBuffer = '\0';
return CFE_SUCCESS;
}

/*----------------------------------------------------------------
Expand Down
26 changes: 12 additions & 14 deletions modules/time/ut-coverage/time_UT.c
Original file line number Diff line number Diff line change
Expand Up @@ -821,7 +821,7 @@ void Test_ConvertTime(void)
**
** NOTE: Test results depend on the epoch values in cfe_mission_cfg.h (the
** tests below assume an epoch of 1980-001-00:00:00.00000). Full
** coverage is possible only when CFE_MISSION_TIME_EPOCH_SECOND > 0
** coverage is possible only when CFE_MISSION_TIME_EPOCH_SECONDS > 0
*/
void Test_Print(void)
{
Expand All @@ -834,20 +834,18 @@ void Test_Print(void)

UtPrintf("Begin Test Print");

if (CFE_MISSION_TIME_EPOCH_YEAR != 1980 || CFE_MISSION_TIME_EPOCH_DAY != 1 || CFE_MISSION_TIME_EPOCH_HOUR != 0 ||
CFE_MISSION_TIME_EPOCH_MINUTE != 0 || CFE_MISSION_TIME_EPOCH_SECOND != 0 || CFE_MISSION_TIME_EPOCH_MICROS != 0)
if (CFE_MISSION_TIME_EPOCH_SECONDS != 0 || CFE_MISSION_TIME_EPOCH_MICROS != 0)
{
UtPrintf("Custom epoch time requires manual inspection for CFE_TIME_Print");
usingDefaultEpoch = false;
}

/* Test print with null print buffer argument */
UT_InitData();
UtAssert_VOIDCALL(CFE_TIME_Print(NULL, time));
CFE_UtAssert_SYSLOG(TIME_SYSLOG_MSGS[6]);
UtAssert_INT32_EQ(CFE_TIME_Print(NULL, time), CFE_TIME_BAD_ARGUMENT);

/* Test with zero time value */
CFE_TIME_Print(timeBuf, time);
CFE_UtAssert_SUCCESS(CFE_TIME_Print(timeBuf, time));
if (usingDefaultEpoch)
{
strcpy(expectedBuf, "1980-001-00:00:00.00000");
Expand All @@ -860,12 +858,12 @@ void Test_Print(void)
}

/* Test with a time value that causes seconds >= 60 when
* CFE_MISSION_TIME_EPOCH_SECOND > 0
* CFE_MISSION_TIME_EPOCH_SECONDS > 0
*/
time.Subseconds = 0;
time.Seconds = 59;

CFE_TIME_Print(timeBuf, time);
CFE_UtAssert_SUCCESS(CFE_TIME_Print(timeBuf, time));
if (usingDefaultEpoch)
{
strcpy(expectedBuf, "1980-001-00:00:59.00000");
Expand All @@ -881,7 +879,7 @@ void Test_Print(void)
time.Subseconds = 215000;
time.Seconds = 1041472984;

CFE_TIME_Print(timeBuf, time);
CFE_UtAssert_SUCCESS(CFE_TIME_Print(timeBuf, time));
if (usingDefaultEpoch)
{
strcpy(expectedBuf, "2013-001-02:03:04.00005");
Expand All @@ -893,14 +891,14 @@ void Test_Print(void)
(unsigned int)time.Seconds, (unsigned int)time.Subseconds, timeBuf);
}

/* Test with maximum seconds and subseconds values */
time.Subseconds = 0xffffffff;
time.Seconds = 0xffffffff;
/* Test with sufficiently-large seconds and subseconds values */
time.Subseconds = 0x7fffffff;
time.Seconds = 0x7fffffff;

CFE_TIME_Print(timeBuf, time);
CFE_UtAssert_SUCCESS(CFE_TIME_Print(timeBuf, time));
if (usingDefaultEpoch)
{
strcpy(expectedBuf, "2116-038-06:28:15.99999");
strcpy(expectedBuf, "2048-019-03:14:07.49999");
UtAssert_STRINGBUF_EQ(timeBuf, sizeof(timeBuf), expectedBuf, sizeof(expectedBuf));
}
else
Expand Down

0 comments on commit 5100004

Please sign in to comment.