CStringSyntaxChecker.cpp 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. //== CStringSyntaxChecker.cpp - CoreFoundation containers API *- C++ -*-==//
  2. //
  3. // The LLVM Compiler Infrastructure
  4. //
  5. // This file is distributed under the University of Illinois Open Source
  6. // License. See LICENSE.TXT for details.
  7. //
  8. //===----------------------------------------------------------------------===//
  9. //
  10. // An AST checker that looks for common pitfalls when using C string APIs.
  11. // - Identifies erroneous patterns in the last argument to strncat - the number
  12. // of bytes to copy.
  13. //
  14. //===----------------------------------------------------------------------===//
  15. #include "ClangSACheckers.h"
  16. #include "clang/AST/Expr.h"
  17. #include "clang/AST/OperationKinds.h"
  18. #include "clang/AST/StmtVisitor.h"
  19. #include "clang/Analysis/AnalysisDeclContext.h"
  20. #include "clang/Basic/TargetInfo.h"
  21. #include "clang/Basic/TypeTraits.h"
  22. #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
  23. #include "clang/StaticAnalyzer/Core/Checker.h"
  24. #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
  25. #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
  26. #include "llvm/ADT/SmallString.h"
  27. #include "llvm/Support/raw_ostream.h"
  28. using namespace clang;
  29. using namespace ento;
  30. namespace {
  31. class WalkAST: public StmtVisitor<WalkAST> {
  32. const CheckerBase *Checker;
  33. BugReporter &BR;
  34. AnalysisDeclContext* AC;
  35. /// Check if two expressions refer to the same declaration.
  36. bool sameDecl(const Expr *A1, const Expr *A2) {
  37. if (const auto *D1 = dyn_cast<DeclRefExpr>(A1->IgnoreParenCasts()))
  38. if (const auto *D2 = dyn_cast<DeclRefExpr>(A2->IgnoreParenCasts()))
  39. return D1->getDecl() == D2->getDecl();
  40. return false;
  41. }
  42. /// Check if the expression E is a sizeof(WithArg).
  43. bool isSizeof(const Expr *E, const Expr *WithArg) {
  44. if (const auto *UE = dyn_cast<UnaryExprOrTypeTraitExpr>(E))
  45. if (UE->getKind() == UETT_SizeOf && !UE->isArgumentType())
  46. return sameDecl(UE->getArgumentExpr(), WithArg);
  47. return false;
  48. }
  49. /// Check if the expression E is a strlen(WithArg).
  50. bool isStrlen(const Expr *E, const Expr *WithArg) {
  51. if (const auto *CE = dyn_cast<CallExpr>(E)) {
  52. const FunctionDecl *FD = CE->getDirectCallee();
  53. if (!FD)
  54. return false;
  55. return (CheckerContext::isCLibraryFunction(FD, "strlen") &&
  56. sameDecl(CE->getArg(0), WithArg));
  57. }
  58. return false;
  59. }
  60. /// Check if the expression is an integer literal with value 1.
  61. bool isOne(const Expr *E) {
  62. if (const auto *IL = dyn_cast<IntegerLiteral>(E))
  63. return (IL->getValue().isIntN(1));
  64. return false;
  65. }
  66. StringRef getPrintableName(const Expr *E) {
  67. if (const auto *D = dyn_cast<DeclRefExpr>(E->IgnoreParenCasts()))
  68. return D->getDecl()->getName();
  69. return StringRef();
  70. }
  71. /// Identify erroneous patterns in the last argument to strncat - the number
  72. /// of bytes to copy.
  73. bool containsBadStrncatPattern(const CallExpr *CE);
  74. /// Identify erroneous patterns in the last argument to strlcpy - the number
  75. /// of bytes to copy.
  76. /// The bad pattern checked is when the size is known
  77. /// to be larger than the destination can handle.
  78. /// char dst[2];
  79. /// size_t cpy = 4;
  80. /// strlcpy(dst, "abcd", sizeof("abcd") - 1);
  81. /// strlcpy(dst, "abcd", 4);
  82. /// strlcpy(dst, "abcd", cpy);
  83. bool containsBadStrlcpyPattern(const CallExpr *CE);
  84. public:
  85. WalkAST(const CheckerBase *Checker, BugReporter &BR, AnalysisDeclContext *AC)
  86. : Checker(Checker), BR(BR), AC(AC) {}
  87. // Statement visitor methods.
  88. void VisitChildren(Stmt *S);
  89. void VisitStmt(Stmt *S) {
  90. VisitChildren(S);
  91. }
  92. void VisitCallExpr(CallExpr *CE);
  93. };
  94. } // end anonymous namespace
  95. // The correct size argument should look like following:
  96. // strncat(dst, src, sizeof(dst) - strlen(dest) - 1);
  97. // We look for the following anti-patterns:
  98. // - strncat(dst, src, sizeof(dst) - strlen(dst));
  99. // - strncat(dst, src, sizeof(dst) - 1);
  100. // - strncat(dst, src, sizeof(dst));
  101. bool WalkAST::containsBadStrncatPattern(const CallExpr *CE) {
  102. if (CE->getNumArgs() != 3)
  103. return false;
  104. const Expr *DstArg = CE->getArg(0);
  105. const Expr *SrcArg = CE->getArg(1);
  106. const Expr *LenArg = CE->getArg(2);
  107. // Identify wrong size expressions, which are commonly used instead.
  108. if (const auto *BE = dyn_cast<BinaryOperator>(LenArg->IgnoreParenCasts())) {
  109. // - sizeof(dst) - strlen(dst)
  110. if (BE->getOpcode() == BO_Sub) {
  111. const Expr *L = BE->getLHS();
  112. const Expr *R = BE->getRHS();
  113. if (isSizeof(L, DstArg) && isStrlen(R, DstArg))
  114. return true;
  115. // - sizeof(dst) - 1
  116. if (isSizeof(L, DstArg) && isOne(R->IgnoreParenCasts()))
  117. return true;
  118. }
  119. }
  120. // - sizeof(dst)
  121. if (isSizeof(LenArg, DstArg))
  122. return true;
  123. // - sizeof(src)
  124. if (isSizeof(LenArg, SrcArg))
  125. return true;
  126. return false;
  127. }
  128. bool WalkAST::containsBadStrlcpyPattern(const CallExpr *CE) {
  129. if (CE->getNumArgs() != 3)
  130. return false;
  131. const Expr *DstArg = CE->getArg(0);
  132. const Expr *LenArg = CE->getArg(2);
  133. const auto *DstArgDecl = dyn_cast<DeclRefExpr>(DstArg->IgnoreParenCasts());
  134. const auto *LenArgDecl = dyn_cast<DeclRefExpr>(LenArg->IgnoreParenLValueCasts());
  135. // - size_t dstlen = sizeof(dst)
  136. if (LenArgDecl) {
  137. const auto *LenArgVal = dyn_cast<VarDecl>(LenArgDecl->getDecl());
  138. if (LenArgVal->getInit())
  139. LenArg = LenArgVal->getInit();
  140. }
  141. // - integral value
  142. // We try to figure out if the last argument is possibly longer
  143. // than the destination can possibly handle if its size can be defined
  144. if (const auto *IL = dyn_cast<IntegerLiteral>(LenArg->IgnoreParenCasts())) {
  145. uint64_t ILRawVal = IL->getValue().getZExtValue();
  146. if (const auto *Buffer = dyn_cast<ConstantArrayType>(DstArgDecl->getType())) {
  147. ASTContext &C = BR.getContext();
  148. uint64_t Usize = C.getTypeSizeInChars(DstArg->getType()).getQuantity();
  149. uint64_t BufferLen = BR.getContext().getTypeSize(Buffer) / Usize;
  150. if (BufferLen < ILRawVal)
  151. return true;
  152. }
  153. }
  154. return false;
  155. }
  156. void WalkAST::VisitCallExpr(CallExpr *CE) {
  157. const FunctionDecl *FD = CE->getDirectCallee();
  158. if (!FD)
  159. return;
  160. if (CheckerContext::isCLibraryFunction(FD, "strncat")) {
  161. if (containsBadStrncatPattern(CE)) {
  162. const Expr *DstArg = CE->getArg(0);
  163. const Expr *LenArg = CE->getArg(2);
  164. PathDiagnosticLocation Loc =
  165. PathDiagnosticLocation::createBegin(LenArg, BR.getSourceManager(), AC);
  166. StringRef DstName = getPrintableName(DstArg);
  167. SmallString<256> S;
  168. llvm::raw_svector_ostream os(S);
  169. os << "Potential buffer overflow. ";
  170. if (!DstName.empty()) {
  171. os << "Replace with 'sizeof(" << DstName << ") "
  172. "- strlen(" << DstName <<") - 1'";
  173. os << " or u";
  174. } else
  175. os << "U";
  176. os << "se a safer 'strlcat' API";
  177. BR.EmitBasicReport(FD, Checker, "Anti-pattern in the argument",
  178. "C String API", os.str(), Loc,
  179. LenArg->getSourceRange());
  180. }
  181. } else if (CheckerContext::isCLibraryFunction(FD, "strlcpy")) {
  182. if (containsBadStrlcpyPattern(CE)) {
  183. const Expr *DstArg = CE->getArg(0);
  184. const Expr *LenArg = CE->getArg(2);
  185. PathDiagnosticLocation Loc =
  186. PathDiagnosticLocation::createBegin(LenArg, BR.getSourceManager(), AC);
  187. StringRef DstName = getPrintableName(DstArg);
  188. SmallString<256> S;
  189. llvm::raw_svector_ostream os(S);
  190. os << "The third argument is larger than the size of the input buffer. ";
  191. if (!DstName.empty())
  192. os << "Replace with the value 'sizeof(" << DstName << ")` or lower";
  193. BR.EmitBasicReport(FD, Checker, "Anti-pattern in the argument",
  194. "C String API", os.str(), Loc,
  195. LenArg->getSourceRange());
  196. }
  197. }
  198. // Recurse and check children.
  199. VisitChildren(CE);
  200. }
  201. void WalkAST::VisitChildren(Stmt *S) {
  202. for (Stmt *Child : S->children())
  203. if (Child)
  204. Visit(Child);
  205. }
  206. namespace {
  207. class CStringSyntaxChecker: public Checker<check::ASTCodeBody> {
  208. public:
  209. void checkASTCodeBody(const Decl *D, AnalysisManager& Mgr,
  210. BugReporter &BR) const {
  211. WalkAST walker(this, BR, Mgr.getAnalysisDeclContext(D));
  212. walker.Visit(D->getBody());
  213. }
  214. };
  215. }
  216. void ento::registerCStringSyntaxChecker(CheckerManager &mgr) {
  217. mgr.registerChecker<CStringSyntaxChecker>();
  218. }