Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

    Background:
    The type , precision, and range of both time_t and clock_t are implementation defined. Local time zone and daylight savings time are also implementation defined. The Unix time standard can also vary slightly.. IE, the type of time_t and clock_t are precisely "It's a number guys!". It is therefore important to be very careful when using time_t and clock_t in C because assumptions can lead to problems ranging from errors in program timing to possible overflow from invalid type conversions. What follows are some recommendations that help one to avoid common pitfalls that cause security vulnerabilities.

    Recommendation #1:
    When comparing time_t, first cast the item you are comparing it with to a time_t 
    Traditionally, time_t is set to be a signed 32 bit integer type on Unix systems, but the C99 standard only requires that time_t is an arithmetic type. It is a common error (and temptation) to use integers interchangeably with time_t. However, doing so could lead to invalid comparisons in your code.

    Non-Compliant Code

is specified as an "arithmetic type capable of representing times." However, the way time is encoded within this arithmetic type by the function time() is unspecified. See unspecified behavior 48 in Annex J of the C Standard. Because the encoding is unspecified, there is no safe way to manually perform arithmetic on the type, and as a result, the values should not be modified directly.

Note that POSIX specifies that the time() function must return a value of type time_t, representing time in seconds since the Epoch. POSIX-conforming applications that are not intended to be portable to other environments therefore may safely perform arithmetic operations on time_t objects.

Noncompliant Code Example

This noncompliant code example attempts to execute do_work() multiple times until at least seconds_to_work has passed. However, because the encoding is not defined, there is no guarantee that adding start to seconds_to_work will result in adding seconds_to_work seconds.

Code Block
bgColor#FFCCCC
langc
int do_work(int seconds_to_work) {
  time_t start
Code Block

int main(void)
{
    time_t now = time(NULL);
   

  if (start now \!== \(time_t)(-1 )) {
        fputs(ctime(&now), stdout);
    }
       /* Handle error */
  }
  while (time(NULL) < start + seconds_to_work) {
    /* ... */
  }
  return 0;

}

    The c standard mandates that time() return (time_t)(-1). Some systems may interpret (time_t)(-1) as something completely different from the integer  -1. This could lead to potential invalid comparison and therefore invalid output (or worse, depending on what else is put in the if statement). Therefore the correct code is as follows:

    Compliant Code

Compliant Solution

This compliant solution uses difftime() to determine the difference between two time_t values. The difftime() function returns the number of seconds, from the second parameter until the first parameter and result, as a double.

Code Block
bgColor#ccccff
langc
int do_work(int seconds_to_work) {
  time_t start
Code Block

int main(void)
{
&nbsp;&nbsp;&nbsp; time_t now = time(NULL);
&nbsp;&nbsp;&nbsp;  time_t current = start;

  if (start now \!== (time_t)(-1 )) {
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; fputs(ctime(&now), stdout);
&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp; return 0;

}

    In the code above the comparison will function as expected.

    Recommendation #2:
   

It is also important to be mindful that as a result of time_t being implementation defined, performing arithmetic operations with integers or floating points may or may not change the time variable's type or overflow the variable. If on your system time_t is an integer, do not divide time_t by an integer if you want your code to work with other machines. In other words, when performing arithmetic operations on time_t, use other time_t's and not integers or floating points. The function mktime() can be used to generate time_t variables.  
    According to the C99 standard "The clock function returns the implementation's best approximation to the processor time used by the program since the beginning of an implementation-de?ned era related only to the program invocation. To determine the time in seconds, the value returned by the clock function should be divided by the value of the macro CLOCKS_PER_SEC..." However, the question remains as to what the proper returned type of this operation should be. If you wish to determine how long a certain process required to operate, the C99 standard recommends code of the following form:

Code Block

clock_t begin, end;
mystery_t mystery;
begin=clock();
/* run process */
end=clock();
mystery = (end-begin)/CLOCKS_PER_SEC

    The type of the macro CLOCKS_PER_SEC is traditionally an integer, however, the C99 standard makes no statement on its type. There is no guarantee as to the type or behavior of clock_t when divided by CLOCK_PER_SEC other than it is an arithmetic type. It is possible for longer processes to check by the standard resolution of time_t as follows:

Code Block

time_t begin, end;
time_t difference;
start=time();
/* run process */
end =time();
difference=end-start;

    The type of time_t-time_t is in fact time_t, and after this point you can convert the difference into a more palatable format for human parsing. However, you are limited to the resolution of time_t, which is probably considerably less useful than the resolution of clock_t.

Wiki Markup
&nbsp;\[This article is incomplete. It will be finished at a later date.\]

Credits/Interesting Links:

- The original idea for this came from the C Language Gotchas site, accessible here

- The wikipedia article on Unix Time is quite enlightening. Read it here

- An article about a denial-of-service in 64bit microsoft time code. Read it here 

    /* Handle error */
  }
  while (difftime(current, start) < seconds_to_work) {
    current = time(NULL);
    if (current == (time_t)(-1)) {
       /* Handle error */
    }
    /* ... */
  }
  return 0;
}

Note that this loop still might not exit because the range of time_t might not be able to represent two times seconds_to_work apart.

Risk Assessment

Using time_t incorrectly can lead to broken logic that can place a program in an infinite loop or cause an expected logic branch to not execute.

Recommendation

Severity

Likelihood

Remediation Cost

Priority

Level

MSC05-C

Low

Unlikely

Medium

P2

L3

Automated Detection

Tool

Version

Checker

Description

Compass/ROSE

 

 

Can detect violations of this recommendation

ECLAIR

Include Page
ECLAIR_V
ECLAIR_V

CC2.MSC05

Fully implemented

LDRA tool suite
Include Page
LDRA_V
LDRA_V
96 S, 101 S, 107 S, 433 S, 458 SPartially Implemented

Related Vulnerabilities

Search for vulnerabilities resulting from the violation of this rule on the CERT website.

Related Guidelines

Bibliography

[Kettlewell 2002]Section 4.1, "time_t"

 

...

Image Added Image Added Image Added- Interesting time_t discussion from which I pulled my example code. Read it here