CertC-MSC12

Detect and remove code that has no effect or is never executed

Required inputs: IR, StaticSemanticAnalysis

Code that has no effect or is never executed (that is, dead or unreachable code) is typically the result of a coding error and can cause unexpected behavior. Such code is usually optimized out of a program during compilation. However, to improve readability and ensure that logic errors are resolved, it should be identified, understood, and eliminated.

Statements or expressions that have no effect should be identified and removed from code. Most modern compilers, in many cases, can warn about code that has no effect or is never executed. (See MSC00-C. Compile cleanly at high warning levels.) 

Noncompliant Code Example

This noncompliant code example demonstrates how dead code can be introduced into a program [ Fortify 2006]. The second conditional statement, if (s), will never evaluate true because it requires that s not be assigned NULL, and the only path where s can be assigned a non-null value ends with a return statement.

int func(int condition) {
    char *s = NULL;
    if (condition) {
        s = (char *)malloc(10);
        if (s == NULL) {
           /* Handle Error */
        }
        /* Process s */
        return 0;
    }
    /* Code that doesn't touch s */
    if (s) {
        /* This code is unreachable */
    }
    return 0;
}
Compliant Solution

Remediation of dead code requires the programmer to determine why the code is never executed and then to resolve the situation appropriately. To correct the preceding noncompliant code, the return is removed from the body of the first conditional statement.

int func(int condition) {
    char *s = NULL;
    if (condition) {
        s = (char *)malloc(10);
        if (s == NULL) {
           /* Handle error */
        }
        /* Process s */
    }
    /* Code that doesn't touch s */
    if (s) {
        /* This code is now reachable */
    }
    return 0;
}
Noncompliant Code Example

In this example, the strlen() function is used to limit the number of times the function s_loop() will iterate. The conditional statement inside the loop evaluates to true when the current character in the string is the null terminator. However, because strlen() returns the number of characters that precede the null terminator, the conditional statement never evaluates true.

int s_loop(char *s) {
    size_t i;
    size_t len = strlen(s);
    for (i=0; i < len; i++) {
        /* Code that doesn't change s, i, or len */
       if (s[i] == '\0') {
         /* This code is never reached */
      }
    }
    return 0;
}
Compliant Solution

Removing the dead code depends on the intent of the programmer. Assuming the intent is to flag and process the last character before the null terminator, the conditional is adjusted to correctly determine if the i refers to the index of the last character before the null terminator.

int s_loop(char *s) {
    size_t i;
    size_t len = strlen(s);
    for (i=0; i < len; i++) {
        /* Code that doesn't change s, i, or len */
       if (s[i+1] == '\0') {
         /* This code is now reached */
      }
    }
    return 0;
}
Noncompliant Code Example (Assignment)

In this noncompliant code example, the comparison of a to b has no effect:

int a;
int b;
/* ... */
a == b;

This code is likely a case of the programmer mistakenly using the equals operator == instead of the assignment operator =.

Compliant Solution (Assignment)

The assignment of b to a is now properly performed:

int a;
int b;
/* ... */
a = b;
Noncompliant Code Example (Dereference)

In this example, a pointer increment and then a dereference occur, but the dereference has no effect:

int *p;
/* ... */
*p++;
Compliant Solution (Dereference)

Correcting this example depends on the intent of the programmer. For example, if dereferencing p was a mistake, then p should not be dereferenced.

int *p;
/* ... */
++p;

If the intent was to increment the value referred to by p, then parentheses can be used to ensure p is dereferenced and then incremented. (See EXP00-C. Use parentheses for precedence of operation.)

int *p;
/* ... */
(*p)++;


Another possibility is that p is being used to reference a memory-mapped device. In this case, the variable p should be declared as volatile.

volatile int *p;
/* ... */
(void) *(p++);
Noncompliant Code Example (if/else if)

A chain of if/else if statements is evaluated from top to bottom. At most, only one branch of the chain will be executed: the first one with a condition that evaluates to true. Consequently, duplicating a condition in a sequence of if/else if statements automatically leads to dead code.

if (param == 1)
   openWindow();
 else if (param == 2)
   closeWindow();
 else if (param == 1) /* Duplicated condition */
   moveWindowToTheBackground();

Note that duplicating a condition violates this guideline only if the duplicate conditions always behave similarly...see a compliant solution below for a condition that is textually a duplicate but behaves differently.

Compliant Solution (if/else if)

In this compliant solution, the third conditional expression has been corrected.

if (param == 1)
   openWindow();
 else if (param == 2)
   closeWindow();
 else if (param == 3)
   moveWindowToTheBackground();
Compliant Solution (Conditional Side-Effects)

This code does not violate this recommendation, because even though the conditions are textually identical, they have different side effects, because the  getc()  function advances the stream marker.

if (getc() == ':')
   readMoreInput();
 else if (getc() == ':')
   readMoreInput();
 else if (getc() == ':')
   readMoreInput();
Noncompliant Code Example (logical operators)

Using the same subexpression on either side of a logical operator is almost always a mistake.  In this noncompliant code example, the rightmost subexpression of the controlling expression of each if statement has no effect.  

