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

Name: Multiplication result converted to larger type

Description: A multiplication result that is converted to a larger type can be a sign that the result can overflow the type converted from.

ID: cpp/integer-multiplication-cast-to-long

Kind: problem

Severity: warning

Precision: high

Query: IntMultToLong.ql
/**
 * @name Multiplication result converted to larger type
 * @description A multiplication result that is converted to a larger type can
 *              be a sign that the result can overflow the type converted from.
 * @kind problem
 * @problem.severity warning
 * @precision high
 * @id cpp/integer-multiplication-cast-to-long
 * @tags reliability
 *       security
 *       correctness
 *       types
 *       external/cwe/cwe-190
 *       external/cwe/cwe-192
 *       external/cwe/cwe-197
 *       external/cwe/cwe-681
 */
import cpp
import semmle.code.cpp.controlflow.SSA

/**
 * Holds if `e` is either:
 *  - a constant
 *  - a char-typed expression, meaning it's a small number
 *  - an array access to an array of constants
 *  - flows from one of the above
 * In these cases the value of `e` is likely to be small and
 * controlled, so we consider it less likely to cause an overflow.
 */
predicate likelySmall(Expr e) {
  e.isConstant() or
  e.getType().getSize() <= 1 or
  e.(ArrayExpr).getArrayBase().getType().(ArrayType).getBaseType().isConst() or
  exists(SsaDefinition def, Variable v |
    def.getAUse(v) = e and
    likelySmall(def.getDefiningValue(v))
  )
}

/**
 * Gets an operand of a multiply expression (we need the restriction
 * to multiply expressions to get the correct transitive closure).
 */
Expr getMulOperand(MulExpr me) {
  result = me.getAnOperand()
}

/**
 * Gets the number of non-constant operands of a multiply expression,
 * exploring into child multiply expressions rather than counting them
 * as an operand directly.  For example the top level multiply here
 * effectively has two non-constant operands:
 * ```
 *   (x * y) * 2
 * ```
 */
int getEffectiveMulOperands(MulExpr me) {
  result = count(Expr op |
    op = getMulOperand*(me) and
    not op instanceof MulExpr and
    not likelySmall(op)
  )
}

from MulExpr me, Type t1, Type t2
where t1 = me.getType().getUnderlyingType() and
      t2 = me.getConversion().getType().getUnderlyingType() and
      t1.getSize() < t2.getSize() and
      (
        (
          t1.getUnspecifiedType() instanceof IntegralType and
          t2.getUnspecifiedType() instanceof IntegralType
        ) or (
          t1.getUnspecifiedType() instanceof FloatingPointType and
          t2.getUnspecifiedType() instanceof FloatingPointType
        )
      ) and

      // exclude explicit conversions
      me.getConversion().isCompilerGenerated() and

      // require the multiply to have two non-constant operands
      // (the intuition here is that multiplying two unknowns is
      // much more likely to produce a result that needs significantly
      // more bits than the operands did, and thus requires a larger
      // type).
      getEffectiveMulOperands(me) >= 2 and

      // exclude varargs promotions
      not exists(FunctionCall fc, int vararg |
        fc.getArgument(vararg) = me and
        vararg >= fc.getTarget().getNumberOfParameters()
      ) and

      // exclude cases where the type was made bigger by a literal
      // (compared to other cases such as assignment, this is more
      // likely to be a trivial accident rather than suggesting a
      // larger type is needed for the result).
      not exists(Expr other, Expr e |
        other = me.getParent().(BinaryOperation).getAnOperand() and
        not other = me and
        (
          e = other or
          e = other.(BinaryOperation).getAnOperand*()
        ) and
        e.(Literal).getType().getSize() = t2.getSize()
      )
select me, "Multiplication result may overflow '" + me.getType().toString() + "' before it is converted to '" + me.getFullyConverted().getType().toString() + "'."

This rule finds code that converts the result of an integer multiplication to a larger type. Since the conversion applies after the multiplication, arithmetic overflow may still occur.

The rule flags every multiplication of two non-constant integer expressions that is (explicitly or implicitly) converted to a larger integer type. The conversion is an indication that the expression would produce a result that would be too large to fit in the smaller integer type.

Recommendation

Use a cast to ensure that the multiplication is done using the larger integer type to avoid overflow.

Example

int i = 2000000000;
long j = i * i; //Wrong: due to overflow on the multiplication between ints, 
                //will result to j being -1651507200, not 4000000000000000000

long k = (long) i * i; //Correct: the multiplication is done on longs instead of ints, 
                       //and will not overflow

References