All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.joern.scanners.c.MissingLengthCheck.scala Maven / Gradle / Ivy

package io.joern.scanners.c

import io.shiftleft.semanticcpg.language.{ICallResolver, NoResolve}
import io.joern.scanners._
import io.joern.console._
import io.shiftleft.codepropertygraph.generated.nodes
import io.joern.dataflowengineoss.queryengine.EngineContext
import io.shiftleft.semanticcpg.language._
import io.joern.macros.QueryMacros._
import io.shiftleft.semanticcpg.language.operatorextension._
import QueryLangExtensions._

object MissingLengthCheck extends QueryBundle {

  implicit val resolver: ICallResolver = NoResolve

  @q
  def constantArrayAccessNoCheck()(implicit context: EngineContext): Query =
    Query.make(
      name = "constant-array-access-no-check",
      author = Crew.fabs,
      title = "Array access at fixed offset but sufficient length check not determined",
      description = """
          |
          |""".stripMargin,
      score = 3,
      withStrRep({ cpg =>
        cpg.method.arrayAccess
          .filter { access =>
            val arrName = access.simpleName
            arrName.nonEmpty && !arrName.forall(x => access.method.local.nameExact(x).nonEmpty)
          }
          .usesConstantOffset
          .flatMap { arrayAccess =>
            val lenFields =
              potentialLengthFields(arrayAccess, arrayAccess.method)
            if (lenFields.nonEmpty) {
              List((arrayAccess, lenFields))
            } else {
              List()
            }
          }
          .collect {
            case (arrayAccess, lenFields) if !checked(arrayAccess, lenFields) =>
              arrayAccess
          }
      }),
      tags = List(QueryTags.default)
    )

  /** Names of potential length fields for the array named `arrayName` in the method `method`. Determined heuristically
    * via name matching.
    */
  private def potentialLengthFields(arrayAccess: OpNodes.ArrayAccess, method: nodes.Method): Seq[String] = {
    arrayAccess.simpleName.flatMap { name =>
      val normalizedName = name.stripSuffix("s")
      val regex          = s"(?i)$normalizedName(s?)(_?)(len|siz).*"
      method.parameter.name(regex).name ++
        method.local.name(regex).name
    }.toSeq
  }

  /** For a given array access with a single constant offset and a set of variable names of potential length fields of
    * the array, determine whether a check of at least one of the potential length fields exist for each literal
    */
  def checked(arrayAccess: OpNodes.ArrayAccess, lens: Seq[String]): Boolean = {
    val arrayIndex = arrayAccess.argument(2).ast.isLiteral.toInt.headOption match {
      case Some(x) => x
      case None    => return true // we can only deal with literals as indices here
    }

    val lowerBounds = arrayAccess.method.controlStructure.condition
      .where(_.ast.isIdentifier.nameExact(lens: _*))
      .ast
      .isLiteral
      .toInt ++
      Option.when(
        arrayAccess.method.controlStructure.condition
          .codeExact(arrayAccess.array.code)
          .nonEmpty
      )(0)
    lowerBounds.exists { bound =>
      bound >= arrayIndex
    }
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy