/**
 * @name Uncontrolled data in arithmetic expression
 * @description Arithmetic operations on uncontrolled data that is not validated can cause
 *              overflows.
 * @kind problem
 * @problem.severity warning
 * @precision medium
 * @id java/uncontrolled-arithmetic
 * @tags security
 *       external/cwe/cwe-190
 *       external/cwe/cwe-191
 */

import java
import semmle.code.java.dataflow.TaintTracking
import semmle.code.java.security.SecurityTests
import ArithmeticCommon

class TaintSource extends DataFlow::ExprNode {
  TaintSource() {
    // Either this is an access to a random number generating method of the right kind, ...
    exists(Method def |
      def = this.getExpr().(MethodAccess).getMethod() and
      (
        // Some random-number methods are omitted:
        // `nextDouble` and `nextFloat` are between 0 and 1,
        // `nextGaussian` is extremely unlikely to hit max values.
        def.getName() = "nextInt" or
        def.getName() = "nextLong"
      ) and
      def.getNumberOfParameters() = 0 and
      def.getDeclaringType().hasQualifiedName("java.util", "Random")
    )
    or
    // ... or this is the array parameter of `nextBytes`, which is filled with random bytes.
    exists(MethodAccess m, Method def |
      m.getAnArgument() = this.getExpr() and
      m.getMethod() = def and
      def.getName() = "nextBytes" and
      def.getNumberOfParameters() = 1 and
      def.getDeclaringType().hasQualifiedName("java.util", "Random")
    )
  }
}

predicate sink(ArithExpr exp, VarAccess tainted, string effect) {
  exp.getAnOperand() = tainted and
  (
    not guardedAgainstUnderflow(exp, tainted) and effect = "underflow"
    or
    not guardedAgainstOverflow(exp, tainted) and effect = "overflow"
  ) and
  // Exclude widening conversions of tainted values due to binary numeric promotion (JLS 5.6.2)
  // unless there is an enclosing cast down to a narrower type.
  narrowerThanOrEqualTo(exp, tainted.getType()) and
  not overflowIrrelevant(exp) and
  not exp.getEnclosingCallable().getDeclaringType() instanceof NonSecurityTestClass
}

class ArithmeticUncontrolledFlowConfig extends TaintTracking::Configuration {
  ArithmeticUncontrolledFlowConfig() { this = "ArithmeticUncontrolledFlowConfig" }

  override predicate isSource(DataFlow::Node source) { source instanceof TaintSource }

  override predicate isSink(DataFlow::Node sink) { sink(_, sink.asExpr(), _) }

  override predicate isSanitizer(DataFlow::Node n) { n.getType() instanceof BooleanType }
}

from
  ArithExpr exp, VarAccess tainted, TaintSource origin, string effect,
  ArithmeticUncontrolledFlowConfig conf
where
  conf.hasFlow(origin, DataFlow::exprNode(tainted)) and
  sink(exp, tainted, effect)
select exp, "$@ flows to here and is used in arithmetic, potentially causing an " + effect + ".",
  origin, "Uncontrolled value"
