Skip to content

Commit

Permalink
[c2cpg] Multiple crash fixes for blender sources (#5259)
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 authored Jan 27, 2025
1 parent d41b158 commit 9165b0f
Show file tree
Hide file tree
Showing 9 changed files with 97 additions and 129 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 @@ -78,46 +78,33 @@ trait AstForExpressionsCreator(implicit withSchemaValidation: ValidationMode) {

private def astForCppCallExpression(call: ICPPASTFunctionCallExpression): Ast = {
val functionNameExpr = call.getFunctionNameExpression
val typ = functionNameExpr.getExpressionType
typ match {
case _: IPointerType =>
createPointerCallAst(call, cleanType(safeGetType(call.getExpressionType)))
case functionType: ICPPFunctionType =>
Try(functionNameExpr.getExpressionType).toOption match {
case Some(_: IPointerType) => createPointerCallAst(call, cleanType(safeGetType(call.getExpressionType)))
case Some(functionType: ICPPFunctionType) =>
functionNameExpr match {
case idExpr: CPPASTIdExpression if idExpr.getName.getBinding.isInstanceOf[ICPPFunction] =>
val function = idExpr.getName.getBinding.asInstanceOf[ICPPFunction]
val name = idExpr.getName.getLastName.toString
val signature =
if (function.isExternC) {
""
} else {
functionTypeToSignature(functionType)
}

val fullName =
if (function.isExternC) {
StringUtils.normalizeSpace(name)
} else {
val fullNameNoSig = StringUtils.normalizeSpace(function.getQualifiedName.mkString("."))
s"$fullNameNoSig:$signature"
}

val dispatchType = DispatchTypes.STATIC_DISPATCH

case idExpr: CPPASTIdExpression if safeGetBinding(idExpr).exists(_.isInstanceOf[ICPPFunction]) =>
val function = idExpr.getName.getBinding.asInstanceOf[ICPPFunction]
val name = idExpr.getName.getLastName.toString
val signature = if function.isExternC then "" else functionTypeToSignature(functionType)
val fullName = if (function.isExternC) {
StringUtils.normalizeSpace(name)
} else {
val fullNameNoSig = StringUtils.normalizeSpace(function.getQualifiedName.mkString("."))
s"$fullNameNoSig:$signature"
}
val callCpgNode = callNode(
call,
code(call),
name,
fullName,
dispatchType,
DispatchTypes.STATIC_DISPATCH,
Some(signature),
Some(registerType(cleanType(safeGetType(call.getExpressionType))))
)
val args = call.getArguments.toList.map(a => astForNode(a))

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 Expand Up @@ -148,36 +135,29 @@ trait AstForExpressionsCreator(implicit withSchemaValidation: ValidationMode) {
case _ =>
astForCppCallExpressionUntyped(call)
}
case classType: ICPPClassType if call.getEvaluation.isInstanceOf[EvalFunctionCall] =>
val evaluation = call.getEvaluation.asInstanceOf[EvalFunctionCall]

case Some(classType: ICPPClassType) if safeGetEvaluation(call).exists(_.isInstanceOf[EvalFunctionCall]) =>
val evaluation = call.getEvaluation.asInstanceOf[EvalFunctionCall]
val functionType = Try(evaluation.getOverload.getType).toOption
val signature = functionType.map(functionTypeToSignature).getOrElse(X2CpgDefines.UnresolvedSignature)
val name = Defines.OperatorCall

classType match {
case _: CPPClosureType =>
val fullName = s"$name:$signature"
val dispatchType = DispatchTypes.DYNAMIC_DISPATCH

val fullName = s"$name:$signature"
val callCpgNode = callNode(
call,
code(call),
name,
fullName,
dispatchType,
DispatchTypes.DYNAMIC_DISPATCH,
Some(signature),
Some(registerType(cleanType(safeGetType(call.getExpressionType))))
)

val receiverAst = astForExpression(functionNameExpr)
val args = call.getArguments.toList.map(a => astForNode(a))

createCallAst(callCpgNode, args, receiver = Some(receiverAst))
case _ =>
val classFullName = cleanType(safeGetType(classType))
val fullName = s"$classFullName.$name:$signature"

val dispatchType = evaluation.getOverload match {
case method: ICPPMethod =>
if (method.isVirtual || method.isPureVirtual) {
Expand All @@ -197,17 +177,11 @@ trait AstForExpressionsCreator(implicit withSchemaValidation: ValidationMode) {
Some(signature),
Some(registerType(cleanType(safeGetType(call.getExpressionType))))
)

val instanceAst = astForExpression(functionNameExpr)
val args = call.getArguments.toList.map(a => astForNode(a))
createCallAst(callCpgNode, args, base = Some(instanceAst), receiver = Some(instanceAst))
}
case _: IProblemType =>
astForCppCallExpressionUntyped(call)
case _: IProblemBinding =>
astForCppCallExpressionUntyped(call)
case _ =>
astForCppCallExpressionUntyped(call)
case _ => astForCppCallExpressionUntyped(call)
}
}

Expand All @@ -216,11 +190,9 @@ trait AstForExpressionsCreator(implicit withSchemaValidation: ValidationMode) {
case fieldRefExpr: ICPPASTFieldReference =>
val instanceAst = astForExpression(fieldRefExpr.getFieldOwner)
val args = call.getArguments.toList.map(a => astForNode(a))

val name = StringUtils.normalizeSpace(fieldRefExpr.getFieldName.toString)
val signature = X2CpgDefines.UnresolvedSignature
val fullName = s"${X2CpgDefines.UnresolvedNamespace}.$name:$signature(${args.size})"

val name = StringUtils.normalizeSpace(fieldRefExpr.getFieldName.toString)
val signature = X2CpgDefines.UnresolvedSignature
val fullName = s"${X2CpgDefines.UnresolvedNamespace}.$name:$signature(${args.size})"
val callCpgNode = callNode(
call,
code(call),
Expand All @@ -232,12 +204,10 @@ trait AstForExpressionsCreator(implicit withSchemaValidation: ValidationMode) {
)
createCallAst(callCpgNode, args, base = Some(instanceAst), receiver = Some(instanceAst))
case idExpr: CPPASTIdExpression =>
val args = call.getArguments.toList.map(a => astForNode(a))

val args = call.getArguments.toList.map(a => astForNode(a))
val name = StringUtils.normalizeSpace(idExpr.getName.getLastName.toString)
val signature = X2CpgDefines.UnresolvedSignature
val fullName = s"${X2CpgDefines.UnresolvedNamespace}.$name:$signature(${args.size})"

val callCpgNode = callNode(
call,
code(call),
Expand All @@ -251,12 +221,10 @@ trait AstForExpressionsCreator(implicit withSchemaValidation: ValidationMode) {
case otherExpr =>
// This could either be a pointer or an operator() call we do not know at this point
// but since it is CPP we opt for the latter.
val args = call.getArguments.toList.map(a => astForNode(a))

val args = call.getArguments.toList.map(a => astForNode(a))
val name = Defines.OperatorCall
val signature = X2CpgDefines.UnresolvedSignature
val fullName = s"${X2CpgDefines.UnresolvedNamespace}.$name:$signature(${args.size})"

val callCpgNode = callNode(
call,
code(call),
Expand All @@ -273,11 +241,10 @@ trait AstForExpressionsCreator(implicit withSchemaValidation: ValidationMode) {

private def astForCCallExpression(call: CASTFunctionCallExpression): Ast = {
val functionNameExpr = call.getFunctionNameExpression
val typ = functionNameExpr.getExpressionType
typ match {
case _: CPointerType =>
Try(functionNameExpr.getExpressionType).toOption match {
case Some(_: CPointerType) =>
createPointerCallAst(call, cleanType(safeGetType(call.getExpressionType)))
case _: CFunctionType =>
case Some(_: CFunctionType) =>
functionNameExpr match {
case idExpr: CASTIdExpression =>
createCFunctionCallAst(call, idExpr, cleanType(safeGetType(call.getExpressionType)))
Expand Down Expand Up @@ -316,8 +283,7 @@ trait AstForExpressionsCreator(implicit withSchemaValidation: ValidationMode) {
}

private def astForCCallExpressionUntyped(call: CASTFunctionCallExpression): Ast = {
val functionNameExpr = call.getFunctionNameExpression
functionNameExpr match {
call.getFunctionNameExpression match {
case idExpr: CASTIdExpression => createCFunctionCallAst(call, idExpr, X2CpgDefines.Any)
case _ => createPointerCallAst(call, X2CpgDefines.Any)
}
Expand All @@ -332,8 +298,7 @@ trait AstForExpressionsCreator(implicit withSchemaValidation: ValidationMode) {

private def astForThrowExpression(expression: IASTUnaryExpression): Ast = {
val operand = nullSafeAst(expression.getOperand)
Ast(controlStructureNode(expression, ControlStructureTypes.THROW, code(expression)))
.withChild(operand)
Ast(controlStructureNode(expression, ControlStructureTypes.THROW, code(expression))).withChild(operand)
}

private def astForUnaryExpression(unary: IASTUnaryExpression): Ast = {
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
Loading

0 comments on commit 9165b0f

Please sign in to comment.