-
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
Changes from all commits
2c52932
7ed205c
ad20fbb
ef67084
750e0f4
2b923f2
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/CharInfo.h" | ||
| #include "clang/Basic/Diagnostic.h" | ||
| #include "clang/Basic/DiagnosticSema.h" | ||
|
|
@@ -111,6 +114,7 @@ | |
| #include <map> | ||
| #include <memory> | ||
| #include <mutex> | ||
| #include <optional> | ||
| #include <set> | ||
| #include <sstream> | ||
| #include <stack> | ||
|
|
@@ -119,6 +123,7 @@ | |
| #ifndef _WIN32 | ||
| #include <unistd.h> | ||
| #endif | ||
| #include <unordered_map> | ||
| #include <utility> | ||
| // Stream redirect. | ||
| #ifdef _WIN32 | ||
|
|
@@ -1587,6 +1592,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::unordered_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 | ||
| if (const auto* DRE = dyn_cast<DeclRefExpr>(finExpr)) { | ||
| 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 or another func call | ||
| if (const auto* CE = dyn_cast<CallExpr>(finExpr)) | ||
| return handleCall(CE); | ||
| 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::unordered_map<const VarDecl*, AllocType> | ||
| getAllVarDecl(const clang::CompoundStmt* CS) { | ||
| struct VarVisitor : RecursiveASTVisitor<VarVisitor> { | ||
| std::unordered_map<const VarDecl*, AllocType> varMap; | ||
| bool VisitVarDecl(VarDecl* VD) { | ||
| Expr* expr = VD->getInit(); | ||
| if (expr) | ||
| varMap[VD] = handleExpr(expr, varMap); | ||
|
Vipul-Cariappa marked this conversation as resolved.
|
||
| else | ||
| varMap[VD] = AllocType::None; | ||
| return true; | ||
| } | ||
|
|
||
| bool VisitBinaryOperator(clang::BinaryOperator* BO) { | ||
| if (BO->getOpcode() != BO_Assign) | ||
| return true; | ||
|
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. Can we add coverage for this line?
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. Hi Vassil, I added one check the fourth commit. Our first assumption was analyzed function does not leak memory, but i wrote the test this way to point the issue so maybe I can fix it later.
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. Can we add a comment capturing that intent?
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. Actually I added a FIXME up to the TESTAC(27, NewArr);Furthermore, I will open some issues I want to work on when the PR is merged, I can open for this one too. No worries |
||
| Expr* LHS = BO->getLHS(); | ||
| 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::unordered_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); ^