File indexing completed on 2024-12-19 04:04:57
0001
0002
0003
0004
0005
0006
0007 #include "MutableMemberModificationChecker.h"
0008 #include <clang/AST/Decl.h>
0009 #include <clang/AST/Type.h>
0010 #include <clang/AST/DeclCXX.h>
0011 #include <clang/AST/ASTContext.h>
0012 #include <clang/AST/Stmt.h>
0013 #include <clang/AST/Expr.h>
0014 #include <clang/AST/ExprCXX.h>
0015 #include <clang/AST/ParentMap.h>
0016 #include <clang/Analysis/AnalysisDeclContext.h>
0017 #include <clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h>
0018
0019 namespace clangcms {
0020 void MutableMemberModificationChecker::checkPreStmt(const clang::MemberExpr *ME,
0021 clang::ento::CheckerContext &C) const {
0022
0023
0024
0025 const auto *RD = llvm::dyn_cast<clang::CXXRecordDecl>(ME->getMemberDecl()->getDeclContext());
0026 if (RD) {
0027 std::string ClassName = RD->getNameAsString();
0028 if (support::isSafeClassName(ClassName)) {
0029 return;
0030 }
0031 }
0032
0033
0034 const clang::FunctionDecl *FuncD = C.getLocationContext()->getStackFrame()->getDecl()->getAsFunction();
0035 const clang::AttrVec &Attrs = FuncD->getAttrs();
0036 for (const auto *A : Attrs) {
0037 if (clang::isa<clang::CMSThreadGuardAttr>(A) || clang::isa<clang::CMSThreadSafeAttr>(A) ||
0038 clang::isa<clang::CMSSaAllowAttr>(A)) {
0039 return;
0040 }
0041 }
0042
0043
0044
0045 clang::ento::PathDiagnosticLocation PathLoc =
0046 clang::ento::PathDiagnosticLocation::createBegin(ME, C.getSourceManager(), C.getLocationContext());
0047
0048
0049 clang::ento::BugReporter &BR = C.getBugReporter();
0050
0051 if (!m_exception.reportMutableMember(PathLoc, BR)) {
0052 return;
0053 }
0054
0055
0056 const auto *FD = llvm::dyn_cast<clang::FieldDecl>(ME->getMemberDecl());
0057 if (!FD || !FD->isMutable()) {
0058 return;
0059 }
0060
0061
0062 if (FD->getAccess() != clang::AS_private) {
0063 return;
0064 }
0065
0066
0067 if (support::isStdAtomic(FD)) {
0068 return;
0069 }
0070
0071
0072 const clang::AttrVec &FAttrs = FD->getAttrs();
0073 for (const auto *A : FAttrs) {
0074 if (clang::isa<clang::CMSThreadGuardAttr>(A) || clang::isa<clang::CMSThreadSafeAttr>(A) ||
0075 clang::isa<clang::CMSSaAllowAttr>(A)) {
0076 return;
0077 }
0078 }
0079
0080
0081 const auto *MethodDecl = llvm::dyn_cast<clang::CXXMethodDecl>(FuncD);
0082 if (!MethodDecl || !MethodDecl->isConst()) {
0083 return;
0084 }
0085
0086
0087 bool ret;
0088 if (checkAssignToMutable(ME, C)) {
0089
0090 std::string MutableMemberName = ME->getMemberDecl()->getQualifiedNameAsString();
0091 if (!BT) {
0092 BT = std::make_unique<clang::ento::BugType>(
0093 this, "Mutable member modification in const member function", "ConstThreadSafety");
0094 }
0095 std::string Description =
0096 "Modifying mutable member '" + MutableMemberName + "' in const member function is potentially thread-unsafe ";
0097 auto Report = std::make_unique<clang::ento::PathSensitiveBugReport>(*BT, Description, C.generateErrorNode());
0098 Report->addRange(ME->getSourceRange());
0099 C.emitReport(std::move(Report));
0100 return;
0101 }
0102
0103 if (checkCallNonConstOfMutable(ME, C)) {
0104 if (RD) {
0105 std::string ClassName = RD->getNameAsString();
0106 std::string MemberName = ME->getMemberDecl()->getNameAsString();
0107 std::string FunctionName = MethodDecl->getNameAsString();
0108 std::string tname = "mutablemember-checker.txt.unsorted";
0109 std::string ostring = "flagged class '" + ClassName + "' modifying mutable member '" + MemberName +
0110 "' in function '" + FunctionName + "'";
0111 support::writeLog(ostring, tname);
0112 }
0113 }
0114 }
0115
0116
0117 bool MutableMemberModificationChecker::checkAssignToMutable(const clang::MemberExpr *ME,
0118 clang::ento::CheckerContext &C) const {
0119
0120
0121
0122 const clang::LocationContext *LC = C.getLocationContext();
0123 const clang::ParentMap &PM = LC->getParentMap();
0124 const clang::Stmt *ParentStmt = PM.getParent(ME);
0125
0126 if (!ParentStmt) {
0127 return false;
0128 }
0129
0130
0131 const auto *BO = llvm::dyn_cast<clang::BinaryOperator>(ParentStmt);
0132 if (BO) {
0133 const auto *LHSAsME = llvm::dyn_cast<clang::MemberExpr>(BO->getLHS());
0134 if (LHSAsME) {
0135 if (BO->isAssignmentOp() && LHSAsME == ME) {
0136
0137 return true;
0138 }
0139 }
0140 }
0141
0142
0143 const auto *CO = llvm::dyn_cast<clang::CXXOperatorCallExpr>(ParentStmt);
0144 if (CO) {
0145 const auto *LHSAsME = llvm::dyn_cast<clang::MemberExpr>(CO->getArg(0));
0146 if (LHSAsME) {
0147 if (CO->isAssignmentOp() && LHSAsME == ME) {
0148
0149 return true;
0150 }
0151 }
0152 }
0153
0154
0155 if (const auto *UO = llvm::dyn_cast<clang::UnaryOperator>(ParentStmt)) {
0156 if (UO->isIncrementDecrementOp() && UO->getSubExpr() == ME) {
0157 return true;
0158 }
0159 }
0160
0161 return false;
0162 }
0163
0164
0165 bool MutableMemberModificationChecker::checkCallNonConstOfMutable(const clang::MemberExpr *ME,
0166 clang::ento::CheckerContext &C) const {
0167
0168 const clang::Expr *E = ME;
0169 while (E) {
0170 if (const clang::CXXMemberCallExpr *Call = llvm::dyn_cast<clang::CXXMemberCallExpr>(E->IgnoreParenCasts())) {
0171 const clang::CXXMethodDecl *CalledMethod = Call->getMethodDecl();
0172 if (CalledMethod && !CalledMethod->isConst()) {
0173
0174 std::string MutableMemberName = ME->getMemberDecl()->getQualifiedNameAsString();
0175
0176
0177 std::string CalledMethodName = CalledMethod->getQualifiedNameAsString();
0178
0179 if (!BT) {
0180 BT = std::make_unique<clang::ento::BugType>(
0181 this, "Mutable member modification in const member function", "ConstThreadSafety");
0182 }
0183 std::string Description = "Calling non-const method '" + CalledMethodName + "' of mutable member '" +
0184 MutableMemberName + "' in a const member function is potentially thread-unsafe.";
0185 auto Report = std::make_unique<clang::ento::PathSensitiveBugReport>(*BT, Description, C.generateErrorNode());
0186 Report->addRange(ME->getSourceRange());
0187 C.emitReport(std::move(Report));
0188 return true;
0189 }
0190 }
0191
0192 const clang::Stmt *ParentStmt = C.getLocationContext()->getParentMap().getParent(E);
0193 E = llvm::dyn_cast_or_null<clang::Expr>(ParentStmt);
0194 }
0195 return false;
0196 }
0197
0198 }