@@ -1650,6 +1650,9 @@ struct AllocationTraverser : RecursiveASTVisitor<AllocationTraverser> {
16501650 // Result var keeps the combination all possible values of previous return
16511651 // statements
16521652 std::optional<AllocType> result;
1653+ // A map every branch writes the previous value of overwriten variables
1654+ std::unordered_map<const clang::VarDecl*, std::optional<AllocType>>* undoLog =
1655+ nullptr ;
16531656
16541657 AllocationTraverser (std::unordered_map<const clang::FunctionDecl*,
16551658 std::optional<AllocType>>& cache)
@@ -1665,6 +1668,109 @@ struct AllocationTraverser : RecursiveASTVisitor<AllocationTraverser> {
16651668 return RecursiveASTVisitor::TraverseDecl (D);
16661669 }
16671670
1671+ std::optional<AllocType> join (std::optional<AllocType> a,
1672+ std::optional<AllocType> b) {
1673+ if (a == AllocType::Null)
1674+ return b;
1675+
1676+ if (b == AllocType::Null)
1677+ return a;
1678+
1679+ if (a == b)
1680+ return a;
1681+ return AllocType::Unknown;
1682+ }
1683+ void undoOnVarMap () {
1684+ for (auto & [VD , oldVal] : *undoLog) {
1685+ auto it = varMap.find (VD );
1686+ if (it == varMap.end ())
1687+ continue ;
1688+ varMap[VD ] = oldVal;
1689+ }
1690+ }
1691+
1692+ bool TraverseIfStmt (clang::IfStmt* IS ) {
1693+ TraverseStmt (IS ->getConditionVariableDeclStmt ());
1694+ TraverseStmt (IS ->getCond ());
1695+ auto * undoLogCopy = undoLog;
1696+ std::unordered_map<const clang::VarDecl*, std::optional<AllocType>>
1697+ undoLogThen;
1698+ undoLog = &undoLogThen;
1699+ TraverseStmt (IS ->getThen ());
1700+ auto * elseBranch = IS ->getElse ();
1701+ // There is no else
1702+ if (!elseBranch) {
1703+ for (auto & [VD , val] : undoLogThen) {
1704+ auto it = varMap.find (VD );
1705+ if (it == varMap.end ())
1706+ continue ;
1707+ // Overwrite varMap with join of overwriten and previous value
1708+ it->second = join (val, it->second );
1709+ // If branch is nested, inform upper branch about your changes
1710+ if (undoLogCopy)
1711+ undoLogCopy->try_emplace (VD , val);
1712+ }
1713+ undoLog = undoLogCopy;
1714+ return true ;
1715+ }
1716+
1717+ // Save overwriten values in if branch to join
1718+ std::unordered_map<const clang::VarDecl*, std::optional<AllocType>>
1719+ valuesInIfBranch;
1720+ for (auto & [VD , val] : undoLogThen) {
1721+ auto it = varMap.find (VD );
1722+ if (it == varMap.end ())
1723+ continue ;
1724+ valuesInIfBranch[VD ] = it->second ;
1725+ }
1726+
1727+ // Take changes on varMap back before going to else branch
1728+ undoOnVarMap ();
1729+
1730+ std::unordered_map<const clang::VarDecl*, std::optional<AllocType>>
1731+ undoLogElse;
1732+ undoLog = &undoLogElse;
1733+ TraverseStmt (elseBranch);
1734+
1735+ for (auto & [VD , val] : undoLogElse) {
1736+ auto it = varMap.find (VD );
1737+ if (it == varMap.end ())
1738+ continue ;
1739+ auto it2 = valuesInIfBranch.find (VD );
1740+ // If var is just changed in else branch
1741+ if (it2 == valuesInIfBranch.end ()) {
1742+ it->second = join (val, it->second );
1743+ continue ;
1744+ }
1745+ // If var is changed in both
1746+ it->second = join (it->second , it2->second );
1747+ }
1748+
1749+ for (auto & [VD , val] : valuesInIfBranch) {
1750+ auto it = varMap.find (VD );
1751+ if (it == varMap.end ())
1752+ continue ;
1753+ // If var is just changed in if branch
1754+ if (undoLogElse.find (VD ) == undoLogElse.end ()) {
1755+ it->second = join (val, it->second );
1756+ continue ;
1757+ }
1758+ // If var is changed in both, here for extra safety
1759+ it->second = join (val, it->second );
1760+ }
1761+
1762+ // Inform upper branch about changes done for both inner if and else branch
1763+ if (undoLogCopy) {
1764+ for (auto & [VD , val] : undoLogThen)
1765+ undoLogCopy->try_emplace (VD , val);
1766+ for (auto & [VD , val] : undoLogElse)
1767+ undoLogCopy->try_emplace (VD , val);
1768+ }
1769+
1770+ undoLog = undoLogCopy;
1771+ return true ;
1772+ }
1773+
16681774 bool VisitVarDecl (VarDecl* VD ) {
16691775 Expr* expr = VD ->getInit ();
16701776 if (expr)
@@ -1675,15 +1781,28 @@ struct AllocationTraverser : RecursiveASTVisitor<AllocationTraverser> {
16751781 }
16761782
16771783 bool VisitBinaryOperator (clang::BinaryOperator* BO ) {
1678- if (BO ->getOpcode () != BO_Assign)
1679- return true ;
16801784 Expr* LHS = BO ->getLHS ();
16811785 LHS = LHS ->IgnoreParenCasts ();
1682- if (auto * DRE = dyn_cast<DeclRefExpr>(LHS )) {
1683- if (auto * VD = dyn_cast<VarDecl>(DRE ->getDecl ())) {
1684- Expr* RHS = BO ->getRHS ();
1685- varMap[VD ] = handleExpr (RHS );
1686- }
1786+ auto * DRE = dyn_cast<DeclRefExpr>(LHS );
1787+ if (!DRE )
1788+ return true ;
1789+
1790+ auto * VD = dyn_cast<VarDecl>(DRE ->getDecl ());
1791+ if (!VD )
1792+ return true ;
1793+
1794+ if (BO ->getOpcode () == BO_Assign) {
1795+ if (undoLog)
1796+ undoLog->try_emplace (VD , varMap[VD ]);
1797+ Expr* RHS = BO ->getRHS ();
1798+ varMap[VD ] = handleExpr (RHS );
1799+ return true ;
1800+ }
1801+ if (BO ->isCompoundAssignmentOp ()) {
1802+ if (undoLog)
1803+ undoLog->try_emplace (VD , varMap[VD ]);
1804+ varMap[VD ] = AllocType::Unknown;
1805+ return true ;
16871806 }
16881807 return true ;
16891808 }
@@ -1695,21 +1814,35 @@ struct AllocationTraverser : RecursiveASTVisitor<AllocationTraverser> {
16951814 std::optional<AllocType> tmp = handleExpr (retExpr);
16961815 if (!tmp.has_value ())
16971816 return true ;
1698- if (!result.has_value ())
1817+ if (!result.has_value ()) {
16991818 result = tmp;
1819+ return true ;
1820+ }
17001821 // If function's allocation behaviour differs between different cases,
17011822 // analyzer returns unknown.
1702- else if (* result != tmp) {
1703- result = AllocType::Unknown;
1823+ result = join ( result, tmp);
1824+ if ( result == AllocType::Unknown)
17041825 return false ;
1705- }
17061826 return true ;
17071827 }
17081828
17091829 std::optional<AllocType> handleCall (const clang::CallExpr* CE ) {
17101830 if (const auto * FD = CE ->getDirectCallee ()) {
17111831 if (FD ->getBuiltinID () == Builtin::ID ::BImalloc)
17121832 return AllocType::Malloc;
1833+ if (FD ->getBuiltinID () == Builtin::ID ::BI__builtin_operator_new)
1834+ return AllocType::OperatorNew;
1835+ // Detects operator new/new[]/delete/delete[]
1836+ if (FD ->isReplaceableGlobalAllocationFunction ()) {
1837+ switch (FD ->getOverloadedOperator ()) {
1838+ case OO_New:
1839+ return AllocType::OperatorNew;
1840+ case OO_Array_New:
1841+ return AllocType::OperatorNewArr;
1842+ default :
1843+ break ; // OO_Delete/OO_Array_Delete
1844+ }
1845+ }
17131846 auto it = visitedFuncs.find (FD );
17141847 if (it == visitedFuncs.end ()) {
17151848 visitedFuncs[FD ] = std::nullopt ;
@@ -1722,8 +1855,16 @@ struct AllocationTraverser : RecursiveASTVisitor<AllocationTraverser> {
17221855 }
17231856
17241857 static AllocType handleNew (const clang::CXXNewExpr* CNE ) {
1725- if (CNE ->getNumPlacementArgs () > 0 )
1726- return AllocType::None;
1858+ if (CNE ->getNumPlacementArgs () > 0 ) {
1859+ /*
1860+ Non-allocating placement allocation functions
1861+ void* operator new ( std::size_t count, void* ptr );
1862+ void* operator new[]( std::size_t count, void* ptr );
1863+ */
1864+ const clang::FunctionDecl* OpNew = CNE ->getOperatorNew ();
1865+ if (OpNew && OpNew->isReservedGlobalPlacementOperator ())
1866+ return AllocType::None;
1867+ }
17271868 if (CNE ->isArray ())
17281869 return AllocType::NewArr;
17291870 return AllocType::New;
@@ -1749,6 +1890,15 @@ struct AllocationTraverser : RecursiveASTVisitor<AllocationTraverser> {
17491890 // Case: malloc or another func call
17501891 if (const auto * CE = dyn_cast<CallExpr>(finExpr))
17511892 return handleCall (CE );
1893+
1894+ // Case: NULL, nullptr or (int*)(__integerLiteral__)
1895+ const clang::Expr* parExpr = expr->IgnoreParens ();
1896+ while (const auto * CE = dyn_cast<CastExpr>(parExpr)) {
1897+ if (CE ->getCastKind () == CK_NullToPointer)
1898+ return AllocType::Null;
1899+ parExpr = CE ->getSubExpr ();
1900+ }
1901+
17521902 return AllocType::None;
17531903 }
17541904};
@@ -1769,6 +1919,8 @@ AnalyzeAllocType(const clang::FunctionDecl* Fn,
17691919 if (!CmpStmt)
17701920 return AllocType::Unknown;
17711921 AllocationTraverser Traverser (visitedFuncs);
1922+ for (auto * parm : Fn->parameters ())
1923+ Traverser.VisitVarDecl (parm);
17721924 Traverser.TraverseStmt (const_cast <clang::CompoundStmt*>(CmpStmt));
17731925 auto res = Traverser.result ;
17741926 visitedFuncs[Fn] = res;
0 commit comments