File indexing completed on 2024-04-06 12:31:50
0001 #include <clang/AST/DeclCXX.h>
0002 #include <clang/AST/Attr.h>
0003 #include <clang/AST/Decl.h>
0004 #include <clang/AST/DeclTemplate.h>
0005 #include <clang/AST/StmtVisitor.h>
0006 #include <clang/AST/ParentMap.h>
0007 #include <clang/Analysis/CFGStmtMap.h>
0008 #include <clang/Analysis/CallGraph.h>
0009 #include <llvm/Support/SaveAndRestore.h>
0010 #include <clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h>
0011 #include <clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h>
0012 #include <clang/StaticAnalyzer/Core/Checker.h>
0013 #include <clang/StaticAnalyzer/Core/BugReporter/BugReporter.h>
0014 #include <clang/StaticAnalyzer/Core/BugReporter/BugType.h>
0015 #include <llvm/ADT/SmallString.h>
0016 #include <iostream>
0017 #include <fstream>
0018 #include <iterator>
0019 #include <string>
0020 #include <algorithm>
0021
0022 #include "FunctionChecker.h"
0023
0024 using namespace clang;
0025 using namespace ento;
0026 using namespace llvm;
0027
0028 namespace clangcms {
0029
0030 class FWalker : public clang::StmtVisitor<FWalker> {
0031 const CheckerBase *Checker;
0032 clang::ento::BugReporter &BR;
0033 clang::AnalysisDeclContext *AC;
0034
0035 public:
0036 FWalker(const CheckerBase *checker, clang::ento::BugReporter &br, clang::AnalysisDeclContext *ac)
0037 : Checker(checker), BR(br), AC(ac) {}
0038
0039 const clang::Stmt *ParentStmt(const Stmt *S) {
0040 const Stmt *P = AC->getParentMap().getParentIgnoreParens(S);
0041 if (!P)
0042 return nullptr;
0043 return P;
0044 }
0045
0046 void VisitChildren(clang::Stmt *S);
0047 void VisitStmt(clang::Stmt *S) { VisitChildren(S); }
0048 void VisitDeclRefExpr(clang::DeclRefExpr *DRE);
0049 void ReportDeclRef(const clang::DeclRefExpr *DRE);
0050 };
0051
0052 void FWalker::VisitChildren(clang::Stmt *S) {
0053 for (clang::Stmt::child_iterator I = S->child_begin(), E = S->child_end(); I != E; ++I)
0054 if (clang::Stmt *child = *I) {
0055 Visit(child);
0056 }
0057 }
0058
0059 void FWalker::VisitDeclRefExpr(clang::DeclRefExpr *DRE) {
0060 if (const clang::VarDecl *D = llvm::dyn_cast_or_null<clang::VarDecl>(DRE->getDecl())) {
0061 if (D && support::isSafeClassName(D->getCanonicalDecl()->getQualifiedNameAsString()))
0062 return;
0063 ReportDeclRef(DRE);
0064 }
0065 }
0066
0067 void FWalker::ReportDeclRef(const clang::DeclRefExpr *DRE) {
0068 const clang::VarDecl *D = llvm::dyn_cast_or_null<clang::VarDecl>(DRE->getDecl());
0069 if (D && (D->hasAttr<CMSThreadGuardAttr>() || D->hasAttr<CMSThreadSafeAttr>()) || D->hasAttr<CMSSaAllowAttr>())
0070 return;
0071 if (support::isSafeClassName(D->getCanonicalDecl()->getQualifiedNameAsString()))
0072 return;
0073
0074 const char *sfile = BR.getSourceManager().getPresumedLoc(D->getLocation()).getFilename();
0075 std::string fname(sfile);
0076 if (!support::isInterestingLocation(fname))
0077 return;
0078 clang::QualType t = D->getType();
0079 if (support::isSafeClassName(t.getCanonicalType().getAsString()))
0080 return;
0081 const Decl *PD = AC->getDecl();
0082 std::string dname = "";
0083 std::string sdname = "";
0084 if (const NamedDecl *ND = llvm::dyn_cast_or_null<NamedDecl>(PD)) {
0085 sdname = support::getQualifiedName(*ND);
0086 dname = ND->getQualifiedNameAsString();
0087 }
0088 clang::ento::PathDiagnosticLocation DLoc;
0089 if (support::isCmsLocalFile(sfile)) {
0090 if (D->getLocation().isMacroID())
0091 DLoc = clang::ento::PathDiagnosticLocation(D->getLocation(), BR.getSourceManager());
0092 else
0093 DLoc = clang::ento::PathDiagnosticLocation::createBegin(D, BR.getSourceManager());
0094 } else
0095 DLoc = clang::ento::PathDiagnosticLocation::createBegin(DRE, BR.getSourceManager(), AC);
0096
0097 std::string tname = "function-checker.txt.unsorted";
0098
0099 std::string vname = support::getQualifiedName(*D);
0100 std::string svname = D->getNameAsString();
0101 if (D->getTSCSpec() == clang::ThreadStorageClassSpecifier::TSCS_thread_local)
0102 return;
0103 if (D->isStaticLocal() && !clangcms::support::isConst(t)) {
0104 std::string buf;
0105 llvm::raw_string_ostream os(buf);
0106 os << "function '" << dname << "' accesses or modifies non-const static local variable '" << svname << "'.\n";
0107 BR.EmitBasicReport(D,
0108 Checker->getCheckerName(),
0109 "non-const static local variable accessed or modified",
0110 "ThreadSafety",
0111 os.str(),
0112 DLoc);
0113 std::string ostring = "function '" + sdname + "' static variable '" + vname + "'.\n";
0114 support::writeLog(ostring, tname);
0115 return;
0116 }
0117
0118 if (D->isStaticDataMember() && !clangcms::support::isConst(t)) {
0119 std::string buf;
0120 llvm::raw_string_ostream os(buf);
0121 os << "function '" << dname << "' accesses or modifies non-const static member data variable '" << svname
0122 << "'.\n";
0123 BR.EmitBasicReport(D,
0124 Checker->getCheckerName(),
0125 "non-const static local variable accessed or modified",
0126 "ThreadSafety",
0127 os.str(),
0128 DLoc);
0129 std::string ostring = "function '" + sdname + "' static variable '" + vname + "'.\n";
0130 support::writeLog(ostring, tname);
0131 return;
0132 }
0133
0134 if (D->hasGlobalStorage() && !D->isStaticDataMember() && !D->isStaticLocal() && !clangcms::support::isConst(t)) {
0135 std::string buf;
0136 llvm::raw_string_ostream os(buf);
0137 os << "function '" << dname << "' accesses or modifies non-const global static variable '" << svname << "'.\n";
0138 BR.EmitBasicReport(D,
0139 Checker->getCheckerName(),
0140 "non-const static local variable accessed or modified",
0141 "ThreadSafety",
0142 os.str(),
0143 DLoc);
0144 std::string ostring = "function '" + sdname + "' static variable '" + vname + "'.\n";
0145 support::writeLog(ostring, tname);
0146 return;
0147 }
0148 }
0149
0150 void FunctionChecker::checkASTDecl(const CXXMethodDecl *MD, AnalysisManager &mgr, BugReporter &BR) const {
0151 if (MD->hasAttr<CMSThreadSafeAttr>() || MD->hasAttr<CMSSaAllowAttr>())
0152 return;
0153 const char *sfile = BR.getSourceManager().getPresumedLoc(MD->getLocation()).getFilename();
0154 if (!support::isCmsLocalFile(sfile))
0155 return;
0156 std::string fname(sfile);
0157 if (!support::isInterestingLocation(fname))
0158 return;
0159 if (!MD->doesThisDeclarationHaveABody())
0160 return;
0161 FWalker walker(this, BR, mgr.getAnalysisDeclContext(MD));
0162 walker.Visit(MD->getBody());
0163 return;
0164 }
0165
0166 void FunctionChecker::checkASTDecl(const FunctionDecl *FD, AnalysisManager &mgr, BugReporter &BR) const {
0167 if (FD->hasAttr<CMSThreadSafeAttr>() || FD->hasAttr<CMSSaAllowAttr>())
0168 return;
0169 if (FD->isInExternCContext()) {
0170 std::string buf;
0171 std::string dname = FD->getQualifiedNameAsString();
0172 if (dname.compare(dname.size() - 1, 1, "_") == 0) {
0173 llvm::raw_string_ostream os(buf);
0174 os << "function '" << dname
0175 << "' is in an extern \"C\" context and most likely accesses or modifies fortran variables in a "
0176 "'COMMONBLOCK'.\n";
0177 clang::ento::PathDiagnosticLocation FDLoc =
0178 clang::ento::PathDiagnosticLocation::createBegin(FD, BR.getSourceManager());
0179 BR.EmitBasicReport(
0180 FD, this->getCheckerName(), "COMMONBLOCK variable accessed or modified", "ThreadSafety", os.str(), FDLoc);
0181 std::string ostring = "function '" + dname + "' static variable 'COMMONBLOCK'.\n";
0182 std::string tname = "function-checker.txt.unsorted";
0183 support::writeLog(ostring, tname);
0184 }
0185 }
0186
0187 const char *sfile = BR.getSourceManager().getPresumedLoc(FD->getLocation()).getFilename();
0188 if (!support::isCmsLocalFile(sfile))
0189 return;
0190 std::string fname(sfile);
0191 if (!support::isInterestingLocation(fname))
0192 return;
0193 if (FD->doesThisDeclarationHaveABody()) {
0194 FWalker walker(this, BR, mgr.getAnalysisDeclContext(FD));
0195 walker.Visit(FD->getBody());
0196 }
0197 }
0198
0199 void FunctionChecker::checkASTDecl(const FunctionTemplateDecl *TD, AnalysisManager &mgr, BugReporter &BR) const {
0200 if (TD->hasAttr<CMSThreadSafeAttr>() || TD->hasAttr<CMSSaAllowAttr>())
0201 return;
0202 const char *sfile = BR.getSourceManager().getPresumedLoc(TD->getLocation()).getFilename();
0203 if (!support::isCmsLocalFile(sfile))
0204 return;
0205 std::string fname(sfile);
0206 if (!support::isInterestingLocation(fname))
0207 return;
0208 for (auto I = TD->spec_begin(), E = TD->spec_end(); I != E; ++I) {
0209 if (I->doesThisDeclarationHaveABody()) {
0210 FWalker walker(this, BR, mgr.getAnalysisDeclContext(*I));
0211 walker.Visit(I->getBody());
0212 }
0213 }
0214 return;
0215 }
0216
0217 }