-
Notifications
You must be signed in to change notification settings - Fork 60
Adds memory allocation analysis functions #1041
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -47,12 +47,15 @@ | |
| #include "clang/AST/GlobalDecl.h" | ||
| #include "clang/AST/Mangle.h" | ||
| #include "clang/AST/NestedNameSpecifier.h" | ||
| #include "clang/AST/OperationKinds.h" | ||
| #include "clang/AST/QualTypeNames.h" | ||
| #include "clang/AST/RawCommentList.h" | ||
| #include "clang/AST/RecordLayout.h" | ||
| #include "clang/AST/RecursiveASTVisitor.h" | ||
| #include "clang/AST/Stmt.h" | ||
| #include "clang/AST/Type.h" | ||
| #include "clang/AST/VTableBuilder.h" | ||
| #include "clang/Basic/Builtins.h" | ||
| #include "clang/Basic/Diagnostic.h" | ||
| #include "clang/Basic/DiagnosticSema.h" | ||
| #include "clang/Basic/LangStandard.h" | ||
|
|
@@ -108,6 +111,7 @@ | |
| #include <map> | ||
| #include <memory> | ||
| #include <mutex> | ||
| #include <optional> | ||
| #include <set> | ||
| #include <sstream> | ||
| #include <stack> | ||
|
|
@@ -1557,6 +1561,137 @@ bool IsFunctionProtoType(ConstTypeRef TyRef) { | |
| return llvm::isa_and_nonnull<clang::FunctionProtoType>(T); | ||
| } | ||
|
|
||
| static AllocType handleNew(const clang::CXXNewExpr* CNE) { | ||
| if (CNE->getNumPlacementArgs() > 0) | ||
| return AllocType::None; | ||
| if (CNE->isArray()) | ||
| return AllocType::NewArr; | ||
| return AllocType::New; | ||
| } | ||
|
|
||
| static AllocType AnalyzeAllocType(const clang::FunctionDecl* Fn); | ||
|
|
||
| static AllocType handleCall(const clang::CallExpr* CE) { | ||
| if (const auto* FD = CE->getDirectCallee()) { | ||
| if (FD->getBuiltinID() == Builtin::ID::BImalloc) | ||
|
keremsahn marked this conversation as resolved.
|
||
| return AllocType::Malloc; | ||
| return AnalyzeAllocType(FD); | ||
| } | ||
| // Function pointer calle | ||
| return AllocType::Unknown; | ||
| } | ||
|
|
||
| static AllocType handleExpr(const clang::Expr* expr, | ||
| std::map<const VarDecl*, AllocType>& varMap) { | ||
| const clang::Expr* finExpr = expr->IgnoreParenCasts(); | ||
| // Case: return new __type__ | ||
| if (const auto* CNE = dyn_cast<CXXNewExpr>(finExpr)) | ||
| return handleNew(CNE); | ||
|
|
||
| // Case: returns a variable | ||
| else if (const auto* DRE = dyn_cast<DeclRefExpr>(finExpr)) { | ||
|
keremsahn marked this conversation as resolved.
Outdated
|
||
| if (const auto* VD = dyn_cast<VarDecl>(DRE->getDecl())) { | ||
| auto it = varMap.find(VD); | ||
| if (it != varMap.end()) | ||
| return it->second; | ||
| } | ||
| // FIXME: BindingDecl, NonTypeTemplateParmDecl are not handled | ||
| return AllocType::None; | ||
| } | ||
|
|
||
| // Case: malloc | ||
| else if (const auto* CE = dyn_cast<CallExpr>(finExpr)) { | ||
| return handleCall(CE); | ||
| } | ||
|
keremsahn marked this conversation as resolved.
Outdated
|
||
| return AllocType::None; | ||
|
keremsahn marked this conversation as resolved.
keremsahn marked this conversation as resolved.
keremsahn marked this conversation as resolved.
|
||
| } | ||
|
|
||
| static std::vector<const ReturnStmt*> | ||
| getAllRetStmt(const clang::CompoundStmt* CS) { | ||
| struct RetStmtVisitor : RecursiveASTVisitor<RetStmtVisitor> { | ||
| std::vector<const ReturnStmt*> vec; | ||
| bool VisitReturnStmt(ReturnStmt* RS) { | ||
| vec.push_back(RS); | ||
| return true; | ||
| } | ||
| }; | ||
| RetStmtVisitor Visitor; | ||
| Visitor.TraverseStmt(const_cast<CompoundStmt*>(CS)); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. warning: do not use const_cast to remove const qualifier [cppcoreguidelines-pro-type-const-cast] isitor;
^ |
||
| return Visitor.vec; | ||
| } | ||
|
|
||
| static std::map<const VarDecl*, AllocType> | ||
|
keremsahn marked this conversation as resolved.
Outdated
|
||
| getAllVarDecl(const clang::CompoundStmt* CS) { | ||
| struct VarVisitor : RecursiveASTVisitor<VarVisitor> { | ||
| std::map<const VarDecl*, AllocType> varMap; | ||
| bool VisitVarDecl(VarDecl* VD) { | ||
| Expr* expr = VD->getInit(); | ||
| if (expr) | ||
| varMap[VD] = handleExpr(expr, varMap); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does the
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These two functions handle different situations. int* x = new int(n);AST of this statement: -DeclStmt <line:16:5, col:24>
| | `-VarDecl <col:5, col:23> col:10 used x 'int *' cinit
| | `-CXXNewExpr <col:14, col:23> 'int *' Function 0x4a8e6ff0 'operator new' 'void *(std::size_t)'
| | `-ImplicitCastExpr <col:22> 'int' <LValueToRValue>
| | `-DeclRefExpr <col:22> 'int' lvalue ParmVar 0x4a999868 'n' 'int'y=x;AST of this: -BinaryOperator <line:18:5, col:7> 'int *' lvalue '='
| | |-DeclRefExpr <col:5> 'int *' lvalue Var 0x4a999b10 'y' 'int *'
| | `-ImplicitCastExpr <col:7> 'int *' <LValueToRValue>
| | `-DeclRefExpr <col:7> 'int *' lvalue Var 0x4a9999c8 'x' 'int *'So basically in variable declarations there is no BinaryOperator node, hence the intersection |
||
| else | ||
| varMap[VD] = AllocType::None; | ||
| return true; | ||
| } | ||
|
|
||
| bool VisitBinaryOperator(clang::BinaryOperator* BO) { | ||
| if (BO->getOpcode() == BO_Assign) { | ||
|
keremsahn marked this conversation as resolved.
Outdated
|
||
| Expr* LHS = BO->getLHS(); | ||
|
keremsahn marked this conversation as resolved.
Outdated
|
||
| LHS = LHS->IgnoreParenCasts(); | ||
| if (auto* DRE = dyn_cast<DeclRefExpr>(LHS)) { | ||
| if (auto* VD = dyn_cast<VarDecl>(DRE->getDecl())) { | ||
| Expr* RHS = BO->getRHS(); | ||
| varMap[VD] = handleExpr(RHS, varMap); | ||
| } | ||
| } | ||
| } | ||
| return true; | ||
| } | ||
| }; | ||
| VarVisitor Visitor; | ||
| Visitor.TraverseStmt(const_cast<CompoundStmt*>(CS)); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. warning: do not use const_cast to remove const qualifier [cppcoreguidelines-pro-type-const-cast] isitor;
^ |
||
| return Visitor.varMap; | ||
| } | ||
|
|
||
| static AllocType AnalyzeAllocType(const clang::FunctionDecl* Fn) { | ||
| const clang::QualType QT = Fn->getReturnType(); | ||
| if (!QT->isPointerType()) | ||
| return AllocType::None; | ||
| const Stmt* fnBody = Fn->getBody(); | ||
| if (!fnBody) | ||
| return AllocType::Unknown; | ||
| const auto* CmpStmt = dyn_cast<clang::CompoundStmt>(fnBody); | ||
| // FIXME:: try catch blocks are not CompoundStmt, only edge case | ||
| if (!CmpStmt) | ||
| return AllocType::Unknown; | ||
| std::map<const VarDecl*, AllocType> varMap = getAllVarDecl(CmpStmt); | ||
| std::vector<const ReturnStmt*> allRetStmt = getAllRetStmt(CmpStmt); | ||
| std::optional<AllocType> res; | ||
|
keremsahn marked this conversation as resolved.
|
||
| for (const ReturnStmt* retStmt : allRetStmt) { | ||
| const clang::Expr* retExpr = retStmt->getRetValue(); | ||
| if (retExpr == nullptr) | ||
| continue; | ||
| AllocType tmp = handleExpr(retExpr, varMap); | ||
| if (!res.has_value()) | ||
| res = tmp; | ||
| // If function's allocation behaviour differs between different cases, | ||
| // analyzer returns unknown. | ||
| else if (*res != tmp) | ||
| return AllocType::Unknown; | ||
| } | ||
| return res.value_or(AllocType::None); | ||
| } | ||
|
|
||
| AllocType GetAllocType(ConstFuncRef Fn) { | ||
| INTEROP_TRACE(Fn); | ||
| if (Fn) { | ||
| const auto* D = unwrap<Decl>(Fn); | ||
| if (const auto* FD = dyn_cast<FunctionDecl>(D)) { | ||
| return INTEROP_RETURN(AnalyzeAllocType(FD)); | ||
| } | ||
| } | ||
| return INTEROP_RETURN(AllocType::None); | ||
| } | ||
|
|
||
| void GetFnTypeSignature(ConstTypeRef fn_type, std::vector<TypeRef>& sig) { | ||
| INTEROP_TRACE(fn_type, INTEROP_OUT(sig)); | ||
| QualType QT = QualType::getFromOpaquePtr(fn_type.data); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
warning: no header providing "Cpp::AllocType" is directly included [misc-include-cleaner]
(T); ^