Semmle 1.20
Skip to end of metadata
Go to start of metadata

Name: Potentially uninitialized local variable

Description: Reading from a local variable that has not been assigned to will typically yield garbage.

ID: cpp/uninitialized-local

Kind: problem

Severity: warning

Precision: medium

Query: UninitializedLocal.ql
/**
 * @name Potentially uninitialized local variable
 * @description Reading from a local variable that has not been assigned to
 *              will typically yield garbage.
 * @kind problem
 * @id cpp/uninitialized-local
 * @problem.severity warning
 * @precision medium
 * @tags security
 *       external/cwe/cwe-665
 *       external/cwe/cwe-457
 */

import cpp
import semmle.code.cpp.controlflow.LocalScopeVariableReachability

/**
 * Auxiliary predicate: Types that don't require initialization
 * before they are used, since they're stack-allocated.
 */
predicate allocatedType(Type t) {
  /* Arrays: "int foo[1]; foo[0] = 42;" is ok. */
  t instanceof ArrayType or
  /* Structs: "struct foo bar; bar.baz = 42" is ok. */
  t instanceof Class or
  /* Typedefs to other allocated types are fine. */
  allocatedType(t.(TypedefType).getUnderlyingType()) or
  /* Type specifiers don't affect whether or not a type is allocated. */
  allocatedType(t.getUnspecifiedType())
}

/**
 * A declaration of a local variable that leaves the
 * variable uninitialized.
 */
DeclStmt declWithNoInit(LocalVariable v) {
  result.getADeclaration() = v and
  not exists(v.getInitializer()) and
  /* The type of the variable is not stack-allocated. */
  not allocatedType(v.getType()) and
  /* The variable is not static (otherwise it is zeroed). */
  not v.isStatic() and
  /* The variable is not extern (otherwise it is zeroed). */
  not v.hasSpecifier("extern")
}

class UninitialisedLocalReachability extends LocalScopeVariableReachability {
  UninitialisedLocalReachability() { this = "UninitialisedLocal" }

  override predicate isSource(ControlFlowNode node, LocalScopeVariable v) {
    node = declWithNoInit(v)
  }

  override predicate isSink(ControlFlowNode node, LocalScopeVariable v) {
    useOfVarActual(v, node)
  }

  override predicate isBarrier(ControlFlowNode node, LocalScopeVariable v) {
    // only report the _first_ possibly uninitialized use
    useOfVarActual(v, node) or
    definitionBarrier(v, node)
  }
}

pragma[noinline]
predicate containsInlineAssembly(Function f) {
  exists(AsmStmt s | s.getEnclosingFunction() = f)
}

/**
 * Auxiliary predicate: List common exceptions or false positives
 * for this check to exclude them.
 */
VariableAccess commonException() {
  /* If the uninitialized use we've found is in a macro expansion, it's
   * typically something like va_start(), and we don't want to complain.
   */
  result.getParent().isInMacroExpansion() or
  result.getParent() instanceof BuiltInOperation or
  /*
   * Finally, exclude functions that contain assembly blocks. It's
   * anyone's guess what happens in those.
   */
  containsInlineAssembly(result.getEnclosingFunction())
}

from UninitialisedLocalReachability r, LocalVariable v, VariableAccess va
where
  r.reaches(_, v, va) and
  not va = commonException()
select va, "The variable $@ may not be initialized here.", v, v.getName()

A local non-static variable of a non-class type has an undefined value before it is initialized. For example, it is incorrect to rely on an uninitialized integer to have the value 0.

Recommendation

Review the code and consider whether the variable should have an initializer or whether some path through the program lacks an assignment to the variable.

Example

The function absWrong does not initialize the variable j in the case where i = 0. Functions absCorrect1 and absCorrect2 remedy this deficiency by adding an initializer and adding an assignment to one of the paths through the program, respectively.

int absWrong(int i) {
	int j;
	if (i > 0) {
		j = i;
	} else if (i < 0) {
		j = -i;
	}
	return j; // wrong: j may not be initialized before use
}

int absCorrect1(int i) {
	int j = 0;
	if (i > 0) {
		j = i;
	} else if (i < 0) {
		j = -i;
	}
	return j; // correct: j always initialized before use
}

int absCorrect2(int i) {
	int j;
	if (i > 0) {
		j = i;
	} else if (i < 0) {
		j = -i;
	} else {
		j = 0;
	}
	return j; // correct: j always initialized before use
}

References