C99 provides the following sample implementation of the asctime()
function in Section 7.23.3.1:
char *asctime(const struct tm *timeptr) { static const char wday_name[7][3] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; static const char mon_name[12][3] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; static char result[26]; sprintf(result, "%.3s %.3s%3d %.2d:%.2d:%.2d %d\n", wday_name[timeptr->tm_wday], mon_name[timeptr->tm_mon], timeptr->tm_mday, timeptr->tm_hour, timeptr->tm_min, timeptr->tm_sec, 1900 + timeptr->tm_year); return result; }
This function is supposed to output a character string of 26 positions at most, including the terminating zero. If we count the length indicated by the format directives we arrive at 25. Taking into account the terminating zero, the array size of the string appears sufficient.
However, this implementation assumes that the values of the struct tm
data in timeptr
are within normal ranges, and does nothing to enforce this. If any of the values print more characters than expected, the sprintf()
function may overflow the result
array. For instance, if tm_year
has the value 12345, then 27 characters (including the terminating null character) are printed, resulting in a buffer overflow.
The asctime()
function primarily exists for compatibility with older implementations. Also, the asctime()
function does not support localized date and time formats. The POSIX standard developers decided to mark the asctime()
function obsolescent even though they are in C99 because of the possibility of buffer overflow.
C99 also provides the strftime()
function which can be used to avoid these problems.
Noncompliant Code Example
Avoid using the asctime()
function with unsanitized data.
struct tm time_tm; /* initialize time_tm */ char *time = asctime(&time_tm);
Compliant Solution
You can sanitize the data before invoking asctime()
.
int validate_tm(struct tm* time) { if (time->tm_sec < 0 || time->tm_sec >= 60) return 0; if (time->tm_min < 0 || time->tm_min >= 60) return 0; if (time->tm_hour < 0 || time->tm_hour >= 24) return 0; if (time->tm_mday <= 0 || time->tm_mday > 31) return 0; if (time->tm_mon < 0 || time->tm_mon >= 12) return 0; /* While other years are legit, they may overflow asctime()'s buffer */ if (time->tm_year < -999 || time->tm_year > 9999) return 0; if (time->tm_wday < 0 || time->tm_wday >= 7) return 0; if (time->tm_yday < 0 || time->tm_yday >= 366) return 0; return 1; } struct tm time_tm; /* initialize time_tm */ if (!validate_tm(&time_tm)) { /* handle error */ } char *time = asctime(&time_tm);
Compliant Solution
The strftime()
function allows you to specify a more rigorous format, and also to specify the maximum size of the resulting time string.
struct tm time; const size_t maxsize = 26; /* or maximum size of time string */; char s[maxsize]; const char *format = "%c"; /* current time representation for locale */ const struct tm *timeptr; size_t size = strftime(s, maxsize, format, timeptr);
While this call has the same effects as asctime()
, it ensures that no more than maxsize
chars are printed, preventing buffer overflow.
Risk Assessment
On implementations that do not detect output string length overflow, it is possible to overflow the output buffers resulting in a vulnerability.
Rule |
Severity |
Likelihood |
Remediation Cost |
Priority |
Level |
---|---|---|---|---|---|
MSC33-C |
high |
high |
low |
P9 |
L2 |
Automated Detection
Unknown.
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
References
[[ISO/IEC 9899:1999]] Section 7.23.3.1, "The asctime function" and Section 7.23.3.5, "The strftime function"
http://www.opengroup.org/onlinepubs/9699919799/functions/asctime.html
MSC32-C. Ensure your random number generator is properly seeded 49. Miscellaneous (MSC) MSC34-C. Do not use deprecated or obsolescent functions