You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 61 Next »

The C Standard states that modifying an object with temporary lifetime results in undefined behavior. This definition differs from the C99 Standard (which defines modifying the result of a function call or accessing it after the next sequence point as undefined behavior) because a temporary object's lifetime ends when the evaluation containing the full expression or full declarator ends, so the result of a function call can be accessed.

C functions may not return arrays; however, they may return structs or unions that contain arrays. Consequently, if a function call's return value contains an array, that array should never be modified within the expression containing the function call. In C99, it should also never be accessed.

Noncompliant Code Example

The following noncompliant code attempts to retrieve an array and increment the first value from a struct that is returned by a function call. Since the array is being modified, this results in undefined behavior.

#include <stdio.h>

struct X { int a[6]; };

struct X addressee(void) {
  struct X result = { 1, 2, 3, 4, 5, 6 };
  return result;
}

int main(void) {
  printf("%x", ++(addressee().a[0]));
  return 0;
}

Compliant Solution

This compliant solution stores the structure returned by the call to addressee() as my_x before calling the printf() function.

#include <stdio.h>

struct X { int a[6]; };

struct X addressee(void) {
  struct X result = { 1, 2, 3, 4, 5, 6 };
  return result;
}

int main(void) {
  struct X my_x = addressee();
  printf("%x", ++(addressee().a[0]));
  return 0;
}

Noncompliant Code Example (C99)

The following noncompliant code attempts to retrieve an array from a struct that is returned by a function call.

#include <stdio.h>

struct X { char a[6]; };

struct X addressee(void) {
  struct X result = { "world" };
  return result;
}

int main(void) {
  printf("Hello, %s!\n", addressee().a);
  return 0;
}

This solution is problematic in C99 because of three inherent properties of C:

  1. In C, the lifetime of a return value ends at the next sequence point. Consequently, by the time printf() is called, the struct returned by the addressee() call is no longer considered valid and could have been overwritten.
  2. C function arguments are passed by value. As a result, copies are made of all objects generated by the arguments. For example, a copy is made of the pointer to "Hello, %s!\n". Under most circumstances, these copies protect you from the effects of sequence points.
  3. Finally, C implicitly converts arrays to pointers when passing them as function arguments. This means that a copy is made of the pointer to the addresee().a array, and that pointer copy is passed to printf(). But the array data itself is not copied and could no longer exist when printf() is called.

Consequently, when printf() tries to dereference the pointer passed as its second argument, it is likely to find garbage.

Note that the behavior of this code in the C Standard is defined, because the lifetime of temporary objects extends to the full expression containing it.

Implementation Details

This code compiles cleanly and runs without error under Microsoft Visual C++ Version 8.0. On the GCC compiler version 4.2, the program compiles with a warning when the -Wall switch is used, and execution on Linux results in a segmentation fault. However, if the flag --std=c99 is passed to the GCC compiler, the program compiles with no warning and runs with no error.

Compliant Solution (C99)

This compliant solution stores the structure returned by the call to addressee() as my_x before calling the printf() function.

#include <stdio.h>

struct X { char a[6]; };

struct X addressee(void) {
  struct X result = { "world" };
  return result;
}

int main(void) {
  struct X my_x = addressee();
  printf("Hello, %s!\n", my_x.a);
  return 0;
}

Risk Assessment

Attempting to access or modify an array within a function after a subsequent sequence point may result in unexpected and perhaps unintended program behavior.

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

EXP35-C

low

probable

medium

P4

L3

Automated Detection

Tool

Version

Checker

Description

GCC

4.3.5

 

Can detect violations of this rule when the -Wall flag is used.

Splint

3.1.1

 

 

Related Vulnerabilities

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

Related Guidelines

ISO/IEC TR 24772Dangling references to stack frames [DCM]
Side-effects and order of evaluation [SAM]

 

  • No labels