if (a == b && a == b) { // if the first one is true, the second one is too
  do_x();
}
if (a == c || a == c ) { // if the first one is true, the second one is too
  do_w();
}
Compliant Solution (logical operators)

In this compliant solution, the rightmost subexpression of the controlling expression of each if statement has been removed.

if (a == b) {
  do_x();
}
if (a == c) {
  do_w();
}
Noncompliant Code Example (Unconditional Jump)

Unconditional jump statements typically has no effect.  

#include <stdio.h>
 
for (int i = 0; i < 10; ++i) {
  printf("i is %d", i);
  continue;  // this is meaningless; the loop would continue anyway
}
Compliant Solution (Unconditional Jump)

The continue statement has been removed from this compliant solution.

#include <stdio.h>
 
for (int i = 0; i < 10; ++i) {
   printf("i is %d", i);
}
Exceptions

MSC12-EX1: In some situations, seemingly dead code may make software resilient. An example is the default label in a switch statement whose controlling expression has an enumerated type and that specifies labels for all enumerations of the type. (See MSC01-C. Strive for logical completeness.) Because valid values of an enumerated type include all those of its underlying integer type, unless enumeration constants are provided for all those values, the default label is appropriate and necessary.

typedef enum { Red, Green, Blue } Color;
const char* f(Color c) {
  switch (c) {
    case Red: return "Red";
    case Green: return "Green";
    case Blue: return "Blue";
    default: return "Unknown color";   /* Not dead code */
  }
}

void g() {
  Color unknown = (Color)123;
  puts(f(unknown));
}

MSC12-EX2: It is permissible to temporarily remove code that may be needed later. (See MSC04-C. Use comments consistently and in a readable fashion for an illustration.)

MSC12-EX3: Unused functions and variables that are part of an exported library do not violate this guideline.  Likewise, code that is never executed because it is  #ifdefed out does not violate this guideline, on the grounds that it could be subsequently used in another application, or built on a different platform.

Risk Assessment

The presence of code that has no effect or is never executed can indicate logic errors that may result in unexpected behavior and vulnerabilities. Such code can be introduced into programs in a variety of ways and eliminating it can require significant analysis.

Recommendation Severity Likelihood Remediation Cost Priority Level
MSC12-C Low Unlikely Medium P2 L3
Related Guidelines
SEI CERT C++ Coding Standard VOID MSC12-CPP. Detect and remove code that has no effect
ISO/IEC TR 24772 Unspecified Functionality [BVQ]
Likely Incorrect Expressions [KOA]
Dead and Deactivated Code [XYQ]
MISRA C:2012 Rule 2.2 (required)
Bibliography


