Skip to content

Commit

Permalink
[c2cpg] Multiple crash fixes for blender sources
Browse files Browse the repository at this point in the history
Seen during testing on https://github.com/blender/blender

For: #5254
  • Loading branch information
max-leuthaeuser committed Jan 27, 2025
1 parent 4c5dce1 commit 9fab4ea
Show file tree
Hide file tree
Showing 9 changed files with 74 additions and 66 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -41,20 +41,8 @@ import scala.annotation.nowarn
import scala.collection.mutable
import scala.util.Try

object AstCreatorHelper {

implicit class OptionSafeAst(val ast: Ast) extends AnyVal {
def withArgEdge(src: NewNode, dst: Option[NewNode]): Ast = dst match {
case Some(value) => ast.withArgEdge(src, value)
case None => ast
}
}
}

trait AstCreatorHelper(implicit withSchemaValidation: ValidationMode) { this: AstCreator =>

import io.joern.c2cpg.astcreation.AstCreatorHelper.*

private var usedVariablePostfix: Int = 0

protected def isIncludedNode(node: IASTNode): Boolean = fileName(node) != filename
Expand Down Expand Up @@ -195,9 +183,21 @@ trait AstCreatorHelper(implicit withSchemaValidation: ValidationMode) { this: As
Try(expr.getEvaluation).toOption
}

protected def safeGetBinding(name: IASTName): Option[IBinding] = {
// In case of unresolved includes etc. this may fail throwing an unrecoverable exception
Try(name.resolveBinding()).toOption
}

protected def safeGetBinding(idExpression: IASTIdExpression): Option[IBinding] = {
// In case of unresolved includes etc. this may fail throwing an unrecoverable exception
safeGetBinding(idExpression.getName).collect {
case binding: IBinding if !binding.isInstanceOf[IProblemBinding] => binding
}
}

protected def safeGetBinding(spec: IASTNamedTypeSpecifier): Option[IBinding] = {
// In case of unresolved includes etc. this may fail throwing an unrecoverable exception
Try(spec.getName.resolveBinding()).toOption.collect {
safeGetBinding(spec.getName).collect {
case binding: IBinding if !binding.isInstanceOf[IProblemBinding] => binding
}
}
Expand Down Expand Up @@ -335,7 +335,7 @@ trait AstCreatorHelper(implicit withSchemaValidation: ValidationMode) { this: As

private def notHandledText(node: IASTNode): String =
s"""Node '${node.getClass.getSimpleName}' not handled yet!
| Code: '${node.getRawSignature}'
| Code: '${shortenCode(node.getRawSignature)}'
| File: '$filename'
| Line: ${line(node).getOrElse(-1)}
| """.stripMargin
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPClosureType
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.EvalFunctionCall
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTFoldExpression

import scala.util.Failure
import scala.util.Success
import scala.util.Try

trait AstForExpressionsCreator(implicit withSchemaValidation: ValidationMode) { this: AstCreator =>
Expand Down Expand Up @@ -78,7 +80,10 @@ trait AstForExpressionsCreator(implicit withSchemaValidation: ValidationMode) {

private def astForCppCallExpression(call: ICPPASTFunctionCallExpression): Ast = {
val functionNameExpr = call.getFunctionNameExpression
val typ = functionNameExpr.getExpressionType
val typ = Try(functionNameExpr.getExpressionType) match {
case Failure(_) => return astForCppCallExpressionUntyped(call)
case Success(tpe) => tpe
}
typ match {
case _: IPointerType =>
createPointerCallAst(call, cleanType(safeGetType(call.getExpressionType)))
Expand Down Expand Up @@ -117,7 +122,7 @@ trait AstForExpressionsCreator(implicit withSchemaValidation: ValidationMode) {

createCallAst(callCpgNode, args)
case fieldRefExpr: ICPPASTFieldReference
if fieldRefExpr.getFieldName.resolveBinding().isInstanceOf[ICPPMethod] =>
if safeGetBinding(fieldRefExpr.getFieldName).exists(_.isInstanceOf[ICPPMethod]) =>
val instanceAst = astForExpression(fieldRefExpr.getFieldOwner)
val args = call.getArguments.toList.map(a => astForNode(a))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTParameterDeclaration
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPClassType
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPEnumeration
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPStructuredBindingComposite
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPVariable
import org.eclipse.cdt.internal.core.model.ASTStringUtil

import scala.annotation.tailrec
Expand Down Expand Up @@ -134,8 +135,8 @@ trait AstForFunctionsCreator(implicit withSchemaValidation: ValidationMode) { th
}

protected def astForFunctionDeclarator(funcDecl: IASTFunctionDeclarator): Ast = {
funcDecl.getName.resolveBinding() match {
case _: IFunction =>
safeGetBinding(funcDecl.getName) match {
case Some(_: IFunction) =>
val MethodFullNameInfo(name, fullName, signature, returnType) = this.methodFullNameInfo(funcDecl)
val codeString = code(funcDecl.getParent)
val filename = fileName(funcDecl)
Expand Down Expand Up @@ -165,25 +166,32 @@ trait AstForFunctionsCreator(implicit withSchemaValidation: ValidationMode) { th
)
registerMethodDeclaration(fullName, methodInfo)
Ast()
case cVariable: CVariable =>
case Some(cVariable: CVariable) =>
val name = shortName(funcDecl)
val tpe = cleanType(ASTTypeUtil.getType(cVariable.getType))
val tpe = cleanType(safeGetType(cVariable.getType))
val codeString = code(funcDecl.getParent)
val node = localNode(funcDecl, name, codeString, registerType(tpe))
scope.addToScope(name, (node, tpe))
Ast(node)
case field: IField =>
case Some(cppVariable: CPPVariable) =>
val name = shortName(funcDecl)
val tpe = cleanType(safeGetType(cppVariable.getType))
val codeString = code(funcDecl.getParent)
val node = localNode(funcDecl, name, codeString, registerType(tpe))
scope.addToScope(name, (node, tpe))
Ast(node)
case Some(field: IField) =>
// TODO create a member for the field
// We get here a least for function pointer member declarations in classes like:
// class A {
// public:
// void (*foo)(int);
// };
Ast()
case typeDef: ITypedef =>
case Some(typeDef: ITypedef) =>
// TODO handle typeDecl for now we just ignore this.
Ast()
case other =>
case _ =>
notHandledYet(funcDecl)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,17 +62,16 @@ trait AstForPrimitivesCreator(implicit withSchemaValidation: ValidationMode) { t
private def maybeMethodRefForIdentifier(ident: IASTNode): Option[NewMethodRef] = {
ident match {
case id: IASTIdExpression if id.getName != null =>
id.getName.resolveBinding()
val (mayBeFullName, mayBeTypeFullName) = id.getName.getBinding match {
case binding: ICInternalBinding if binding.getDefinition.isInstanceOf[IASTFunctionDeclarator] =>
val (mayBeFullName, mayBeTypeFullName) = safeGetBinding(id) match {
case Some(binding: ICInternalBinding) if binding.getDefinition.isInstanceOf[IASTFunctionDeclarator] =>
namesForBinding(binding)
case binding: ICInternalBinding
case Some(binding: ICInternalBinding)
if binding.getDeclarations != null &&
binding.getDeclarations.exists(_.isInstanceOf[IASTFunctionDeclarator]) =>
namesForBinding(binding)
case binding: ICPPInternalBinding if binding.getDefinition.isInstanceOf[IASTFunctionDeclarator] =>
case Some(binding: ICPPInternalBinding) if binding.getDefinition.isInstanceOf[IASTFunctionDeclarator] =>
namesForBinding(binding)
case binding: ICPPInternalBinding
case Some(binding: ICPPInternalBinding)
if binding.getDeclarations != null &&
binding.getDeclarations.exists(_.isInstanceOf[CPPASTFunctionDeclarator]) =>
namesForBinding(binding)
Expand Down Expand Up @@ -102,15 +101,15 @@ trait AstForPrimitivesCreator(implicit withSchemaValidation: ValidationMode) { t
case id: IASTIdExpression => ASTStringUtil.getSimpleName(id.getName)
case id: IASTName =>
val name = ASTStringUtil.getSimpleName(id)
if (name.isEmpty) Try(id.resolveBinding().getName).getOrElse(uniqueName("name", "", "")._1)
if (name.isEmpty) safeGetBinding(id).map(_.getName).getOrElse(uniqueName("name", "", "")._1)
else name
case _ => code(ident)
}
}

private def syntheticThisAccess(ident: CPPASTIdExpression, identifierName: String): String | Ast = {
val tpe = ident.getName.getBinding match {
case f: CPPField => f.getType.toString
case f: CPPField => safeGetType(f.getType)
case _ => typeFor(ident)
}
Try(ident.getEvaluation).toOption match {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@ import scala.collection.mutable

trait AstForStatementsCreator(implicit withSchemaValidation: ValidationMode) { this: AstCreator =>

import io.joern.c2cpg.astcreation.AstCreatorHelper.OptionSafeAst

protected def astForBlockStatement(blockStmt: IASTCompoundStatement, order: Int = -1): Ast = {
val codeString = code(blockStmt)
val blockCode = if (codeString == "{}" || codeString.isEmpty) Defines.Empty else codeString
Expand Down Expand Up @@ -161,13 +159,15 @@ trait AstForStatementsCreator(implicit withSchemaValidation: ValidationMode) { t
case alias: CPPASTNamespaceAlias => Seq(astForNamespaceAlias(alias))
case asm: IASTASMDeclaration => Seq(astForASMDeclaration(asm))
case _: ICPPASTUsingDirective => Seq.empty
case declaration => Seq(astForNode(declaration))
case declaration => astsForDeclaration(declaration)
}

private def astForReturnStatement(ret: IASTReturnStatement): Ast = {
val cpgReturn = returnNode(ret, code(ret))
val expr = nullSafeAst(ret.getReturnValue)
Ast(cpgReturn).withChild(expr).withArgEdge(cpgReturn, expr.root)
nullSafeAst(ret.getReturnValue) match {
case retAst if retAst.root.isDefined => Ast(cpgReturn).withChild(retAst).withArgEdge(cpgReturn, retAst.root.get)
case _ => Ast(cpgReturn)
}
}

private def astForBreakStatement(br: IASTBreakStatement): Ast = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ import org.eclipse.cdt.internal.core.model.ASTStringUtil
import io.joern.x2cpg.datastructures.Stack.*
import org.apache.commons.lang3.StringUtils

import scala.util.Try

trait AstForTypesCreator(implicit withSchemaValidation: ValidationMode) { this: AstCreator =>

private def parentIsClassDef(node: IASTNode): Boolean = Option(node.getParent) match {
Expand Down Expand Up @@ -73,7 +71,7 @@ trait AstForTypesCreator(implicit withSchemaValidation: ValidationMode) { this:
case d if isTypeDef(d) && shortName(d.getDeclSpecifier).nonEmpty =>
val filename = fileName(declaration)
val typeDefName = if (name.isEmpty) {
Try(declarator.getName.resolveBinding()).toOption.map(b => registerType(b.getName))
safeGetBinding(declarator.getName).map(b => registerType(b.getName))
} else {
Option(registerType(name))
}
Expand Down Expand Up @@ -233,7 +231,7 @@ trait AstForTypesCreator(implicit withSchemaValidation: ValidationMode) { this:
case _ if declaration.getDeclarators.isEmpty => Seq(astForNode(declaration))
}
case alias: CPPASTAliasDeclaration => Seq(astForAliasDeclaration(alias))
case functDef: IASTFunctionDefinition => Seq(astForFunctionDefinition(functDef))
case functionDefinition: IASTFunctionDefinition => Seq(astForFunctionDefinition(functionDefinition))
case namespaceAlias: ICPPASTNamespaceAlias => Seq(astForNamespaceAlias(namespaceAlias))
case namespaceDefinition: ICPPASTNamespaceDefinition => Seq(astForNamespaceDefinition(namespaceDefinition))
case a: ICPPASTStaticAssertDeclaration => Seq(astForStaticAssert(a))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,5 @@ object Defines {
val OperatorCall = "<operator>()"
val OperatorExpressionList = "<operator>.expressionList"
val OperatorNew = "<operator>.new"
val OperatorThrow = "<operator>.throw"
val OperatorBracketedPrimary = "<operator>.bracketedPrimary"
}
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ trait FullNameProvider { this: AstCreator =>
}

private def returnTypeForIASTFunctionDeclarator(declarator: IASTFunctionDeclarator): String = {
Try(declarator.getName.resolveBinding()).toOption match {
safeGetBinding(declarator.getName) match {
case Some(value: ICPPMethod) =>
cleanType(value.getType.getReturnType.toString)
case _ =>
Expand All @@ -176,7 +176,7 @@ trait FullNameProvider { this: AstCreator =>
if (isCppConstructor(definition)) {
typeFor(definition.asInstanceOf[CPPASTFunctionDefinition].getMemberInitializers.head.getInitializer)
} else {
Try(definition.getDeclarator.getName.resolveBinding()).toOption match {
safeGetBinding(definition.getDeclarator.getName) match {
case Some(value: ICPPMethod) =>
cleanType(value.getType.getReturnType.toString)
case _ =>
Expand Down Expand Up @@ -221,7 +221,7 @@ trait FullNameProvider { this: AstCreator =>
}

private def shortNameForIASTDeclarator(declarator: IASTDeclarator): String = {
Try(declarator.getName.resolveBinding().getName).getOrElse {
safeGetBinding(declarator.getName).map(_.getName).getOrElse {
if (ASTStringUtil.getSimpleName(declarator.getName).isEmpty && declarator.getNestedDeclarator != null) {
shortName(declarator.getNestedDeclarator)
} else {
Expand Down Expand Up @@ -294,8 +294,8 @@ trait FullNameProvider { this: AstCreator =>
case _ => None
}
case declarator: CPPASTFunctionDeclarator =>
declarator.getName.resolveBinding() match {
case function: ICPPFunction if declarator.getName.isInstanceOf[ICPPASTConversionName] =>
safeGetBinding(declarator.getName) match {
case Some(function: ICPPFunction) if declarator.getName.isInstanceOf[ICPPASTConversionName] =>
val tpe = cleanType(typeFor(declarator.getName.asInstanceOf[ICPPASTConversionName].getTypeId))
val fullNameNoSig = fixQualifiedName(
function.getQualifiedName.takeWhile(!_.startsWith("operator ")).mkString(".")
Expand All @@ -306,7 +306,7 @@ trait FullNameProvider { this: AstCreator =>
s"$fullNameNoSig.$tpe:${functionTypeToSignature(function.getType)}"
}
Option(fn)
case function: ICPPFunction =>
case Some(function: ICPPFunction) =>
val fullNameNoSig = fixQualifiedName(replaceOperator(function.getQualifiedName.mkString(".")))
val fn = if (function.isExternC) {
replaceOperator(function.getName)
Expand All @@ -319,15 +319,15 @@ trait FullNameProvider { this: AstCreator =>
s"$fullNameNoSig:$sig"
}
Option(fn)
case x @ (_: ICPPField | _: CPPVariable) =>
case Some(x @ (_: ICPPField | _: CPPVariable)) =>
val fullNameNoSig = fixQualifiedName(x.getQualifiedName.mkString("."))
val fn = if (x.isExternC) {
x.getName
} else {
s"$fullNameNoSig:${cleanType(safeGetType(x.getType))}"
}
Option(fn)
case _: IProblemBinding =>
case Some(_: IProblemBinding) =>
val fullNameNoSig = replaceOperator(ASTStringUtil.getQualifiedName(declarator.getName))
val fixedFullName = fixQualifiedName(fullNameNoSig)
if (fixedFullName.isEmpty) {
Expand All @@ -338,31 +338,32 @@ trait FullNameProvider { this: AstCreator =>
case _ => None
}
case declarator: CASTFunctionDeclarator =>
declarator.getName.resolveBinding() match {
case cVariable: CVariable => Option(cVariable.getName)
case _ => Option(declarator.getName.toString)
safeGetBinding(declarator.getName) match {
case Some(cVariable: CVariable) => Option(cVariable.getName)
case Some(cppVariable: CPPVariable) => Option(cppVariable.getName)
case _ => Option(declarator.getName.toString)
}
case definition: ICPPASTFunctionDefinition =>
Some(fullName(definition.getDeclarator))
case namespace: ICPPASTNamespaceDefinition =>
namespace.getName.resolveBinding() match {
case b: ICPPBinding if b.getName.nonEmpty => Option(b.getQualifiedName.mkString("."))
case _ => None
safeGetBinding(namespace.getName) match {
case Some(b: ICPPBinding) if b.getName.nonEmpty => Option(b.getQualifiedName.mkString("."))
case _ => None
}
case compType: IASTCompositeTypeSpecifier =>
compType.getName.resolveBinding() match {
case b: ICPPBinding if b.getName.nonEmpty => Option(b.getQualifiedName.mkString("."))
case _ => None
safeGetBinding(compType.getName) match {
case Some(b: ICPPBinding) if b.getName.nonEmpty => Option(b.getQualifiedName.mkString("."))
case _ => None
}
case enumSpecifier: IASTEnumerationSpecifier =>
enumSpecifier.getName.resolveBinding() match {
case b: ICPPBinding if b.getName.nonEmpty => Option(b.getQualifiedName.mkString("."))
case _ => None
safeGetBinding(enumSpecifier.getName) match {
case Some(b: ICPPBinding) if b.getName.nonEmpty => Option(b.getQualifiedName.mkString("."))
case _ => None
}
case e: IASTElaboratedTypeSpecifier =>
e.getName.resolveBinding() match {
case b: ICPPBinding if b.getName.nonEmpty => Option(b.getQualifiedName.mkString("."))
case _ => None
safeGetBinding(e.getName) match {
case Some(b: ICPPBinding) if b.getName.nonEmpty => Option(b.getQualifiedName.mkString("."))
case _ => None
}
case _ => None
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@ import scala.collection.mutable
* `setExpandedMacroArgument`, we can intercept arguments and store them in a list for later retrieval. We wrap this
* rather complicated way of accessing the macro arguments in the single public method `getArguments` of the
* `MacroArgumentExtractor`.
*
* This class must be in this package in order to have access to `PreprocessorMacro`.
*/
class MacroArgumentExtractor(tu: IASTTranslationUnit, loc: IASTFileLocation) {

Expand Down

0 comments on commit 9fab4ea

Please sign in to comment.