Versions Compared

Key

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

Avoid excessive stack allocations, particularly in situations where the growth of the stack can be controlled or influenced by an attacker. See INT04-C. Enforce limits on integer values originating from tainted sources for more information on preventing attacker-controlled integers from exhausting memory.

Noncompliant Code Example

The C Standard includes support for variable length arrays (VLAs). If the array length is derived from an untrusted data source, an attacker can cause the process to perform an excessive allocation on the stack.

This noncompliant code example temporarily stores data read from a source file into a buffer. The buffer is allocated on the stack as a VLA of size bufsize. If bufsize can be controlled by a malicious user, this code can be exploited to cause a denial-of-service attack:

Code Block
bgColor#FFcccc
langc

The stack is frequently used for convenient temporary storage, because allocated memory is automatically freed when the function returns. Generally, the operating system will grow the stack as needed. However, this can fail due to a lack of memory or collision with other allocated areas of the address space (depending on the architecture). When this occurs, the operating system may terminate the program abnormally. If user input is able to influence the amount of stack memory allocated, then an attacker could use this in a denial-of-service attack.

Non-Compliant Code Example

C99 includes support for variable length arrays. If the value used for the length of the array is influenced by user input, an attacker could cause the program to use a large number of stack pages, possibly resulting in the process being killed due to lack of memory, or simply cause the stack pointer to point to a different region of memory. The latter could be used to write to an arbitrary memory location.

The following non-compliant code copies a file. It allocates a buffer of user-defined size on the stack to temporarily store data read from the source file.

Code Block
bgColor#FFcccc

int copy_file(FILE *src, FILE *dst, size_t bufsize) {
  char buf[bufsize];

  while (fgets(buf, bufsize, src)) {
    if (fputs(buf, dst);) == EOF) {
      /* Handle error */
    }
  }

  return 0;
}

If the size of the buffer is not constrained, a malicious user could specify a buffer of several gigabytes which might cause a crash. If the architecture is set up in a way that the heap exists "below" the stack in memory, a buffer exactly long enough to place the stack pointer into the heap could be used to overwrite memory there with what fputs() and fgets() store on the stackThe BSD extension function alloca() behaves in a similar fashion to VLAs; its use is not recommended [Loosemore 2007].

Compliant Solution

This compliant solution replaces the dynamic array the VLA with a call to malloc(). A If malloc() failure should not cause a program to terminate abnormally, and fails, the return value of malloc() can be checked for success to see if it is safe to continueto prevent the program from terminating abnormally.

Code Block
bgColor#ccccff
langc

int copy_file(FILE *src, FILE *dst, size_t bufsize) {
  if (bufsize == 0) {
    /* Handle error */
  }
  char *buf = (char *)malloc(bufsize);
  if (!buf) {
    return -1;/* Handle error */
  }

  while (fgets(buf, bufsize, src)) {
    if (fputs(buf, dst);) == EOF) {
      /* Handle error */
    }
  }
  /* ... */
  free(buf);
  return 0;
}

...

Noncompliant Code Example

Using recursion Recursion can also lead to large stack allocations. It needs to be ensured that functions which are recursive do not recurse so deep that the stack grows too large.Recursive functions must ensure that they do not exhaust the stack as a result of excessive recursions.

This noncompliant The following implementation of the Fibonacci function uses recursion.:

Code Block
bgColor#FFcccc
langc

unsigned long fib1(unsigned int n) {
  if (n == 0) {
    return 0;
  }
  else if (n == 1 || n == 2) {
    return 1;
  }
  else {
    return fib1(n-1) + fib1(n-2);
  }
}

The stack space needed grows exponentially The amount of stack space needed grows linearly with respect to the parameter n. When tested on a Linux system, fib1(100) crashes with a segmentation faultLarge values of n have been shown to cause abnormal program termination.

Compliant Solution

This implementation of the Fibonacci functions eliminates the use of recursion.:

Code Block
bgColor#ccccff
langc

unsigned long fib2(unsigned int n) {
  if (n == 0) {
    return 0;
  }
  else if (n == 1 || n == 2) {
    return 1;
  }

  unsigned long prev = 1;
  unsigned long cur = 1;

  unsigned int i;

  for (i = 3; i <= n; i++) {
    unsigned long tmp = cur;
    cur = cur + prev;
    prev = tmp;
  }

  return cur;
}

Because there is no recursion, the amount of stack space needed does not depend on the parameter n, greatly reducing the risk of stack overflow.

Risk Assessment

Stack overflow caused by excessive stack allocations or recursion could lead to abnormal termination and denial-of-service attacks.

Program stacks are frequently used for convenient temporary storage because allocated memory is automatically freed when the function returns. Generally, the operating system grows the stack as needed. However, growing the stack can fail because of a lack of memory or a collision with other allocated areas of the address space (depending on the architecture). When the stack is exhausted, the operating system can terminate the program abnormally. This behavior can be exploited, and an attacker can cause a denial-of-service attack if he or she can control or influence the amount of stack memory allocated.

Recommendation

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

MEM05-

A

1 (low)

1 (unlikely)

2 (medium)

P2

L3

Automated Detection

...

C

Medium

Likely

Medium

P12

L1

Automated Detection

Tool

Version

Checker

Description

CodeSonar
Include Page
CodeSonar_V
CodeSonar_V

IO.TAINT.SIZE

MISC.MEM.SIZE.BAD

Tainted Allocation Size

Unreasonable Size Argument

Coverity

Include Page
Coverity_V
Coverity_V

STACK_USE

Can help detect single stack allocations that are dangerously large, although it will not detect excessive stack use resulting from recursion

...

Helix QAC

Include Page
Helix QAC_V
Helix QAC_V

C1051, C1520, C3670
Klocwork
Include Page
Klocwork_V
Klocwork_V
MISRA.FUNC.RECUR
LDRA tool suite
Include Page
LDRA_V
LDRA_V
44 SEnhanced Enforcement
Parasoft C/C++test

Include Page
Parasoft_V
Parasoft_V

CERT_C-MEM05-a
CERT_C-MEM05-b

Do not use recursion
Ensure the size of the variable length array is in valid range

PC-lint Plus

Include Page
PC-lint Plus_V
PC-lint Plus_V

9035, 9070

Partially supported: reports use of variable length arrays and recursion

Polyspace Bug Finder

Include Page
Polyspace Bug Finder_V
Polyspace Bug Finder_V

CERT C: Rec. MEM05-C


Checks for:

  • Direct or indirect function call to itself
  • Variable length array with nonpositive size
  • Tainted size of variable length array

Rec. partially covered.

PVS-Studio

Include Page
PVS-Studio_V
PVS-Studio_V

V505

Related Vulnerabilities

Stack overflow has been implicated in Toyota unintended acceleration cases, where Camry and other Toyota vehicles accelerated unexpectedly.  Michael Barr testified at the trial that a stack overflow could corrupt the critical variables of the operating system, because they were located in memory adjacent to the top of the stack [Samek 2014].

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

Related Guidelines

Bibliography

[Loosemore 2007]Section 3.2.5, "Automatic Storage with Variable Size"
[Samek 2014]

Are We Shooting Ourselves in the Foot with Stack Overflow?

Monday, February 17th, 2014 by Miro Samek

[Seacord 2013]Chapter 4, "Dynamic Memory Management"


...

Image Added Image Added Image Added

References

Wiki Markup\[[van Sprundel 06|http://ilja.netric.org/files/Unusual%20bugs.pdf]\] "Stack Overflow"