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  /// Identify erroneous patterns in the last argument to strlcat - the number
94  /// of bytes to copy.
95  /// The bad pattern checked is when the last argument is basically
96  /// pointing to the destination buffer size or argument larger or
97  /// equal to.
98  /// char dst[2];
99  /// strlcat(dst, src2, sizeof(dst));
100  /// strlcat(dst, src2, 2);
101  /// strlcat(dst, src2, 10);
102  bool containsBadStrlcpyStrlcatPattern(const CallExpr *CE);
103 
104 public:
105  WalkAST(const CheckerBase *Checker, BugReporter &BR, AnalysisDeclContext *AC)
106  : Checker(Checker), BR(BR), AC(AC) {}
107 
108  // Statement visitor methods.
109  void VisitChildren(Stmt *S);
110  void VisitStmt(Stmt *S) {
111  VisitChildren(S);
112  }
113  void VisitCallExpr(CallExpr *CE);
114 };
115 } // end anonymous namespace
116 
117 // The correct size argument should look like following:
118 // strncat(dst, src, sizeof(dst) - strlen(dest) - 1);
119 // We look for the following anti-patterns:
120 // - strncat(dst, src, sizeof(dst) - strlen(dst));
121 // - strncat(dst, src, sizeof(dst) - 1);
122 // - strncat(dst, src, sizeof(dst));
123 bool WalkAST::containsBadStrncatPattern(const CallExpr *CE) {
124  if (CE->getNumArgs() != 3)
125  return false;
126  const Expr *DstArg = CE->getArg(0);
127  const Expr *SrcArg = CE->getArg(1);
128  const Expr *LenArg = CE->getArg(2);
129 
130  // Identify wrong size expressions, which are commonly used instead.
131  if (const auto *BE = dyn_cast<BinaryOperator>(LenArg->IgnoreParenCasts())) {
132  // - sizeof(dst) - strlen(dst)
133  if (BE->getOpcode() == BO_Sub) {
134  const Expr *L = BE->getLHS();
135  const Expr *R = BE->getRHS();
136  if (isSizeof(L, DstArg) && isStrlen(R, DstArg))
137  return true;
138 
139  // - sizeof(dst) - 1
140  if (isSizeof(L, DstArg) && isOne(R->IgnoreParenCasts()))
141  return true;
142  }
143  }
144  // - sizeof(dst)
145  if (isSizeof(LenArg, DstArg))
146  return true;
147 
148  // - sizeof(src)
149  if (isSizeof(LenArg, SrcArg))
150  return true;
151  return false;
152 }
153 
154 bool WalkAST::containsBadStrlcpyStrlcatPattern(const CallExpr *CE) {
155  if (CE->getNumArgs() != 3)
156  return false;
157  const FunctionDecl *FD = CE->getDirectCallee();
158  bool Append = CheckerContext::isCLibraryFunction(FD, "strlcat");
159  const Expr *DstArg = CE->getArg(0);
160  const Expr *LenArg = CE->getArg(2);
161 
162  const auto *DstArgDecl = dyn_cast<DeclRefExpr>(DstArg->IgnoreParenImpCasts());
163  const auto *LenArgDecl = dyn_cast<DeclRefExpr>(LenArg->IgnoreParenLValueCasts());
164  uint64_t DstOff = 0;
165  if (isSizeof(LenArg, DstArg))
166  return false;
167  // - size_t dstlen = sizeof(dst)
168  if (LenArgDecl) {
169  const auto *LenArgVal = dyn_cast<VarDecl>(LenArgDecl->getDecl());
170  if (LenArgVal->getInit())
171  LenArg = LenArgVal->getInit();
172  }
173 
174  // - integral value
175  // We try to figure out if the last argument is possibly longer
176  // than the destination can possibly handle if its size can be defined.
177  if (const auto *IL = dyn_cast<IntegerLiteral>(LenArg->IgnoreParenImpCasts())) {
178  uint64_t ILRawVal = IL->getValue().getZExtValue();
179 
180  // Case when there is pointer arithmetic on the destination buffer
181  // especially when we offset from the base decreasing the
182  // buffer length accordingly.
183  if (!DstArgDecl) {
184  if (const auto *BE = dyn_cast<BinaryOperator>(DstArg->IgnoreParenImpCasts())) {
185  DstArgDecl = dyn_cast<DeclRefExpr>(BE->getLHS()->IgnoreParenImpCasts());
186  if (BE->getOpcode() == BO_Add) {
187  if ((IL = dyn_cast<IntegerLiteral>(BE->getRHS()->IgnoreParenImpCasts()))) {
188  DstOff = IL->getValue().getZExtValue();
189  }
190  }
191  }
192  }
193  if (DstArgDecl) {
194  if (const auto *Buffer = dyn_cast<ConstantArrayType>(DstArgDecl->getType())) {
195  ASTContext &C = BR.getContext();
196  uint64_t BufferLen = C.getTypeSize(Buffer) / 8;
197  auto RemainingBufferLen = BufferLen - DstOff;
198  if (Append) {
199  if (RemainingBufferLen <= ILRawVal)
200  return true;
201  } else {
202  if (RemainingBufferLen < ILRawVal)
203  return true;
204  }
205  }
206  }
207  }
208 
209  return false;
210 }
211 
212 void WalkAST::VisitCallExpr(CallExpr *CE) {
213  const FunctionDecl *FD = CE->getDirectCallee();
214  if (!FD)
215  return;
216 
217  if (CheckerContext::isCLibraryFunction(FD, "strncat")) {
218  if (containsBadStrncatPattern(CE)) {
219  const Expr *DstArg = CE->getArg(0);
220  const Expr *LenArg = CE->getArg(2);
221  PathDiagnosticLocation Loc =
222  PathDiagnosticLocation::createBegin(LenArg, BR.getSourceManager(), AC);
223 
224  StringRef DstName = getPrintableName(DstArg);
225 
227  llvm::raw_svector_ostream os(S);
228  os << "Potential buffer overflow. ";
229  if (!DstName.empty()) {
230  os << "Replace with 'sizeof(" << DstName << ") "
231  "- strlen(" << DstName <<") - 1'";
232  os << " or u";
233  } else
234  os << "U";
235  os << "se a safer 'strlcat' API";
236 
237  BR.EmitBasicReport(FD, Checker, "Anti-pattern in the argument",
238  "C String API", os.str(), Loc,
239  LenArg->getSourceRange());
240  }
241  } else if (CheckerContext::isCLibraryFunction(FD, "strlcpy") ||
242  CheckerContext::isCLibraryFunction(FD, "strlcat")) {
243  if (containsBadStrlcpyStrlcatPattern(CE)) {
244  const Expr *DstArg = CE->getArg(0);
245  const Expr *LenArg = CE->getArg(2);
246  PathDiagnosticLocation Loc =
247  PathDiagnosticLocation::createBegin(LenArg, BR.getSourceManager(), AC);
248 
249  StringRef DstName = getPrintableName(DstArg);
250 
252  llvm::raw_svector_ostream os(S);
253  os << "The third argument allows to potentially copy more bytes than it should. ";
254  os << "Replace with the value ";
255  if (!DstName.empty())
256  os << "sizeof(" << DstName << ")";
257  else
258  os << "sizeof(<destination buffer>)";
259  os << " or lower";
260 
261  BR.EmitBasicReport(FD, Checker, "Anti-pattern in the argument",
262  "C String API", os.str(), Loc,
263  LenArg->getSourceRange());
264  }
265  }
266 
267  // Recurse and check children.
268  VisitChildren(CE);
269 }
270 
271 void WalkAST::VisitChildren(Stmt *S) {
272  for (Stmt *Child : S->children())
273  if (Child)
274  Visit(Child);
275 }
276 
277 namespace {
278 class CStringSyntaxChecker: public Checker<check::ASTCodeBody> {
279 public:
280 
281  void checkASTCodeBody(const Decl *D, AnalysisManager& Mgr,
282  BugReporter &BR) const {
283  WalkAST walker(this, BR, Mgr.getAnalysisDeclContext(D));
284  walker.Visit(D->getBody());
285  }
286 };
287 }
288 
289 void ento::registerCStringSyntaxChecker(CheckerManager &mgr) {
290  mgr.registerChecker<CStringSyntaxChecker>();
291 }
292 
Represents a function declaration or definition.
Definition: Decl.h:1739
Expr * getArg(unsigned Arg)
getArg - Return the specified argument.
Definition: Expr.h:2495
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:979
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:2483
Decl - This represents one declaration (or definition), e.g.
Definition: DeclBase.h:87
Represents a variable declaration or definition.
Definition: Decl.h:812
static void Append(char *Start, char *End, char *&Buffer, unsigned &BufferSize, unsigned &BufferCapacity)
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:237
Expr * IgnoreParenCasts() LLVM_READONLY
IgnoreParenCasts - Ignore parentheses and casts.
Definition: Expr.cpp:2557
This represents one expression.
Definition: Expr.h:106
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:183
const Decl * getDecl() const
Dataflow Directional Tag Classes.
FunctionDecl * getDirectCallee()
If the callee is a FunctionDecl, return it. Otherwise return 0.
Definition: Expr.cpp:1286
Expr * IgnoreParenImpCasts() LLVM_READONLY
IgnoreParenImpCasts - Ignore parentheses and implicit casts.
Definition: Expr.cpp:2655
Expr * IgnoreParenLValueCasts() LLVM_READONLY
Ignore parentheses and lvalue casts.
Definition: Expr.cpp:2612
uint64_t getTypeSize(QualType T) const
Return the size of the specified (complete) type T, in bits.
Definition: ASTContext.h:2069
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
Definition: Stmt.cpp:276
Defines the clang::TargetInfo interface.
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
Definition: Expr.h:2407
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:1042