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

Compare with Current View Page History

« Previous Version 73 Next »

The C Standard, subclause 3.4.3 [ISO/IEC 9899:2011], defines undefined behavior as

behavior, upon use of a nonportable or erroneous program construct or of erroneous data, for which this International Standard imposes no requirements.

Subclause 4 explains how the standard identifies undefined behavior (see also undefined behavior 1 of Annex J).

If a "shall" or "shall not" requirement that appears outside of a constraint is violated, the behavior is undefined. Undefined behavior is otherwise indicated in this International Standard by the words "undefined behavior" or by the omission of any explicit definition of behavior. There is no difference in emphasis among these three; they all describe "behavior that is undefined".

Annex J, subclause J.2, "Undefined behavior," enumerates the circumstances under which the behavior of a program is undefined. This list is duplicated on the CC. Undefined Behavior page.

Behavior can be classified as undefined by the C standards committee for the following reasons:

  • To give the implementor license not to catch certain program errors that are difficult to diagnose
  • To avoid defining obscure corner cases that would favor one implementation strategy over another
  • To identify areas of possible conforming language extension: the implementor may augment the language by providing a definition of the officially undefined behavior

Conforming implementations can deal with undefined behavior in a variety of fashions, such as ignoring the situation completely, with unpredictable results; translating or executing the program in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message); or terminating a translation or execution (with the issuance of a diagnostic message). Because compilers are not obligated to generate code for undefined behavior, these behaviors are candidates for optimization. By assuming that undefined behaviors will not occur, compilers can generate code with better performance characteristics.

Increasingly, compiler writers are taking advantage of undefined behaviors in the C programming languages to improve optimizations. These optimizations frequently interfere with the ability of developers to perform cause-effect analysis on their source code—that is, to analyze the dependence of downstream results on prior results. Consequently, these optimizations are eliminating causality in software and are increasing the probability of software faults, defects, and vulnerabilities.

All of this puts the onus on the programmer to develop code that is free from undefined behaviors, with or without the help of the compiler.

Noncompliant Code Example

An example of undefined behavior in C is the behavior on signed integer overflow (see also INT32-C. Ensure that operations on signed integers do not result in overflow). This noncompliant code example depends on this behavior to catch the overflow:

#include <assert.h>
#include <limits.h>
#include <stdio.h>
 
int foo(int a) {
  assert(a + 100 > a);
  printf("%d %d\n", a + 100, a);
  return a;
}

int main(void) {
  foo(100);
  foo(INT_MAX);
  return 0;
}

This code checks for signed integer overflow by testing whether a + 100 > a. This test cannot evaluate to false unless an integer overflow occurs. However, because a conforming implementation is not required to generate code for undefined behavior, and signed integer overflow is undefined behavior, this code may be compiled out. For example, GCC 4.1.1 optimizes out the assertion for all optimization levels, and GCC 4.2.3 optimizes out the assertion for programs compiled with -O2-level optimization and higher.

On some platforms, the integer overflow causes the program to terminate (before it has an opportunity to test).

Compliant Solution

This compliant solution does not depend on undefined behavior:

#include <assert.h>
#include <limits.h>
#include <stdio.h>

int foo(int a) {
  assert(a < (INT_MAX - 100));
  printf("%d %d\n", a + 100, a);
  return a;
}

int main(void) {
  foo(100);
  foo(INT_MAX);
  return 0;
}

Risk Assessment

Although it is rare that the entire application can be strictly conforming, the goal should be that almost all the code is allowed for a strictly conforming program (which among other things means that it avoids undefined behavior), with the implementation-dependent parts confined to modules that the programmer knows are needed to adapt to the platform when it changes.

Recommendation

Severity

Likelihood

Remediation Cost

Priority

Level

MSC15-C

High

Likely

Medium

P18

L1

Automated Detection

Tool

Version

Checker

Description

Astrée
24.04

Supported, but no explicit checker
LDRA tool suite
9.7.1

48 D, 63 D, 84 D, 113 D, 5 Q, 64 S, 65 S, 100 S, 109 S, 156 S, 296 S, 324 S, 335 S, 336 S, 339 S, 412 S, 427 S, 465 S, 482 S, 497 S, 545 S, 587 S, 608 S, 642 S, 62 X, 63 X

Partially implemented
Parasoft C/C++test
2023.1
CERT_C-MSC15-a

Evaluation of constant unsigned integer expressions should not lead to wrap-around

Polyspace Bug Finder

R2024a

Array access out of bounds

Copy of overlapping memory

Declaration mismatch

Format string specifiers and arguments mismatch

Integer overflow

Invalid use of standard library memory routine

Invalid use of standard library routine

Invalid use of standard library string routine

Non-initialized pointer

Non-initialized variable

Null pointer

Overlapping assignment

Pointer access out of bounds

Standard function call with incorrect arguments

Unreliable cast of function pointer

Unreliable cast of pointer

Use of tainted pointer

Writing to const qualified object

Array index outside bounds during array access

Source and destination arguments of a copy function have overlapping memory

Mismatch between function or variable declarations

String specifiers do not match corresponding arguments

Overflow from operation between integers

Standard library memory function called with invalid arguments

Wrong arguments to standard library function

Standard library string function called with invalid arguments

Pointer not initialized before dereference

Variable not initialized before use

NULL pointer dereferenced

Memory overlap between left and right sides of an assignment

Pointer dereferenced outside its bounds

Argument to a standard function does not meet requirements for use in the function

Function pointer cast to another function pointer with different argument or return type

Pointer implicitly cast to different data type

Pointer from an unsecure source may be NULL or point to unknown memory

Object declared with a const qualifier is modified

PRQA QA-C
Unable to render {include} The included page could not be found.

0160, 0161, 0162, 0163, 0164, 0165, 0166, 0167, 0168, 0169, 0170, 0171, 0172, 0173, 0174, 0175, 0176, 0177, 0178, 0179, 0184, 0185, 0186, 0190, 0191, 0192, 0193, 0194, 0195, 0196, 0197, 0198, 0199, 0200, 0201, 0203, 0204, 0206, 0207, 0208, 0235, 0275, 0304, 0309, 0337, 0400, 0401, 0402, 0403, 0543, 0544, 0545, 0602, 0623, 0625, 0626, 0630, 0632, 0636, 0654, 0658, 0661, 0667, 0668, 0672, 0706, 0745, 0777, 0779, 0809, 0813, 0814, 0836, 0837, 0848, 0853, 0854, 0864, 0865, 0867, 0872, 0874, 0885, 0887, 0888, 0914, 0915, 0942, 3113, 3114, 3239, 3319, 3438, 0301, 0302, 0307, 0475, 0676, 0678, 0680, 3311, 3312, 3437,1509, 1510

Partially implemented
PVS-Studio

7.30

V772

Related Vulnerabilities

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

Related Guidelines

SEI CERT C++ Coding StandardVOID MSC15-CPP. Do not depend on undefined behavior
ISO/IEC TR 24772Unspecified Behaviour [BQF]
Undefined Behaviour [EWF]
Implementation-Defined Behaviour [FAB]

Bibliography

[ISO/IEC 9899:2011]Subclause 3.4.3, "Undefined Behavior"
Subclause 4, "Conformance"
Subclause J.2, "Undefined Behavior"
[Seacord 2013]Chapter 5, "Integer Security"



  • No labels