clang  8.0.0svn
CStringSyntaxChecker.cpp
Go to the documentation of this file.
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"
18 #include "clang/AST/StmtVisitor.h"
20 #include "clang/Basic/TargetInfo.h"
21 #include "clang/Basic/TypeTraits.h"
26 #include "llvm/ADT/SmallString.h"
27 #include "llvm/Support/raw_ostream.h"
28 
29 using namespace clang;
30 using namespace ento;
31 
32 namespace {
33 class WalkAST: public StmtVisitor<WalkAST> {
34  const CheckerBase *Checker;
35  BugReporter &BR;
37 
38  /// Check if two expressions refer to the same declaration.
39  bool sameDecl(const Expr *A1, const Expr *A2) {
40  if (const auto *D1 = dyn_cast<DeclRefExpr>(A1->IgnoreParenCasts()))
41  if (const auto *D2 = dyn_cast<DeclRefExpr>(A2->IgnoreParenCasts()))
42  return D1->getDecl() == D2->getDecl();
43  return false;
44  }
45 
46  /// Check if the expression E is a sizeof(WithArg).
47  bool isSizeof(const Expr *E, const Expr *WithArg) {
48  if (const auto *UE = dyn_cast<UnaryExprOrTypeTraitExpr>(E))
49  if (UE->getKind() == UETT_SizeOf && !UE->isArgumentType())
50  return sameDecl(UE->getArgumentExpr(), WithArg);
51  return false;
52  }
53 
54  /// Check if the expression E is a strlen(WithArg).
55  bool isStrlen(const Expr *E, const Expr *WithArg) {
56  if (const auto *CE = dyn_cast<CallExpr>(E)) {
57  const FunctionDecl *FD = CE->getDirectCallee();
58  if (!FD)
59  return false;
60  return (CheckerContext::isCLibraryFunction(FD, "strlen") &&
61  sameDecl(CE->getArg(0), WithArg));
62  }
63  return false;
64  }
65 
66  /// Check if the expression is an integer literal with value 1.
67  bool isOne(const Expr *E) {
68  if (const auto *IL = dyn_cast<IntegerLiteral>(E))
69  return (IL->getValue().isIntN(1));
70  return false;
71  }
72 
73  StringRef getPrintableName(const Expr *E) {
74  if (const auto *D = dyn_cast<DeclRefExpr>(E->IgnoreParenCasts()))
75  return D->getDecl()->getName();
76  return StringRef();
77  }
78 
79  /// Identify erroneous patterns in the last argument to strncat - the number
80  /// of bytes to copy.
81  bool containsBadStrncatPattern(const CallExpr *CE);
82 
83  /// Identify erroneous patterns in the last argument to strlcpy - the number
84  /// of bytes to copy.
85  /// The bad pattern checked is when the size is known
86  /// to be larger than the destination can handle.
87  /// char dst[2];
88  /// size_t cpy = 4;
89  /// strlcpy(dst, "abcd", sizeof("abcd") - 1);
90  /// strlcpy(dst, "abcd", 4);
91  /// strlcpy(dst + 3, "abcd", 2);
92  /// strlcpy(dst, "abcd", cpy);
93  bool containsBadStrlcpyPattern(const CallExpr *CE);
94 
95 public:
96  WalkAST(const CheckerBase *Checker, BugReporter &BR, AnalysisDeclContext *AC)
97  : Checker(Checker), BR(BR), AC(AC) {}
98 
99  // Statement visitor methods.
100  void VisitChildren(Stmt *S);
101  void VisitStmt(Stmt *S) {
102  VisitChildren(S);
103  }
104  void VisitCallExpr(CallExpr *CE);
105 };
106 } // end anonymous namespace
107 
108 // The correct size argument should look like following:
109 // strncat(dst, src, sizeof(dst) - strlen(dest) - 1);
110 // We look for the following anti-patterns:
111 // - strncat(dst, src, sizeof(dst) - strlen(dst));
112 // - strncat(dst, src, sizeof(dst) - 1);
113 // - strncat(dst, src, sizeof(dst));
114 bool WalkAST::containsBadStrncatPattern(const CallExpr *CE) {
115  if (CE->getNumArgs() != 3)
116  return false;
117  const Expr *DstArg = CE->getArg(0);
118  const Expr *SrcArg = CE->getArg(1);
119  const Expr *LenArg = CE->getArg(2);
120 
121  // Identify wrong size expressions, which are commonly used instead.
122  if (const auto *BE = dyn_cast<BinaryOperator>(LenArg->IgnoreParenCasts())) {
123  // - sizeof(dst) - strlen(dst)
124  if (BE->getOpcode() == BO_Sub) {
125  const Expr *L = BE->getLHS();
126  const Expr *R = BE->getRHS();
127  if (isSizeof(L, DstArg) && isStrlen(R, DstArg))
128  return true;
129 
130  // - sizeof(dst) - 1
131  if (isSizeof(L, DstArg) && isOne(R->IgnoreParenCasts()))
132  return true;
133  }
134  }
135  // - sizeof(dst)
136  if (isSizeof(LenArg, DstArg))
137  return true;
138 
139  // - sizeof(src)
140  if (isSizeof(LenArg, SrcArg))
141  return true;
142  return false;
143 }
144 
145 bool WalkAST::containsBadStrlcpyPattern(const CallExpr *CE) {
146  if (CE->getNumArgs() != 3)
147  return false;
148  const Expr *DstArg = CE->getArg(0);
149  const Expr *LenArg = CE->getArg(2);
150 
151  const auto *DstArgDecl = dyn_cast<DeclRefExpr>(DstArg->IgnoreParenImpCasts());
152  const auto *LenArgDecl = dyn_cast<DeclRefExpr>(LenArg->IgnoreParenLValueCasts());
153  uint64_t DstOff = 0;
154  // - size_t dstlen = sizeof(dst)
155  if (LenArgDecl) {
156  const auto *LenArgVal = dyn_cast<VarDecl>(LenArgDecl->getDecl());
157  if (LenArgVal->getInit())
158  LenArg = LenArgVal->getInit();
159  }
160 
161  // - integral value
162  // We try to figure out if the last argument is possibly longer
163  // than the destination can possibly handle if its size can be defined.
164  if (const auto *IL = dyn_cast<IntegerLiteral>(LenArg->IgnoreParenImpCasts())) {
165  uint64_t ILRawVal = IL->getValue().getZExtValue();
166 
167  // Case when there is pointer arithmetic on the destination buffer
168  // especially when we offset from the base decreasing the
169  // buffer length accordingly.
170  if (!DstArgDecl) {
171  if (const auto *BE = dyn_cast<BinaryOperator>(DstArg->IgnoreParenImpCasts())) {
172  DstArgDecl = dyn_cast<DeclRefExpr>(BE->getLHS()->IgnoreParenImpCasts());
173  if (BE->getOpcode() == BO_Add) {
174  if ((IL = dyn_cast<IntegerLiteral>(BE->getRHS()->IgnoreParenImpCasts()))) {
175  DstOff = IL->getValue().getZExtValue();
176  }
177  }
178  }
179  }
180  if (DstArgDecl) {
181  if (const auto *Buffer = dyn_cast<ConstantArrayType>(DstArgDecl->getType())) {
182  ASTContext &C = BR.getContext();
183  uint64_t BufferLen = C.getTypeSize(Buffer) / 8;
184  if ((BufferLen - DstOff) < ILRawVal)
185  return true;
186  }
187  }
188  }
189 
190  return false;
191 }
192 
193 void WalkAST::VisitCallExpr(CallExpr *CE) {
194  const FunctionDecl *FD = CE->getDirectCallee();
195  if (!FD)
196  return;
197 
198  if (CheckerContext::isCLibraryFunction(FD, "strncat")) {
199  if (containsBadStrncatPattern(CE)) {
200  const Expr *DstArg = CE->getArg(0);
201  const Expr *LenArg = CE->getArg(2);
202  PathDiagnosticLocation Loc =
203  PathDiagnosticLocation::createBegin(LenArg, BR.getSourceManager(), AC);
204 
205  StringRef DstName = getPrintableName(DstArg);
206 
208  llvm::raw_svector_ostream os(S);
209  os << "Potential buffer overflow. ";
210  if (!DstName.empty()) {
211  os << "Replace with 'sizeof(" << DstName << ") "
212  "- strlen(" << DstName <<") - 1'";
213  os << " or u";
214  } else
215  os << "U";
216  os << "se a safer 'strlcat' API";
217 
218  BR.EmitBasicReport(FD, Checker, "Anti-pattern in the argument",
219  "C String API", os.str(), Loc,
220  LenArg->getSourceRange());
221  }
222  } else if (CheckerContext::isCLibraryFunction(FD, "strlcpy")) {
223  if (containsBadStrlcpyPattern(CE)) {
224  const Expr *DstArg = CE->getArg(0);
225  const Expr *LenArg = CE->getArg(2);
226  PathDiagnosticLocation Loc =
227  PathDiagnosticLocation::createBegin(LenArg, BR.getSourceManager(), AC);
228 
229  StringRef DstName = getPrintableName(DstArg);
230 
232  llvm::raw_svector_ostream os(S);
233  os << "The third argument is larger than the size of the input buffer. ";
234  if (!DstName.empty())
235  os << "Replace with the value 'sizeof(" << DstName << ")` or lower";
236 
237  BR.EmitBasicReport(FD, Checker, "Anti-pattern in the argument",
238  "C String API", os.str(), Loc,
239  LenArg->getSourceRange());
240  }
241  }
242 
243  // Recurse and check children.
244  VisitChildren(CE);
245 }
246 
247 void WalkAST::VisitChildren(Stmt *S) {
248  for (Stmt *Child : S->children())
249  if (Child)
250  Visit(Child);
251 }
252 
253 namespace {
254 class CStringSyntaxChecker: public Checker<check::ASTCodeBody> {
255 public:
256 
257  void checkASTCodeBody(const Decl *D, AnalysisManager& Mgr,
258  BugReporter &BR) const {
259  WalkAST walker(this, BR, Mgr.getAnalysisDeclContext(D));
260  walker.Visit(D->getBody());
261  }
262 };
263 }
264 
265 void ento::registerCStringSyntaxChecker(CheckerManager &mgr) {
266  mgr.registerChecker<CStringSyntaxChecker>();
267 }
268 
Represents a function declaration or definition.
Definition: Decl.h:1717
Expr * getArg(unsigned Arg)
getArg - Return the specified argument.
Definition: Expr.h:2354
Defines enumerations for the type traits support.
virtual Stmt * getBody() const
getBody - If this Decl represents a declaration for a body of code, such as a function or method defi...
Definition: DeclBase.h:985
Stmt - This represents one statement.
Definition: Stmt.h:66
unsigned getNumArgs() const
getNumArgs - Return the number of actual arguments to this call.
Definition: Expr.h:2342
Decl - This represents one declaration (or definition), e.g.
Definition: DeclBase.h:87
Represents a variable declaration or definition.
Definition: Decl.h:812
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition: ASTContext.h:154
AnalysisDeclContext contains the context data for the function or method under analysis.
child_range children()
Definition: Stmt.cpp:229
Expr * IgnoreParenCasts() LLVM_READONLY
IgnoreParenCasts - Ignore parentheses and casts.
Definition: Expr.cpp:2544
This represents one expression.
Definition: Expr.h:105
static bool isCLibraryFunction(const FunctionDecl *FD, StringRef Name=StringRef())
Returns true if the callee is an externally-visible function in the top-level namespace, such as malloc.
static PathDiagnosticLocation createBegin(const Decl *D, const SourceManager &SM)
Create a location for the beginning of the declaration.
StmtVisitor - This class implements a simple visitor for Stmt subclasses.
Definition: StmtVisitor.h:186
const Decl * getDecl() const
Dataflow Directional Tag Classes.
FunctionDecl * getDirectCallee()
If the callee is a FunctionDecl, return it. Otherwise return 0.
Definition: Expr.cpp:1251
Expr * IgnoreParenImpCasts() LLVM_READONLY
IgnoreParenImpCasts - Ignore parentheses and implicit casts.
Definition: Expr.cpp:2631
Expr * IgnoreParenLValueCasts() LLVM_READONLY
Ignore parentheses and lvalue casts.
Definition: Expr.cpp:2591
uint64_t getTypeSize(QualType T) const
Return the size of the specified (complete) type T, in bits.
Definition: ASTContext.h:2076
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
Definition: Stmt.cpp:268
Defines the clang::TargetInfo interface.
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
Definition: Expr.h:2285
StringRef getName() const
Get the name of identifier for this declaration as a StringRef.
Definition: Decl.h:275
A reference to a declared variable, function, enum, etc.
Definition: Expr.h:971