[ Fortify 2006] Code Quality, "Dead Code"
[ Coverity 2007]
Excerpt from SEI CERT C Coding Standard: Rules for Developing Safe, Reliable, and Secure Systems (2016 Edition) and SEI CERT C Coding Standard [https://cmu-sei.github.io/secure-coding-standards/sei-cert-c-coding-standard/recommendations/miscellaneous-msc/msc12-c], Copyright (C) 1995-2026 Carnegie Mellon University. See section 9.4. "3rd-Party Licenses" in the documentation for full details.

Possible Messages

Key

Text

Severity

Disabled

conditional_unused_def

Result of assignment is not used along some path(s)

None

True

dead_call_false_branch

Function call condition is always true{dead_branch_type}

None

False

dead_call_false_branch_type_limits

Function call condition is always true due to limited range of data type{dead_branch_type}

None

False

dead_call_true_branch

Function call condition is always false

None

False

dead_call_true_branch_type_limits

Function call condition is always false due to limited range of data type{dead_branch_type}

None

False

dead_false_branch

Condition is always true{dead_branch_type}

None

False

dead_false_branch_type_limits

Condition is always true due to limited range of data type{dead_branch_type}

None

False

dead_false_branch_type_limits_in_context

Condition is true in context due to limited range of data type{dead_branch_type}

None

False

dead_global_var_false_branch

Global variable condition is always true{dead_branch_type}

None

False

dead_global_var_false_branch_type_limits

Global variable condition is always true due to limited range of data type{dead_branch_type}

None

False

dead_global_var_true_branch

Global variable condition is always false{dead_branch_type}

None

False

dead_global_var_true_branch_type_limits

Global variable condition is always false due to limited range of data type{dead_branch_type}

None

False

dead_param_dependent_var_false_branch

Parameter-dependent variable condition is always true{dead_branch_type}

None

False

dead_param_dependent_var_true_branch

Parameter-dependent variable condition is always false{dead_branch_type}

None

False

dead_param_false_branch

Parameter condition is always true{dead_branch_type}

None

False

dead_param_false_branch_type_limits

Parameter condition is always true due to limited range of data type{dead_branch_type}

None

False

dead_param_null_false_branch

Parameter is always equal to NULL{dead_branch_type}

None

False

dead_param_null_true_branch

Parameter is never equal to NULL{dead_branch_type}

None

False

dead_param_true_branch

Parameter condition is always false{dead_branch_type}

None

False

dead_param_true_branch_type_limits

Parameter condition is always false due to limited range of data type{dead_branch_type}

None

False

dead_true_branch

Condition is always false{dead_branch_type}

None

False

dead_true_branch_type_limits

Condition is always false due to limited range of data type{dead_branch_type}

None

False

dead_true_branch_type_limits_in_context

Condition is false in context due to limited range of data type{dead_branch_type}

None

False

dead_var_false_branch

Variable condition is always true{dead_branch_type}

None

False

dead_var_false_branch_type_limits

Variable condition is always true due to limited range of data type{dead_branch_type}

None

False

dead_var_true_branch

Variable condition is always false{dead_branch_type}

None

False

dead_var_true_branch_type_limits

Variable condition is always false due to limited range of data type{dead_branch_type}

None

False

do_while_only_once

Loop is only executed once, loop condition is always false

None

False

do_while_only_once_type_limits

Loop is only executed once, loop condition is always false due to limited range of data type

None

False

function_never_called

{what} never called

None

False

function_only_called_from_uncalled_function

{what} only called from uncalled function

None

False

function_only_called_from_uncalled_function_or_dead_branches

{what} only called from uncalled function or from dead branches

None

False

init_used_in_other_isr

Initialization is not used except within code reached from an entry point that might be triggered by an interrupt

None

True

loop_cond_false

Loop body is dead, condition is always false

None

False

loop_cond_false_type_limits

Loop body is dead, condition is always false due to limited range of data type

None

False

loop_cond_true

Loop condition is always true

None

False

loop_cond_true_type_limits

Loop condition is always true due to limited range of data type

None

False

no_effect

Non-null statement without side-effect

None

False

redundant_expression

Binary operator with this operand is redundant, expression can be simplified (operator can be removed) without affecting program behaviour

None

False

removable_declaration

Declaration can be removed

None

False

removable_expression

Expression (but not necessarily all subexpressions) can be removed without affecting program behaviour

None

False

removable_statement

Statement can be removed

None

False

subexpression_false

Subexpression always evaluates to false

None

False

subexpression_false_type_limits

Subexpression always evaluates to false due to limited range of data type

None

False

subexpression_true

Subexpression always evaluates to true

None

False

subexpression_true_type_limits

Subexpression always evaluates to true due to limited range of data type

None

False

unreachable_code

Unreachable code

None

False

unreachable_short_circuit

Subexpression never evaluated due to short-circuiting operator

None

False

unused_catch_parameter

Unused implicit initialization of catch clause parameter (you can use an unnamed one instead)

None

False

unused_def

Result of assignment is not used

None

False

unused_init

Unused initialization

None

False

used_in_other_isr

Result of assignment is not used except within code reached from an entry point that might be triggered by an interrupt

None

True

Options

allow_void_var

allow_void_var : bool = True

Whether a cast of "var" to "void" (e.g. (void)var;) should be allowed or reported.
 

assume_effect_for_all_calls

assume_effect_for_all_calls : bool = False

Whether all calls should be seen as having a side-effect.
 

assume_effect_when_undefined

assume_effect_when_undefined : bool = True

Whether all calls to undefined (external) functions should be seen as having a side-effect. Note that this option is ignored to avoid false positives when only a single unit is analysed (running_on_unit is overwritten and --unit is passed to axivion_analysis).
 

check_preprocessor_expressions

check_preprocessor_expressions : bool = False

Whether preprocessor expressions should be checked for redundant operations like expr || 0 as well.
 

disregard_logical_shortcut

disregard_logical_shortcut : bool = False

Whether constant literal conditions must not be caused by logical shortcut evaluations like false && ... or true || ....
 

list_side_effect_free_statements

list_side_effect_free_statements : bool = False

Whether toplevel expressions from completely side-effect free statements should be listed as well.
 

report_dead_if_branch_with_const_literal_condition

report_dead_if_branch_with_const_literal_condition : bool = True

Whether bodies of if-branches with constant literal conditions should be reported.
 

report_dead_initializations

report_dead_initializations : bool = True

Whether initializations may be reported as dead.
 

report_do_while_false

report_do_while_false : bool = True

Whether do ... while(0) should be reported.
 

report_for_true

report_for_true : bool = True

Whether for(;;) ... should be reported.
 

report_redundant_expressions_from_macros

report_redundant_expressions_from_macros : bool = False

Whether redundant expressions like expr + 0 should be reported if the constant comes from a macro (and the operator does not come from the same macro).
 

report_redundant_sizeof_expressions

report_redundant_sizeof_expressions : bool = False

Whether redundant expressions like expr * 1 should be reported if the constant comes from evaluating sizeof().
 

report_while_true

report_while_true : bool = True

Whether while(1) ... should be reported.
 

tolerate_void_cast

tolerate_void_cast : bool = True

Whether a cast to "void" is accepted.
 

use_semantic_analysis

use_semantic_analysis : bool = True

If false, ignore results from static semantic analysis and only consider syntactical information to compute reachability.