clang 24.0.0git
UnsafeBufferUsage.cpp
Go to the documentation of this file.
1//===- UnsafeBufferUsage.cpp - Replace pointers with modern C++ -----------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
10#include "clang/AST/APValue.h"
13#include "clang/AST/Attr.h"
14#include "clang/AST/Decl.h"
15#include "clang/AST/DeclCXX.h"
18#include "clang/AST/Expr.h"
21#include "clang/AST/Stmt.h"
23#include "clang/AST/Type.h"
27#include "clang/Lex/Lexer.h"
29#include "llvm/ADT/APInt.h"
30#include "llvm/ADT/APSInt.h"
31#include "llvm/ADT/STLExtras.h"
32#include "llvm/ADT/STLFunctionalExtras.h"
33#include "llvm/ADT/SmallVector.h"
34#include "llvm/ADT/StringRef.h"
35#include <cstddef>
36#include <optional>
37#include <queue>
38#include <set>
39#include <sstream>
40#include <vector>
41
42using namespace clang;
43
44#ifndef NDEBUG
45namespace {
46class StmtDebugPrinter
47 : public ConstStmtVisitor<StmtDebugPrinter, std::string> {
48public:
49 std::string VisitStmt(const Stmt *S) { return S->getStmtClassName(); }
50
51 std::string VisitBinaryOperator(const BinaryOperator *BO) {
52 return "BinaryOperator(" + BO->getOpcodeStr().str() + ")";
53 }
54
55 std::string VisitUnaryOperator(const UnaryOperator *UO) {
56 return "UnaryOperator(" + UO->getOpcodeStr(UO->getOpcode()).str() + ")";
57 }
58
59 std::string VisitImplicitCastExpr(const ImplicitCastExpr *ICE) {
60 return "ImplicitCastExpr(" + std::string(ICE->getCastKindName()) + ")";
61 }
62};
63
64// Returns a string of ancestor `Stmt`s of the given `DRE` in such a form:
65// "DRE ==> parent-of-DRE ==> grandparent-of-DRE ==> ...".
66static std::string getDREAncestorString(const DeclRefExpr *DRE,
67 ASTContext &Ctx) {
68 std::stringstream SS;
69 const Stmt *St = DRE;
70 StmtDebugPrinter StmtPriner;
71
72 do {
73 SS << StmtPriner.Visit(St);
74
75 DynTypedNodeList StParents = Ctx.getParents(*St);
76
77 if (StParents.size() > 1)
78 return "unavailable due to multiple parents";
79 if (StParents.empty())
80 break;
81 St = StParents.begin()->get<Stmt>();
82 if (St)
83 SS << " ==> ";
84 } while (St);
85 return SS.str();
86}
87
88} // namespace
89#endif /* NDEBUG */
90
91namespace {
92// Using a custom `FastMatcher` instead of ASTMatchers to achieve better
93// performance. FastMatcher uses simple function `matches` to find if a node
94// is a match, avoiding the dependency on the ASTMatchers framework which
95// provide a nice abstraction, but incur big performance costs.
96class FastMatcher {
97public:
98 virtual bool matches(const DynTypedNode &DynNode, ASTContext &Ctx,
99 const UnsafeBufferUsageHandler &Handler) = 0;
100 virtual ~FastMatcher() = default;
101};
102
103class MatchResult {
104
105public:
106 template <typename T> const T *getNodeAs(StringRef ID) const {
107 auto It = Nodes.find(ID);
108 if (It == Nodes.end()) {
109 return nullptr;
110 }
111 return It->second.get<T>();
112 }
113
114 void addNode(StringRef ID, const DynTypedNode &Node) { Nodes[ID] = Node; }
115
116private:
117 llvm::StringMap<DynTypedNode> Nodes;
118};
119} // namespace
120
121#define SIZED_CONTAINER_OR_VIEW_LIST \
122 "span", "array", "vector", "basic_string_view", "basic_string", \
123 "initializer_list",
124
125// A `RecursiveASTVisitor` that traverses all descendants of a given node "n"
126// except for those belonging to a different callable of "n".
128public:
129 // Creates an AST visitor that matches `Matcher` on all
130 // descendants of a given node "n" except for the ones
131 // belonging to a different callable of "n".
132 MatchDescendantVisitor(ASTContext &Context, FastMatcher &Matcher,
133 bool FindAll, bool IgnoreUnevaluatedContext,
134 const UnsafeBufferUsageHandler &NewHandler)
135 : Matcher(&Matcher), FindAll(FindAll), Matches(false),
136 IgnoreUnevaluatedContext(IgnoreUnevaluatedContext),
137 ActiveASTContext(&Context), Handler(&NewHandler) {
139 ShouldVisitImplicitCode = false; // TODO: let's ignore implicit code for now
140 }
141
142 // Returns true if a match is found in a subtree of `DynNode`, which belongs
143 // to the same callable of `DynNode`.
144 bool findMatch(const DynTypedNode &DynNode) {
145 Matches = false;
146 if (const Stmt *StmtNode = DynNode.get<Stmt>()) {
147 TraverseStmt(const_cast<Stmt *>(StmtNode));
148 return Matches;
149 }
150 return false;
151 }
152
153 // The following are overriding methods from the base visitor class.
154 // They are public only to allow CRTP to work. They are *not *part
155 // of the public API of this class.
156
157 // For the matchers so far used in safe buffers, we only need to match
158 // `Stmt`s. To override more as needed.
159
160 bool TraverseDecl(Decl *Node) override {
161 if (!Node)
162 return true;
163 if (!match(*Node))
164 return false;
165 // To skip callables:
167 return true;
168 // Traverse descendants
170 }
171
173 // These are unevaluated, except the result expression.
174 if (IgnoreUnevaluatedContext)
175 return TraverseStmt(Node->getResultExpr());
176 return DynamicRecursiveASTVisitor::TraverseGenericSelectionExpr(Node);
177 }
178
179 bool
181 // Unevaluated context.
182 if (IgnoreUnevaluatedContext)
183 return true;
184 return DynamicRecursiveASTVisitor::TraverseUnaryExprOrTypeTraitExpr(Node);
185 }
186
188 bool TraverseQualifier) override {
189 // Unevaluated context.
190 if (IgnoreUnevaluatedContext)
191 return true;
192 return DynamicRecursiveASTVisitor::TraverseTypeOfExprTypeLoc(
193 Node, TraverseQualifier);
194 }
195
197 bool TraverseQualifier) override {
198 // Unevaluated context.
199 if (IgnoreUnevaluatedContext)
200 return true;
201 return DynamicRecursiveASTVisitor::TraverseDecltypeTypeLoc(
202 Node, TraverseQualifier);
203 }
204
206 // Unevaluated context.
207 if (IgnoreUnevaluatedContext)
208 return true;
209 return DynamicRecursiveASTVisitor::TraverseCXXNoexceptExpr(Node);
210 }
211
213 // Unevaluated context.
214 if (IgnoreUnevaluatedContext)
215 return true;
216 return DynamicRecursiveASTVisitor::TraverseCXXTypeidExpr(Node);
217 }
218
220 if (!TraverseStmt(Node->getExpr()))
221 return false;
222 return DynamicRecursiveASTVisitor::TraverseCXXDefaultInitExpr(Node);
223 }
224
225 bool TraverseStmt(Stmt *Node) override {
226 if (!Node)
227 return true;
228 if (!match(*Node))
229 return false;
231 }
232
233private:
234 // Sets 'Matched' to true if 'Matcher' matches 'Node'
235 //
236 // Returns 'true' if traversal should continue after this function
237 // returns, i.e. if no match is found or 'Bind' is 'BK_All'.
238 template <typename T> bool match(const T &Node) {
239 if (Matcher->matches(DynTypedNode::create(Node), *ActiveASTContext,
240 *Handler)) {
241 Matches = true;
242 if (!FindAll)
243 return false; // Abort as soon as a match is found.
244 }
245 return true;
246 }
247
248 FastMatcher *const Matcher;
249 // When true, finds all matches. When false, finds the first match and stops.
250 const bool FindAll;
251 bool Matches;
252 bool IgnoreUnevaluatedContext;
253 ASTContext *ActiveASTContext;
254 const UnsafeBufferUsageHandler *Handler;
255};
256
257// Because we're dealing with raw pointers, let's define what we mean by that.
258static bool hasPointerType(const Expr &E) {
260}
261
262static bool hasArrayType(const Expr &E) {
264}
265
266static void
268 const UnsafeBufferUsageHandler &Handler,
269 FastMatcher &Matcher) {
270 MatchDescendantVisitor Visitor(Ctx, Matcher, /*FindAll=*/true,
271 /*IgnoreUnevaluatedContext=*/true, Handler);
272 Visitor.findMatch(DynTypedNode::create(*S));
273}
274
275static void forEachDescendantStmt(const Stmt *S, ASTContext &Ctx,
276 const UnsafeBufferUsageHandler &Handler,
277 FastMatcher &Matcher) {
278 MatchDescendantVisitor Visitor(Ctx, Matcher, /*FindAll=*/true,
279 /*IgnoreUnevaluatedContext=*/false, Handler);
280 Visitor.findMatch(DynTypedNode::create(*S));
281}
282
283// Matches a `Stmt` node iff the node is in a safe-buffer opt-out region
284static bool notInSafeBufferOptOut(const Stmt &Node,
285 const UnsafeBufferUsageHandler *Handler) {
286 return !Handler->isSafeBufferOptOut(Node.getBeginLoc());
287}
288
289static bool
291 const UnsafeBufferUsageHandler *Handler) {
292 return Handler->ignoreUnsafeBufferInContainer(Node.getBeginLoc());
293}
294
295static bool ignoreUnsafeLibcCall(const ASTContext &Ctx, const Stmt &Node,
296 const UnsafeBufferUsageHandler *Handler) {
297 if (Ctx.getLangOpts().CPlusPlus)
298 return Handler->ignoreUnsafeBufferInLibcCall(Node.getBeginLoc());
299 return true; /* Only warn about libc calls for C++ */
300}
301
302// Finds any expression 'e' such that `OnResult`
303// matches 'e' and 'e' is in an Unspecified Lvalue Context.
305 const Stmt *S, const llvm::function_ref<void(const Expr *)> OnResult) {
306 if (const auto *CE = dyn_cast<ImplicitCastExpr>(S);
307 CE && CE->getCastKind() == CastKind::CK_LValueToRValue)
308 OnResult(CE->getSubExpr());
309 if (const auto *BO = dyn_cast<BinaryOperator>(S);
310 BO && BO->getOpcode() == BO_Assign)
311 OnResult(BO->getLHS());
312}
313
314// Finds any expression `e` such that `InnerMatcher` matches `e` and
315// `e` is in an Unspecified Pointer Context (UPC).
317 const Stmt *S, llvm::function_ref<void(const Stmt *)> InnerMatcher) {
318 // A UPC can be
319 // 1. an argument of a function call (except the callee has [[unsafe_...]]
320 // attribute), or
321 // 2. the operand of a pointer-to-(integer or bool) cast operation; or
322 // 3. the operand of a comparator operation; or
323 // 4. the operand of a pointer subtraction operation
324 // (i.e., computing the distance between two pointers); or ...
325
326 if (auto *CE = dyn_cast<CallExpr>(S)) {
327 if (const auto *FnDecl = CE->getDirectCallee();
328 FnDecl && FnDecl->hasAttr<UnsafeBufferUsageAttr>())
329 return;
331 *CE, [&InnerMatcher](QualType Type, const Expr *Arg) {
332 if (Type->isAnyPointerType())
333 InnerMatcher(Arg);
334 });
335 }
336
337 if (auto *CE = dyn_cast<CastExpr>(S)) {
338 if (CE->getCastKind() != CastKind::CK_PointerToIntegral &&
339 CE->getCastKind() != CastKind::CK_PointerToBoolean)
340 return;
341 if (!hasPointerType(*CE->getSubExpr()))
342 return;
343 InnerMatcher(CE->getSubExpr());
344 }
345
346 // Pointer comparison operator.
347 if (const auto *BO = dyn_cast<BinaryOperator>(S);
348 BO && (BO->getOpcode() == BO_EQ || BO->getOpcode() == BO_NE ||
349 BO->getOpcode() == BO_LT || BO->getOpcode() == BO_LE ||
350 BO->getOpcode() == BO_GT || BO->getOpcode() == BO_GE)) {
351 auto *LHS = BO->getLHS();
352 if (hasPointerType(*LHS))
353 InnerMatcher(LHS);
354
355 auto *RHS = BO->getRHS();
356 if (hasPointerType(*RHS))
357 InnerMatcher(RHS);
358 }
359
360 // Pointer subtractions.
361 if (const auto *BO = dyn_cast<BinaryOperator>(S);
362 BO && BO->getOpcode() == BO_Sub && hasPointerType(*BO->getLHS()) &&
363 hasPointerType(*BO->getRHS())) {
364 // Note that here we need both LHS and RHS to be
365 // pointer. Then the inner matcher can match any of
366 // them:
367 InnerMatcher(BO->getLHS());
368 InnerMatcher(BO->getRHS());
369 }
370 // FIXME: any more cases? (UPC excludes the RHS of an assignment. For now
371 // we don't have to check that.)
372}
373
374// Finds statements in unspecified untyped context i.e. any expression 'e' such
375// that `InnerMatcher` matches 'e' and 'e' is in an unspecified untyped context
376// (i.e the expression 'e' isn't evaluated to an RValue). For example, consider
377// the following code:
378// int *p = new int[4];
379// int *q = new int[4];
380// if ((p = q)) {}
381// p = q;
382// The expression `p = q` in the conditional of the `if` statement
383// `if ((p = q))` is evaluated as an RValue, whereas the expression `p = q;`
384// in the assignment statement is in an untyped context.
386 const Stmt *S, llvm::function_ref<void(const Stmt *)> InnerMatcher) {
387 // An unspecified context can be
388 // 1. A compound statement,
389 // 2. The body of an if statement
390 // 3. Body of a loop
391 if (auto *CS = dyn_cast<CompoundStmt>(S)) {
392 for (auto *Child : CS->body())
393 InnerMatcher(Child);
394 }
395 if (auto *IfS = dyn_cast<IfStmt>(S)) {
396 if (IfS->getThen())
397 InnerMatcher(IfS->getThen());
398 if (IfS->getElse())
399 InnerMatcher(IfS->getElse());
400 }
401 // FIXME: Handle loop bodies.
402}
403
404// Returns true iff integer E1 is equivalent to integer E2.
405//
406// For now we only support such expressions:
407// expr := DRE | const-value | expr BO expr
408// BO := '*' | '+'
409//
410// FIXME: We can reuse the expression comparator of the interop analysis after
411// it has been upstreamed.
412static bool areEqualIntegers(const Expr *E1, const Expr *E2, ASTContext &Ctx);
414 const Expr *E2_LHS,
416 const Expr *E2_RHS,
417 ASTContext &Ctx) {
418 if (E1->getOpcode() == BOP) {
419 switch (BOP) {
420 // Commutative operators:
421 case BO_Mul:
422 case BO_Add:
423 return (areEqualIntegers(E1->getLHS(), E2_LHS, Ctx) &&
424 areEqualIntegers(E1->getRHS(), E2_RHS, Ctx)) ||
425 (areEqualIntegers(E1->getLHS(), E2_RHS, Ctx) &&
426 areEqualIntegers(E1->getRHS(), E2_LHS, Ctx));
427 default:
428 return false;
429 }
430 }
431 return false;
432}
433
434static bool areEqualIntegers(const Expr *E1, const Expr *E2, ASTContext &Ctx) {
435 E1 = E1->IgnoreParenImpCasts();
436 E2 = E2->IgnoreParenImpCasts();
437 if (!E1->getType()->isIntegerType() || E1->getType() != E2->getType())
438 return false;
439
440 Expr::EvalResult ER1, ER2;
441
442 // If both are constants:
443 if (E1->EvaluateAsInt(ER1, Ctx) && E2->EvaluateAsInt(ER2, Ctx))
444 return ER1.Val.getInt() == ER2.Val.getInt();
445
446 // Otherwise, they should have identical stmt kind:
447 if (E1->getStmtClass() != E2->getStmtClass())
448 return false;
449 switch (E1->getStmtClass()) {
450 case Stmt::DeclRefExprClass:
451 return cast<DeclRefExpr>(E1)->getDecl() == cast<DeclRefExpr>(E2)->getDecl();
452 case Stmt::BinaryOperatorClass: {
453 auto BO2 = cast<BinaryOperator>(E2);
455 BO2->getLHS(), BO2->getOpcode(),
456 BO2->getRHS(), Ctx);
457 }
458 default:
459 return false;
460 }
461}
462
463// Given an expression like `&X` or `std::addressof(X)`, returns the `Expr`
464// corresponding to `X` (after removing parens and implicit casts).
465// Returns null if the input expression `E` is not an address-of expression.
466static const Expr *getSubExprInAddressOfExpr(const Expr &E) {
467 if (!E.getType()->isPointerType())
468 return nullptr;
469 const Expr *Ptr = E.IgnoreParenImpCasts();
470
471 // `&X` where `X` is an `Expr`.
472 if (const auto *UO = dyn_cast<UnaryOperator>(Ptr)) {
473 if (UO->getOpcode() != UnaryOperator::Opcode::UO_AddrOf)
474 return nullptr;
475 return UO->getSubExpr()->IgnoreParenImpCasts();
476 }
477
478 // `std::addressof(X)` where `X` is an `Expr`.
479 if (const auto *CE = dyn_cast<CallExpr>(Ptr)) {
480 const FunctionDecl *FnDecl = CE->getDirectCallee();
481 if (!FnDecl || !FnDecl->isInStdNamespace() ||
482 FnDecl->getNameAsString() != "addressof" || CE->getNumArgs() != 1)
483 return nullptr;
484 return CE->getArg(0)->IgnoreParenImpCasts();
485 }
486
487 return nullptr;
488}
489
490// Given an expression like `sizeof(X)`, returns the `Expr` corresponding to `X`
491// (after removing parens and implicit casts). Returns null if the expression
492// `E` is not a `sizeof` expression or is `sizeof(T)` for a type `T`.
493static const Expr *getSubExprInSizeOfExpr(const Expr &E) {
494 const auto *SizeOfExpr =
495 dyn_cast<UnaryExprOrTypeTraitExpr>(E.IgnoreParenImpCasts());
496 if (!SizeOfExpr || SizeOfExpr->getKind() != UETT_SizeOf)
497 return nullptr;
498 if (SizeOfExpr->isArgumentType())
499 return nullptr;
500 return SizeOfExpr->getArgumentExpr()->IgnoreParenImpCasts();
501}
502
503// Providing that `Ptr` is a pointer and `Size` is an unsigned-integral
504// expression, returns true iff they follow one of the following safe
505// patterns:
506// 1. Ptr is `DRE.data()` and Size is `DRE.size()`, where DRE is a hardened
507// container or view;
508//
509// 2. Ptr is `a` and Size is `n`, where `a` is of an array-of-T with constant
510// size `n`;
511//
512// 3. Ptr is `&var` and Size is `1`; or
513// Ptr is `std::addressof(...)` and Size is `1`;
514//
515// 4. Size is `0`;
516static bool isPtrBufferSafe(const Expr *Ptr, const Expr *Size,
517 ASTContext &Ctx) {
518 // Pattern 1:
519 if (auto *MCEPtr = dyn_cast<CXXMemberCallExpr>(Ptr->IgnoreParenImpCasts()))
520 if (auto *MCESize =
521 dyn_cast<CXXMemberCallExpr>(Size->IgnoreParenImpCasts())) {
522 auto *DREOfPtr = dyn_cast<DeclRefExpr>(
523 MCEPtr->getImplicitObjectArgument()->IgnoreParenImpCasts());
524 auto *DREOfSize = dyn_cast<DeclRefExpr>(
525 MCESize->getImplicitObjectArgument()->IgnoreParenImpCasts());
526
527 if (!DREOfPtr || !DREOfSize)
528 return false; // not in safe pattern
529 // We need to make sure 'a' is identical to 'b' for 'a.data()' and
530 // 'b.size()' otherwise we do not know they match:
531 if (DREOfPtr->getDecl() != DREOfSize->getDecl())
532 return false;
533 if (MCEPtr->getMethodDecl()->getName() != "data")
534 return false;
535 // `MCEPtr->getRecordDecl()` must be non-null as `DREOfPtr` is non-null:
536 if (!MCEPtr->getRecordDecl()->isInStdNamespace())
537 return false;
538
539 auto *ObjII = MCEPtr->getRecordDecl()->getIdentifier();
540
541 if (!ObjII)
542 return false;
543
544 bool AcceptSizeBytes = Ptr->getType()->getPointeeType()->isCharType();
545
546 if (!((AcceptSizeBytes &&
547 MCESize->getMethodDecl()->getName() == "size_bytes") ||
548 // Note here the pointer must be a pointer-to-char type unless there
549 // is explicit casting. If there is explicit casting, this branch
550 // is unreachable. Thus, at this branch "size" and "size_bytes" are
551 // equivalent as the pointer is a char pointer:
552 MCESize->getMethodDecl()->getName() == "size"))
553 return false;
554
555 return llvm::is_contained({SIZED_CONTAINER_OR_VIEW_LIST},
556 ObjII->getName());
557 }
558
560
561 // Pattern 2-4:
562 if (Size->EvaluateAsInt(ER, Ctx)) {
563 // Pattern 2:
564 if (auto *DRE = dyn_cast<DeclRefExpr>(Ptr->IgnoreParenImpCasts())) {
565 if (auto *CAT = Ctx.getAsConstantArrayType(DRE->getType())) {
566 llvm::APSInt SizeInt = ER.Val.getInt();
567
568 return llvm::APSInt::compareValues(
569 SizeInt, llvm::APSInt(CAT->getSize(), true)) == 0;
570 }
571 return false;
572 }
573
574 // Pattern 3:
575 if (ER.Val.getInt().isOne() && getSubExprInAddressOfExpr(*Ptr) != nullptr)
576 return true;
577
578 // Pattern 4:
579 if (ER.Val.getInt().isZero())
580 return true;
581 }
582
583 return false;
584}
585
586// Given a two-param std::span construct call, matches iff the call has the
587// following forms:
588// 1. `std::span<T>{new T[n], n}`, where `n` is a literal or a DRE
589// 2. `std::span<T>{new T, 1}`
590// 3. `std::span<T>{ (char *)f(args), args[N] * arg*[M]}`, where
591// `f` is a function with attribute `alloc_size(N, M)`;
592// `args` represents the list of arguments;
593// `N, M` are parameter indexes to the allocating element number and size.
594// Sometimes, there is only one parameter index representing the total
595// size.
596// 4. `std::span<T>{x.begin(), x.end()}` where `x` is an object in the
597// SIZED_CONTAINER_OR_VIEW_LIST.
598// 5. `isPtrBufferSafe` returns true for the two arguments of the span
599// constructor
601 ASTContext &Ctx) {
602 assert(Node.getNumArgs() == 2 &&
603 "expecting a two-parameter std::span constructor");
604 const Expr *Arg0 = Node.getArg(0)->IgnoreParenImpCasts();
605 const Expr *Arg1 = Node.getArg(1)->IgnoreParenImpCasts();
606 auto HaveEqualConstantValues = [&Ctx](const Expr *E0, const Expr *E1) {
607 if (auto E0CV = E0->getIntegerConstantExpr(Ctx))
608 if (auto E1CV = E1->getIntegerConstantExpr(Ctx)) {
609 return llvm::APSInt::compareValues(*E0CV, *E1CV) == 0;
610 }
611 return false;
612 };
613 auto AreSameDRE = [](const Expr *E0, const Expr *E1) {
614 if (auto *DRE0 = dyn_cast<DeclRefExpr>(E0))
615 if (auto *DRE1 = dyn_cast<DeclRefExpr>(E1)) {
616 return DRE0->getDecl() == DRE1->getDecl();
617 }
618 return false;
619 };
620 std::optional<llvm::APSInt> Arg1CV = Arg1->getIntegerConstantExpr(Ctx);
621
622 if (Arg1CV && Arg1CV->isZero())
623 // Check form 5:
624 return true;
625
626 // Check forms 1-2:
627 switch (Arg0->getStmtClass()) {
628 case Stmt::CXXNewExprClass:
629 if (auto Size = cast<CXXNewExpr>(Arg0)->getArraySize()) {
630 // Check form 1:
631 return AreSameDRE((*Size)->IgnoreImplicit(), Arg1) ||
632 HaveEqualConstantValues(*Size, Arg1);
633 }
634 // TODO: what's placeholder type? avoid it for now.
635 if (!cast<CXXNewExpr>(Arg0)->hasPlaceholderType()) {
636 // Check form 2:
637 return Arg1CV && Arg1CV->isOne();
638 }
639 break;
640 default:
641 break;
642 }
643
644 // Check form 3:
645 if (auto CCast = dyn_cast<CStyleCastExpr>(Arg0)) {
646 if (!CCast->getType()->isPointerType())
647 return false;
648
649 QualType PteTy = CCast->getType()->getPointeeType();
650
651 if (!(PteTy->isConstantSizeType() && Ctx.getTypeSizeInChars(PteTy).isOne()))
652 return false;
653
654 if (const auto *Call = dyn_cast<CallExpr>(CCast->getSubExpr())) {
655 if (const FunctionDecl *FD = Call->getDirectCallee())
656 if (auto *AllocAttr = FD->getAttr<AllocSizeAttr>()) {
657 const Expr *EleSizeExpr =
658 Call->getArg(AllocAttr->getElemSizeParam().getASTIndex());
659 // NumElemIdx is invalid if AllocSizeAttr has 1 argument:
660 ParamIdx NumElemIdx = AllocAttr->getNumElemsParam();
661
662 if (!NumElemIdx.isValid())
663 return areEqualIntegers(Arg1, EleSizeExpr, Ctx);
664
665 const Expr *NumElesExpr = Call->getArg(NumElemIdx.getASTIndex());
666
667 if (auto BO = dyn_cast<BinaryOperator>(Arg1))
668 return areEqualIntegralBinaryOperators(BO, NumElesExpr, BO_Mul,
669 EleSizeExpr, Ctx);
670 }
671 }
672 }
673 // Check form 4:
674 auto IsMethodCallToSizedObject = [](const Stmt *Node, StringRef MethodName) {
675 if (const auto *MC = dyn_cast<CXXMemberCallExpr>(Node)) {
676 const auto *MD = MC->getMethodDecl();
677 const auto *RD = MC->getRecordDecl();
678
679 if (RD && MD)
680 if (auto *II = RD->getDeclName().getAsIdentifierInfo();
681 II && RD->isInStdNamespace())
682 return llvm::is_contained({SIZED_CONTAINER_OR_VIEW_LIST},
683 II->getName()) &&
684 MD->getName() == MethodName;
685 }
686 return false;
687 };
688
689 if (IsMethodCallToSizedObject(Arg0, "begin") &&
690 IsMethodCallToSizedObject(Arg1, "end"))
691 return AreSameDRE(
692 // We know Arg0 and Arg1 are `CXXMemberCallExpr`s:
694 ->getImplicitObjectArgument()
695 ->IgnoreParenImpCasts(),
697 ->getImplicitObjectArgument()
698 ->IgnoreParenImpCasts());
699
700 // Check 5:
701 return isPtrBufferSafe(Arg0, Arg1, Ctx);
702}
703
705 ASTContext &Ctx) {
706 const Expr *Arg0 = Node.getArg(0)->IgnoreParenImpCasts();
707 const Expr *Arg1 = Node.getArg(1)->IgnoreParenImpCasts();
708
709 // Pattern 1: String Literals
710 if (const auto *SL = dyn_cast<StringLiteral>(Arg0)) {
711 if (auto ArgSize = Arg1->getIntegerConstantExpr(Ctx)) {
712 if (llvm::APSInt::compareValues(
713 llvm::APSInt::getUnsigned(SL->getLength()), *ArgSize) >= 0)
714 return true;
715 return false; // Explicitly unsafe if size > length
716 }
717 }
718
719 // Pattern 2: Constant Arrays
720 if (const auto *CAT = Ctx.getAsConstantArrayType(Arg0->getType())) {
721 if (auto ArgSize = Arg1->getIntegerConstantExpr(Ctx)) {
722 if (llvm::APSInt::compareValues(llvm::APSInt(CAT->getSize(), true),
723 *ArgSize) >= 0)
724 return true;
725 return false; // Explicitly unsafe if size > ArraySize
726 }
727 }
728
729 // Pattern 3: Zero length
730 if (auto Val = Arg1->getIntegerConstantExpr(Ctx)) {
731 if (Val->isZero())
732 return true;
733 }
734
735 // Pattern 4: string_view(it, it) - Only safe if it's .begin() and .end() of
736 // the SAME object
737 auto GetContainerObj = [](const Expr *E) -> const Expr * {
738 E = E->IgnoreParenImpCasts();
739 if (const auto *MCE = dyn_cast<CXXMemberCallExpr>(E)) {
740 const auto *MD = MCE->getMethodDecl();
741 if (MD && MD->getIdentifier())
742 if (MD->getName() == "begin" || MD->getName() == "end")
743 return MCE->getImplicitObjectArgument()->IgnoreParenImpCasts();
744 }
745 return nullptr;
746 };
747
748 const Expr *Obj0 = GetContainerObj(Arg0);
749 const Expr *Obj1 = GetContainerObj(Arg1);
750
751 if (Obj0 && Obj1) {
752 const auto *DRE0 = dyn_cast<DeclRefExpr>(Obj0);
753 const auto *DRE1 = dyn_cast<DeclRefExpr>(Obj1);
754
755 // If both are references to variables, they MUST point to the same
756 // declaration.
757 if (DRE0 && DRE1) {
758 if (DRE0->getDecl()->getCanonicalDecl() ==
759 DRE1->getDecl()->getCanonicalDecl())
760 return true;
761 }
762
763 // If they aren't both DeclRefExprs or don't match, we DO NOT return true.
764 // This ensures v1.begin(), v2.end() triggers a warning.
765 }
766
767 return false; // Default to unsafe
768}
769
771 const ASTContext &Ctx,
772 const bool IgnoreStaticSizedArrays) {
773 // FIXME: Proper solution:
774 // - refactor Sema::CheckArrayAccess
775 // - split safe/OOB/unknown decision logic from diagnostics emitting code
776 // - e. g. "Try harder to find a NamedDecl to point at in the note."
777 // already duplicated
778 // - call both from Sema and from here
779
780 uint64_t limit;
781 if (const auto *CATy =
782 dyn_cast<ConstantArrayType>(Node.getBase()
784 ->getType()
786 limit = CATy->getLimitedSize();
787 } else if (const auto *SLiteral = dyn_cast<clang::StringLiteral>(
788 Node.getBase()->IgnoreParenImpCasts())) {
789 limit = SLiteral->getLength() + 1;
790 } else {
791 return false;
792 }
793
794 if (IgnoreStaticSizedArrays) {
795 // If we made it here, it means a size was found for the var being accessed
796 // (either string literal or array). If it's fixed size, we can ignore it.
797 return true;
798 }
799
800 Expr::EvalResult EVResult;
801 const Expr *IndexExpr = Node.getIdx();
802 if (!IndexExpr->isValueDependent() &&
803 IndexExpr->EvaluateAsInt(EVResult, Ctx)) {
804 llvm::APSInt ArrIdx = EVResult.Val.getInt();
805 // FIXME: ArrIdx.isNegative() we could immediately emit an error as that's a
806 // bug
807 if (ArrIdx.isNonNegative() && ArrIdx.getLimitedValue() < limit)
808 return true;
809 } else if (const auto *BE = dyn_cast<BinaryOperator>(IndexExpr)) {
810 // For an integer expression `e` and an integer constant `n`, `e & n` and
811 // `n & e` are bounded by `n`:
812 if (BE->getOpcode() != BO_And && BE->getOpcode() != BO_Rem)
813 return false;
814
815 const Expr *LHS = BE->getLHS();
816 const Expr *RHS = BE->getRHS();
817
818 if (BE->getOpcode() == BO_Rem) {
819 // If n is a negative number, then n % const can be greater than const
820 if (!LHS->getType()->isUnsignedIntegerType()) {
821 return false;
822 }
823
824 if (!RHS->isValueDependent() && RHS->EvaluateAsInt(EVResult, Ctx)) {
825 llvm::APSInt result = EVResult.Val.getInt();
826 if (result.isNonNegative() && result.getLimitedValue() <= limit)
827 return true;
828 }
829
830 return false;
831 }
832
833 if ((!LHS->isValueDependent() &&
834 LHS->EvaluateAsInt(EVResult, Ctx)) || // case: `n & e`
835 (!RHS->isValueDependent() &&
836 RHS->EvaluateAsInt(EVResult, Ctx))) { // `e & n`
837 llvm::APSInt result = EVResult.Val.getInt();
838 if (result.isNonNegative() && result.getLimitedValue() < limit)
839 return true;
840 }
841 return false;
842 }
843 return false;
844}
845
846static bool isSafePointerArithmetic(const Expr *Ptr, const Expr *OffsetExpr,
847 BinaryOperatorKind Opcode,
848 const ASTContext &Ctx) {
849 Expr::EvalResult EVResult;
850
851 if (OffsetExpr->isValueDependent() ||
852 !OffsetExpr->EvaluateAsInt(EVResult, Ctx)) {
853 // Dynamic offsets are not safe.
854 return false;
855 }
856
857 uint64_t limit = 0;
858 const Expr *Base = Ptr->IgnoreParenImpCasts();
859
860 if (const auto *CATy = dyn_cast<ConstantArrayType>(
861 Base->getType()->getUnqualifiedDesugaredType())) {
862 limit = CATy->getLimitedSize();
863 } else if (const auto *SLiteral = dyn_cast<clang::StringLiteral>(Base)) {
864 limit = SLiteral->getLength() + 1;
865 } else {
866 return false;
867 }
868
869 llvm::APSInt OffsetVal = EVResult.Val.getInt();
870 if (Opcode == BO_Sub)
871 OffsetVal = -OffsetVal;
872
873 // If the offset is a constant, and it is within the bounds of the
874 // array, then it is safe.
875 return OffsetVal.isNonNegative() && OffsetVal.getLimitedValue() < limit;
876}
877
878// Constant fold a conditional expression 'cond ? A : B' to
879// - 'A', if 'cond' has constant true value;
880// - 'B', if 'cond' has constant false value.
882 const ASTContext &Ctx) {
883 // FIXME: more places can use this function
884 if (const auto *CE = dyn_cast<ConditionalOperator>(E)) {
885 bool CondEval;
886 const auto *Cond = CE->getCond();
887
888 if (!Cond->isValueDependent() &&
889 Cond->EvaluateAsBooleanCondition(CondEval, Ctx))
890 return CondEval ? CE->getLHS() : CE->getRHS();
891 }
892 return E;
893}
894
895// A pointer type expression is known to be null-terminated, if it has the
896// form: E.c_str(), for any expression E of `std::string` type.
897static bool isNullTermPointer(const Expr *Ptr, ASTContext &Ctx) {
898 // Strip CXXDefaultArgExpr before check:
899 Ptr = Ptr->IgnoreParenImpCasts();
900 if (const auto *DefaultArgE = dyn_cast<CXXDefaultArgExpr>(Ptr))
901 Ptr = DefaultArgE->getExpr()->IgnoreParenImpCasts();
902 // Try to perform constant fold recursively:
903 if (const auto *NewPtr = tryConstantFoldConditionalExpr(Ptr, Ctx);
904 NewPtr != Ptr)
905 return isNullTermPointer(NewPtr, Ctx);
906 // Split the analysis for conditional expressions that cannot be
907 // constant-folded:
908 if (const auto *CondE = dyn_cast<ConditionalOperator>(Ptr)) {
909 return isNullTermPointer(CondE->getLHS(), Ctx) &&
910 isNullTermPointer(CondE->getRHS(), Ctx);
911 }
912
914 return true;
915 if (isa<PredefinedExpr>(Ptr))
916 return true;
917 if (auto *MCE = dyn_cast<CXXMemberCallExpr>(Ptr)) {
918 const CXXMethodDecl *MD = MCE->getMethodDecl();
919 const CXXRecordDecl *RD = MCE->getRecordDecl()->getCanonicalDecl();
920
921 if (MD && RD && RD->isInStdNamespace() && MD->getIdentifier())
922 if (MD->getName() == "c_str" && RD->getName() == "basic_string")
923 return true;
924 }
925
926 // Functions known to return properly null terminated strings.
927 static const llvm::StringSet<> NullTermFunctions = {"strerror"};
928 if (auto *CE = dyn_cast<CallExpr>(Ptr)) {
929 const FunctionDecl *F = CE->getDirectCallee();
930 if (F && F->getIdentifier() && NullTermFunctions.contains(F->getName()))
931 return true;
932 }
933 return false;
934}
935
936// Under `libc_func_matchers`, define a set of matchers that match unsafe
937// functions in libc and unsafe calls to them.
939// A tiny parser to strip off common prefix and suffix of libc function names
940// in real code.
941//
942// Given a function name, `matchName()` returns `CoreName` according to the
943// following grammar:
944//
945// LibcName := CoreName | CoreName + "_s"
946// MatchingName := "__builtin_" + LibcName |
947// "__builtin___" + LibcName + "_chk" |
948// "__asan_" + LibcName
949//
950static StringRef matchLibcName(StringRef Name) {
951 if (Name.ends_with("_s"))
952 return Name.drop_back(2 /* truncate "_s" */);
953 return Name;
954}
955
956// Parameter `Name` is the substring after stripping off the prefix
957// "__builtin_".
958static StringRef matchLibcNameOrBuiltinChk(StringRef Name) {
959 if (Name.starts_with("__") && Name.ends_with("_chk"))
960 return matchLibcName(
961 Name.drop_front(2).drop_back(4) /* truncate "__" and "_chk" */);
962 return matchLibcName(Name);
963}
964
965static StringRef matchName(StringRef FunName, bool isBuiltin) {
966 // Try to match __builtin_:
967 if (isBuiltin && FunName.starts_with("__builtin_"))
968 // Then either it is __builtin_LibcName or __builtin___LibcName_chk or no
969 // match:
971 FunName.drop_front(10 /* truncate "__builtin_" */));
972 // Try to match __asan_:
973 if (FunName.starts_with("__asan_"))
974 return matchLibcName(FunName.drop_front(7 /* truncate of "__asan_" */));
975 return matchLibcName(FunName);
976}
977
978// Return true iff at least one of following cases holds:
979// 1. Format string is a literal and there is an unsafe pointer argument
980// corresponding to an `s` specifier;
981// 2. Format string is not a literal and there is least an unsafe pointer
982// argument (including the formatter argument).
983//
984// `UnsafeArg` is the output argument that will be set only if this function
985// returns true.
986//
987// Format arguments start at `FmtIdx` + 1, if `FmtArgIdx` is insignificant.
988static bool
990 const Expr *&UnsafeArg, const unsigned FmtIdx,
991 std::optional<const unsigned> FmtArgIdx = std::nullopt,
992 bool isKprintf = false) {
993 class StringFormatStringHandler
995 const CallExpr *Call;
996 unsigned FmtArgIdx;
997 const Expr *&UnsafeArg;
998 ASTContext &Ctx;
999 bool UnsafeArgSet;
1000
1001 // Returns an `Expr` representing the precision if specified, null
1002 // otherwise.
1003 // The parameter `Call` is a printf call and the parameter `Precision` is
1004 // the precision of a format specifier of the `Call`.
1005 //
1006 // For example, for the `printf("%d, %.10s", 10, p)` call
1007 // `Precision` can be the precision of either "%d" or "%.10s". The former
1008 // one will have `NotSpecified` kind.
1009 const Expr *
1010 getPrecisionAsExpr(const analyze_printf::OptionalAmount &Precision,
1011 const CallExpr *Call) {
1012 if (Precision.hasDataArgument()) {
1013 unsigned PArgIdx = Precision.getArgIndex() + FmtArgIdx;
1014
1015 if (PArgIdx < Call->getNumArgs()) {
1016 const Expr *PArg = Call->getArg(PArgIdx);
1017
1018 // Strip the cast if `PArg` is a cast-to-int expression:
1019 if (auto *CE = dyn_cast<CastExpr>(PArg);
1020 CE && CE->getType()->isSignedIntegerType())
1021 PArg = CE->getSubExpr();
1022 return PArg;
1023 }
1024 }
1025 if (Precision.getHowSpecified() ==
1026 analyze_printf::OptionalAmount::HowSpecified::Constant) {
1027 auto SizeTy = Ctx.getSizeType();
1028 llvm::APSInt PArgVal = llvm::APSInt(
1029 llvm::APInt(Ctx.getTypeSize(SizeTy), Precision.getConstantAmount()),
1030 true);
1031
1032 return IntegerLiteral::Create(Ctx, PArgVal, Ctx.getSizeType(), {});
1033 }
1034 return nullptr;
1035 }
1036
1037 public:
1038 StringFormatStringHandler(const CallExpr *Call, unsigned FmtArgIdx,
1039 const Expr *&UnsafeArg, ASTContext &Ctx)
1040 : Call(Call), FmtArgIdx(FmtArgIdx), UnsafeArg(UnsafeArg), Ctx(Ctx),
1041 UnsafeArgSet(false) {}
1042
1043 bool HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier &FS,
1044 const char *startSpecifier,
1045 unsigned specifierLen,
1046 const TargetInfo &Target) override {
1047 if (FS.getConversionSpecifier().getKind() !=
1049 return true; // continue parsing
1050
1051 unsigned ArgIdx = FS.getArgIndex() + FmtArgIdx;
1052
1053 if (ArgIdx >= Call->getNumArgs())
1054 // If the `ArgIdx` is invalid, give up.
1055 return true; // continue parsing
1056
1057 const Expr *Arg = Call->getArg(ArgIdx);
1058
1059 if (isNullTermPointer(Arg, Ctx))
1060 // If Arg is a null-terminated pointer, it is safe anyway.
1061 return true; // continue parsing
1062
1063 // Otherwise, check if the specifier has a precision and if the character
1064 // pointer is safely bound by the precision:
1066 QualType ArgType = Arg->getType();
1067 bool IsArgTypeValid = // Is ArgType a character pointer type?
1068 ArgType->isPointerType() &&
1070 ? ArgType->getPointeeType()->isWideCharType()
1071 : ArgType->getPointeeType()->isCharType());
1072
1073 if (auto *Precision = getPrecisionAsExpr(FS.getPrecision(), Call);
1074 Precision && IsArgTypeValid)
1075 if (isPtrBufferSafe(Arg, Precision, Ctx))
1076 return true;
1077 // Handle unsafe case:
1078 UnsafeArg = Call->getArg(ArgIdx); // output
1079 UnsafeArgSet = true;
1080 return false; // returning false stops parsing immediately
1081 }
1082
1083 bool isUnsafeArgSet() { return UnsafeArgSet; }
1084 };
1085
1086 const Expr *Fmt = Call->getArg(FmtIdx);
1087 unsigned FmtArgStartingIdx =
1088 FmtArgIdx.has_value() ? static_cast<unsigned>(*FmtArgIdx) : FmtIdx + 1;
1089
1090 if (auto *SL = dyn_cast<clang::StringLiteral>(Fmt->IgnoreParenImpCasts())) {
1091 if (SL->getCharByteWidth() == 1) {
1092 StringRef FmtStr = SL->getString();
1093 StringFormatStringHandler Handler(Call, FmtArgStartingIdx, UnsafeArg,
1094 Ctx);
1095
1097 Handler, FmtStr.begin(), FmtStr.end(), Ctx.getLangOpts(),
1098 Ctx.getTargetInfo(), isKprintf) &&
1099 Handler.isUnsafeArgSet();
1100 }
1101
1102 if (auto FmtStr = SL->tryEvaluateString(Ctx)) {
1103 StringFormatStringHandler Handler(Call, FmtArgStartingIdx, UnsafeArg,
1104 Ctx);
1106 Handler, FmtStr->data(), FmtStr->data() + FmtStr->size(),
1107 Ctx.getLangOpts(), Ctx.getTargetInfo(), isKprintf) &&
1108 Handler.isUnsafeArgSet();
1109 }
1110 }
1111 // If format is not a string literal, we cannot analyze the format string.
1112 // In this case, this call is considered unsafe if at least one argument
1113 // (including the format argument) is unsafe pointer.
1114 return llvm::any_of(
1115 llvm::make_range(Call->arg_begin() + FmtIdx, Call->arg_end()),
1116 [&UnsafeArg, &Ctx](const Expr *Arg) -> bool {
1117 if (Arg->getType()->isPointerType() && !isNullTermPointer(Arg, Ctx)) {
1118 UnsafeArg = Arg;
1119 return true;
1120 }
1121 return false;
1122 });
1123}
1124
1125// Matches a FunctionDecl node such that
1126// 1. It's name, after stripping off predefined prefix and suffix, is
1127// `CoreName`; and
1128// 2. `CoreName` or `CoreName[str/wcs]` is one of the `PredefinedNames`, which
1129// is a set of libc function names.
1130//
1131// Note: For predefined prefix and suffix, see `matchName()`.
1132// The notation `CoreName[str/wcs]` means a new name obtained from replace
1133// string "wcs" with "str" in `CoreName`.
1135 static const std::set<StringRef> PredefinedNames = {
1136 // numeric conversion:
1137 "atof",
1138 "atoi",
1139 "atol",
1140 "atoll",
1141 "strtol",
1142 "strtoll",
1143 "strtoul",
1144 "strtoull",
1145 "strtof",
1146 "strtod",
1147 "strtold",
1148 "strtoimax",
1149 "strtoumax",
1150 // "strfromf", "strfromd", "strfroml", // C23?
1151 // string manipulation:
1152 "strcpy",
1153 "strncpy",
1154 "strlcpy",
1155 "strcat",
1156 "strncat",
1157 "strlcat",
1158 "strxfrm",
1159 "strdup",
1160 "strndup",
1161 // string examination:
1162 "strlen",
1163 "strnlen",
1164 "strcmp",
1165 "strncmp",
1166 "stricmp",
1167 "strcasecmp",
1168 "strcoll",
1169 "strchr",
1170 "strrchr",
1171 "strspn",
1172 "strcspn",
1173 "strpbrk",
1174 "strstr",
1175 "strtok",
1176 // "mem-" functions
1177 "memchr",
1178 "wmemchr",
1179 "memcmp",
1180 "wmemcmp",
1181 "memcpy",
1182 "memccpy",
1183 "mempcpy",
1184 "wmemcpy",
1185 "memmove",
1186 "wmemmove",
1187 "wmemset",
1188 // IO:
1189 "fread",
1190 "fwrite",
1191 "fgets",
1192 "fgetws",
1193 "gets",
1194 "fputs",
1195 "fputws",
1196 "puts",
1197 // others
1198 "strerror_s",
1199 "strerror_r",
1200 "bcopy",
1201 "bzero",
1202 "bsearch",
1203 "qsort",
1204 };
1205
1206 auto *II = Node.getIdentifier();
1207
1208 if (!II)
1209 return false;
1210
1211 StringRef Name = matchName(II->getName(), Node.getBuiltinID());
1212
1213 // Match predefined names:
1214 if (PredefinedNames.count(Name))
1215 return true;
1216
1217 std::string NameWCS = Name.str();
1218 size_t WcsPos = NameWCS.find("wcs");
1219
1220 while (WcsPos != std::string::npos) {
1221 NameWCS[WcsPos++] = 's';
1222 NameWCS[WcsPos++] = 't';
1223 NameWCS[WcsPos++] = 'r';
1224 WcsPos = NameWCS.find("wcs", WcsPos);
1225 }
1226 if (PredefinedNames.count(NameWCS))
1227 return true;
1228 // All `scanf` functions are unsafe (including `sscanf`, `vsscanf`, etc.. They
1229 // all should end with "scanf"):
1230 return Name.ends_with("scanf");
1231}
1232
1233// Returns true if this is an unsafe call to `memset`.
1234// The only call we currently consider safe is of the form
1235// `memset(&x, 0, sizeof(x))`, with possible variations in parentheses.
1236static bool isUnsafeMemset(const CallExpr &Node, ASTContext &Ctx) {
1237 const FunctionDecl *FD = Node.getDirectCallee();
1238 assert(FD && "It should have been checked that FD is non-null.");
1239
1240 const IdentifierInfo *II = FD->getIdentifier();
1241 if (!II)
1242 return false;
1243
1244 StringRef Name = matchName(II->getName(), FD->getBuiltinID());
1245 if (Name != "memset")
1246 return false;
1247
1248 // We currently only handle the basic forms of `memset` with 3 parameters.
1249 // There is also `__builtin___memset_chk()` which takes a 4th `destlen`
1250 // parameter for bounds checking, but we don't consider its safe forms yet.
1251 // https://refspecs.linuxbase.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/libc---memset-chk-1.html
1252 if (FD->getNumParams() != 3)
1253 return true;
1254
1255 // Now we have a known version of `memset`, consider it unsafe unless it's in
1256 // the form `memset(&x, 0, sizeof(x))`.
1257 const auto *AddressOfVar = dyn_cast_if_present<DeclRefExpr>(
1259 if (!AddressOfVar)
1260 return true;
1261
1262 const auto *SizeOfVar =
1263 dyn_cast_if_present<DeclRefExpr>(getSubExprInSizeOfExpr(*Node.getArg(2)));
1264 if (!SizeOfVar)
1265 return true;
1266
1267 return AddressOfVar->getDecl() != SizeOfVar->getDecl();
1268}
1269
1270// Match a call to one of the `v*printf` functions taking `va_list`. We cannot
1271// check safety for these functions so they should be changed to their
1272// non-va_list versions.
1273static bool isUnsafeVaListPrintfFunc(const FunctionDecl &Node) {
1274 auto *II = Node.getIdentifier();
1275
1276 if (!II)
1277 return false;
1278
1279 StringRef Name = matchName(II->getName(), Node.getBuiltinID());
1280
1281 return Name.starts_with("v") && Name.ends_with("printf");
1282}
1283
1284// Matches a call to one of the `sprintf` functions as they are always unsafe
1285// and should be changed to `snprintf`.
1286static bool isUnsafeSprintfFunc(const FunctionDecl &Node) {
1287 auto *II = Node.getIdentifier();
1288
1289 if (!II)
1290 return false;
1291
1292 StringRef Name = matchName(II->getName(), Node.getBuiltinID());
1293
1294 return Name == "sprintf" || Name == "swprintf";
1295}
1296
1297// Match function declarations of `printf`, `fprintf`, `snprintf` and their wide
1298// character versions. Calls to these functions can be safe if their arguments
1299// are carefully made safe.
1300static bool isNormalPrintfFunc(const FunctionDecl &Node) {
1301 auto *II = Node.getIdentifier();
1302
1303 if (!II)
1304 return false;
1305
1306 StringRef Name = matchName(II->getName(), Node.getBuiltinID());
1307
1308 if (!Name.ends_with("printf"))
1309 return false;
1310
1311 StringRef Prefix = Name.drop_back(6);
1312
1313 if (Prefix.ends_with("w"))
1314 Prefix = Prefix.drop_back(1);
1315
1316 return Prefix.empty() || Prefix == "k" || Prefix == "f" || Prefix == "sn";
1317}
1318
1319// This matcher requires that it is known that the callee `isNormalPrintf`.
1320// Then if the format string is a string literal, this matcher matches when at
1321// least one string argument is unsafe. If the format is not a string literal,
1322// this matcher matches when at least one pointer type argument is unsafe.
1323static bool hasUnsafePrintfStringArg(const CallExpr &Node, ASTContext &Ctx,
1324 MatchResult &Result, llvm::StringRef Tag) {
1325 // Determine what printf it is by examining formal parameters:
1326 const FunctionDecl *FD = Node.getDirectCallee();
1327
1328 assert(FD && "It should have been checked that FD is non-null.");
1329
1330 unsigned NumParms = FD->getNumParams();
1331
1332 if (NumParms < 1)
1333 return false; // possibly some user-defined printf function
1334
1335 QualType FirstParmTy = FD->getParamDecl(0)->getType();
1336
1337 if (!FirstParmTy->isPointerType())
1338 return false; // possibly some user-defined printf function
1339
1340 QualType FirstPteTy = FirstParmTy->castAs<PointerType>()->getPointeeType();
1341
1342 if (!Ctx.getFILEType()
1343 .isNull() && //`FILE *` must be in the context if it is fprintf
1344 FirstPteTy.getCanonicalType() == Ctx.getFILEType().getCanonicalType()) {
1345 // It is a fprintf:
1346 const Expr *UnsafeArg;
1347
1348 if (hasUnsafeFormatOrSArg(Ctx, &Node, UnsafeArg, /* FmtIdx= */ 1)) {
1349 Result.addNode(Tag, DynTypedNode::create(*UnsafeArg));
1350 return true;
1351 }
1352 return false;
1353 }
1354
1355 if (FirstPteTy.isConstQualified()) {
1356 // If the first parameter is a `const char *`, it is a printf/kprintf:
1357 bool isKprintf = false;
1358 const Expr *UnsafeArg;
1359
1360 if (auto *II = FD->getIdentifier())
1361 isKprintf = II->getName() == "kprintf";
1362 if (hasUnsafeFormatOrSArg(Ctx, &Node, UnsafeArg, /* FmtIdx= */ 0,
1363 /* FmtArgIdx= */ std::nullopt, isKprintf)) {
1364 Result.addNode(Tag, DynTypedNode::create(*UnsafeArg));
1365 return true;
1366 }
1367 return false;
1368 }
1369
1370 if (NumParms > 2) {
1371 QualType SecondParmTy = FD->getParamDecl(1)->getType();
1372
1373 if (!FirstPteTy.isConstQualified() && SecondParmTy->isIntegerType()) {
1374 // If the first parameter type is non-const qualified `char *` and the
1375 // second is an integer, it is a snprintf:
1376 const Expr *UnsafeArg;
1377
1378 if (hasUnsafeFormatOrSArg(Ctx, &Node, UnsafeArg, /* FmtIdx= */ 2)) {
1379 Result.addNode(Tag, DynTypedNode::create(*UnsafeArg));
1380 return true;
1381 }
1382 return false;
1383 }
1384 }
1385 // We don't really recognize this "normal" printf, the only thing we
1386 // can do is to require all pointers to be null-terminated:
1387 for (const auto *Arg : Node.arguments())
1388 if (Arg->getType()->isPointerType() && !isNullTermPointer(Arg, Ctx)) {
1389 Result.addNode(Tag, DynTypedNode::create(*Arg));
1390 return true;
1391 }
1392 return false;
1393}
1394
1395// This function requires that it is known that the callee `isNormalPrintf`.
1396// It returns true iff the first two arguments of the call is a pointer
1397// `Ptr` and an unsigned integer `Size` and they are NOT safe, i.e.,
1398// `!isPtrBufferSafe(Ptr, Size)`.
1399static bool hasUnsafeSnprintfBuffer(const CallExpr &Node, ASTContext &Ctx) {
1400 const FunctionDecl *FD = Node.getDirectCallee();
1401
1402 assert(FD && "It should have been checked that FD is non-null.");
1403
1404 if (FD->getNumParams() < 3)
1405 return false; // Not an snprint
1406
1407 QualType FirstParmTy = FD->getParamDecl(0)->getType();
1408
1409 if (!FirstParmTy->isPointerType())
1410 return false; // Not an snprint
1411
1412 QualType FirstPteTy = FirstParmTy->castAs<PointerType>()->getPointeeType();
1413 const Expr *Buf = Node.getArg(0), *Size = Node.getArg(1);
1414
1415 if (FirstPteTy.isConstQualified() || !FirstPteTy->isAnyCharacterType() ||
1416 !Buf->getType()->isPointerType() ||
1417 !Size->getType()->isUnsignedIntegerType())
1418 return false; // not an snprintf call
1419
1420 return !isPtrBufferSafe(Buf, Size, Ctx);
1421}
1422} // namespace libc_func_matchers
1423
1424namespace {
1425// Because the analysis revolves around variables and their types, we'll need to
1426// track uses of variables (aka DeclRefExprs).
1427using DeclUseList = SmallVector<const DeclRefExpr *, 1>;
1428
1429// Convenience typedef.
1430using FixItList = SmallVector<FixItHint, 4>;
1431} // namespace
1432
1433namespace {
1434/// Gadget is an individual operation in the code that may be of interest to
1435/// this analysis. Each (non-abstract) subclass corresponds to a specific
1436/// rigid AST structure that constitutes an operation on a pointer-type object.
1437/// Discovery of a gadget in the code corresponds to claiming that we understand
1438/// what this part of code is doing well enough to potentially improve it.
1439/// Gadgets can be warning (immediately deserving a warning) or fixable (not
1440/// always deserving a warning per se, but requires our attention to identify
1441/// it warrants a fixit).
1442class Gadget {
1443public:
1444 enum class Kind {
1445#define GADGET(x) x,
1446#include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
1447 };
1448
1449 Gadget(Kind K) : K(K) {}
1450
1451 Kind getKind() const { return K; }
1452
1453#ifndef NDEBUG
1454 StringRef getDebugName() const {
1455 switch (K) {
1456#define GADGET(x) \
1457 case Kind::x: \
1458 return #x;
1459#include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
1460 }
1461 llvm_unreachable("Unhandled Gadget::Kind enum");
1462 }
1463#endif
1464
1465 virtual bool isWarningGadget() const = 0;
1466 // TODO remove this method from WarningGadget interface. It's only used for
1467 // debug prints in FixableGadget.
1468 virtual SourceLocation getSourceLoc() const = 0;
1469
1470 /// Returns the list of pointer-type variables on which this gadget performs
1471 /// its operation. Typically, there's only one variable. This isn't a list
1472 /// of all DeclRefExprs in the gadget's AST!
1473 virtual DeclUseList getClaimedVarUseSites() const = 0;
1474
1475 virtual ~Gadget() = default;
1476
1477private:
1478 Kind K;
1479};
1480
1481/// Warning gadgets correspond to unsafe code patterns that warrants
1482/// an immediate warning.
1483class WarningGadget : public Gadget {
1484public:
1485 WarningGadget(Kind K) : Gadget(K) {}
1486
1487 static bool classof(const Gadget *G) { return G->isWarningGadget(); }
1488 bool isWarningGadget() const final { return true; }
1489
1490 virtual void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
1491 bool IsRelatedToDecl,
1492 ASTContext &Ctx) const = 0;
1493
1494 virtual SmallVector<const Expr *, 1> getUnsafePtrs() const = 0;
1495};
1496
1497/// Fixable gadgets correspond to code patterns that aren't always unsafe but
1498/// need to be properly recognized in order to emit fixes. For example, if a raw
1499/// pointer-type variable is replaced by a safe C++ container, every use of such
1500/// variable must be carefully considered and possibly updated.
1501class FixableGadget : public Gadget {
1502public:
1503 FixableGadget(Kind K) : Gadget(K) {}
1504
1505 static bool classof(const Gadget *G) { return !G->isWarningGadget(); }
1506 bool isWarningGadget() const final { return false; }
1507
1508 /// Returns a fixit that would fix the current gadget according to
1509 /// the current strategy. Returns std::nullopt if the fix cannot be produced;
1510 /// returns an empty list if no fixes are necessary.
1511 virtual std::optional<FixItList> getFixits(const FixitStrategy &) const {
1512 return std::nullopt;
1513 }
1514
1515 /// Returns a list of two elements where the first element is the LHS of a
1516 /// pointer assignment statement and the second element is the RHS. This
1517 /// two-element list represents the fact that the LHS buffer gets its bounds
1518 /// information from the RHS buffer. This information will be used later to
1519 /// group all those variables whose types must be modified together to prevent
1520 /// type mismatches.
1521 virtual std::optional<std::pair<const VarDecl *, const VarDecl *>>
1522 getStrategyImplications() const {
1523 return std::nullopt;
1524 }
1525};
1526
1527static bool isSupportedVariable(const DeclRefExpr &Node) {
1528 const Decl *D = Node.getDecl();
1529 return D != nullptr && isa<VarDecl>(D);
1530}
1531
1532// Returns true for RecordDecl of type std::unique_ptr<T[]>
1533static bool isUniquePtrArray(const CXXRecordDecl *RecordDecl) {
1535 RecordDecl->getNameAsString() != "unique_ptr")
1536 return false;
1537
1538 const ClassTemplateSpecializationDecl *class_template_specialization_decl =
1539 dyn_cast<ClassTemplateSpecializationDecl>(RecordDecl);
1540 if (!class_template_specialization_decl)
1541 return false;
1542
1543 const TemplateArgumentList &template_args =
1544 class_template_specialization_decl->getTemplateArgs();
1545 if (template_args.size() == 0)
1546 return false;
1547
1548 const TemplateArgument &first_arg = template_args[0];
1549 if (first_arg.getKind() != TemplateArgument::Type)
1550 return false;
1551
1552 QualType referred_type = first_arg.getAsType();
1553 return referred_type->isArrayType();
1554}
1555
1556class UniquePtrArrayAccessGadget : public WarningGadget {
1557private:
1558 static constexpr const char *const AccessorTag = "unique_ptr_array_access";
1559 const CXXOperatorCallExpr *AccessorExpr;
1560
1561public:
1562 UniquePtrArrayAccessGadget(const MatchResult &Result)
1563 : WarningGadget(Kind::UniquePtrArrayAccess),
1564 AccessorExpr(Result.getNodeAs<CXXOperatorCallExpr>(AccessorTag)) {
1565 assert(AccessorExpr &&
1566 "UniquePtrArrayAccessGadget requires a matched CXXOperatorCallExpr");
1567 }
1568
1569 static bool classof(const Gadget *G) {
1570 return G->getKind() == Kind::UniquePtrArrayAccess;
1571 }
1572
1573 static bool matches(const Stmt *S, const ASTContext &Ctx,
1575
1576 const CXXOperatorCallExpr *OpCall = dyn_cast<CXXOperatorCallExpr>(S);
1577 if (!OpCall || OpCall->getOperator() != OO_Subscript)
1578 return false;
1579
1580 const Expr *Callee = OpCall->getCallee()->IgnoreParenImpCasts();
1581 if (!Callee)
1582 return false;
1583
1584 const CXXMethodDecl *Method =
1585 dyn_cast_or_null<CXXMethodDecl>(OpCall->getDirectCallee());
1586 if (!Method)
1587 return false;
1588
1589 if (Method->getOverloadedOperator() != OO_Subscript)
1590 return false;
1591
1592 const CXXRecordDecl *RecordDecl = Method->getParent();
1593 if (!isUniquePtrArray(RecordDecl))
1594 return false;
1595
1596 const Expr *IndexExpr = OpCall->getArg(1);
1597 clang::Expr::EvalResult Eval;
1598
1599 // Allow [0]
1600 if (IndexExpr->EvaluateAsInt(Eval, Ctx) && Eval.Val.getInt().isZero())
1601 return false;
1602
1603 Result.addNode(AccessorTag, DynTypedNode::create(*OpCall));
1604 return true;
1605 }
1606 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
1607 bool IsRelatedToDecl,
1608 ASTContext &Ctx) const override {
1610 DynTypedNode::create(*AccessorExpr), IsRelatedToDecl, Ctx);
1611 }
1612
1613 SourceLocation getSourceLoc() const override {
1614 if (AccessorExpr)
1615 return AccessorExpr->getOperatorLoc();
1616 return SourceLocation();
1617 }
1618
1619 DeclUseList getClaimedVarUseSites() const override { return {}; }
1620 SmallVector<const Expr *, 1> getUnsafePtrs() const override { return {}; }
1621};
1622
1623using FixableGadgetList = std::vector<std::unique_ptr<FixableGadget>>;
1624using WarningGadgetList = std::vector<std::unique_ptr<WarningGadget>>;
1625
1626/// An increment of a pointer-type value is unsafe as it may run the pointer
1627/// out of bounds.
1628class IncrementGadget : public WarningGadget {
1629 static constexpr const char *const OpTag = "op";
1630 const UnaryOperator *Op;
1631
1632public:
1633 IncrementGadget(const MatchResult &Result)
1634 : WarningGadget(Kind::Increment),
1635 Op(Result.getNodeAs<UnaryOperator>(OpTag)) {}
1636
1637 static bool classof(const Gadget *G) {
1638 return G->getKind() == Kind::Increment;
1639 }
1640
1641 static bool matches(const Stmt *S, const ASTContext &Ctx,
1643 const auto *UO = dyn_cast<UnaryOperator>(S);
1644 if (!UO || !UO->isIncrementOp())
1645 return false;
1647 return false;
1648 Result.addNode(OpTag, DynTypedNode::create(*UO));
1649 return true;
1650 }
1651
1652 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
1653 bool IsRelatedToDecl,
1654 ASTContext &Ctx) const override {
1655 Handler.handleUnsafeOperation(Op, IsRelatedToDecl, Ctx);
1656 }
1657 SourceLocation getSourceLoc() const override { return Op->getBeginLoc(); }
1658
1659 DeclUseList getClaimedVarUseSites() const override {
1660 SmallVector<const DeclRefExpr *, 2> Uses;
1661 if (const auto *DRE =
1662 dyn_cast<DeclRefExpr>(Op->getSubExpr()->IgnoreParenImpCasts())) {
1663 Uses.push_back(DRE);
1664 }
1665
1666 return std::move(Uses);
1667 }
1668
1669 SmallVector<const Expr *, 1> getUnsafePtrs() const override {
1670 return {Op->getSubExpr()->IgnoreParenImpCasts()};
1671 }
1672};
1673
1674/// A decrement of a pointer-type value is unsafe as it may run the pointer
1675/// out of bounds.
1676class DecrementGadget : public WarningGadget {
1677 static constexpr const char *const OpTag = "op";
1678 const UnaryOperator *Op;
1679
1680public:
1681 DecrementGadget(const MatchResult &Result)
1682 : WarningGadget(Kind::Decrement),
1683 Op(Result.getNodeAs<UnaryOperator>(OpTag)) {}
1684
1685 static bool classof(const Gadget *G) {
1686 return G->getKind() == Kind::Decrement;
1687 }
1688
1689 static bool matches(const Stmt *S, const ASTContext &Ctx,
1691 const auto *UO = dyn_cast<UnaryOperator>(S);
1692 if (!UO || !UO->isDecrementOp())
1693 return false;
1695 return false;
1696 Result.addNode(OpTag, DynTypedNode::create(*UO));
1697 return true;
1698 }
1699
1700 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
1701 bool IsRelatedToDecl,
1702 ASTContext &Ctx) const override {
1703 Handler.handleUnsafeOperation(Op, IsRelatedToDecl, Ctx);
1704 }
1705 SourceLocation getSourceLoc() const override { return Op->getBeginLoc(); }
1706
1707 DeclUseList getClaimedVarUseSites() const override {
1708 if (const auto *DRE =
1709 dyn_cast<DeclRefExpr>(Op->getSubExpr()->IgnoreParenImpCasts())) {
1710 return {DRE};
1711 }
1712
1713 return {};
1714 }
1715
1716 SmallVector<const Expr *, 1> getUnsafePtrs() const override {
1717 return {Op->getSubExpr()->IgnoreParenImpCasts()};
1718 }
1719};
1720
1721/// Array subscript expressions on raw pointers as if they're arrays. Unsafe as
1722/// it doesn't have any bounds checks for the array.
1723class ArraySubscriptGadget : public WarningGadget {
1724 static constexpr const char *const ArraySubscrTag = "ArraySubscript";
1725 const ArraySubscriptExpr *ASE;
1726
1727public:
1728 ArraySubscriptGadget(const MatchResult &Result)
1729 : WarningGadget(Kind::ArraySubscript),
1730 ASE(Result.getNodeAs<ArraySubscriptExpr>(ArraySubscrTag)) {}
1731
1732 static bool classof(const Gadget *G) {
1733 return G->getKind() == Kind::ArraySubscript;
1734 }
1735
1736 static bool matches(const Stmt *S, const ASTContext &Ctx,
1737 const UnsafeBufferUsageHandler *Handler,
1739 const auto *ASE = dyn_cast<ArraySubscriptExpr>(S);
1740 if (!ASE)
1741 return false;
1742 const auto *const Base = ASE->getBase()->IgnoreParenImpCasts();
1743 if (!hasPointerType(*Base) && !hasArrayType(*Base))
1744 return false;
1745 const auto *Idx = dyn_cast<IntegerLiteral>(ASE->getIdx());
1746 bool IsSafeIndex = (Idx && Idx->getValue().isZero()) ||
1747 isa<ArrayInitIndexExpr>(ASE->getIdx());
1748 if (IsSafeIndex ||
1750 *ASE, Ctx,
1752 return false;
1753 Result.addNode(ArraySubscrTag, DynTypedNode::create(*ASE));
1754 return true;
1755 }
1756
1757 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
1758 bool IsRelatedToDecl,
1759 ASTContext &Ctx) const override {
1760 Handler.handleUnsafeOperation(ASE, IsRelatedToDecl, Ctx);
1761 }
1762 SourceLocation getSourceLoc() const override { return ASE->getBeginLoc(); }
1763
1764 DeclUseList getClaimedVarUseSites() const override {
1765 if (const auto *DRE =
1766 dyn_cast<DeclRefExpr>(ASE->getBase()->IgnoreParenImpCasts())) {
1767 return {DRE};
1768 }
1769
1770 return {};
1771 }
1772
1773 SmallVector<const Expr *, 1> getUnsafePtrs() const override {
1774 return {ASE->getBase()->IgnoreParenImpCasts()};
1775 }
1776};
1777
1778/// A pointer arithmetic expression of one of the forms:
1779/// \code
1780/// ptr + n | n + ptr | ptr - n | ptr += n | ptr -= n
1781/// \endcode
1782class PointerArithmeticGadget : public WarningGadget {
1783 static constexpr const char *const PointerArithmeticTag = "ptrAdd";
1784 static constexpr const char *const PointerArithmeticPointerTag = "ptrAddPtr";
1785 const BinaryOperator *PA; // pointer arithmetic expression
1786 const Expr *Ptr; // the pointer expression in `PA`
1787
1788public:
1789 PointerArithmeticGadget(const MatchResult &Result)
1790 : WarningGadget(Kind::PointerArithmetic),
1791 PA(Result.getNodeAs<BinaryOperator>(PointerArithmeticTag)),
1792 Ptr(Result.getNodeAs<Expr>(PointerArithmeticPointerTag)) {}
1793
1794 static bool classof(const Gadget *G) {
1795 return G->getKind() == Kind::PointerArithmetic;
1796 }
1797
1798 static bool matches(const Stmt *S, const ASTContext &Ctx,
1799 const UnsafeBufferUsageHandler *Handler,
1801 const auto *BO = dyn_cast<BinaryOperator>(S);
1802 if (!BO)
1803 return false;
1804 const auto *LHS = BO->getLHS();
1805 const auto *RHS = BO->getRHS();
1806
1807 const Expr *Ptr = nullptr;
1808 const Expr *OffsetExpr = nullptr;
1809
1810 // ptr at left
1811 if (BO->getOpcode() == BO_Add || BO->getOpcode() == BO_Sub ||
1812 BO->getOpcode() == BO_AddAssign || BO->getOpcode() == BO_SubAssign) {
1813 if (hasPointerType(*LHS) && (RHS->getType()->isIntegerType() ||
1814 RHS->getType()->isEnumeralType())) {
1815 Ptr = LHS;
1816 OffsetExpr = RHS;
1817 }
1818 }
1819 // ptr at right
1820 if (BO->getOpcode() == BO_Add && hasPointerType(*RHS) &&
1821 (LHS->getType()->isIntegerType() || LHS->getType()->isEnumeralType())) {
1822 Ptr = RHS;
1823 OffsetExpr = LHS;
1824 }
1825
1826 if (!Ptr || !OffsetExpr)
1827 return false;
1828
1829 // If -Wno-unsafe-buffer-usage-in-static-sized-array is used, suppress
1830 // warnings for guaranteed safe pointer arithmetic.
1832 isSafePointerArithmetic(Ptr, OffsetExpr, BO->getOpcode(), Ctx)) {
1833 return false;
1834 }
1835
1836 // Default: warn on all pointer arithmetic
1837 Result.addNode(PointerArithmeticPointerTag, DynTypedNode::create(*Ptr));
1838 Result.addNode(PointerArithmeticTag, DynTypedNode::create(*BO));
1839 return true;
1840 }
1841
1842 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
1843 bool IsRelatedToDecl,
1844 ASTContext &Ctx) const override {
1845 Handler.handleUnsafeOperation(PA, IsRelatedToDecl, Ctx);
1846 }
1847 SourceLocation getSourceLoc() const override { return PA->getBeginLoc(); }
1848
1849 DeclUseList getClaimedVarUseSites() const override {
1850 if (const auto *DRE = dyn_cast<DeclRefExpr>(Ptr->IgnoreParenImpCasts())) {
1851 return {DRE};
1852 }
1853
1854 return {};
1855 }
1856
1857 SmallVector<const Expr *, 1> getUnsafePtrs() const override {
1858 return {Ptr->IgnoreParenImpCasts()};
1859 }
1860
1861 // FIXME: pointer adding zero should be fine
1862 // FIXME: this gadge will need a fix-it
1863};
1864
1865class SpanTwoParamConstructorGadget : public WarningGadget {
1866 static constexpr const char *const SpanTwoParamConstructorTag =
1867 "spanTwoParamConstructor";
1868 const CXXConstructExpr *Ctor; // the span constructor expression
1869
1870public:
1871 SpanTwoParamConstructorGadget(const MatchResult &Result)
1872 : WarningGadget(Kind::SpanTwoParamConstructor),
1873 Ctor(Result.getNodeAs<CXXConstructExpr>(SpanTwoParamConstructorTag)) {}
1874
1875 static bool classof(const Gadget *G) {
1876 return G->getKind() == Kind::SpanTwoParamConstructor;
1877 }
1878
1879 static bool matches(const Stmt *S, ASTContext &Ctx, MatchResult &Result) {
1880 const auto *CE = dyn_cast<CXXConstructExpr>(S);
1881 if (!CE)
1882 return false;
1883 const auto *CDecl = CE->getConstructor();
1884 const auto *CRecordDecl = CDecl->getParent();
1885 auto HasTwoParamSpanCtorDecl =
1886 CRecordDecl->isInStdNamespace() &&
1887 CDecl->getDeclName().getAsString() == "span" && CE->getNumArgs() == 2;
1888 if (!HasTwoParamSpanCtorDecl || isSafeSpanTwoParamConstruct(*CE, Ctx))
1889 return false;
1890 Result.addNode(SpanTwoParamConstructorTag, DynTypedNode::create(*CE));
1891 return true;
1892 }
1893
1894 static bool matches(const Stmt *S, ASTContext &Ctx,
1895 const UnsafeBufferUsageHandler *Handler,
1897 if (ignoreUnsafeBufferInContainer(*S, Handler))
1898 return false;
1899 return matches(S, Ctx, Result);
1900 }
1901
1902 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
1903 bool IsRelatedToDecl,
1904 ASTContext &Ctx) const override {
1905 Handler.handleUnsafeOperationInContainer(Ctor, IsRelatedToDecl, Ctx);
1906 }
1907 SourceLocation getSourceLoc() const override { return Ctor->getBeginLoc(); }
1908
1909 DeclUseList getClaimedVarUseSites() const override {
1910 // If the constructor call is of the form `std::span{var, n}`, `var` is
1911 // considered an unsafe variable.
1912 if (auto *DRE = dyn_cast<DeclRefExpr>(Ctor->getArg(0))) {
1913 if (isa<VarDecl>(DRE->getDecl()))
1914 return {DRE};
1915 }
1916 return {};
1917 }
1918
1919 SmallVector<const Expr *, 1> getUnsafePtrs() const override { return {}; }
1920};
1921
1922class StringViewTwoParamConstructorGadget : public WarningGadget {
1923 static constexpr const char *const StringViewTwoParamConstructorTag =
1924 "stringViewTwoParamConstructor";
1925 const CXXConstructExpr *Ctor; // the string_view constructor expression
1926
1927public:
1928 StringViewTwoParamConstructorGadget(const MatchResult &Result)
1929 : WarningGadget(Kind::StringViewTwoParamConstructor),
1930 Ctor(Result.getNodeAs<CXXConstructExpr>(
1931 StringViewTwoParamConstructorTag)) {}
1932
1933 static bool classof(const Gadget *G) {
1934 return G->getKind() == Kind::StringViewTwoParamConstructor;
1935 }
1936
1937 static bool matches(const Stmt *S, ASTContext &Ctx, MatchResult &Result) {
1938 const auto *CE = dyn_cast<CXXConstructExpr>(S);
1939 if (!CE)
1940 return false;
1941 const auto *CDecl = CE->getConstructor();
1942 const auto *CRecordDecl = CDecl->getParent();
1943
1944 // MATCH: std::basic_string_view
1945 bool IsStringView =
1946 CRecordDecl->isInStdNamespace() &&
1947 CDecl->getDeclName().getAsString() == "basic_string_view" &&
1948 CE->getNumArgs() == 2;
1949
1950 if (!IsStringView || isSafeStringViewTwoParamConstruct(*CE, Ctx))
1951 return false;
1952
1953 Result.addNode(StringViewTwoParamConstructorTag, DynTypedNode::create(*CE));
1954 return true;
1955 }
1956
1957 static bool matches(const Stmt *S, ASTContext &Ctx,
1958 const UnsafeBufferUsageHandler *Handler,
1960 if (ignoreUnsafeBufferInContainer(*S, Handler))
1961 return false;
1962 return matches(S, Ctx, Result);
1963 }
1964
1965 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
1966 bool IsRelatedToDecl,
1967 ASTContext &Ctx) const override {
1968 Handler.handleUnsafeOperationInStringView(Ctor, IsRelatedToDecl, Ctx);
1969 }
1970
1971 SourceLocation getSourceLoc() const override { return Ctor->getBeginLoc(); }
1972
1973 DeclUseList getClaimedVarUseSites() const override {
1974 // If the constructor call is of the form `std::string_view{var, n}`, `var`
1975 // is considered an unsafe variable.
1976 if (auto *DRE = dyn_cast<DeclRefExpr>(Ctor->getArg(0))) {
1977 if (isa<VarDecl>(DRE->getDecl()))
1978 return {DRE};
1979 }
1980 return {};
1981 }
1982
1983 SmallVector<const Expr *, 1> getUnsafePtrs() const override { return {}; }
1984};
1985
1986/// A pointer initialization expression of the form:
1987/// \code
1988/// int *p = q;
1989/// \endcode
1990class PointerInitGadget : public FixableGadget {
1991private:
1992 static constexpr const char *const PointerInitLHSTag = "ptrInitLHS";
1993 static constexpr const char *const PointerInitRHSTag = "ptrInitRHS";
1994 const VarDecl *PtrInitLHS; // the LHS pointer expression in `PI`
1995 const DeclRefExpr *PtrInitRHS; // the RHS pointer expression in `PI`
1996
1997public:
1998 PointerInitGadget(const MatchResult &Result)
1999 : FixableGadget(Kind::PointerInit),
2000 PtrInitLHS(Result.getNodeAs<VarDecl>(PointerInitLHSTag)),
2001 PtrInitRHS(Result.getNodeAs<DeclRefExpr>(PointerInitRHSTag)) {}
2002
2003 static bool classof(const Gadget *G) {
2004 return G->getKind() == Kind::PointerInit;
2005 }
2006
2007 static bool matches(const Stmt *S,
2008 llvm::SmallVectorImpl<MatchResult> &Results) {
2009 const DeclStmt *DS = dyn_cast<DeclStmt>(S);
2010 if (!DS || !DS->isSingleDecl())
2011 return false;
2012 const VarDecl *VD = dyn_cast<VarDecl>(DS->getSingleDecl());
2013 if (!VD)
2014 return false;
2015 const Expr *Init = VD->getAnyInitializer();
2016 if (!Init)
2017 return false;
2018 const auto *DRE = dyn_cast<DeclRefExpr>(Init->IgnoreImpCasts());
2019 if (!DRE || !hasPointerType(*DRE) || !isSupportedVariable(*DRE)) {
2020 return false;
2021 }
2022 MatchResult R;
2023 R.addNode(PointerInitLHSTag, DynTypedNode::create(*VD));
2024 R.addNode(PointerInitRHSTag, DynTypedNode::create(*DRE));
2025 Results.emplace_back(std::move(R));
2026 return true;
2027 }
2028
2029 virtual std::optional<FixItList>
2030 getFixits(const FixitStrategy &S) const override;
2031 SourceLocation getSourceLoc() const override {
2032 return PtrInitRHS->getBeginLoc();
2033 }
2034
2035 virtual DeclUseList getClaimedVarUseSites() const override {
2036 return DeclUseList{PtrInitRHS};
2037 }
2038
2039 virtual std::optional<std::pair<const VarDecl *, const VarDecl *>>
2040 getStrategyImplications() const override {
2041 return std::make_pair(PtrInitLHS, cast<VarDecl>(PtrInitRHS->getDecl()));
2042 }
2043};
2044
2045/// A pointer assignment expression of the form:
2046/// \code
2047/// p = q;
2048/// \endcode
2049/// where both `p` and `q` are pointers.
2050class PtrToPtrAssignmentGadget : public FixableGadget {
2051private:
2052 static constexpr const char *const PointerAssignLHSTag = "ptrLHS";
2053 static constexpr const char *const PointerAssignRHSTag = "ptrRHS";
2054 const DeclRefExpr *PtrLHS; // the LHS pointer expression in `PA`
2055 const DeclRefExpr *PtrRHS; // the RHS pointer expression in `PA`
2056
2057public:
2058 PtrToPtrAssignmentGadget(const MatchResult &Result)
2059 : FixableGadget(Kind::PtrToPtrAssignment),
2060 PtrLHS(Result.getNodeAs<DeclRefExpr>(PointerAssignLHSTag)),
2061 PtrRHS(Result.getNodeAs<DeclRefExpr>(PointerAssignRHSTag)) {}
2062
2063 static bool classof(const Gadget *G) {
2064 return G->getKind() == Kind::PtrToPtrAssignment;
2065 }
2066
2067 static bool matches(const Stmt *S,
2068 llvm::SmallVectorImpl<MatchResult> &Results) {
2069 size_t SizeBefore = Results.size();
2070 findStmtsInUnspecifiedUntypedContext(S, [&Results](const Stmt *S) {
2071 const auto *BO = dyn_cast<BinaryOperator>(S);
2072 if (!BO || BO->getOpcode() != BO_Assign)
2073 return;
2074 const auto *RHS = BO->getRHS()->IgnoreParenImpCasts();
2075 if (const auto *RHSRef = dyn_cast<DeclRefExpr>(RHS);
2076 !RHSRef || !hasPointerType(*RHSRef) ||
2077 !isSupportedVariable(*RHSRef)) {
2078 return;
2079 }
2080 const auto *LHS = BO->getLHS();
2081 if (const auto *LHSRef = dyn_cast<DeclRefExpr>(LHS);
2082 !LHSRef || !hasPointerType(*LHSRef) ||
2083 !isSupportedVariable(*LHSRef)) {
2084 return;
2085 }
2086 MatchResult R;
2087 R.addNode(PointerAssignLHSTag, DynTypedNode::create(*LHS));
2088 R.addNode(PointerAssignRHSTag, DynTypedNode::create(*RHS));
2089 Results.emplace_back(std::move(R));
2090 });
2091 return SizeBefore != Results.size();
2092 }
2093
2094 virtual std::optional<FixItList>
2095 getFixits(const FixitStrategy &S) const override;
2096 SourceLocation getSourceLoc() const override { return PtrLHS->getBeginLoc(); }
2097
2098 virtual DeclUseList getClaimedVarUseSites() const override {
2099 return DeclUseList{PtrLHS, PtrRHS};
2100 }
2101
2102 virtual std::optional<std::pair<const VarDecl *, const VarDecl *>>
2103 getStrategyImplications() const override {
2104 return std::make_pair(cast<VarDecl>(PtrLHS->getDecl()),
2105 cast<VarDecl>(PtrRHS->getDecl()));
2106 }
2107};
2108
2109/// An assignment expression of the form:
2110/// \code
2111/// ptr = array;
2112/// \endcode
2113/// where `p` is a pointer and `array` is a constant size array.
2114class CArrayToPtrAssignmentGadget : public FixableGadget {
2115private:
2116 static constexpr const char *const PointerAssignLHSTag = "ptrLHS";
2117 static constexpr const char *const PointerAssignRHSTag = "ptrRHS";
2118 const DeclRefExpr *PtrLHS; // the LHS pointer expression in `PA`
2119 const DeclRefExpr *PtrRHS; // the RHS pointer expression in `PA`
2120
2121public:
2122 CArrayToPtrAssignmentGadget(const MatchResult &Result)
2123 : FixableGadget(Kind::CArrayToPtrAssignment),
2124 PtrLHS(Result.getNodeAs<DeclRefExpr>(PointerAssignLHSTag)),
2125 PtrRHS(Result.getNodeAs<DeclRefExpr>(PointerAssignRHSTag)) {}
2126
2127 static bool classof(const Gadget *G) {
2128 return G->getKind() == Kind::CArrayToPtrAssignment;
2129 }
2130
2131 static bool matches(const Stmt *S,
2132 llvm::SmallVectorImpl<MatchResult> &Results) {
2133 size_t SizeBefore = Results.size();
2134 findStmtsInUnspecifiedUntypedContext(S, [&Results](const Stmt *S) {
2135 const auto *BO = dyn_cast<BinaryOperator>(S);
2136 if (!BO || BO->getOpcode() != BO_Assign)
2137 return;
2138 const auto *RHS = BO->getRHS()->IgnoreParenImpCasts();
2139 if (const auto *RHSRef = dyn_cast<DeclRefExpr>(RHS);
2140 !RHSRef ||
2141 !isa<ConstantArrayType>(RHSRef->getType().getCanonicalType()) ||
2142 !isSupportedVariable(*RHSRef)) {
2143 return;
2144 }
2145 const auto *LHS = BO->getLHS();
2146 if (const auto *LHSRef = dyn_cast<DeclRefExpr>(LHS);
2147 !LHSRef || !hasPointerType(*LHSRef) ||
2148 !isSupportedVariable(*LHSRef)) {
2149 return;
2150 }
2151 MatchResult R;
2152 R.addNode(PointerAssignLHSTag, DynTypedNode::create(*LHS));
2153 R.addNode(PointerAssignRHSTag, DynTypedNode::create(*RHS));
2154 Results.emplace_back(std::move(R));
2155 });
2156 return SizeBefore != Results.size();
2157 }
2158
2159 virtual std::optional<FixItList>
2160 getFixits(const FixitStrategy &S) const override;
2161 SourceLocation getSourceLoc() const override { return PtrLHS->getBeginLoc(); }
2162
2163 virtual DeclUseList getClaimedVarUseSites() const override {
2164 return DeclUseList{PtrLHS, PtrRHS};
2165 }
2166
2167 virtual std::optional<std::pair<const VarDecl *, const VarDecl *>>
2168 getStrategyImplications() const override {
2169 return {};
2170 }
2171};
2172
2173/// A call of a function or method that performs unchecked buffer operations
2174/// over one of its pointer parameters.
2175class UnsafeBufferUsageAttrGadget : public WarningGadget {
2176 constexpr static const char *const OpTag = "attr_expr";
2177 const Expr *Op;
2178
2179public:
2180 UnsafeBufferUsageAttrGadget(const MatchResult &Result)
2181 : WarningGadget(Kind::UnsafeBufferUsageAttr),
2182 Op(Result.getNodeAs<Expr>(OpTag)) {}
2183
2184 static bool classof(const Gadget *G) {
2185 return G->getKind() == Kind::UnsafeBufferUsageAttr;
2186 }
2187
2188 static bool matches(const Stmt *S, const ASTContext &Ctx,
2190 if (auto *CE = dyn_cast<CallExpr>(S)) {
2191 if (CE->getDirectCallee() &&
2192 CE->getDirectCallee()->hasAttr<UnsafeBufferUsageAttr>()) {
2193 Result.addNode(OpTag, DynTypedNode::create(*CE));
2194 return true;
2195 }
2196 }
2197 if (auto *ME = dyn_cast<MemberExpr>(S)) {
2198 if (!isa<FieldDecl>(ME->getMemberDecl()))
2199 return false;
2200 if (ME->getMemberDecl()->hasAttr<UnsafeBufferUsageAttr>()) {
2201 Result.addNode(OpTag, DynTypedNode::create(*ME));
2202 return true;
2203 }
2204 }
2205 return false;
2206 }
2207
2208 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
2209 bool IsRelatedToDecl,
2210 ASTContext &Ctx) const override {
2211 Handler.handleUnsafeOperation(Op, IsRelatedToDecl, Ctx);
2212 }
2213 SourceLocation getSourceLoc() const override { return Op->getBeginLoc(); }
2214
2215 DeclUseList getClaimedVarUseSites() const override { return {}; }
2216
2217 SmallVector<const Expr *, 1> getUnsafePtrs() const override { return {}; }
2218};
2219
2220/// A call of a constructor that performs unchecked buffer operations
2221/// over one of its pointer parameters, or constructs a class object that will
2222/// perform buffer operations that depend on the correctness of the parameters.
2223class UnsafeBufferUsageCtorAttrGadget : public WarningGadget {
2224 constexpr static const char *const OpTag = "cxx_construct_expr";
2225 const CXXConstructExpr *Op;
2226
2227public:
2228 UnsafeBufferUsageCtorAttrGadget(const MatchResult &Result)
2229 : WarningGadget(Kind::UnsafeBufferUsageCtorAttr),
2230 Op(Result.getNodeAs<CXXConstructExpr>(OpTag)) {}
2231
2232 static bool classof(const Gadget *G) {
2233 return G->getKind() == Kind::UnsafeBufferUsageCtorAttr;
2234 }
2235
2236 static bool matches(const Stmt *S, ASTContext &Ctx, MatchResult &Result) {
2237 const auto *CE = dyn_cast<CXXConstructExpr>(S);
2238 if (!CE || !CE->getConstructor()->hasAttr<UnsafeBufferUsageAttr>())
2239 return false;
2240 // std::span(ptr, size) ctor is handled by SpanTwoParamConstructorGadget.
2241 MatchResult Tmp;
2242 if (SpanTwoParamConstructorGadget::matches(CE, Ctx, Tmp))
2243 return false;
2244 Result.addNode(OpTag, DynTypedNode::create(*CE));
2245 return true;
2246 }
2247
2248 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
2249 bool IsRelatedToDecl,
2250 ASTContext &Ctx) const override {
2251 Handler.handleUnsafeOperation(Op, IsRelatedToDecl, Ctx);
2252 }
2253 SourceLocation getSourceLoc() const override { return Op->getBeginLoc(); }
2254
2255 DeclUseList getClaimedVarUseSites() const override { return {}; }
2256
2257 SmallVector<const Expr *, 1> getUnsafePtrs() const override { return {}; }
2258};
2259
2260// Warning gadget for unsafe invocation of span::data method.
2261// Triggers when the pointer returned by the invocation is immediately
2262// cast to a larger type.
2263
2264class DataInvocationGadget : public WarningGadget {
2265 constexpr static const char *const OpTag = "data_invocation_expr";
2266 const ExplicitCastExpr *Op;
2267
2268public:
2269 DataInvocationGadget(const MatchResult &Result)
2270 : WarningGadget(Kind::DataInvocation),
2271 Op(Result.getNodeAs<ExplicitCastExpr>(OpTag)) {}
2272
2273 static bool classof(const Gadget *G) {
2274 return G->getKind() == Kind::DataInvocation;
2275 }
2276
2277 static bool matches(const Stmt *S, const ASTContext &Ctx,
2279 auto *CE = dyn_cast<ExplicitCastExpr>(S);
2280 if (!CE)
2281 return false;
2282 for (auto *Child : CE->children()) {
2283 if (auto *MCE = dyn_cast<CXXMemberCallExpr>(Child);
2284 MCE && isDataFunction(MCE)) {
2285 Result.addNode(OpTag, DynTypedNode::create(*CE));
2286 return true;
2287 }
2288 if (auto *Paren = dyn_cast<ParenExpr>(Child)) {
2289 if (auto *MCE = dyn_cast<CXXMemberCallExpr>(Paren->getSubExpr());
2290 MCE && isDataFunction(MCE)) {
2291 Result.addNode(OpTag, DynTypedNode::create(*CE));
2292 return true;
2293 }
2294 }
2295 }
2296 return false;
2297 }
2298
2299 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
2300 bool IsRelatedToDecl,
2301 ASTContext &Ctx) const override {
2302 Handler.handleUnsafeOperation(Op, IsRelatedToDecl, Ctx);
2303 }
2304 SourceLocation getSourceLoc() const override { return Op->getBeginLoc(); }
2305
2306 DeclUseList getClaimedVarUseSites() const override { return {}; }
2307
2308private:
2309 static bool isDataFunction(const CXXMemberCallExpr *call) {
2310 if (!call)
2311 return false;
2312 auto *callee = call->getDirectCallee();
2313 if (!callee || !isa<CXXMethodDecl>(callee))
2314 return false;
2315 auto *method = cast<CXXMethodDecl>(callee);
2316 if (method->getNameAsString() == "data" &&
2317 method->getParent()->isInStdNamespace() &&
2318 llvm::is_contained({SIZED_CONTAINER_OR_VIEW_LIST},
2319 method->getParent()->getName()))
2320 return true;
2321 return false;
2322 }
2323
2324 SmallVector<const Expr *, 1> getUnsafePtrs() const override { return {}; }
2325};
2326
2327class UnsafeLibcFunctionCallGadget : public WarningGadget {
2328 const CallExpr *const Call;
2329 const Expr *UnsafeArg = nullptr;
2330 constexpr static const char *const Tag = "UnsafeLibcFunctionCall";
2331 // Extra tags for additional information:
2332 constexpr static const char *const UnsafeSprintfTag =
2333 "UnsafeLibcFunctionCall_sprintf";
2334 constexpr static const char *const UnsafeSizedByTag =
2335 "UnsafeLibcFunctionCall_sized_by";
2336 constexpr static const char *const UnsafeStringTag =
2337 "UnsafeLibcFunctionCall_string";
2338 constexpr static const char *const UnsafeVaListTag =
2339 "UnsafeLibcFunctionCall_va_list";
2340
2341public:
2342 enum UnsafeKind {
2343 OTHERS = 0, // no specific information, the callee function is unsafe
2344 SPRINTF = 1, // never call `-sprintf`s, call `-snprintf`s instead.
2345 SIZED_BY =
2346 2, // the first two arguments of `snprintf` function have
2347 // "__sized_by" relation but they do not conform to safe patterns
2348 STRING = 3, // an argument is a pointer-to-char-as-string but does not
2349 // guarantee null-termination
2350 VA_LIST = 4, // one of the `-printf`s function that take va_list, which is
2351 // considered unsafe as it is not compile-time check
2352 FORMAT_ATTR = 8, // flag: the callee has the format attribute
2353 } WarnedFunKind = OTHERS;
2354
2355 UnsafeLibcFunctionCallGadget(const MatchResult &Result)
2356 : WarningGadget(Kind::UnsafeLibcFunctionCall),
2357 Call(Result.getNodeAs<CallExpr>(Tag)) {
2358 if (Result.getNodeAs<Decl>(UnsafeSprintfTag))
2359 WarnedFunKind = SPRINTF;
2360 else if (auto *E = Result.getNodeAs<Expr>(UnsafeStringTag)) {
2361 WarnedFunKind = STRING;
2362 UnsafeArg = E;
2363 } else if (Result.getNodeAs<CallExpr>(UnsafeSizedByTag)) {
2364 WarnedFunKind = SIZED_BY;
2365 UnsafeArg = Call->getArg(0);
2366 } else if (Result.getNodeAs<Decl>(UnsafeVaListTag))
2367 WarnedFunKind = VA_LIST;
2368 }
2369
2370 static bool matches(const Stmt *S, ASTContext &Ctx,
2371 const UnsafeBufferUsageHandler *Handler,
2373 if (ignoreUnsafeLibcCall(Ctx, *S, Handler))
2374 return false;
2375 const auto *CE = dyn_cast<CallExpr>(S);
2376 if (!CE)
2377 return false;
2378 const auto *FD = CE->getDirectCallee();
2379 if (!FD)
2380 return false;
2381
2382 const bool IsGlobalAndNotInAnyNamespace =
2383 FD->isGlobal() && !FD->getEnclosingNamespaceContext()->isNamespace();
2384
2385 // A libc function must either be in the std:: namespace or a global
2386 // function that is not in any namespace:
2387 if (!FD->isInStdNamespace() && !IsGlobalAndNotInAnyNamespace)
2388 return false;
2389 // If the call has a sole null-terminated argument, e.g., strlen,
2390 // printf, atoi, we consider it safe:
2391 if (CE->getNumArgs() == 1 && isNullTermPointer(CE->getArg(0), Ctx))
2392 return false;
2393
2394 const bool isSingleStringLiteralArg =
2395 CE->getNumArgs() == 1 &&
2396 isa<clang::StringLiteral>(CE->getArg(0)->IgnoreParenImpCasts());
2397 if (!isSingleStringLiteralArg) {
2398 // (unless the call has a sole string literal argument):
2400 Result.addNode(Tag, DynTypedNode::create(*CE));
2401 return true;
2402 }
2403 if (libc_func_matchers::isUnsafeMemset(*CE, Ctx)) {
2404 Result.addNode(Tag, DynTypedNode::create(*CE));
2405 return true;
2406 }
2408 Result.addNode(Tag, DynTypedNode::create(*CE));
2409 Result.addNode(UnsafeVaListTag, DynTypedNode::create(*FD));
2410 return true;
2411 }
2413 Result.addNode(Tag, DynTypedNode::create(*CE));
2414 Result.addNode(UnsafeSprintfTag, DynTypedNode::create(*FD));
2415 return true;
2416 }
2417 }
2420 Result.addNode(Tag, DynTypedNode::create(*CE));
2421 Result.addNode(UnsafeSizedByTag, DynTypedNode::create(*CE));
2422 return true;
2423 }
2425 UnsafeStringTag)) {
2426 Result.addNode(Tag, DynTypedNode::create(*CE));
2427 return true;
2428 }
2429 }
2430 return false;
2431 }
2432
2433 const Stmt *getBaseStmt() const { return Call; }
2434
2435 SourceLocation getSourceLoc() const override { return Call->getBeginLoc(); }
2436
2437 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
2438 bool IsRelatedToDecl,
2439 ASTContext &Ctx) const override {
2440 Handler.handleUnsafeLibcCall(Call, WarnedFunKind, Ctx, UnsafeArg);
2441 }
2442
2443 DeclUseList getClaimedVarUseSites() const override { return {}; }
2444
2445 SmallVector<const Expr *, 1> getUnsafePtrs() const override { return {}; }
2446};
2447
2448class UnsafeFormatAttributedFunctionCallGadget : public WarningGadget {
2449 const CallExpr *const Call;
2450 const Expr *UnsafeArg = nullptr;
2451 constexpr static const char *const Tag = "UnsafeFormatAttributedFunctionCall";
2452 constexpr static const char *const UnsafeStringTag =
2453 "UnsafeFormatAttributedFunctionCall_string";
2454
2455public:
2456 UnsafeFormatAttributedFunctionCallGadget(const MatchResult &Result)
2457 : WarningGadget(Kind::UnsafeLibcFunctionCall),
2458 Call(Result.getNodeAs<CallExpr>(Tag)),
2459 UnsafeArg(Result.getNodeAs<Expr>(UnsafeStringTag)) {}
2460
2461 static bool matches(const Stmt *S, ASTContext &Ctx,
2462 const UnsafeBufferUsageHandler *Handler,
2464 if (ignoreUnsafeLibcCall(Ctx, *S, Handler))
2465 return false;
2466 auto *CE = dyn_cast<CallExpr>(S);
2467 if (!CE || !CE->getDirectCallee())
2468 return false;
2469 const FunctionDecl *FD = CE->getDirectCallee();
2470 if (!FD)
2471 return false;
2472
2473 const FormatAttr *Attr = nullptr;
2474 bool IsPrintf = false;
2475 bool AnyAttr = llvm::any_of(
2476 FD->specific_attrs<FormatAttr>(),
2477 [&Attr, &IsPrintf](const FormatAttr *FA) -> bool {
2478 if (const auto *II = FA->getType()) {
2479 if (II->getName() == "printf" || II->getName() == "scanf") {
2480 Attr = FA;
2481 IsPrintf = II->getName() == "printf";
2482 return true;
2483 }
2484 }
2485 return false;
2486 });
2487 const Expr *UnsafeArg;
2488
2489 if (!AnyAttr)
2490 return false;
2491
2492 // FormatAttribute indexes are 1-based:
2493 unsigned FmtIdx = Attr->getFormatIdx() - 1;
2494 std::optional<unsigned> FmtArgIdx = Attr->getFirstArg() - 1;
2495
2496 if (isa<CXXMemberCallExpr>(CE)) {
2497 // For CXX member calls, attribute parameters are specified as if there is
2498 // an implicit "this". The implicit "this" is invisible through CallExpr
2499 // `CE`. (What makes it even less ergonomic is that the
2500 // implicit "this" is visible through CallExpr `CE` for CXX operator
2501 // calls!)
2502 --FmtIdx;
2503 --*FmtArgIdx;
2504 } else if (CE->getStmtClass() != Stmt::CallExprClass &&
2506 return false; // Ignore unsupported CallExpr subclasses
2507 if (*FmtArgIdx >= CE->getNumArgs())
2508 // Format arguments are allowed to be absent when variadic parameter is
2509 // used. So we need to check if those arguments exist. Moreover, when
2510 // variadic parameter is NOT used, `Attr->getFirstArg()` could be an
2511 // out-of-bound value. E.g.,
2512 // clang does not complain about `__attribute__((__format__(__printf__, 2,
2513 // 99))) void f(int, char *);`.
2514 FmtArgIdx = std::nullopt;
2515
2516 if (AnyAttr && !IsPrintf && FmtArgIdx) {
2517 // For scanf-like functions, any format argument is considered unsafe:
2518 Result.addNode(Tag, DynTypedNode::create(*CE));
2519 return true;
2520 }
2521 // For printf-like functions:
2523 Ctx, CE, UnsafeArg, FmtIdx, FmtArgIdx)) {
2524 Result.addNode(Tag, DynTypedNode::create(*CE));
2525 Result.addNode(UnsafeStringTag, DynTypedNode::create(*UnsafeArg));
2526 return true;
2527 }
2528 return false;
2529 }
2530
2531 const Stmt *getBaseStmt() const { return Call; }
2532
2533 SourceLocation getSourceLoc() const override { return Call->getBeginLoc(); }
2534
2535 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
2536 bool IsRelatedToDecl,
2537 ASTContext &Ctx) const override {
2538 if (UnsafeArg)
2539 Handler.handleUnsafeLibcCall(
2540 Call,
2541 UnsafeLibcFunctionCallGadget::UnsafeKind::STRING |
2542 UnsafeLibcFunctionCallGadget::UnsafeKind::FORMAT_ATTR,
2543 Ctx, UnsafeArg);
2544 else
2545 Handler.handleUnsafeLibcCall(
2546 Call,
2547 UnsafeLibcFunctionCallGadget::UnsafeKind::OTHERS |
2548 UnsafeLibcFunctionCallGadget::UnsafeKind::FORMAT_ATTR,
2549 Ctx);
2550 }
2551
2552 DeclUseList getClaimedVarUseSites() const override { return {}; }
2553
2554 SmallVector<const Expr *, 1> getUnsafePtrs() const override { return {}; }
2555};
2556
2557// Represents expressions of the form `DRE[*]` in the Unspecified Lvalue
2558// Context (see `findStmtsInUnspecifiedLvalueContext`).
2559// Note here `[]` is the built-in subscript operator.
2560class ULCArraySubscriptGadget : public FixableGadget {
2561private:
2562 static constexpr const char *const ULCArraySubscriptTag =
2563 "ArraySubscriptUnderULC";
2564 const ArraySubscriptExpr *Node;
2565
2566public:
2567 ULCArraySubscriptGadget(const MatchResult &Result)
2568 : FixableGadget(Kind::ULCArraySubscript),
2569 Node(Result.getNodeAs<ArraySubscriptExpr>(ULCArraySubscriptTag)) {
2570 assert(Node != nullptr && "Expecting a non-null matching result");
2571 }
2572
2573 static bool classof(const Gadget *G) {
2574 return G->getKind() == Kind::ULCArraySubscript;
2575 }
2576
2577 static bool matches(const Stmt *S,
2578 llvm::SmallVectorImpl<MatchResult> &Results) {
2579 size_t SizeBefore = Results.size();
2580 findStmtsInUnspecifiedLvalueContext(S, [&Results](const Expr *E) {
2581 const auto *ASE = dyn_cast<ArraySubscriptExpr>(E);
2582 if (!ASE)
2583 return;
2584 const auto *DRE =
2585 dyn_cast<DeclRefExpr>(ASE->getBase()->IgnoreParenImpCasts());
2586 if (!DRE || !(hasPointerType(*DRE) || hasArrayType(*DRE)) ||
2587 !isSupportedVariable(*DRE))
2588 return;
2589 MatchResult R;
2590 R.addNode(ULCArraySubscriptTag, DynTypedNode::create(*ASE));
2591 Results.emplace_back(std::move(R));
2592 });
2593 return SizeBefore != Results.size();
2594 }
2595
2596 virtual std::optional<FixItList>
2597 getFixits(const FixitStrategy &S) const override;
2598 SourceLocation getSourceLoc() const override { return Node->getBeginLoc(); }
2599
2600 virtual DeclUseList getClaimedVarUseSites() const override {
2601 if (const auto *DRE =
2602 dyn_cast<DeclRefExpr>(Node->getBase()->IgnoreImpCasts())) {
2603 return {DRE};
2604 }
2605 return {};
2606 }
2607};
2608
2609// Fixable gadget to handle stand alone pointers of the form `UPC(DRE)` in the
2610// unspecified pointer context (findStmtsInUnspecifiedPointerContext). The
2611// gadget emits fixit of the form `UPC(DRE.data())`.
2612class UPCStandalonePointerGadget : public FixableGadget {
2613private:
2614 static constexpr const char *const DeclRefExprTag = "StandalonePointer";
2615 const DeclRefExpr *Node;
2616
2617public:
2618 UPCStandalonePointerGadget(const MatchResult &Result)
2619 : FixableGadget(Kind::UPCStandalonePointer),
2620 Node(Result.getNodeAs<DeclRefExpr>(DeclRefExprTag)) {
2621 assert(Node != nullptr && "Expecting a non-null matching result");
2622 }
2623
2624 static bool classof(const Gadget *G) {
2625 return G->getKind() == Kind::UPCStandalonePointer;
2626 }
2627
2628 static bool matches(const Stmt *S,
2629 llvm::SmallVectorImpl<MatchResult> &Results) {
2630 size_t SizeBefore = Results.size();
2631 findStmtsInUnspecifiedPointerContext(S, [&Results](const Stmt *S) {
2632 auto *E = dyn_cast<Expr>(S);
2633 if (!E)
2634 return;
2635 const auto *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts());
2636 if (!DRE || (!hasPointerType(*DRE) && !hasArrayType(*DRE)) ||
2637 !isSupportedVariable(*DRE))
2638 return;
2639 MatchResult R;
2640 R.addNode(DeclRefExprTag, DynTypedNode::create(*DRE));
2641 Results.emplace_back(std::move(R));
2642 });
2643 return SizeBefore != Results.size();
2644 }
2645
2646 virtual std::optional<FixItList>
2647 getFixits(const FixitStrategy &S) const override;
2648 SourceLocation getSourceLoc() const override { return Node->getBeginLoc(); }
2649
2650 virtual DeclUseList getClaimedVarUseSites() const override { return {Node}; }
2651};
2652
2653class PointerDereferenceGadget : public FixableGadget {
2654 static constexpr const char *const BaseDeclRefExprTag = "BaseDRE";
2655 static constexpr const char *const OperatorTag = "op";
2656
2657 const DeclRefExpr *BaseDeclRefExpr = nullptr;
2658 const UnaryOperator *Op = nullptr;
2659
2660public:
2661 PointerDereferenceGadget(const MatchResult &Result)
2662 : FixableGadget(Kind::PointerDereference),
2663 BaseDeclRefExpr(Result.getNodeAs<DeclRefExpr>(BaseDeclRefExprTag)),
2664 Op(Result.getNodeAs<UnaryOperator>(OperatorTag)) {}
2665
2666 static bool classof(const Gadget *G) {
2667 return G->getKind() == Kind::PointerDereference;
2668 }
2669
2670 static bool matches(const Stmt *S,
2671 llvm::SmallVectorImpl<MatchResult> &Results) {
2672 size_t SizeBefore = Results.size();
2673 findStmtsInUnspecifiedLvalueContext(S, [&Results](const Stmt *S) {
2674 const auto *UO = dyn_cast<UnaryOperator>(S);
2675 if (!UO || UO->getOpcode() != UO_Deref)
2676 return;
2677 const Expr *CE = UO->getSubExpr();
2678 if (!CE)
2679 return;
2680 CE = CE->IgnoreParenImpCasts();
2681 const auto *DRE = dyn_cast<DeclRefExpr>(CE);
2682 if (!DRE || !isSupportedVariable(*DRE))
2683 return;
2684 MatchResult R;
2685 R.addNode(BaseDeclRefExprTag, DynTypedNode::create(*DRE));
2686 R.addNode(OperatorTag, DynTypedNode::create(*UO));
2687 Results.emplace_back(std::move(R));
2688 });
2689 return SizeBefore != Results.size();
2690 }
2691
2692 DeclUseList getClaimedVarUseSites() const override {
2693 return {BaseDeclRefExpr};
2694 }
2695
2696 virtual std::optional<FixItList>
2697 getFixits(const FixitStrategy &S) const override;
2698 SourceLocation getSourceLoc() const override { return Op->getBeginLoc(); }
2699};
2700
2701// Represents expressions of the form `&DRE[any]` in the Unspecified Pointer
2702// Context (see `findStmtsInUnspecifiedPointerContext`).
2703// Note here `[]` is the built-in subscript operator.
2704class UPCAddressofArraySubscriptGadget : public FixableGadget {
2705private:
2706 static constexpr const char *const UPCAddressofArraySubscriptTag =
2707 "AddressofArraySubscriptUnderUPC";
2708 const UnaryOperator *Node; // the `&DRE[any]` node
2709
2710public:
2711 UPCAddressofArraySubscriptGadget(const MatchResult &Result)
2712 : FixableGadget(Kind::ULCArraySubscript),
2713 Node(Result.getNodeAs<UnaryOperator>(UPCAddressofArraySubscriptTag)) {
2714 assert(Node != nullptr && "Expecting a non-null matching result");
2715 }
2716
2717 static bool classof(const Gadget *G) {
2718 return G->getKind() == Kind::UPCAddressofArraySubscript;
2719 }
2720
2721 static bool matches(const Stmt *S,
2722 llvm::SmallVectorImpl<MatchResult> &Results) {
2723 size_t SizeBefore = Results.size();
2724 findStmtsInUnspecifiedPointerContext(S, [&Results](const Stmt *S) {
2725 auto *E = dyn_cast<Expr>(S);
2726 if (!E)
2727 return;
2728 const auto *UO = dyn_cast<UnaryOperator>(E->IgnoreImpCasts());
2729 if (!UO || UO->getOpcode() != UO_AddrOf)
2730 return;
2731 const auto *ASE = dyn_cast<ArraySubscriptExpr>(UO->getSubExpr());
2732 if (!ASE)
2733 return;
2734 const auto *DRE =
2735 dyn_cast<DeclRefExpr>(ASE->getBase()->IgnoreParenImpCasts());
2736 if (!DRE || !isSupportedVariable(*DRE))
2737 return;
2738 MatchResult R;
2739 R.addNode(UPCAddressofArraySubscriptTag, DynTypedNode::create(*UO));
2740 Results.emplace_back(std::move(R));
2741 });
2742 return SizeBefore != Results.size();
2743 }
2744
2745 virtual std::optional<FixItList>
2746 getFixits(const FixitStrategy &) const override;
2747 SourceLocation getSourceLoc() const override { return Node->getBeginLoc(); }
2748
2749 virtual DeclUseList getClaimedVarUseSites() const override {
2750 const auto *ArraySubst = cast<ArraySubscriptExpr>(Node->getSubExpr());
2751 const auto *DRE =
2752 cast<DeclRefExpr>(ArraySubst->getBase()->IgnoreParenImpCasts());
2753 return {DRE};
2754 }
2755};
2756} // namespace
2757
2758namespace {
2759// An auxiliary tracking facility for the fixit analysis. It helps connect
2760// declarations to its uses and make sure we've covered all uses with our
2761// analysis before we try to fix the declaration.
2762class DeclUseTracker {
2763 using UseSetTy = llvm::SmallPtrSet<const DeclRefExpr *, 16>;
2764 using DefMapTy = llvm::DenseMap<const VarDecl *, const DeclStmt *>;
2765
2766 // Allocate on the heap for easier move.
2767 std::unique_ptr<UseSetTy> Uses{std::make_unique<UseSetTy>()};
2768 DefMapTy Defs{};
2769
2770public:
2771 DeclUseTracker() = default;
2772 DeclUseTracker(const DeclUseTracker &) = delete; // Let's avoid copies.
2773 DeclUseTracker &operator=(const DeclUseTracker &) = delete;
2774 DeclUseTracker(DeclUseTracker &&) = default;
2775 DeclUseTracker &operator=(DeclUseTracker &&) = default;
2776
2777 // Start tracking a freshly discovered DRE.
2778 void discoverUse(const DeclRefExpr *DRE) { Uses->insert(DRE); }
2779
2780 // Stop tracking the DRE as it's been fully figured out.
2781 void claimUse(const DeclRefExpr *DRE) {
2782 assert(Uses->count(DRE) &&
2783 "DRE not found or claimed by multiple matchers!");
2784 Uses->erase(DRE);
2785 }
2786
2787 // A variable is unclaimed if at least one use is unclaimed.
2788 bool hasUnclaimedUses(const VarDecl *VD) const {
2789 // FIXME: Can this be less linear? Maybe maintain a map from VDs to DREs?
2790 return any_of(*Uses, [VD](const DeclRefExpr *DRE) {
2791 return DRE->getDecl()->getCanonicalDecl() == VD->getCanonicalDecl();
2792 });
2793 }
2794
2795 UseSetTy getUnclaimedUses(const VarDecl *VD) const {
2796 UseSetTy ReturnSet;
2797 for (auto use : *Uses) {
2798 if (use->getDecl()->getCanonicalDecl() == VD->getCanonicalDecl()) {
2799 ReturnSet.insert(use);
2800 }
2801 }
2802 return ReturnSet;
2803 }
2804
2805 void discoverDecl(const DeclStmt *DS) {
2806 for (const Decl *D : DS->decls()) {
2807 if (const auto *VD = dyn_cast<VarDecl>(D)) {
2808 // FIXME: Assertion temporarily disabled due to a bug in
2809 // ASTMatcher internal behavior in presence of GNU
2810 // statement-expressions. We need to properly investigate this
2811 // because it can screw up our algorithm in other ways.
2812 // assert(Defs.count(VD) == 0 && "Definition already discovered!");
2813 Defs[VD] = DS;
2814 }
2815 }
2816 }
2817
2818 const DeclStmt *lookupDecl(const VarDecl *VD) const {
2819 return Defs.lookup(VD);
2820 }
2821};
2822} // namespace
2823
2824// Representing a pointer type expression of the form `++Ptr` in an Unspecified
2825// Pointer Context (UPC):
2826class UPCPreIncrementGadget : public FixableGadget {
2827private:
2828 static constexpr const char *const UPCPreIncrementTag =
2829 "PointerPreIncrementUnderUPC";
2830 const UnaryOperator *Node; // the `++Ptr` node
2831
2832public:
2833 UPCPreIncrementGadget(const MatchResult &Result)
2834 : FixableGadget(Kind::UPCPreIncrement),
2835 Node(Result.getNodeAs<UnaryOperator>(UPCPreIncrementTag)) {
2836 assert(Node != nullptr && "Expecting a non-null matching result");
2837 }
2838
2839 static bool classof(const Gadget *G) {
2840 return G->getKind() == Kind::UPCPreIncrement;
2841 }
2842
2843 static bool matches(const Stmt *S,
2845 // Note here we match `++Ptr` for any expression `Ptr` of pointer type.
2846 // Although currently we can only provide fix-its when `Ptr` is a DRE, we
2847 // can have the matcher be general, so long as `getClaimedVarUseSites` does
2848 // things right.
2849 size_t SizeBefore = Results.size();
2850 findStmtsInUnspecifiedPointerContext(S, [&Results](const Stmt *S) {
2851 auto *E = dyn_cast<Expr>(S);
2852 if (!E)
2853 return;
2854 const auto *UO = dyn_cast<UnaryOperator>(E->IgnoreImpCasts());
2855 if (!UO || UO->getOpcode() != UO_PreInc)
2856 return;
2857 const auto *DRE = dyn_cast<DeclRefExpr>(UO->getSubExpr());
2858 if (!DRE || !isSupportedVariable(*DRE))
2859 return;
2860 MatchResult R;
2861 R.addNode(UPCPreIncrementTag, DynTypedNode::create(*UO));
2862 Results.emplace_back(std::move(R));
2863 });
2864 return SizeBefore != Results.size();
2865 }
2866
2867 virtual std::optional<FixItList>
2868 getFixits(const FixitStrategy &S) const override;
2869 SourceLocation getSourceLoc() const override { return Node->getBeginLoc(); }
2870
2871 virtual DeclUseList getClaimedVarUseSites() const override {
2872 return {dyn_cast<DeclRefExpr>(Node->getSubExpr())};
2873 }
2874};
2875
2876// Representing a pointer type expression of the form `Ptr += n` in an
2877// Unspecified Untyped Context (UUC):
2878class UUCAddAssignGadget : public FixableGadget {
2879private:
2880 static constexpr const char *const UUCAddAssignTag =
2881 "PointerAddAssignUnderUUC";
2882 static constexpr const char *const OffsetTag = "Offset";
2883
2884 const BinaryOperator *Node; // the `Ptr += n` node
2885 const Expr *Offset = nullptr;
2886
2887public:
2888 UUCAddAssignGadget(const MatchResult &Result)
2889 : FixableGadget(Kind::UUCAddAssign),
2890 Node(Result.getNodeAs<BinaryOperator>(UUCAddAssignTag)),
2891 Offset(Result.getNodeAs<Expr>(OffsetTag)) {
2892 assert(Node != nullptr && "Expecting a non-null matching result");
2893 }
2894
2895 static bool classof(const Gadget *G) {
2896 return G->getKind() == Kind::UUCAddAssign;
2897 }
2898
2899 static bool matches(const Stmt *S,
2901 size_t SizeBefore = Results.size();
2902 findStmtsInUnspecifiedUntypedContext(S, [&Results](const Stmt *S) {
2903 const auto *E = dyn_cast<Expr>(S);
2904 if (!E)
2905 return;
2906 const auto *BO = dyn_cast<BinaryOperator>(E->IgnoreImpCasts());
2907 if (!BO || BO->getOpcode() != BO_AddAssign)
2908 return;
2909 const auto *DRE = dyn_cast<DeclRefExpr>(BO->getLHS());
2910 if (!DRE || !hasPointerType(*DRE) || !isSupportedVariable(*DRE))
2911 return;
2912 MatchResult R;
2913 R.addNode(UUCAddAssignTag, DynTypedNode::create(*BO));
2914 R.addNode(OffsetTag, DynTypedNode::create(*BO->getRHS()));
2915 Results.emplace_back(std::move(R));
2916 });
2917 return SizeBefore != Results.size();
2918 }
2919
2920 virtual std::optional<FixItList>
2921 getFixits(const FixitStrategy &S) const override;
2922 SourceLocation getSourceLoc() const override { return Node->getBeginLoc(); }
2923
2924 virtual DeclUseList getClaimedVarUseSites() const override {
2925 return {dyn_cast<DeclRefExpr>(Node->getLHS())};
2926 }
2927};
2928
2929// Representing a fixable expression of the form `*(ptr + 123)` or `*(123 +
2930// ptr)`:
2931class DerefSimplePtrArithFixableGadget : public FixableGadget {
2932 static constexpr const char *const BaseDeclRefExprTag = "BaseDRE";
2933 static constexpr const char *const DerefOpTag = "DerefOp";
2934 static constexpr const char *const AddOpTag = "AddOp";
2935 static constexpr const char *const OffsetTag = "Offset";
2936
2937 const DeclRefExpr *BaseDeclRefExpr = nullptr;
2938 const UnaryOperator *DerefOp = nullptr;
2939 const BinaryOperator *AddOp = nullptr;
2940 const IntegerLiteral *Offset = nullptr;
2941
2942public:
2944 : FixableGadget(Kind::DerefSimplePtrArithFixable),
2945 BaseDeclRefExpr(Result.getNodeAs<DeclRefExpr>(BaseDeclRefExprTag)),
2946 DerefOp(Result.getNodeAs<UnaryOperator>(DerefOpTag)),
2947 AddOp(Result.getNodeAs<BinaryOperator>(AddOpTag)),
2948 Offset(Result.getNodeAs<IntegerLiteral>(OffsetTag)) {}
2949
2950 static bool matches(const Stmt *S,
2952 auto IsPtr = [](const Expr *E, MatchResult &R) {
2953 if (!E || !hasPointerType(*E))
2954 return false;
2955 const auto *DRE = dyn_cast<DeclRefExpr>(E->IgnoreImpCasts());
2956 if (!DRE || !isSupportedVariable(*DRE))
2957 return false;
2958 R.addNode(BaseDeclRefExprTag, DynTypedNode::create(*DRE));
2959 return true;
2960 };
2961 const auto IsPlusOverPtrAndInteger = [&IsPtr](const Expr *E,
2962 MatchResult &R) {
2963 const auto *BO = dyn_cast<BinaryOperator>(E);
2964 if (!BO || BO->getOpcode() != BO_Add)
2965 return false;
2966
2967 const auto *LHS = BO->getLHS();
2968 const auto *RHS = BO->getRHS();
2969 if (isa<IntegerLiteral>(RHS) && IsPtr(LHS, R)) {
2970 R.addNode(OffsetTag, DynTypedNode::create(*RHS));
2971 R.addNode(AddOpTag, DynTypedNode::create(*BO));
2972 return true;
2973 }
2974 if (isa<IntegerLiteral>(LHS) && IsPtr(RHS, R)) {
2975 R.addNode(OffsetTag, DynTypedNode::create(*LHS));
2976 R.addNode(AddOpTag, DynTypedNode::create(*BO));
2977 return true;
2978 }
2979 return false;
2980 };
2981 size_t SizeBefore = Results.size();
2982 const auto InnerMatcher = [&IsPlusOverPtrAndInteger,
2983 &Results](const Expr *E) {
2984 const auto *UO = dyn_cast<UnaryOperator>(E);
2985 if (!UO || UO->getOpcode() != UO_Deref)
2986 return;
2987
2988 const auto *Operand = UO->getSubExpr()->IgnoreParens();
2989 MatchResult R;
2990 if (IsPlusOverPtrAndInteger(Operand, R)) {
2991 R.addNode(DerefOpTag, DynTypedNode::create(*UO));
2992 Results.emplace_back(std::move(R));
2993 }
2994 };
2995 findStmtsInUnspecifiedLvalueContext(S, InnerMatcher);
2996 return SizeBefore != Results.size();
2997 }
2998
2999 virtual std::optional<FixItList>
3000 getFixits(const FixitStrategy &s) const final;
3001 SourceLocation getSourceLoc() const override {
3002 return DerefOp->getBeginLoc();
3003 }
3004
3005 virtual DeclUseList getClaimedVarUseSites() const final {
3006 return {BaseDeclRefExpr};
3007 }
3008};
3009
3010class WarningGadgetMatcher : public FastMatcher {
3011
3012public:
3013 WarningGadgetMatcher(WarningGadgetList &WarningGadgets)
3014 : WarningGadgets(WarningGadgets) {}
3015
3016 bool matches(const DynTypedNode &DynNode, ASTContext &Ctx,
3017 const UnsafeBufferUsageHandler &Handler) override {
3018 const Stmt *S = DynNode.get<Stmt>();
3019 if (!S)
3020 return false;
3021
3022 MatchResult Result;
3023#define WARNING_GADGET(name) \
3024 if (name##Gadget::matches(S, Ctx, Result) && \
3025 notInSafeBufferOptOut(*S, &Handler)) { \
3026 WarningGadgets.push_back(std::make_unique<name##Gadget>(Result)); \
3027 return true; \
3028 }
3029#define WARNING_OPTIONAL_GADGET(name) \
3030 if (name##Gadget::matches(S, Ctx, &Handler, Result) && \
3031 notInSafeBufferOptOut(*S, &Handler)) { \
3032 WarningGadgets.push_back(std::make_unique<name##Gadget>(Result)); \
3033 return true; \
3034 }
3035#include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
3036 return false;
3037 }
3038
3039private:
3040 WarningGadgetList &WarningGadgets;
3041};
3042
3043class FixableGadgetMatcher : public FastMatcher {
3044
3045public:
3046 FixableGadgetMatcher(FixableGadgetList &FixableGadgets,
3047 DeclUseTracker &Tracker)
3048 : FixableGadgets(FixableGadgets), Tracker(Tracker) {}
3049
3050 bool matches(const DynTypedNode &DynNode, ASTContext &Ctx,
3051 const UnsafeBufferUsageHandler &Handler) override {
3052 bool matchFound = false;
3053 const Stmt *S = DynNode.get<Stmt>();
3054 if (!S) {
3055 return matchFound;
3056 }
3057
3059#define FIXABLE_GADGET(name) \
3060 if (name##Gadget::matches(S, Results)) { \
3061 for (const auto &R : Results) { \
3062 FixableGadgets.push_back(std::make_unique<name##Gadget>(R)); \
3063 matchFound = true; \
3064 } \
3065 Results = {}; \
3066 }
3067#include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
3068 // In parallel, match all DeclRefExprs so that to find out
3069 // whether there are any uncovered by gadgets.
3070 if (auto *DRE = findDeclRefExpr(S); DRE) {
3071 Tracker.discoverUse(DRE);
3072 matchFound = true;
3073 }
3074 // Also match DeclStmts because we'll need them when fixing
3075 // their underlying VarDecls that otherwise don't have
3076 // any backreferences to DeclStmts.
3077 if (auto *DS = findDeclStmt(S); DS) {
3078 Tracker.discoverDecl(DS);
3079 matchFound = true;
3080 }
3081 return matchFound;
3082 }
3083
3084private:
3085 const DeclRefExpr *findDeclRefExpr(const Stmt *S) {
3086 const auto *DRE = dyn_cast<DeclRefExpr>(S);
3087 if (!DRE || (!hasPointerType(*DRE) && !hasArrayType(*DRE)))
3088 return nullptr;
3089 const Decl *D = DRE->getDecl();
3090 if (!D || (!isa<VarDecl>(D) && !isa<BindingDecl>(D)))
3091 return nullptr;
3092 return DRE;
3093 }
3094 const DeclStmt *findDeclStmt(const Stmt *S) {
3095 const auto *DS = dyn_cast<DeclStmt>(S);
3096 if (!DS)
3097 return nullptr;
3098 return DS;
3099 }
3100 FixableGadgetList &FixableGadgets;
3101 DeclUseTracker &Tracker;
3102};
3103
3104// Scan the function and return a list of gadgets found with provided kits.
3105static void findGadgets(const Stmt *S, ASTContext &Ctx,
3106 const UnsafeBufferUsageHandler &Handler,
3107 bool EmitSuggestions, FixableGadgetList &FixableGadgets,
3108 WarningGadgetList &WarningGadgets,
3109 DeclUseTracker &Tracker) {
3110 WarningGadgetMatcher WMatcher{WarningGadgets};
3111 forEachDescendantEvaluatedStmt(S, Ctx, Handler, WMatcher);
3112 if (EmitSuggestions) {
3113 FixableGadgetMatcher FMatcher{FixableGadgets, Tracker};
3114 forEachDescendantStmt(S, Ctx, Handler, FMatcher);
3115 }
3116}
3117
3118// Compares AST nodes by source locations.
3119template <typename NodeTy> struct CompareNode {
3120 bool operator()(const NodeTy *N1, const NodeTy *N2) const {
3121 return N1->getBeginLoc().getRawEncoding() <
3122 N2->getBeginLoc().getRawEncoding();
3123 }
3124};
3125
3126// Populate `Stmts` with the body/initializer Stmt of `D`, if `D` is one of the
3127// followings:
3128// VarDecl
3129// FieldDecl
3130// FunctionDecl
3131// BlockDecl
3132// ObjCMethodDecl
3134 const Decl *D) {
3135 auto AddStmt = [&Stmts](const Stmt *S) {
3136 if (S)
3137 Stmts.push_back(S);
3138 };
3139 if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
3140 AddStmt(FD->getBody());
3141 for (const auto *PD : FD->parameters())
3142 if (PD->hasDefaultArg() && !PD->hasUninstantiatedDefaultArg())
3143 AddStmt(PD->getDefaultArg());
3144 if (const auto *CtorD = dyn_cast<CXXConstructorDecl>(FD))
3145 llvm::append_range(
3146 Stmts, llvm::map_range(CtorD->inits(),
3147 std::mem_fn(&CXXCtorInitializer::getInit)));
3148 } else if (isa<BlockDecl>(D) || isa<ObjCMethodDecl>(D)) {
3149 AddStmt(D->getBody());
3150 } else if (const auto *VD = dyn_cast<VarDecl>(D)) {
3151 AddStmt(VD->getInit()); // FIXME: default arg for ParmVarDecl?
3152 } else if (const auto *FD = dyn_cast<FieldDecl>(D)) {
3153 AddStmt(FD->getInClassInitializer());
3154 }
3155}
3156
3158 std::map<const VarDecl *, std::set<const WarningGadget *>,
3159 // To keep keys sorted by their locations in the map so that the
3160 // order is deterministic:
3163 // These Gadgets are not related to pointer variables (e. g. temporaries).
3165};
3166
3167static WarningGadgetSets
3168groupWarningGadgetsByVar(const WarningGadgetList &AllUnsafeOperations) {
3169 WarningGadgetSets result;
3170 // If some gadgets cover more than one
3171 // variable, they'll appear more than once in the map.
3172 for (auto &G : AllUnsafeOperations) {
3173 DeclUseList ClaimedVarUseSites = G->getClaimedVarUseSites();
3174
3175 bool AssociatedWithVarDecl = false;
3176 for (const DeclRefExpr *DRE : ClaimedVarUseSites) {
3177 if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
3178 result.byVar[VD].insert(G.get());
3179 AssociatedWithVarDecl = true;
3180 }
3181 }
3182
3183 if (!AssociatedWithVarDecl) {
3184 result.noVar.push_back(G.get());
3185 continue;
3186 }
3187 }
3188 return result;
3189}
3190
3192 std::map<const VarDecl *, std::set<const FixableGadget *>,
3193 // To keep keys sorted by their locations in the map so that the
3194 // order is deterministic:
3197};
3198
3199static FixableGadgetSets
3200groupFixablesByVar(FixableGadgetList &&AllFixableOperations) {
3201 FixableGadgetSets FixablesForUnsafeVars;
3202 for (auto &F : AllFixableOperations) {
3203 DeclUseList DREs = F->getClaimedVarUseSites();
3204
3205 for (const DeclRefExpr *DRE : DREs) {
3206 if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
3207 FixablesForUnsafeVars.byVar[VD].insert(F.get());
3208 }
3209 }
3210 }
3211 return FixablesForUnsafeVars;
3212}
3213
3215 const SourceManager &SM) {
3216 // A simple interval overlap detection algorithm. Sorts all ranges by their
3217 // begin location then finds the first overlap in one pass.
3218 std::vector<const FixItHint *> All; // a copy of `FixIts`
3219
3220 for (const FixItHint &H : FixIts)
3221 All.push_back(&H);
3222 std::sort(All.begin(), All.end(),
3223 [&SM](const FixItHint *H1, const FixItHint *H2) {
3224 return SM.isBeforeInTranslationUnit(H1->RemoveRange.getBegin(),
3225 H2->RemoveRange.getBegin());
3226 });
3227
3228 const FixItHint *CurrHint = nullptr;
3229
3230 for (const FixItHint *Hint : All) {
3231 if (!CurrHint ||
3233 Hint->RemoveRange.getBegin())) {
3234 // Either to initialize `CurrHint` or `CurrHint` does not
3235 // overlap with `Hint`:
3236 CurrHint = Hint;
3237 } else
3238 // In case `Hint` overlaps the `CurrHint`, we found at least one
3239 // conflict:
3240 return true;
3241 }
3242 return false;
3243}
3244
3245std::optional<FixItList>
3246PtrToPtrAssignmentGadget::getFixits(const FixitStrategy &S) const {
3247 const auto *LeftVD = cast<VarDecl>(PtrLHS->getDecl());
3248 const auto *RightVD = cast<VarDecl>(PtrRHS->getDecl());
3249 switch (S.lookup(LeftVD)) {
3251 if (S.lookup(RightVD) == FixitStrategy::Kind::Span)
3252 return FixItList{};
3253 return std::nullopt;
3255 return std::nullopt;
3258 return std::nullopt;
3260 llvm_unreachable("unsupported strategies for FixableGadgets");
3261 }
3262 return std::nullopt;
3263}
3264
3265/// \returns fixit that adds .data() call after \DRE.
3266static inline std::optional<FixItList> createDataFixit(const ASTContext &Ctx,
3267 const DeclRefExpr *DRE);
3268
3269std::optional<FixItList>
3270CArrayToPtrAssignmentGadget::getFixits(const FixitStrategy &S) const {
3271 const auto *LeftVD = cast<VarDecl>(PtrLHS->getDecl());
3272 const auto *RightVD = cast<VarDecl>(PtrRHS->getDecl());
3273 // TLDR: Implementing fixits for non-Wontfix strategy on both LHS and RHS is
3274 // non-trivial.
3275 //
3276 // CArrayToPtrAssignmentGadget doesn't have strategy implications because
3277 // constant size array propagates its bounds. Because of that LHS and RHS are
3278 // addressed by two different fixits.
3279 //
3280 // At the same time FixitStrategy S doesn't reflect what group a fixit belongs
3281 // to and can't be generally relied on in multi-variable Fixables!
3282 //
3283 // E. g. If an instance of this gadget is fixing variable on LHS then the
3284 // variable on RHS is fixed by a different fixit and its strategy for LHS
3285 // fixit is as if Wontfix.
3286 //
3287 // The only exception is Wontfix strategy for a given variable as that is
3288 // valid for any fixit produced for the given input source code.
3289 if (S.lookup(LeftVD) == FixitStrategy::Kind::Span) {
3290 if (S.lookup(RightVD) == FixitStrategy::Kind::Wontfix) {
3291 return FixItList{};
3292 }
3293 } else if (S.lookup(LeftVD) == FixitStrategy::Kind::Wontfix) {
3294 if (S.lookup(RightVD) == FixitStrategy::Kind::Array) {
3295 return createDataFixit(RightVD->getASTContext(), PtrRHS);
3296 }
3297 }
3298 return std::nullopt;
3299}
3300
3301std::optional<FixItList>
3302PointerInitGadget::getFixits(const FixitStrategy &S) const {
3303 const auto *LeftVD = PtrInitLHS;
3304 const auto *RightVD = cast<VarDecl>(PtrInitRHS->getDecl());
3305 switch (S.lookup(LeftVD)) {
3306 case FixitStrategy::Kind::Span:
3307 if (S.lookup(RightVD) == FixitStrategy::Kind::Span)
3308 return FixItList{};
3309 return std::nullopt;
3310 case FixitStrategy::Kind::Wontfix:
3311 return std::nullopt;
3312 case FixitStrategy::Kind::Iterator:
3313 case FixitStrategy::Kind::Array:
3314 return std::nullopt;
3315 case FixitStrategy::Kind::Vector:
3316 llvm_unreachable("unsupported strategies for FixableGadgets");
3317 }
3318 return std::nullopt;
3319}
3320
3321static bool isNonNegativeIntegerExpr(const Expr *Expr, const VarDecl *VD,
3322 const ASTContext &Ctx) {
3323 if (auto ConstVal = Expr->getIntegerConstantExpr(Ctx)) {
3324 if (ConstVal->isNegative())
3325 return false;
3326 } else if (!Expr->getType()->isUnsignedIntegerType())
3327 return false;
3328 return true;
3329}
3330
3331std::optional<FixItList>
3332ULCArraySubscriptGadget::getFixits(const FixitStrategy &S) const {
3333 if (const auto *DRE =
3334 dyn_cast<DeclRefExpr>(Node->getBase()->IgnoreImpCasts()))
3335 if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
3336 switch (S.lookup(VD)) {
3337 case FixitStrategy::Kind::Span: {
3338
3339 // If the index has a negative constant value, we give up as no valid
3340 // fix-it can be generated:
3341 const ASTContext &Ctx = // FIXME: we need ASTContext to be passed in!
3342 VD->getASTContext();
3343 if (!isNonNegativeIntegerExpr(Node->getIdx(), VD, Ctx))
3344 return std::nullopt;
3345 // no-op is a good fix-it, otherwise
3346 return FixItList{};
3347 }
3348 case FixitStrategy::Kind::Array:
3349 return FixItList{};
3350 case FixitStrategy::Kind::Wontfix:
3351 case FixitStrategy::Kind::Iterator:
3352 case FixitStrategy::Kind::Vector:
3353 llvm_unreachable("unsupported strategies for FixableGadgets");
3354 }
3355 }
3356 return std::nullopt;
3357}
3358
3359static std::optional<FixItList> // forward declaration
3360fixUPCAddressofArraySubscriptWithSpan(const UnaryOperator *Node);
3361
3362std::optional<FixItList>
3363UPCAddressofArraySubscriptGadget::getFixits(const FixitStrategy &S) const {
3364 auto DREs = getClaimedVarUseSites();
3365 const auto *VD = cast<VarDecl>(DREs.front()->getDecl());
3366
3367 switch (S.lookup(VD)) {
3368 case FixitStrategy::Kind::Span:
3370 case FixitStrategy::Kind::Wontfix:
3371 case FixitStrategy::Kind::Iterator:
3372 case FixitStrategy::Kind::Array:
3373 return std::nullopt;
3374 case FixitStrategy::Kind::Vector:
3375 llvm_unreachable("unsupported strategies for FixableGadgets");
3376 }
3377 return std::nullopt; // something went wrong, no fix-it
3378}
3379
3380// FIXME: this function should be customizable through format
3381static StringRef getEndOfLine() {
3382 static const char *const EOL = "\n";
3383 return EOL;
3384}
3385
3386// Returns the text indicating that the user needs to provide input there:
3387static std::string
3388getUserFillPlaceHolder(StringRef HintTextToUser = "placeholder") {
3389 std::string s = std::string("<# ");
3390 s += HintTextToUser;
3391 s += " #>";
3392 return s;
3393}
3394
3395// Return the source location of the last character of the AST `Node`.
3396template <typename NodeTy>
3397static std::optional<SourceLocation>
3398getEndCharLoc(const NodeTy *Node, const SourceManager &SM,
3399 const LangOptions &LangOpts) {
3400 if (unsigned TkLen =
3401 Lexer::MeasureTokenLength(Node->getEndLoc(), SM, LangOpts)) {
3402 SourceLocation Loc = Node->getEndLoc().getLocWithOffset(TkLen - 1);
3403
3404 if (Loc.isValid())
3405 return Loc;
3406 }
3407 return std::nullopt;
3408}
3409
3410// We cannot fix a variable declaration if it has some other specifiers than the
3411// type specifier. Because the source ranges of those specifiers could overlap
3412// with the source range that is being replaced using fix-its. Especially when
3413// we often cannot obtain accurate source ranges of cv-qualified type
3414// specifiers.
3415// FIXME: also deal with type attributes
3416static bool hasUnsupportedSpecifiers(const VarDecl *VD,
3417 const SourceManager &SM) {
3418 // AttrRangeOverlapping: true if at least one attribute of `VD` overlaps the
3419 // source range of `VD`:
3420 bool AttrRangeOverlapping = llvm::any_of(VD->attrs(), [&](Attr *At) -> bool {
3421 return !(SM.isBeforeInTranslationUnit(At->getRange().getEnd(),
3422 VD->getBeginLoc())) &&
3423 !(SM.isBeforeInTranslationUnit(VD->getEndLoc(),
3424 At->getRange().getBegin()));
3425 });
3426 return VD->isInlineSpecified() || VD->isConstexpr() ||
3428 AttrRangeOverlapping;
3429}
3430
3431// Returns the `SourceRange` of `D`. The reason why this function exists is
3432// that `D->getSourceRange()` may return a range where the end location is the
3433// starting location of the last token. The end location of the source range
3434// returned by this function is the last location of the last token.
3436 const SourceManager &SM,
3437 const LangOptions &LangOpts) {
3438 SourceLocation Begin = D->getBeginLoc();
3440 End = // `D->getEndLoc` should always return the starting location of the
3441 // last token, so we should get the end of the token
3442 Lexer::getLocForEndOfToken(D->getEndLoc(), 0, SM, LangOpts);
3443
3444 return SourceRange(Begin, End);
3445}
3446
3447// Returns the text of the name (with qualifiers) of a `FunctionDecl`.
3448static std::optional<StringRef> getFunNameText(const FunctionDecl *FD,
3449 const SourceManager &SM,
3450 const LangOptions &LangOpts) {
3451 SourceLocation BeginLoc = FD->getQualifier()
3453 : FD->getNameInfo().getBeginLoc();
3454 // Note that `FD->getNameInfo().getEndLoc()` returns the begin location of the
3455 // last token:
3457 FD->getNameInfo().getEndLoc(), 0, SM, LangOpts);
3458 SourceRange NameRange{BeginLoc, EndLoc};
3459
3460 return getRangeText(NameRange, SM, LangOpts);
3461}
3462
3463// Returns the text representing a `std::span` type where the element type is
3464// represented by `EltTyText`.
3465//
3466// Note the optional parameter `Qualifiers`: one needs to pass qualifiers
3467// explicitly if the element type needs to be qualified.
3468static std::string
3469getSpanTypeText(StringRef EltTyText,
3470 std::optional<Qualifiers> Quals = std::nullopt) {
3471 const char *const SpanOpen = "std::span<";
3472
3473 if (Quals)
3474 return SpanOpen + EltTyText.str() + ' ' + Quals->getAsString() + '>';
3475 return SpanOpen + EltTyText.str() + '>';
3476}
3477
3478std::optional<FixItList>
3480 const VarDecl *VD = dyn_cast<VarDecl>(BaseDeclRefExpr->getDecl());
3481
3482 if (VD && s.lookup(VD) == FixitStrategy::Kind::Span) {
3483 ASTContext &Ctx = VD->getASTContext();
3484 // std::span can't represent elements before its begin()
3485 if (auto ConstVal = Offset->getIntegerConstantExpr(Ctx))
3486 if (ConstVal->isNegative())
3487 return std::nullopt;
3488
3489 // note that the expr may (oddly) has multiple layers of parens
3490 // example:
3491 // *((..(pointer + 123)..))
3492 // goal:
3493 // pointer[123]
3494 // Fix-It:
3495 // remove '*('
3496 // replace ' + ' with '['
3497 // replace ')' with ']'
3498
3499 // example:
3500 // *((..(123 + pointer)..))
3501 // goal:
3502 // 123[pointer]
3503 // Fix-It:
3504 // remove '*('
3505 // replace ' + ' with '['
3506 // replace ')' with ']'
3507
3508 const Expr *LHS = AddOp->getLHS(), *RHS = AddOp->getRHS();
3509 const SourceManager &SM = Ctx.getSourceManager();
3510 const LangOptions &LangOpts = Ctx.getLangOpts();
3511 CharSourceRange StarWithTrailWhitespace =
3512 clang::CharSourceRange::getCharRange(DerefOp->getOperatorLoc(),
3513 LHS->getBeginLoc());
3514
3515 std::optional<SourceLocation> LHSLocation = getPastLoc(LHS, SM, LangOpts);
3516 if (!LHSLocation)
3517 return std::nullopt;
3518
3519 CharSourceRange PlusWithSurroundingWhitespace =
3520 clang::CharSourceRange::getCharRange(*LHSLocation, RHS->getBeginLoc());
3521
3522 std::optional<SourceLocation> AddOpLocation =
3523 getPastLoc(AddOp, SM, LangOpts);
3524 std::optional<SourceLocation> DerefOpLocation =
3525 getPastLoc(DerefOp, SM, LangOpts);
3526
3527 if (!AddOpLocation || !DerefOpLocation)
3528 return std::nullopt;
3529
3530 CharSourceRange ClosingParenWithPrecWhitespace =
3531 clang::CharSourceRange::getCharRange(*AddOpLocation, *DerefOpLocation);
3532
3533 return FixItList{
3534 {FixItHint::CreateRemoval(StarWithTrailWhitespace),
3535 FixItHint::CreateReplacement(PlusWithSurroundingWhitespace, "["),
3536 FixItHint::CreateReplacement(ClosingParenWithPrecWhitespace, "]")}};
3537 }
3538 return std::nullopt; // something wrong or unsupported, give up
3539}
3540
3541std::optional<FixItList>
3542PointerDereferenceGadget::getFixits(const FixitStrategy &S) const {
3543 const VarDecl *VD = cast<VarDecl>(BaseDeclRefExpr->getDecl());
3544 switch (S.lookup(VD)) {
3546 ASTContext &Ctx = VD->getASTContext();
3547 SourceManager &SM = Ctx.getSourceManager();
3548 // Required changes: *(ptr); => (ptr[0]); and *ptr; => ptr[0]
3549 // Deletes the *operand
3551 Op->getBeginLoc(), Op->getBeginLoc().getLocWithOffset(1));
3552 // Inserts the [0]
3553 if (auto LocPastOperand =
3554 getPastLoc(BaseDeclRefExpr, SM, Ctx.getLangOpts())) {
3555 return FixItList{{FixItHint::CreateRemoval(derefRange),
3556 FixItHint::CreateInsertion(*LocPastOperand, "[0]")}};
3557 }
3558 break;
3559 }
3560 case FixitStrategy::Kind::Iterator:
3561 case FixitStrategy::Kind::Array:
3562 return std::nullopt;
3563 case FixitStrategy::Kind::Vector:
3564 llvm_unreachable("FixitStrategy not implemented yet!");
3565 case FixitStrategy::Kind::Wontfix:
3566 llvm_unreachable("Invalid strategy!");
3567 }
3568
3569 return std::nullopt;
3570}
3571
3572static inline std::optional<FixItList> createDataFixit(const ASTContext &Ctx,
3573 const DeclRefExpr *DRE) {
3574 const SourceManager &SM = Ctx.getSourceManager();
3575 // Inserts the .data() after the DRE
3576 std::optional<SourceLocation> EndOfOperand =
3577 getPastLoc(DRE, SM, Ctx.getLangOpts());
3578
3579 if (EndOfOperand)
3580 return FixItList{{FixItHint::CreateInsertion(*EndOfOperand, ".data()")}};
3581
3582 return std::nullopt;
3583}
3584
3585// Generates fix-its replacing an expression of the form UPC(DRE) with
3586// `DRE.data()`
3587std::optional<FixItList>
3588UPCStandalonePointerGadget::getFixits(const FixitStrategy &S) const {
3589 const auto VD = cast<VarDecl>(Node->getDecl());
3590 switch (S.lookup(VD)) {
3591 case FixitStrategy::Kind::Array:
3592 case FixitStrategy::Kind::Span: {
3593 return createDataFixit(VD->getASTContext(), Node);
3594 // FIXME: Points inside a macro expansion.
3595 break;
3596 }
3597 case FixitStrategy::Kind::Wontfix:
3598 case FixitStrategy::Kind::Iterator:
3599 return std::nullopt;
3600 case FixitStrategy::Kind::Vector:
3601 llvm_unreachable("unsupported strategies for FixableGadgets");
3602 }
3603
3604 return std::nullopt;
3605}
3606
3607// Generates fix-its replacing an expression of the form `&DRE[e]` with
3608// `&DRE.data()[e]`:
3609static std::optional<FixItList>
3611 const auto *ArraySub = cast<ArraySubscriptExpr>(Node->getSubExpr());
3612 const auto *DRE = cast<DeclRefExpr>(ArraySub->getBase()->IgnoreImpCasts());
3613 // FIXME: this `getASTContext` call is costly, we should pass the
3614 // ASTContext in:
3615 const ASTContext &Ctx = DRE->getDecl()->getASTContext();
3616 const Expr *Idx = ArraySub->getIdx();
3617 const SourceManager &SM = Ctx.getSourceManager();
3618 const LangOptions &LangOpts = Ctx.getLangOpts();
3619 std::stringstream SS;
3620 bool IdxIsLitZero = false;
3621
3622 if (auto ICE = Idx->getIntegerConstantExpr(Ctx))
3623 if ((*ICE).isZero())
3624 IdxIsLitZero = true;
3625 std::optional<StringRef> DreString = getExprText(DRE, SM, LangOpts);
3626 if (!DreString)
3627 return std::nullopt;
3628
3629 if (IdxIsLitZero) {
3630 // If the index is literal zero, we produce the most concise fix-it:
3631 SS << (*DreString).str() << ".data()";
3632 } else {
3633 std::optional<StringRef> IndexString = getExprText(Idx, SM, LangOpts);
3634 if (!IndexString)
3635 return std::nullopt;
3636
3637 SS << "&" << (*DreString).str() << ".data()"
3638 << "[" << (*IndexString).str() << "]";
3639 }
3640 return FixItList{
3641 FixItHint::CreateReplacement(Node->getSourceRange(), SS.str())};
3642}
3643
3644std::optional<FixItList>
3646 DeclUseList DREs = getClaimedVarUseSites();
3647
3648 if (DREs.size() != 1)
3649 return std::nullopt; // In cases of `Ptr += n` where `Ptr` is not a DRE, we
3650 // give up
3651 if (const VarDecl *VD = dyn_cast<VarDecl>(DREs.front()->getDecl())) {
3652 if (S.lookup(VD) == FixitStrategy::Kind::Span) {
3653 FixItList Fixes;
3654
3655 const Stmt *AddAssignNode = Node;
3656 StringRef varName = VD->getName();
3657 const ASTContext &Ctx = VD->getASTContext();
3658
3659 if (!isNonNegativeIntegerExpr(Offset, VD, Ctx))
3660 return std::nullopt;
3661
3662 // To transform UUC(p += n) to UUC(p = p.subspan(..)):
3663 bool NotParenExpr =
3664 (Offset->IgnoreParens()->getBeginLoc() == Offset->getBeginLoc());
3665 std::string SS = varName.str() + " = " + varName.str() + ".subspan";
3666 if (NotParenExpr)
3667 SS += "(";
3668
3669 std::optional<SourceLocation> AddAssignLocation = getEndCharLoc(
3670 AddAssignNode, Ctx.getSourceManager(), Ctx.getLangOpts());
3671 if (!AddAssignLocation)
3672 return std::nullopt;
3673
3674 Fixes.push_back(FixItHint::CreateReplacement(
3675 SourceRange(AddAssignNode->getBeginLoc(), Node->getOperatorLoc()),
3676 SS));
3677 if (NotParenExpr)
3678 Fixes.push_back(FixItHint::CreateInsertion(
3679 Offset->getEndLoc().getLocWithOffset(1), ")"));
3680 return Fixes;
3681 }
3682 }
3683 return std::nullopt; // Not in the cases that we can handle for now, give up.
3684}
3685
3686std::optional<FixItList>
3688 DeclUseList DREs = getClaimedVarUseSites();
3689
3690 if (DREs.size() != 1)
3691 return std::nullopt; // In cases of `++Ptr` where `Ptr` is not a DRE, we
3692 // give up
3693 if (const VarDecl *VD = dyn_cast<VarDecl>(DREs.front()->getDecl())) {
3694 if (S.lookup(VD) == FixitStrategy::Kind::Span) {
3695 FixItList Fixes;
3696 std::stringstream SS;
3697 StringRef varName = VD->getName();
3698 const ASTContext &Ctx = VD->getASTContext();
3699
3700 // To transform UPC(++p) to UPC((p = p.subspan(1)).data()):
3701 SS << "(" << varName.data() << " = " << varName.data()
3702 << ".subspan(1)).data()";
3703 std::optional<SourceLocation> PreIncLocation =
3704 getEndCharLoc(Node, Ctx.getSourceManager(), Ctx.getLangOpts());
3705 if (!PreIncLocation)
3706 return std::nullopt;
3707
3708 Fixes.push_back(FixItHint::CreateReplacement(
3709 SourceRange(Node->getBeginLoc(), *PreIncLocation), SS.str()));
3710 return Fixes;
3711 }
3712 }
3713 return std::nullopt; // Not in the cases that we can handle for now, give up.
3714}
3715
3716// For a non-null initializer `Init` of `T *` type, this function returns
3717// `FixItHint`s producing a list initializer `{Init, S}` as a part of a fix-it
3718// to output stream.
3719// In many cases, this function cannot figure out the actual extent `S`. It
3720// then will use a place holder to replace `S` to ask users to fill `S` in. The
3721// initializer shall be used to initialize a variable of type `std::span<T>`.
3722// In some cases (e. g. constant size array) the initializer should remain
3723// unchanged and the function returns empty list. In case the function can't
3724// provide the right fixit it will return nullopt.
3725//
3726// FIXME: Support multi-level pointers
3727//
3728// Parameters:
3729// `Init` a pointer to the initializer expression
3730// `Ctx` a reference to the ASTContext
3731static std::optional<FixItList>
3733 const StringRef UserFillPlaceHolder) {
3734 const SourceManager &SM = Ctx.getSourceManager();
3735 const LangOptions &LangOpts = Ctx.getLangOpts();
3736
3737 // If `Init` has a constant value that is (or equivalent to) a
3738 // NULL pointer, we use the default constructor to initialize the span
3739 // object, i.e., a `std:span` variable declaration with no initializer.
3740 // So the fix-it is just to remove the initializer.
3741 if (Init->isNullPointerConstant(
3742 Ctx,
3743 // FIXME: Why does this function not ask for `const ASTContext
3744 // &`? It should. Maybe worth an NFC patch later.
3746 NPC_ValueDependentIsNotNull)) {
3747 std::optional<SourceLocation> InitLocation =
3748 getEndCharLoc(Init, SM, LangOpts);
3749 if (!InitLocation)
3750 return std::nullopt;
3751
3752 SourceRange SR(Init->getBeginLoc(), *InitLocation);
3753
3754 return FixItList{FixItHint::CreateRemoval(SR)};
3755 }
3756
3757 FixItList FixIts{};
3758 std::string ExtentText = UserFillPlaceHolder.data();
3759 StringRef One = "1";
3760
3761 // Insert `{` before `Init`:
3762 FixIts.push_back(FixItHint::CreateInsertion(Init->getBeginLoc(), "{"));
3763 // Try to get the data extent. Break into different cases:
3764 if (auto CxxNew = dyn_cast<CXXNewExpr>(Init->IgnoreImpCasts())) {
3765 // In cases `Init` is `new T[n]` and there is no explicit cast over
3766 // `Init`, we know that `Init` must evaluates to a pointer to `n` objects
3767 // of `T`. So the extent is `n` unless `n` has side effects. Similar but
3768 // simpler for the case where `Init` is `new T`.
3769 if (const Expr *Ext = CxxNew->getArraySize().value_or(nullptr)) {
3770 if (!Ext->HasSideEffects(Ctx)) {
3771 std::optional<StringRef> ExtentString = getExprText(Ext, SM, LangOpts);
3772 if (!ExtentString)
3773 return std::nullopt;
3774 ExtentText = *ExtentString;
3775 }
3776 } else if (!CxxNew->isArray())
3777 // Although the initializer is not allocating a buffer, the pointer
3778 // variable could still be used in buffer access operations.
3779 ExtentText = One;
3780 } else if (Ctx.getAsConstantArrayType(Init->IgnoreImpCasts()->getType())) {
3781 // std::span has a single parameter constructor for initialization with
3782 // constant size array. The size is auto-deduced as the constructor is a
3783 // function template. The correct fixit is empty - no changes should happen.
3784 return FixItList{};
3785 } else {
3786 // In cases `Init` is of the form `&Var` after stripping of implicit
3787 // casts, where `&` is the built-in operator, the extent is 1.
3788 if (auto AddrOfExpr = dyn_cast<UnaryOperator>(Init->IgnoreImpCasts()))
3789 if (AddrOfExpr->getOpcode() == UnaryOperatorKind::UO_AddrOf &&
3790 isa_and_present<DeclRefExpr>(AddrOfExpr->getSubExpr()))
3791 ExtentText = One;
3792 // TODO: we can handle more cases, e.g., `&a[0]`, `&a`, `std::addressof`,
3793 // and explicit casting, etc. etc.
3794 }
3795
3796 SmallString<32> StrBuffer{};
3797 std::optional<SourceLocation> LocPassInit = getPastLoc(Init, SM, LangOpts);
3798
3799 if (!LocPassInit)
3800 return std::nullopt;
3801
3802 StrBuffer.append(", ");
3803 StrBuffer.append(ExtentText);
3804 StrBuffer.append("}");
3805 FixIts.push_back(FixItHint::CreateInsertion(*LocPassInit, StrBuffer.str()));
3806 return FixIts;
3807}
3808
3809#ifndef NDEBUG
3810#define DEBUG_NOTE_DECL_FAIL(D, Msg) \
3811 Handler.addDebugNoteForVar((D), (D)->getBeginLoc(), \
3812 "failed to produce fixit for declaration '" + \
3813 (D)->getNameAsString() + "'" + (Msg))
3814#else
3815#define DEBUG_NOTE_DECL_FAIL(D, Msg)
3816#endif
3817
3818// For the given variable declaration with a pointer-to-T type, returns the text
3819// `std::span<T>`. If it is unable to generate the text, returns
3820// `std::nullopt`.
3821static std::optional<std::string>
3823 assert(VD->getType()->isPointerType());
3824
3825 std::optional<Qualifiers> PteTyQualifiers = std::nullopt;
3826 std::optional<std::string> PteTyText = getPointeeTypeText(
3827 VD, Ctx.getSourceManager(), Ctx.getLangOpts(), &PteTyQualifiers);
3828
3829 if (!PteTyText)
3830 return std::nullopt;
3831
3832 std::string SpanTyText = "std::span<";
3833
3834 SpanTyText.append(*PteTyText);
3835 // Append qualifiers to span element type if any:
3836 if (PteTyQualifiers) {
3837 SpanTyText.append(" ");
3838 SpanTyText.append(PteTyQualifiers->getAsString());
3839 }
3840 SpanTyText.append(">");
3841 return SpanTyText;
3842}
3843
3844// For a `VarDecl` of the form `T * var (= Init)?`, this
3845// function generates fix-its that
3846// 1) replace `T * var` with `std::span<T> var`; and
3847// 2) change `Init` accordingly to a span constructor, if it exists.
3848//
3849// FIXME: support Multi-level pointers
3850//
3851// Parameters:
3852// `D` a pointer the variable declaration node
3853// `Ctx` a reference to the ASTContext
3854// `UserFillPlaceHolder` the user-input placeholder text
3855// Returns:
3856// the non-empty fix-it list, if fix-its are successfuly generated; empty
3857// list otherwise.
3858static FixItList fixLocalVarDeclWithSpan(const VarDecl *D, ASTContext &Ctx,
3859 const StringRef UserFillPlaceHolder,
3860 UnsafeBufferUsageHandler &Handler) {
3862 return {};
3863
3864 FixItList FixIts{};
3865 std::optional<std::string> SpanTyText = createSpanTypeForVarDecl(D, Ctx);
3866
3867 if (!SpanTyText) {
3868 DEBUG_NOTE_DECL_FAIL(D, " : failed to generate 'std::span' type");
3869 return {};
3870 }
3871
3872 // Will hold the text for `std::span<T> Ident`:
3873 std::stringstream SS;
3874
3875 SS << *SpanTyText;
3876 // Fix the initializer if it exists:
3877 if (const Expr *Init = D->getInit()) {
3878 std::optional<FixItList> InitFixIts =
3879 FixVarInitializerWithSpan(Init, Ctx, UserFillPlaceHolder);
3880 if (!InitFixIts)
3881 return {};
3882 FixIts.insert(FixIts.end(), std::make_move_iterator(InitFixIts->begin()),
3883 std::make_move_iterator(InitFixIts->end()));
3884 }
3885 // For declaration of the form `T * ident = init;`, we want to replace
3886 // `T * ` with `std::span<T>`.
3887 // We ignore CV-qualifiers so for `T * const ident;` we also want to replace
3888 // just `T *` with `std::span<T>`.
3889 const SourceLocation EndLocForReplacement = D->getTypeSpecEndLoc();
3890 if (!EndLocForReplacement.isValid()) {
3891 DEBUG_NOTE_DECL_FAIL(D, " : failed to locate the end of the declaration");
3892 return {};
3893 }
3894 // The only exception is that for `T *ident` we'll add a single space between
3895 // "std::span<T>" and "ident".
3896 // FIXME: The condition is false for identifiers expended from macros.
3897 if (EndLocForReplacement.getLocWithOffset(1) == getVarDeclIdentifierLoc(D))
3898 SS << " ";
3899
3900 FixIts.push_back(FixItHint::CreateReplacement(
3901 SourceRange(D->getBeginLoc(), EndLocForReplacement), SS.str()));
3902 return FixIts;
3903}
3904
3905static bool hasConflictingOverload(const FunctionDecl *FD) {
3906 return !FD->getDeclContext()->lookup(FD->getDeclName()).isSingleResult();
3907}
3908
3909// For a `FunctionDecl`, whose `ParmVarDecl`s are being changed to have new
3910// types, this function produces fix-its to make the change self-contained. Let
3911// 'F' be the entity defined by the original `FunctionDecl` and "NewF" be the
3912// entity defined by the `FunctionDecl` after the change to the parameters.
3913// Fix-its produced by this function are
3914// 1. Add the `[[clang::unsafe_buffer_usage]]` attribute to each declaration
3915// of 'F';
3916// 2. Create a declaration of "NewF" next to each declaration of `F`;
3917// 3. Create a definition of "F" (as its original definition is now belongs
3918// to "NewF") next to its original definition. The body of the creating
3919// definition calls to "NewF".
3920//
3921// Example:
3922//
3923// void f(int *p); // original declaration
3924// void f(int *p) { // original definition
3925// p[5];
3926// }
3927//
3928// To change the parameter `p` to be of `std::span<int>` type, we
3929// also add overloads:
3930//
3931// [[clang::unsafe_buffer_usage]] void f(int *p); // original decl
3932// void f(std::span<int> p); // added overload decl
3933// void f(std::span<int> p) { // original def where param is changed
3934// p[5];
3935// }
3936// [[clang::unsafe_buffer_usage]] void f(int *p) { // added def
3937// return f(std::span(p, <# size #>));
3938// }
3939//
3940static std::optional<FixItList>
3942 const ASTContext &Ctx,
3943 UnsafeBufferUsageHandler &Handler) {
3944 // FIXME: need to make this conflict checking better:
3945 if (hasConflictingOverload(FD))
3946 return std::nullopt;
3947
3948 const SourceManager &SM = Ctx.getSourceManager();
3949 const LangOptions &LangOpts = Ctx.getLangOpts();
3950 const unsigned NumParms = FD->getNumParams();
3951 std::vector<std::string> NewTysTexts(NumParms);
3952 std::vector<bool> ParmsMask(NumParms, false);
3953 bool AtLeastOneParmToFix = false;
3954
3955 for (unsigned i = 0; i < NumParms; i++) {
3956 const ParmVarDecl *PVD = FD->getParamDecl(i);
3957
3959 continue;
3960 if (S.lookup(PVD) != FixitStrategy::Kind::Span)
3961 // Not supported, not suppose to happen:
3962 return std::nullopt;
3963
3964 std::optional<Qualifiers> PteTyQuals = std::nullopt;
3965 std::optional<std::string> PteTyText =
3966 getPointeeTypeText(PVD, SM, LangOpts, &PteTyQuals);
3967
3968 if (!PteTyText)
3969 // something wrong in obtaining the text of the pointee type, give up
3970 return std::nullopt;
3971 // FIXME: whether we should create std::span type depends on the
3972 // FixitStrategy.
3973 NewTysTexts[i] = getSpanTypeText(*PteTyText, PteTyQuals);
3974 ParmsMask[i] = true;
3975 AtLeastOneParmToFix = true;
3976 }
3977 if (!AtLeastOneParmToFix)
3978 // No need to create function overloads:
3979 return {};
3980 // FIXME Respect indentation of the original code.
3981
3982 // A lambda that creates the text representation of a function declaration
3983 // with the new type signatures:
3984 const auto NewOverloadSignatureCreator =
3985 [&SM, &LangOpts, &NewTysTexts,
3986 &ParmsMask](const FunctionDecl *FD) -> std::optional<std::string> {
3987 std::stringstream SS;
3988
3989 SS << ";";
3990 SS << getEndOfLine().str();
3991 // Append: ret-type func-name "("
3992 if (auto Prefix = getRangeText(
3993 SourceRange(FD->getBeginLoc(), (*FD->param_begin())->getBeginLoc()),
3994 SM, LangOpts))
3995 SS << Prefix->str();
3996 else
3997 return std::nullopt; // give up
3998 // Append: parameter-type-list
3999 const unsigned NumParms = FD->getNumParams();
4000
4001 for (unsigned i = 0; i < NumParms; i++) {
4002 const ParmVarDecl *Parm = FD->getParamDecl(i);
4003
4004 if (Parm->isImplicit())
4005 continue;
4006 if (ParmsMask[i]) {
4007 // This `i`-th parameter will be fixed with `NewTysTexts[i]` being its
4008 // new type:
4009 SS << NewTysTexts[i];
4010 // print parameter name if provided:
4011 if (IdentifierInfo *II = Parm->getIdentifier())
4012 SS << ' ' << II->getName().str();
4013 } else if (auto ParmTypeText =
4014 getRangeText(getSourceRangeToTokenEnd(Parm, SM, LangOpts),
4015 SM, LangOpts)) {
4016 // print the whole `Parm` without modification:
4017 SS << ParmTypeText->str();
4018 } else
4019 return std::nullopt; // something wrong, give up
4020 if (i != NumParms - 1)
4021 SS << ", ";
4022 }
4023 SS << ")";
4024 return SS.str();
4025 };
4026
4027 // A lambda that creates the text representation of a function definition with
4028 // the original signature:
4029 const auto OldOverloadDefCreator =
4030 [&Handler, &SM, &LangOpts, &NewTysTexts,
4031 &ParmsMask](const FunctionDecl *FD) -> std::optional<std::string> {
4032 std::stringstream SS;
4033
4034 SS << getEndOfLine().str();
4035 // Append: attr-name ret-type func-name "(" param-list ")" "{"
4036 if (auto FDPrefix = getRangeText(
4037 SourceRange(FD->getBeginLoc(), FD->getBody()->getBeginLoc()), SM,
4038 LangOpts))
4039 SS << Handler.getUnsafeBufferUsageAttributeTextAt(FD->getBeginLoc(), " ")
4040 << FDPrefix->str() << "{";
4041 else
4042 return std::nullopt;
4043 // Append: "return" func-name "("
4044 if (auto FunQualName = getFunNameText(FD, SM, LangOpts))
4045 SS << "return " << FunQualName->str() << "(";
4046 else
4047 return std::nullopt;
4048
4049 // Append: arg-list
4050 const unsigned NumParms = FD->getNumParams();
4051 for (unsigned i = 0; i < NumParms; i++) {
4052 const ParmVarDecl *Parm = FD->getParamDecl(i);
4053
4054 if (Parm->isImplicit())
4055 continue;
4056 // FIXME: If a parameter has no name, it is unused in the
4057 // definition. So we could just leave it as it is.
4058 if (!Parm->getIdentifier())
4059 // If a parameter of a function definition has no name:
4060 return std::nullopt;
4061 if (ParmsMask[i])
4062 // This is our spanified paramter!
4063 SS << NewTysTexts[i] << "(" << Parm->getIdentifier()->getName().str()
4064 << ", " << getUserFillPlaceHolder("size") << ")";
4065 else
4066 SS << Parm->getIdentifier()->getName().str();
4067 if (i != NumParms - 1)
4068 SS << ", ";
4069 }
4070 // finish call and the body
4071 SS << ");}" << getEndOfLine().str();
4072 // FIXME: 80-char line formatting?
4073 return SS.str();
4074 };
4075
4076 FixItList FixIts{};
4077 for (FunctionDecl *FReDecl : FD->redecls()) {
4078 std::optional<SourceLocation> Loc = getPastLoc(FReDecl, SM, LangOpts);
4079
4080 if (!Loc)
4081 return {};
4082 if (FReDecl->isThisDeclarationADefinition()) {
4083 assert(FReDecl == FD && "inconsistent function definition");
4084 // Inserts a definition with the old signature to the end of
4085 // `FReDecl`:
4086 if (auto OldOverloadDef = OldOverloadDefCreator(FReDecl))
4087 FixIts.emplace_back(FixItHint::CreateInsertion(*Loc, *OldOverloadDef));
4088 else
4089 return {}; // give up
4090 } else {
4091 // Adds the unsafe-buffer attribute (if not already there) to `FReDecl`:
4092 if (!FReDecl->hasAttr<UnsafeBufferUsageAttr>()) {
4093 FixIts.emplace_back(FixItHint::CreateInsertion(
4094 FReDecl->getBeginLoc(), Handler.getUnsafeBufferUsageAttributeTextAt(
4095 FReDecl->getBeginLoc(), " ")));
4096 }
4097 // Inserts a declaration with the new signature to the end of `FReDecl`:
4098 if (auto NewOverloadDecl = NewOverloadSignatureCreator(FReDecl))
4099 FixIts.emplace_back(FixItHint::CreateInsertion(*Loc, *NewOverloadDecl));
4100 else
4101 return {};
4102 }
4103 }
4104 return FixIts;
4105}
4106
4107// To fix a `ParmVarDecl` to be of `std::span` type.
4108static FixItList fixParamWithSpan(const ParmVarDecl *PVD, const ASTContext &Ctx,
4109 UnsafeBufferUsageHandler &Handler) {
4111 DEBUG_NOTE_DECL_FAIL(PVD, " : has unsupport specifier(s)");
4112 return {};
4113 }
4114 if (PVD->hasDefaultArg()) {
4115 // FIXME: generate fix-its for default values:
4116 DEBUG_NOTE_DECL_FAIL(PVD, " : has default arg");
4117 return {};
4118 }
4119
4120 std::optional<Qualifiers> PteTyQualifiers = std::nullopt;
4121 std::optional<std::string> PteTyText = getPointeeTypeText(
4122 PVD, Ctx.getSourceManager(), Ctx.getLangOpts(), &PteTyQualifiers);
4123
4124 if (!PteTyText) {
4125 DEBUG_NOTE_DECL_FAIL(PVD, " : invalid pointee type");
4126 return {};
4127 }
4128
4129 std::optional<StringRef> PVDNameText = PVD->getIdentifier()->getName();
4130
4131 if (!PVDNameText) {
4132 DEBUG_NOTE_DECL_FAIL(PVD, " : invalid identifier name");
4133 return {};
4134 }
4135
4136 std::stringstream SS;
4137 std::optional<std::string> SpanTyText = createSpanTypeForVarDecl(PVD, Ctx);
4138
4139 if (PteTyQualifiers)
4140 // Append qualifiers if they exist:
4141 SS << getSpanTypeText(*PteTyText, PteTyQualifiers);
4142 else
4143 SS << getSpanTypeText(*PteTyText);
4144 // Append qualifiers to the type of the parameter:
4145 if (PVD->getType().hasQualifiers())
4146 SS << ' ' << PVD->getType().getQualifiers().getAsString();
4147 // Append parameter's name:
4148 SS << ' ' << PVDNameText->str();
4149 // Add replacement fix-it:
4150 return {FixItHint::CreateReplacement(PVD->getSourceRange(), SS.str())};
4151}
4152
4153static FixItList fixVariableWithSpan(const VarDecl *VD,
4154 const DeclUseTracker &Tracker,
4155 ASTContext &Ctx,
4156 UnsafeBufferUsageHandler &Handler) {
4157 const DeclStmt *DS = Tracker.lookupDecl(VD);
4158 if (!DS) {
4160 " : variables declared this way not implemented yet");
4161 return {};
4162 }
4163 if (!DS->isSingleDecl()) {
4164 // FIXME: to support handling multiple `VarDecl`s in a single `DeclStmt`
4165 DEBUG_NOTE_DECL_FAIL(VD, " : multiple VarDecls");
4166 return {};
4167 }
4168 // Currently DS is an unused variable but we'll need it when
4169 // non-single decls are implemented, where the pointee type name
4170 // and the '*' are spread around the place.
4171 (void)DS;
4172
4173 // FIXME: handle cases where DS has multiple declarations
4174 return fixLocalVarDeclWithSpan(VD, Ctx, getUserFillPlaceHolder(), Handler);
4175}
4176
4177static FixItList fixVarDeclWithArray(const VarDecl *D, const ASTContext &Ctx,
4178 UnsafeBufferUsageHandler &Handler) {
4179 FixItList FixIts{};
4180
4181 // Note: the code below expects the declaration to not use any type sugar like
4182 // typedef.
4183 if (auto CAT = Ctx.getAsConstantArrayType(D->getType())) {
4184 const QualType &ArrayEltT = CAT->getElementType();
4185 assert(!ArrayEltT.isNull() && "Trying to fix a non-array type variable!");
4186 // FIXME: support multi-dimensional arrays
4187 if (isa<clang::ArrayType>(ArrayEltT.getCanonicalType()))
4188 return {};
4189
4191
4192 // Get the spelling of the element type as written in the source file
4193 // (including macros, etc.).
4194 auto MaybeElemTypeTxt =
4196 Ctx.getLangOpts());
4197 if (!MaybeElemTypeTxt)
4198 return {};
4199 const llvm::StringRef ElemTypeTxt = MaybeElemTypeTxt->trim();
4200
4201 // Find the '[' token.
4202 std::optional<Token> NextTok = Lexer::findNextToken(
4204 while (NextTok && !NextTok->is(tok::l_square) &&
4205 NextTok->getLocation() <= D->getSourceRange().getEnd())
4206 NextTok = Lexer::findNextToken(NextTok->getLocation(),
4207 Ctx.getSourceManager(), Ctx.getLangOpts());
4208 if (!NextTok)
4209 return {};
4210 const SourceLocation LSqBracketLoc = NextTok->getLocation();
4211
4212 // Get the spelling of the array size as written in the source file
4213 // (including macros, etc.).
4214 auto MaybeArraySizeTxt = getRangeText(
4215 {LSqBracketLoc.getLocWithOffset(1), D->getTypeSpecEndLoc()},
4216 Ctx.getSourceManager(), Ctx.getLangOpts());
4217 if (!MaybeArraySizeTxt)
4218 return {};
4219 const llvm::StringRef ArraySizeTxt = MaybeArraySizeTxt->trim();
4220 if (ArraySizeTxt.empty()) {
4221 // FIXME: Support array size getting determined from the initializer.
4222 // Examples:
4223 // int arr1[] = {0, 1, 2};
4224 // int arr2{3, 4, 5};
4225 // We might be able to preserve the non-specified size with `auto` and
4226 // `std::to_array`:
4227 // auto arr1 = std::to_array<int>({0, 1, 2});
4228 return {};
4229 }
4230
4231 std::optional<StringRef> IdentText =
4233
4234 if (!IdentText) {
4235 DEBUG_NOTE_DECL_FAIL(D, " : failed to locate the identifier");
4236 return {};
4237 }
4238
4239 SmallString<32> Replacement;
4240 llvm::raw_svector_ostream OS(Replacement);
4241 OS << "std::array<" << ElemTypeTxt << ", " << ArraySizeTxt << "> "
4242 << IdentText->str();
4243
4244 FixIts.push_back(FixItHint::CreateReplacement(
4245 SourceRange{D->getBeginLoc(), D->getTypeSpecEndLoc()}, OS.str()));
4246 }
4247
4248 return FixIts;
4249}
4250
4251static FixItList fixVariableWithArray(const VarDecl *VD,
4252 const DeclUseTracker &Tracker,
4253 const ASTContext &Ctx,
4254 UnsafeBufferUsageHandler &Handler) {
4255 const DeclStmt *DS = Tracker.lookupDecl(VD);
4256 assert(DS && "Fixing non-local variables not implemented yet!");
4257 if (!DS->isSingleDecl()) {
4258 // FIXME: to support handling multiple `VarDecl`s in a single `DeclStmt`
4259 return {};
4260 }
4261 // Currently DS is an unused variable but we'll need it when
4262 // non-single decls are implemented, where the pointee type name
4263 // and the '*' are spread around the place.
4264 (void)DS;
4265
4266 // FIXME: handle cases where DS has multiple declarations
4267 return fixVarDeclWithArray(VD, Ctx, Handler);
4268}
4269
4270// TODO: we should be consistent to use `std::nullopt` to represent no-fix due
4271// to any unexpected problem.
4272static FixItList
4274 /* The function decl under analysis */ const Decl *D,
4275 const DeclUseTracker &Tracker, ASTContext &Ctx,
4276 UnsafeBufferUsageHandler &Handler) {
4277 if (const auto *PVD = dyn_cast<ParmVarDecl>(VD)) {
4278 auto *FD = dyn_cast<clang::FunctionDecl>(PVD->getDeclContext());
4279 if (!FD || FD != D) {
4280 // `FD != D` means that `PVD` belongs to a function that is not being
4281 // analyzed currently. Thus `FD` may not be complete.
4282 DEBUG_NOTE_DECL_FAIL(VD, " : function not currently analyzed");
4283 return {};
4284 }
4285
4286 // TODO If function has a try block we can't change params unless we check
4287 // also its catch block for their use.
4288 // FIXME We might support static class methods, some select methods,
4289 // operators and possibly lamdas.
4290 if (FD->isMain() || FD->isConstexpr() ||
4292 FD->isVariadic() ||
4293 // also covers call-operator of lamdas
4294 isa<CXXMethodDecl>(FD) ||
4295 // skip when the function body is a try-block
4296 (FD->hasBody() && isa<CXXTryStmt>(FD->getBody())) ||
4297 FD->isOverloadedOperator()) {
4298 DEBUG_NOTE_DECL_FAIL(VD, " : unsupported function decl");
4299 return {}; // TODO test all these cases
4300 }
4301 }
4302
4303 switch (K) {
4305 if (VD->getType()->isPointerType()) {
4306 if (const auto *PVD = dyn_cast<ParmVarDecl>(VD))
4307 return fixParamWithSpan(PVD, Ctx, Handler);
4308
4309 if (VD->isLocalVarDecl())
4310 return fixVariableWithSpan(VD, Tracker, Ctx, Handler);
4311 }
4312 DEBUG_NOTE_DECL_FAIL(VD, " : not a pointer");
4313 return {};
4314 }
4316 if (VD->isLocalVarDecl() && Ctx.getAsConstantArrayType(VD->getType()))
4317 return fixVariableWithArray(VD, Tracker, Ctx, Handler);
4318
4319 DEBUG_NOTE_DECL_FAIL(VD, " : not a local const-size array");
4320 return {};
4321 }
4324 llvm_unreachable("FixitStrategy not implemented yet!");
4326 llvm_unreachable("Invalid strategy!");
4327 }
4328 llvm_unreachable("Unknown strategy!");
4329}
4330
4331// Returns true iff there exists a `FixItHint` 'h' in `FixIts` such that the
4332// `RemoveRange` of 'h' overlaps with a macro use.
4333static bool overlapWithMacro(const FixItList &FixIts) {
4334 // FIXME: For now we only check if the range (or the first token) is (part of)
4335 // a macro expansion. Ideally, we want to check for all tokens in the range.
4336 return llvm::any_of(FixIts, [](const FixItHint &Hint) {
4337 auto Range = Hint.RemoveRange;
4338 if (Range.getBegin().isMacroID() || Range.getEnd().isMacroID())
4339 // If the range (or the first token) is (part of) a macro expansion:
4340 return true;
4341 return false;
4342 });
4343}
4344
4345// Returns true iff `VD` is a parameter of the declaration `D`:
4346static bool isParameterOf(const VarDecl *VD, const Decl *D) {
4347 return isa<ParmVarDecl>(VD) &&
4348 VD->getDeclContext() == dyn_cast<DeclContext>(D);
4349}
4350
4351// Erases variables in `FixItsForVariable`, if such a variable has an unfixable
4352// group mate. A variable `v` is unfixable iff `FixItsForVariable` does not
4353// contain `v`.
4355 std::map<const VarDecl *, FixItList> &FixItsForVariable,
4356 const VariableGroupsManager &VarGrpMgr) {
4357 // Variables will be removed from `FixItsForVariable`:
4359
4360 for (const auto &[VD, Ignore] : FixItsForVariable) {
4361 VarGrpRef Grp = VarGrpMgr.getGroupOfVar(VD);
4362 if (llvm::any_of(Grp,
4363 [&FixItsForVariable](const VarDecl *GrpMember) -> bool {
4364 return !FixItsForVariable.count(GrpMember);
4365 })) {
4366 // At least one group member cannot be fixed, so we have to erase the
4367 // whole group:
4368 for (const VarDecl *Member : Grp)
4369 ToErase.push_back(Member);
4370 }
4371 }
4372 for (auto *VarToErase : ToErase)
4373 FixItsForVariable.erase(VarToErase);
4374}
4375
4376// Returns the fix-its that create bounds-safe function overloads for the
4377// function `D`, if `D`'s parameters will be changed to safe-types through
4378// fix-its in `FixItsForVariable`.
4379//
4380// NOTE: In case `D`'s parameters will be changed but bounds-safe function
4381// overloads cannot created, the whole group that contains the parameters will
4382// be erased from `FixItsForVariable`.
4384 std::map<const VarDecl *, FixItList> &FixItsForVariable /* mutable */,
4385 const VariableGroupsManager &VarGrpMgr, const FunctionDecl *FD,
4386 const FixitStrategy &S, ASTContext &Ctx,
4387 UnsafeBufferUsageHandler &Handler) {
4388 FixItList FixItsSharedByParms{};
4389
4390 std::optional<FixItList> OverloadFixes =
4391 createOverloadsForFixedParams(S, FD, Ctx, Handler);
4392
4393 if (OverloadFixes) {
4394 FixItsSharedByParms.append(*OverloadFixes);
4395 } else {
4396 // Something wrong in generating `OverloadFixes`, need to remove the
4397 // whole group, where parameters are in, from `FixItsForVariable` (Note
4398 // that all parameters should be in the same group):
4399 for (auto *Member : VarGrpMgr.getGroupOfParms())
4400 FixItsForVariable.erase(Member);
4401 }
4402 return FixItsSharedByParms;
4403}
4404
4405// Constructs self-contained fix-its for each variable in `FixablesForAllVars`.
4406static std::map<const VarDecl *, FixItList>
4407getFixIts(FixableGadgetSets &FixablesForAllVars, const FixitStrategy &S,
4408 ASTContext &Ctx,
4409 /* The function decl under analysis */ const Decl *D,
4410 const DeclUseTracker &Tracker, UnsafeBufferUsageHandler &Handler,
4411 const VariableGroupsManager &VarGrpMgr) {
4412 // `FixItsForVariable` will map each variable to a set of fix-its directly
4413 // associated to the variable itself. Fix-its of distinct variables in
4414 // `FixItsForVariable` are disjoint.
4415 std::map<const VarDecl *, FixItList> FixItsForVariable;
4416
4417 // Populate `FixItsForVariable` with fix-its directly associated with each
4418 // variable. Fix-its directly associated to a variable 'v' are the ones
4419 // produced by the `FixableGadget`s whose claimed variable is 'v'.
4420 for (const auto &[VD, Fixables] : FixablesForAllVars.byVar) {
4421 FixItsForVariable[VD] =
4422 fixVariable(VD, S.lookup(VD), D, Tracker, Ctx, Handler);
4423 // If we fail to produce Fix-It for the declaration we have to skip the
4424 // variable entirely.
4425 if (FixItsForVariable[VD].empty()) {
4426 FixItsForVariable.erase(VD);
4427 continue;
4428 }
4429 for (const auto &F : Fixables) {
4430 std::optional<FixItList> Fixits = F->getFixits(S);
4431
4432 if (Fixits) {
4433 FixItsForVariable[VD].insert(FixItsForVariable[VD].end(),
4434 Fixits->begin(), Fixits->end());
4435 continue;
4436 }
4437#ifndef NDEBUG
4438 Handler.addDebugNoteForVar(
4439 VD, F->getSourceLoc(),
4440 ("gadget '" + F->getDebugName() + "' refused to produce a fix")
4441 .str());
4442#endif
4443 FixItsForVariable.erase(VD);
4444 break;
4445 }
4446 }
4447
4448 // `FixItsForVariable` now contains only variables that can be
4449 // fixed. A variable can be fixed if its declaration and all Fixables
4450 // associated to it can all be fixed.
4451
4452 // To further remove from `FixItsForVariable` variables whose group mates
4453 // cannot be fixed...
4454 eraseVarsForUnfixableGroupMates(FixItsForVariable, VarGrpMgr);
4455 // Now `FixItsForVariable` gets further reduced: a variable is in
4456 // `FixItsForVariable` iff it can be fixed and all its group mates can be
4457 // fixed.
4458
4459 // Fix-its of bounds-safe overloads of `D` are shared by parameters of `D`.
4460 // That is, when fixing multiple parameters in one step, these fix-its will
4461 // be applied only once (instead of being applied per parameter).
4462 FixItList FixItsSharedByParms{};
4463
4464 if (auto *FD = dyn_cast<FunctionDecl>(D))
4465 FixItsSharedByParms = createFunctionOverloadsForParms(
4466 FixItsForVariable, VarGrpMgr, FD, S, Ctx, Handler);
4467
4468 // The map that maps each variable `v` to fix-its for the whole group where
4469 // `v` is in:
4470 std::map<const VarDecl *, FixItList> FinalFixItsForVariable{
4471 FixItsForVariable};
4472
4473 for (auto &[Var, Ignore] : FixItsForVariable) {
4474 bool AnyParm = false;
4475 const auto VarGroupForVD = VarGrpMgr.getGroupOfVar(Var, &AnyParm);
4476
4477 for (const VarDecl *GrpMate : VarGroupForVD) {
4478 if (Var == GrpMate)
4479 continue;
4480 if (FixItsForVariable.count(GrpMate))
4481 FinalFixItsForVariable[Var].append(FixItsForVariable[GrpMate]);
4482 }
4483 if (AnyParm) {
4484 // This assertion should never fail. Otherwise we have a bug.
4485 assert(!FixItsSharedByParms.empty() &&
4486 "Should not try to fix a parameter that does not belong to a "
4487 "FunctionDecl");
4488 FinalFixItsForVariable[Var].append(FixItsSharedByParms);
4489 }
4490 }
4491 // Fix-its that will be applied in one step shall NOT:
4492 // 1. overlap with macros or/and templates; or
4493 // 2. conflict with each other.
4494 // Otherwise, the fix-its will be dropped.
4495 for (auto Iter = FinalFixItsForVariable.begin();
4496 Iter != FinalFixItsForVariable.end();)
4497 if (overlapWithMacro(Iter->second) ||
4498 clang::internal::anyConflict(Iter->second, Ctx.getSourceManager())) {
4499 Iter = FinalFixItsForVariable.erase(Iter);
4500 } else
4501 Iter++;
4502 return FinalFixItsForVariable;
4503}
4504
4505template <typename VarDeclIterTy>
4506static FixitStrategy
4507getNaiveStrategy(llvm::iterator_range<VarDeclIterTy> UnsafeVars) {
4508 FixitStrategy S;
4509 for (const VarDecl *VD : UnsafeVars) {
4512 else
4514 }
4515 return S;
4516}
4517
4518// Manages variable groups:
4520 const std::vector<VarGrpTy> &Groups;
4521 const std::map<const VarDecl *, unsigned> &VarGrpMap;
4522 const llvm::SetVector<const VarDecl *> &GrpsUnionForParms;
4523
4524public:
4526 const std::vector<VarGrpTy> &Groups,
4527 const std::map<const VarDecl *, unsigned> &VarGrpMap,
4528 const llvm::SetVector<const VarDecl *> &GrpsUnionForParms)
4529 : Groups(Groups), VarGrpMap(VarGrpMap),
4530 GrpsUnionForParms(GrpsUnionForParms) {}
4531
4532 VarGrpRef getGroupOfVar(const VarDecl *Var, bool *HasParm) const override {
4533 if (GrpsUnionForParms.contains(Var)) {
4534 if (HasParm)
4535 *HasParm = true;
4536 return GrpsUnionForParms.getArrayRef();
4537 }
4538 if (HasParm)
4539 *HasParm = false;
4540
4541 auto It = VarGrpMap.find(Var);
4542
4543 if (It == VarGrpMap.end())
4544 return {};
4545 return Groups[It->second];
4546 }
4547
4548 VarGrpRef getGroupOfParms() const override {
4549 return GrpsUnionForParms.getArrayRef();
4550 }
4551};
4552
4553static void applyGadgets(const Decl *D, FixableGadgetList FixableGadgets,
4554 WarningGadgetList WarningGadgets,
4555 DeclUseTracker Tracker,
4556 UnsafeBufferUsageHandler &Handler,
4557 bool EmitSuggestions) {
4558 if (!EmitSuggestions) {
4559 // Our job is very easy without suggestions. Just warn about
4560 // every problematic operation and consider it done. No need to deal
4561 // with fixable gadgets, no need to group operations by variable.
4562 for (const auto &G : WarningGadgets) {
4563 G->handleUnsafeOperation(Handler, /*IsRelatedToDecl=*/false,
4564 D->getASTContext());
4565 }
4566
4567 // This return guarantees that most of the machine doesn't run when
4568 // suggestions aren't requested.
4569 assert(FixableGadgets.empty() &&
4570 "Fixable gadgets found but suggestions not requested!");
4571 return;
4572 }
4573
4574 // If no `WarningGadget`s ever matched, there is no unsafe operations in the
4575 // function under the analysis. No need to fix any Fixables.
4576 if (!WarningGadgets.empty()) {
4577 // Gadgets "claim" variables they're responsible for. Once this loop
4578 // finishes, the tracker will only track DREs that weren't claimed by any
4579 // gadgets, i.e. not understood by the analysis.
4580 for (const auto &G : FixableGadgets) {
4581 for (const auto *DRE : G->getClaimedVarUseSites()) {
4582 Tracker.claimUse(DRE);
4583 }
4584 }
4585 }
4586
4587 // If no `WarningGadget`s ever matched, there is no unsafe operations in the
4588 // function under the analysis. Thus, it early returns here as there is
4589 // nothing needs to be fixed.
4590 //
4591 // Note this claim is based on the assumption that there is no unsafe
4592 // variable whose declaration is invisible from the analyzing function.
4593 // Otherwise, we need to consider if the uses of those unsafe varuables needs
4594 // fix.
4595 // So far, we are not fixing any global variables or class members. And,
4596 // lambdas will be analyzed along with the enclosing function. So this early
4597 // return is correct for now.
4598 if (WarningGadgets.empty())
4599 return;
4600
4601 WarningGadgetSets UnsafeOps =
4602 groupWarningGadgetsByVar(std::move(WarningGadgets));
4603 FixableGadgetSets FixablesForAllVars =
4604 groupFixablesByVar(std::move(FixableGadgets));
4605
4606 std::map<const VarDecl *, FixItList> FixItsForVariableGroup;
4607
4608 // Filter out non-local vars and vars with unclaimed DeclRefExpr-s.
4609 for (auto it = FixablesForAllVars.byVar.cbegin();
4610 it != FixablesForAllVars.byVar.cend();) {
4611 // FIXME: need to deal with global variables later
4612 if ((!it->first->isLocalVarDecl() && !isa<ParmVarDecl>(it->first))) {
4613#ifndef NDEBUG
4614 Handler.addDebugNoteForVar(it->first, it->first->getBeginLoc(),
4615 ("failed to produce fixit for '" +
4616 it->first->getNameAsString() +
4617 "' : neither local nor a parameter"));
4618#endif
4619 it = FixablesForAllVars.byVar.erase(it);
4620 } else if (it->first->getType().getCanonicalType()->isReferenceType()) {
4621#ifndef NDEBUG
4622 Handler.addDebugNoteForVar(it->first, it->first->getBeginLoc(),
4623 ("failed to produce fixit for '" +
4624 it->first->getNameAsString() +
4625 "' : has a reference type"));
4626#endif
4627 it = FixablesForAllVars.byVar.erase(it);
4628 } else if (Tracker.hasUnclaimedUses(it->first)) {
4629 it = FixablesForAllVars.byVar.erase(it);
4630 } else if (it->first->isInitCapture()) {
4631#ifndef NDEBUG
4632 Handler.addDebugNoteForVar(it->first, it->first->getBeginLoc(),
4633 ("failed to produce fixit for '" +
4634 it->first->getNameAsString() +
4635 "' : init capture"));
4636#endif
4637 it = FixablesForAllVars.byVar.erase(it);
4638 } else {
4639 ++it;
4640 }
4641 }
4642
4643#ifndef NDEBUG
4644 for (const auto &it : UnsafeOps.byVar) {
4645 const VarDecl *const UnsafeVD = it.first;
4646 auto UnclaimedDREs = Tracker.getUnclaimedUses(UnsafeVD);
4647 if (UnclaimedDREs.empty())
4648 continue;
4649 const auto UnfixedVDName = UnsafeVD->getNameAsString();
4650 for (const clang::DeclRefExpr *UnclaimedDRE : UnclaimedDREs) {
4651 std::string UnclaimedUseTrace =
4652 getDREAncestorString(UnclaimedDRE, D->getASTContext());
4653
4654 Handler.addDebugNoteForVar(
4655 UnsafeVD, UnclaimedDRE->getBeginLoc(),
4656 ("failed to produce fixit for '" + UnfixedVDName +
4657 "' : has an unclaimed use\nThe unclaimed DRE trace: " +
4658 UnclaimedUseTrace));
4659 }
4660 }
4661#endif
4662
4663 // Fixpoint iteration for pointer assignments
4664 using DepMapTy =
4665 llvm::DenseMap<const VarDecl *, llvm::SetVector<const VarDecl *>>;
4666 DepMapTy DependenciesMap{};
4667 DepMapTy PtrAssignmentGraph{};
4668
4669 for (const auto &it : FixablesForAllVars.byVar) {
4670 for (const FixableGadget *fixable : it.second) {
4671 std::optional<std::pair<const VarDecl *, const VarDecl *>> ImplPair =
4672 fixable->getStrategyImplications();
4673 if (ImplPair) {
4674 std::pair<const VarDecl *, const VarDecl *> Impl = std::move(*ImplPair);
4675 PtrAssignmentGraph[Impl.first].insert(Impl.second);
4676 }
4677 }
4678 }
4679
4680 /*
4681 The following code does a BFS traversal of the `PtrAssignmentGraph`
4682 considering all unsafe vars as starting nodes and constructs an undirected
4683 graph `DependenciesMap`. Constructing the `DependenciesMap` in this manner
4684 elimiates all variables that are unreachable from any unsafe var. In other
4685 words, this removes all dependencies that don't include any unsafe variable
4686 and consequently don't need any fixit generation.
4687 Note: A careful reader would observe that the code traverses
4688 `PtrAssignmentGraph` using `CurrentVar` but adds edges between `Var` and
4689 `Adj` and not between `CurrentVar` and `Adj`. Both approaches would
4690 achieve the same result but the one used here dramatically cuts the
4691 amount of hoops the second part of the algorithm needs to jump, given that
4692 a lot of these connections become "direct". The reader is advised not to
4693 imagine how the graph is transformed because of using `Var` instead of
4694 `CurrentVar`. The reader can continue reading as if `CurrentVar` was used,
4695 and think about why it's equivalent later.
4696 */
4697 std::set<const VarDecl *> VisitedVarsDirected{};
4698 for (const auto &[Var, ignore] : UnsafeOps.byVar) {
4699 if (VisitedVarsDirected.find(Var) == VisitedVarsDirected.end()) {
4700
4701 std::queue<const VarDecl *> QueueDirected{};
4702 QueueDirected.push(Var);
4703 while (!QueueDirected.empty()) {
4704 const VarDecl *CurrentVar = QueueDirected.front();
4705 QueueDirected.pop();
4706 VisitedVarsDirected.insert(CurrentVar);
4707 auto AdjacentNodes = PtrAssignmentGraph[CurrentVar];
4708 for (const VarDecl *Adj : AdjacentNodes) {
4709 if (VisitedVarsDirected.find(Adj) == VisitedVarsDirected.end()) {
4710 QueueDirected.push(Adj);
4711 }
4712 DependenciesMap[Var].insert(Adj);
4713 DependenciesMap[Adj].insert(Var);
4714 }
4715 }
4716 }
4717 }
4718
4719 // `Groups` stores the set of Connected Components in the graph.
4720 std::vector<VarGrpTy> Groups;
4721 // `VarGrpMap` maps variables that need fix to the groups (indexes) that the
4722 // variables belong to. Group indexes refer to the elements in `Groups`.
4723 // `VarGrpMap` is complete in that every variable that needs fix is in it.
4724 std::map<const VarDecl *, unsigned> VarGrpMap;
4725 // The union group over the ones in "Groups" that contain parameters of `D`:
4726 llvm::SetVector<const VarDecl *>
4727 GrpsUnionForParms; // these variables need to be fixed in one step
4728
4729 // Group Connected Components for Unsafe Vars
4730 // (Dependencies based on pointer assignments)
4731 std::set<const VarDecl *> VisitedVars{};
4732 for (const auto &[Var, ignore] : UnsafeOps.byVar) {
4733 if (VisitedVars.find(Var) == VisitedVars.end()) {
4734 VarGrpTy &VarGroup = Groups.emplace_back();
4735 std::queue<const VarDecl *> Queue{};
4736
4737 Queue.push(Var);
4738 while (!Queue.empty()) {
4739 const VarDecl *CurrentVar = Queue.front();
4740 Queue.pop();
4741 VisitedVars.insert(CurrentVar);
4742 VarGroup.push_back(CurrentVar);
4743 auto AdjacentNodes = DependenciesMap[CurrentVar];
4744 for (const VarDecl *Adj : AdjacentNodes) {
4745 if (VisitedVars.find(Adj) == VisitedVars.end()) {
4746 Queue.push(Adj);
4747 }
4748 }
4749 }
4750
4751 bool HasParm = false;
4752 unsigned GrpIdx = Groups.size() - 1;
4753
4754 for (const VarDecl *V : VarGroup) {
4755 VarGrpMap[V] = GrpIdx;
4756 if (!HasParm && isParameterOf(V, D))
4757 HasParm = true;
4758 }
4759 if (HasParm)
4760 GrpsUnionForParms.insert_range(VarGroup);
4761 }
4762 }
4763
4764 // Remove a `FixableGadget` if the associated variable is not in the graph
4765 // computed above. We do not want to generate fix-its for such variables,
4766 // since they are neither warned nor reachable from a warned one.
4767 //
4768 // Note a variable is not warned if it is not directly used in any unsafe
4769 // operation. A variable `v` is NOT reachable from an unsafe variable, if it
4770 // does not exist another variable `u` such that `u` is warned and fixing `u`
4771 // (transitively) implicates fixing `v`.
4772 //
4773 // For example,
4774 // ```
4775 // void f(int * p) {
4776 // int * a = p; *p = 0;
4777 // }
4778 // ```
4779 // `*p = 0` is a fixable gadget associated with a variable `p` that is neither
4780 // warned nor reachable from a warned one. If we add `a[5] = 0` to the end of
4781 // the function above, `p` becomes reachable from a warned variable.
4782 for (auto I = FixablesForAllVars.byVar.begin();
4783 I != FixablesForAllVars.byVar.end();) {
4784 // Note `VisitedVars` contain all the variables in the graph:
4785 if (!VisitedVars.count((*I).first)) {
4786 // no such var in graph:
4787 I = FixablesForAllVars.byVar.erase(I);
4788 } else
4789 ++I;
4790 }
4791
4792 // We assign strategies to variables that are 1) in the graph and 2) can be
4793 // fixed. Other variables have the default "Won't fix" strategy.
4794 FixitStrategy NaiveStrategy = getNaiveStrategy(llvm::make_filter_range(
4795 VisitedVars, [&FixablesForAllVars](const VarDecl *V) {
4796 // If a warned variable has no "Fixable", it is considered unfixable:
4797 return FixablesForAllVars.byVar.count(V);
4798 }));
4799 VariableGroupsManagerImpl VarGrpMgr(Groups, VarGrpMap, GrpsUnionForParms);
4800
4801 if (isa<NamedDecl>(D))
4802 // The only case where `D` is not a `NamedDecl` is when `D` is a
4803 // `BlockDecl`. Let's not fix variables in blocks for now
4804 FixItsForVariableGroup =
4805 getFixIts(FixablesForAllVars, NaiveStrategy, D->getASTContext(), D,
4806 Tracker, Handler, VarGrpMgr);
4807
4808 for (const auto &G : UnsafeOps.noVar) {
4809 G->handleUnsafeOperation(Handler, /*IsRelatedToDecl=*/false,
4810 D->getASTContext());
4811 }
4812
4813 for (const auto &[VD, WarningGadgets] : UnsafeOps.byVar) {
4814 auto FixItsIt = FixItsForVariableGroup.find(VD);
4815 Handler.handleUnsafeVariableGroup(VD, VarGrpMgr,
4816 FixItsIt != FixItsForVariableGroup.end()
4817 ? std::move(FixItsIt->second)
4818 : FixItList{},
4819 D, NaiveStrategy);
4820 for (const auto &G : WarningGadgets) {
4821 G->handleUnsafeOperation(Handler, /*IsRelatedToDecl=*/true,
4822 D->getASTContext());
4823 }
4824 }
4825}
4826
4828 UnsafeBufferUsageHandler &Handler,
4829 bool EmitSuggestions) {
4830#ifndef NDEBUG
4831 Handler.clearDebugNotes();
4832#endif
4833
4834 assert(D);
4835 if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
4836 // Consteval functions are free of UB by the spec, so we don't need to
4837 // visit them or produce diagnostics.
4838 if (FD->isConsteval())
4839 return;
4840 // We do not want to visit a Lambda expression defined inside a method
4841 // independently. Instead, it should be visited along with the outer method.
4842 // FIXME: do we want to do the same thing for `BlockDecl`s?
4843 if (const auto *MD = dyn_cast<CXXMethodDecl>(D)) {
4844 if (MD->getParent()->isLambda() && MD->getParent()->isLocalClass())
4845 return;
4846 }
4847
4848 for (FunctionDecl *FReDecl : FD->redecls()) {
4849 if (FReDecl->isExternC()) {
4850 // Do not emit fixit suggestions for functions declared in an
4851 // extern "C" block.
4852 EmitSuggestions = false;
4853 break;
4854 }
4855 }
4856 }
4857
4859
4861
4862 assert(!Stmts.empty());
4863
4864 FixableGadgetList FixableGadgets;
4865 WarningGadgetList WarningGadgets;
4866 DeclUseTracker Tracker;
4867 for (const Stmt *S : Stmts) {
4868 findGadgets(S, D->getASTContext(), Handler, EmitSuggestions, FixableGadgets,
4869 WarningGadgets, Tracker);
4870 }
4871 applyGadgets(D, std::move(FixableGadgets), std::move(WarningGadgets),
4872 std::move(Tracker), Handler, EmitSuggestions);
4873}
4874
4876 std::set<const Expr *> &UnsafePointers) {
4877 class MockReporter : public UnsafeBufferUsageHandler {
4878 public:
4879 MockReporter() {}
4880 void handleUnsafeOperation(const Stmt *, bool, ASTContext &) override {}
4881 void handleUnsafeLibcCall(const CallExpr *, unsigned, ASTContext &,
4882 const Expr *UnsafeArg = nullptr) override {}
4883 void handleUnsafeOperationInContainer(const Stmt *, bool,
4884 ASTContext &) override {}
4885 void handleUnsafeOperationInStringView(const Stmt *, bool,
4886 ASTContext &) override {}
4887 void handleUnsafeVariableGroup(const VarDecl *,
4888 const VariableGroupsManager &, FixItList &&,
4889 const Decl *,
4890 const FixitStrategy &) override {}
4891 void handleUnsafeUniquePtrArrayAccess(const DynTypedNode &Node,
4892 bool IsRelatedToDecl,
4893 ASTContext &Ctx) override {}
4894 bool ignoreUnsafeBufferInContainer(const SourceLocation &) const override {
4895 return false;
4896 }
4897 bool isSafeBufferOptOut(const SourceLocation &) const override {
4898 return false;
4899 }
4900 bool ignoreUnsafeBufferInLibcCall(const SourceLocation &) const override {
4901 return false;
4902 }
4903 bool ignoreUnsafeBufferInStaticSizedArray(
4904 const SourceLocation &Loc) const override {
4905 return false;
4906 }
4907 std::string getUnsafeBufferUsageAttributeTextAt(
4908 SourceLocation, StringRef WSSuffix = "") const override {
4909 return "";
4910 }
4911 } Handler;
4912
4913 const Stmt *S = N.get<Stmt>();
4914 if (!S)
4915 return false;
4916
4917 MatchResult Result;
4918 WarningGadgetList WarningGadgets;
4919 bool Matched = false;
4920
4921 // FIXME: By design, we don't need MockReporter, and we are supposed to
4922 // only define WARNING_GADGET when we want to treat WARNING_OPTIONAL_GADGET
4923 // the same as WARNING_GADGET. The reason we have to do it this way now is
4924 // that some WARNING_OPTIONAL_GADGETs do not have the 3-argument `matches`
4925 // overload. We need to fix this problem in a separate patch.
4926
4927#define WARNING_GADGET(name) \
4928 if (name##Gadget::matches(S, Ctx, Result)) \
4929 WarningGadgets.push_back(std::make_unique<name##Gadget>(Result));
4930#define WARNING_OPTIONAL_GADGET(name) \
4931 if (name##Gadget::matches(S, Ctx, &Handler, Result)) \
4932 WarningGadgets.push_back(std::make_unique<name##Gadget>(Result));
4933#include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
4934
4935 for (auto &WG : WarningGadgets)
4936 for (auto *E : WG->getUnsafePtrs()) {
4937 UnsafePointers.insert(E);
4938 Matched = true;
4939 }
4940 return Matched;
4941}
Defines the clang::ASTContext interface.
#define V(N, I)
static bool ignoreUnsafeLibcCall(const ASTContext &Ctx, const Stmt &Node, const UnsafeBufferUsageHandler *Handler)
static void findStmtsInUnspecifiedLvalueContext(const Stmt *S, const llvm::function_ref< void(const Expr *)> OnResult)
static std::string getUserFillPlaceHolder(StringRef HintTextToUser="placeholder")
static FixItList fixVariableWithSpan(const VarDecl *VD, const DeclUseTracker &Tracker, ASTContext &Ctx, UnsafeBufferUsageHandler &Handler)
static std::optional< FixItList > fixUPCAddressofArraySubscriptWithSpan(const UnaryOperator *Node)
static bool ignoreUnsafeBufferInContainer(const Stmt &Node, const UnsafeBufferUsageHandler *Handler)
static WarningGadgetSets groupWarningGadgetsByVar(const WarningGadgetList &AllUnsafeOperations)
static bool hasArrayType(const Expr &E)
static StringRef getEndOfLine()
static bool notInSafeBufferOptOut(const Stmt &Node, const UnsafeBufferUsageHandler *Handler)
static std::optional< FixItList > FixVarInitializerWithSpan(const Expr *Init, ASTContext &Ctx, const StringRef UserFillPlaceHolder)
static std::optional< SourceLocation > getEndCharLoc(const NodeTy *Node, const SourceManager &SM, const LangOptions &LangOpts)
static FixItList fixVariableWithArray(const VarDecl *VD, const DeclUseTracker &Tracker, const ASTContext &Ctx, UnsafeBufferUsageHandler &Handler)
static bool areEqualIntegralBinaryOperators(const BinaryOperator *E1, const Expr *E2_LHS, BinaryOperatorKind BOP, const Expr *E2_RHS, ASTContext &Ctx)
static bool hasPointerType(const Expr &E)
static std::string getSpanTypeText(StringRef EltTyText, std::optional< Qualifiers > Quals=std::nullopt)
static SourceRange getSourceRangeToTokenEnd(const Decl *D, const SourceManager &SM, const LangOptions &LangOpts)
static FixItList fixLocalVarDeclWithSpan(const VarDecl *D, ASTContext &Ctx, const StringRef UserFillPlaceHolder, UnsafeBufferUsageHandler &Handler)
static bool isSafeArraySubscript(const ArraySubscriptExpr &Node, const ASTContext &Ctx, const bool IgnoreStaticSizedArrays)
static std::optional< FixItList > createDataFixit(const ASTContext &Ctx, const DeclRefExpr *DRE)
static FixItList createFunctionOverloadsForParms(std::map< const VarDecl *, FixItList > &FixItsForVariable, const VariableGroupsManager &VarGrpMgr, const FunctionDecl *FD, const FixitStrategy &S, ASTContext &Ctx, UnsafeBufferUsageHandler &Handler)
static bool isNullTermPointer(const Expr *Ptr, ASTContext &Ctx)
static bool isSafeSpanTwoParamConstruct(const CXXConstructExpr &Node, ASTContext &Ctx)
static bool isSafeStringViewTwoParamConstruct(const CXXConstructExpr &Node, ASTContext &Ctx)
static FixItList fixVarDeclWithArray(const VarDecl *D, const ASTContext &Ctx, UnsafeBufferUsageHandler &Handler)
static FixItList fixVariable(const VarDecl *VD, FixitStrategy::Kind K, const Decl *D, const DeclUseTracker &Tracker, ASTContext &Ctx, UnsafeBufferUsageHandler &Handler)
static FixItList fixParamWithSpan(const ParmVarDecl *PVD, const ASTContext &Ctx, UnsafeBufferUsageHandler &Handler)
static FixitStrategy getNaiveStrategy(llvm::iterator_range< VarDeclIterTy > UnsafeVars)
static std::optional< std::string > createSpanTypeForVarDecl(const VarDecl *VD, const ASTContext &Ctx)
static bool hasConflictingOverload(const FunctionDecl *FD)
static void findStmtsInUnspecifiedPointerContext(const Stmt *S, llvm::function_ref< void(const Stmt *)> InnerMatcher)
static bool isNonNegativeIntegerExpr(const Expr *Expr, const VarDecl *VD, const ASTContext &Ctx)
static bool overlapWithMacro(const FixItList &FixIts)
static void forEachDescendantStmt(const Stmt *S, ASTContext &Ctx, const UnsafeBufferUsageHandler &Handler, FastMatcher &Matcher)
static bool hasUnsupportedSpecifiers(const VarDecl *VD, const SourceManager &SM)
static const Expr * tryConstantFoldConditionalExpr(const Expr *E, const ASTContext &Ctx)
#define DEBUG_NOTE_DECL_FAIL(D, Msg)
static void applyGadgets(const Decl *D, FixableGadgetList FixableGadgets, WarningGadgetList WarningGadgets, DeclUseTracker Tracker, UnsafeBufferUsageHandler &Handler, bool EmitSuggestions)
static bool isSafePointerArithmetic(const Expr *Ptr, const Expr *OffsetExpr, BinaryOperatorKind Opcode, const ASTContext &Ctx)
static bool areEqualIntegers(const Expr *E1, const Expr *E2, ASTContext &Ctx)
static void findGadgets(const Stmt *S, ASTContext &Ctx, const UnsafeBufferUsageHandler &Handler, bool EmitSuggestions, FixableGadgetList &FixableGadgets, WarningGadgetList &WarningGadgets, DeclUseTracker &Tracker)
static const Expr * getSubExprInSizeOfExpr(const Expr &E)
static std::map< const VarDecl *, FixItList > getFixIts(FixableGadgetSets &FixablesForAllVars, const FixitStrategy &S, ASTContext &Ctx, const Decl *D, const DeclUseTracker &Tracker, UnsafeBufferUsageHandler &Handler, const VariableGroupsManager &VarGrpMgr)
static bool isPtrBufferSafe(const Expr *Ptr, const Expr *Size, ASTContext &Ctx)
static const Expr * getSubExprInAddressOfExpr(const Expr &E)
static void forEachDescendantEvaluatedStmt(const Stmt *S, ASTContext &Ctx, const UnsafeBufferUsageHandler &Handler, FastMatcher &Matcher)
static void findStmtsInUnspecifiedUntypedContext(const Stmt *S, llvm::function_ref< void(const Stmt *)> InnerMatcher)
static std::optional< FixItList > createOverloadsForFixedParams(const FixitStrategy &S, const FunctionDecl *FD, const ASTContext &Ctx, UnsafeBufferUsageHandler &Handler)
static void eraseVarsForUnfixableGroupMates(std::map< const VarDecl *, FixItList > &FixItsForVariable, const VariableGroupsManager &VarGrpMgr)
static FixableGadgetSets groupFixablesByVar(FixableGadgetList &&AllFixableOperations)
static bool isParameterOf(const VarDecl *VD, const Decl *D)
#define SIZED_CONTAINER_OR_VIEW_LIST
static void populateStmtsForFindingGadgets(SmallVector< const Stmt * > &Stmts, const Decl *D)
static std::optional< StringRef > getFunNameText(const FunctionDecl *FD, const SourceManager &SM, const LangOptions &LangOpts)
static Decl::Kind getKind(const Decl *D)
Defines the C++ Decl subclasses, other than those for templates (found in DeclTemplate....
Defines the C++ template declaration subclasses.
Defines the clang::Preprocessor interface.
MatchFinder::MatchResult MatchResult
Defines the clang::SourceLocation class and associated facilities.
static QualType getPointeeType(const MemRegion *R)
C Language Family Type Representation.
virtual std::optional< FixItList > getFixits(const FixitStrategy &s) const final
static bool matches(const Stmt *S, llvm::SmallVectorImpl< MatchResult > &Results)
DerefSimplePtrArithFixableGadget(const MatchResult &Result)
SourceLocation getSourceLoc() const override
virtual DeclUseList getClaimedVarUseSites() const final
FixableGadgetMatcher(FixableGadgetList &FixableGadgets, DeclUseTracker &Tracker)
bool matches(const DynTypedNode &DynNode, ASTContext &Ctx, const UnsafeBufferUsageHandler &Handler) override
Represents the length modifier in a format string in scanf/printf.
Kind getKind() const
bool TraverseCXXTypeidExpr(CXXTypeidExpr *Node) override
bool TraverseDecltypeTypeLoc(DecltypeTypeLoc Node, bool TraverseQualifier) override
bool TraverseTypeOfExprTypeLoc(TypeOfExprTypeLoc Node, bool TraverseQualifier) override
bool TraverseGenericSelectionExpr(GenericSelectionExpr *Node) override
MatchDescendantVisitor(ASTContext &Context, FastMatcher &Matcher, bool FindAll, bool IgnoreUnevaluatedContext, const UnsafeBufferUsageHandler &NewHandler)
bool TraverseDecl(Decl *Node) override
bool TraverseUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *Node) override
bool findMatch(const DynTypedNode &DynNode)
bool TraverseCXXDefaultInitExpr(CXXDefaultInitExpr *Node) override
bool TraverseCXXNoexceptExpr(CXXNoexceptExpr *Node) override
bool TraverseStmt(Stmt *Node) override
virtual std::optional< FixItList > getFixits(const FixitStrategy &S) const override
static bool matches(const Stmt *S, llvm::SmallVectorImpl< MatchResult > &Results)
SourceLocation getSourceLoc() const override
virtual DeclUseList getClaimedVarUseSites() const override
UPCPreIncrementGadget(const MatchResult &Result)
static bool classof(const Gadget *G)
static bool classof(const Gadget *G)
UUCAddAssignGadget(const MatchResult &Result)
virtual std::optional< FixItList > getFixits(const FixitStrategy &S) const override
static bool matches(const Stmt *S, llvm::SmallVectorImpl< MatchResult > &Results)
virtual DeclUseList getClaimedVarUseSites() const override
SourceLocation getSourceLoc() const override
VariableGroupsManagerImpl(const std::vector< VarGrpTy > &Groups, const std::map< const VarDecl *, unsigned > &VarGrpMap, const llvm::SetVector< const VarDecl * > &GrpsUnionForParms)
VarGrpRef getGroupOfVar(const VarDecl *Var, bool *HasParm) const override
Returns the set of variables (including Var) that need to be fixed together in one step.
VarGrpRef getGroupOfParms() const override
Returns the non-empty group of variables that include parameters of the analyzing function,...
bool matches(const DynTypedNode &DynNode, ASTContext &Ctx, const UnsafeBufferUsageHandler &Handler) override
WarningGadgetMatcher(WarningGadgetList &WarningGadgets)
APSInt & getInt()
Definition APValue.h:511
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition ASTContext.h:223
SourceManager & getSourceManager()
Definition ASTContext.h:887
const ConstantArrayType * getAsConstantArrayType(QualType T) const
DynTypedNodeList getParents(const NodeT &Node)
Forwards to get node parents from the ParentMapContext.
QualType getFILEType() const
Retrieve the C FILE type.
const LangOptions & getLangOpts() const
Definition ASTContext.h:983
uint64_t getTypeSize(QualType T) const
Return the size of the specified (complete) type T, in bits.
CharUnits getTypeSizeInChars(QualType T) const
Return the size of the specified (complete) type T, in characters.
QualType getSizeType() const
Return the unique type for "size_t" (C99 7.17), defined in <stddef.h>.
const TargetInfo & getTargetInfo() const
Definition ASTContext.h:945
ArraySubscriptExpr - [C99 6.5.2.1] Array Subscripting.
Definition Expr.h:2765
Attr - This represents one attribute.
Definition Attr.h:46
A builtin binary operation expression such as "x + y" or "x <= y".
Definition Expr.h:4082
Expr * getLHS() const
Definition Expr.h:4132
static StringRef getOpcodeStr(Opcode Op)
getOpcodeStr - Turn an Opcode enum value into the punctuation char it corresponds to,...
Definition Expr.cpp:2164
Expr * getRHS() const
Definition Expr.h:4134
Opcode getOpcode() const
Definition Expr.h:4127
Represents a call to a C++ constructor.
Definition ExprCXX.h:1552
Expr * getArg(unsigned Arg)
Return the specified argument.
Definition ExprCXX.h:1695
unsigned getNumArgs() const
Return the number of arguments to the constructor call.
Definition ExprCXX.h:1692
Expr * getInit() const
Get the initializer.
Definition DeclCXX.h:2608
A use of a default initializer in a constructor or in aggregate initialization.
Definition ExprCXX.h:1381
Expr * getExpr()
Get the initialization expression that will be used.
Definition ExprCXX.cpp:1138
Represents a static or instance method of a struct/union/class.
Definition DeclCXX.h:2149
Represents a C++11 noexcept expression (C++ [expr.unary.noexcept]).
Definition ExprCXX.h:4360
OverloadedOperatorKind getOperator() const
Returns the kind of overloaded operator that this expression refers to.
Definition ExprCXX.h:115
Represents a C++ struct/union/class.
Definition DeclCXX.h:258
CXXRecordDecl * getCanonicalDecl() override
Retrieves the "canonical" declaration of the given declaration.
Definition DeclCXX.h:522
A C++ typeid expression (C++ [expr.typeid]), which gets the type_info that corresponds to the supplie...
Definition ExprCXX.h:852
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
Definition Expr.h:2987
Expr * getArg(unsigned Arg)
getArg - Return the specified argument.
Definition Expr.h:3191
FunctionDecl * getDirectCallee()
If the callee is a FunctionDecl, return it. Otherwise return null.
Definition Expr.h:3170
Expr * getCallee()
Definition Expr.h:3134
arg_range arguments()
Definition Expr.h:3239
static const char * getCastKindName(CastKind CK)
Definition Expr.cpp:1981
Represents a byte-granular source range.
static CharSourceRange getCharRange(SourceRange R)
SourceLocation getEnd() const
bool isOne() const
isOne - Test whether the quantity equals one.
Definition CharUnits.h:125
Represents a class template specialization, which refers to a class template with a given set of temp...
const TemplateArgumentList & getTemplateArgs() const
Retrieve the template arguments of the class template specialization.
ConstStmtVisitor - This class implements a simple visitor for Stmt subclasses.
DeclContext * getParent()
getParent - Returns the containing DeclContext.
Definition DeclBase.h:2126
lookup_result lookup(DeclarationName Name) const
lookup - Find the declarations (if any) with the given Name in this context.
A reference to a declared variable, function, enum, etc.
Definition Expr.h:1290
ValueDecl * getDecl()
Definition Expr.h:1358
DeclStmt - Adaptor class for mixing declarations with statements and expressions.
Definition Stmt.h:1643
bool isSingleDecl() const
isSingleDecl - This method returns true if this DeclStmt refers to a single Decl.
Definition Stmt.h:1656
decl_range decls()
Definition Stmt.h:1691
const Decl * getSingleDecl() const
Definition Stmt.h:1658
Decl - This represents one declaration (or definition), e.g.
Definition DeclBase.h:86
bool isInStdNamespace() const
Definition DeclBase.cpp:453
SourceLocation getEndLoc() const LLVM_READONLY
Definition DeclBase.h:443
ASTContext & getASTContext() const LLVM_READONLY
Definition DeclBase.cpp:550
bool isImplicit() const
isImplicit - Indicates whether the declaration was implicitly generated by the implementation.
Definition DeclBase.h:601
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:1104
llvm::iterator_range< specific_attr_iterator< T > > specific_attrs() const
Definition DeclBase.h:567
DeclContext * getDeclContext()
Definition DeclBase.h:456
attr_range attrs() const
Definition DeclBase.h:543
SourceLocation getBeginLoc() const LLVM_READONLY
Definition DeclBase.h:439
virtual Decl * getCanonicalDecl()
Retrieves the "canonical" declaration of the given declaration.
Definition DeclBase.h:995
SourceLocation getTypeSpecEndLoc() const
Definition Decl.cpp:2012
SourceLocation getBeginLoc() const LLVM_READONLY
Definition Decl.h:831
NestedNameSpecifierLoc getQualifierLoc() const
Retrieve the nested-name-specifier (with source-location information) that qualifies the name of this...
Definition Decl.h:845
NestedNameSpecifier getQualifier() const
Retrieve the nested-name-specifier that qualifies the name of this declaration, if it was present in ...
Definition Decl.h:837
Container for either a single DynTypedNode or for an ArrayRef to DynTypedNode.
const DynTypedNode * begin() const
A dynamically typed AST node container.
const T * get() const
Retrieve the stored node as type T.
static DynTypedNode create(const T &Node)
Creates a DynTypedNode from Node.
virtual bool TraverseDecl(MaybeConst< Decl > *D)
virtual bool TraverseStmt(MaybeConst< Stmt > *S)
This represents one expression.
Definition Expr.h:113
bool EvaluateAsInt(EvalResult &Result, const ASTContext &Ctx, SideEffectsKind AllowSideEffects=SE_NoSideEffects, bool InConstantContext=false) const
EvaluateAsInt - Return true if this is a constant which we can fold and convert to an integer,...
bool isValueDependent() const
Determines whether the value of this expression depends on.
Definition Expr.h:178
Expr * IgnoreParenImpCasts() LLVM_READONLY
Skip past any parentheses and implicit casts which might surround this expression until reaching a fi...
Definition Expr.cpp:3123
Expr * IgnoreParens() LLVM_READONLY
Skip past any parentheses which might surround this expression until reaching a fixed point.
Definition Expr.cpp:3119
NullPointerConstantValueDependence
Enumeration used to describe how isNullPointerConstant() should cope with value-dependent expressions...
Definition Expr.h:845
std::optional< llvm::APSInt > getIntegerConstantExpr(const ASTContext &Ctx, bool AllowRelaxedEval=false) const
isIntegerConstantExpr - Return the value if this expression is a valid integer constant expression.
Expr * IgnoreImpCasts() LLVM_READONLY
Skip past any implicit casts which might surround this expression until reaching a fixed point.
Definition Expr.cpp:3103
QualType getType() const
Definition Expr.h:145
Annotates a diagnostic with some code that should be inserted, removed, or replaced to fix the proble...
Definition Diagnostic.h:81
CharSourceRange RemoveRange
Code that should be replaced to correct the error.
Definition Diagnostic.h:85
static FixItHint CreateReplacement(CharSourceRange RemoveRange, StringRef Code)
Create a code modification hint that replaces the given source range with the given code string.
Definition Diagnostic.h:142
static FixItHint CreateRemoval(CharSourceRange RemoveRange)
Create a code modification hint that removes the given source range.
Definition Diagnostic.h:131
static FixItHint CreateInsertion(SourceLocation InsertionLoc, StringRef Code, bool BeforePreviousInsertions=false)
Create a code modification hint that inserts the given code string at a specific location.
Definition Diagnostic.h:105
Kind lookup(const VarDecl *VD) const
void set(const VarDecl *VD, Kind K)
Represents a function declaration or definition.
Definition Decl.h:2058
const ParmVarDecl * getParamDecl(unsigned i) const
Definition Decl.h:2927
Stmt * getBody(const FunctionDecl *&Definition) const
Retrieve the body (definition) of the function.
Definition Decl.cpp:3268
unsigned getBuiltinID(bool ConsiderWrapperFunctions=false) const
Returns a value indicating whether this function corresponds to a builtin function.
Definition Decl.cpp:3806
ArrayRef< ParmVarDecl * > parameters() const
Definition Decl.h:2904
param_iterator param_begin()
Definition Decl.h:2916
bool isVariadic() const
Whether this function is variadic.
Definition Decl.cpp:3121
TemplatedKind getTemplatedKind() const
What kind of templated function this is.
Definition Decl.cpp:4188
bool isConstexpr() const
Whether this is a (C++11) constexpr function or constexpr constructor.
Definition Decl.h:2596
redecl_range redecls() const
Returns an iterator range for all the redeclarations of the same decl.
bool isMain() const
Determines whether this function is "main", which is the entry point into an executable program.
Definition Decl.cpp:3412
bool isOverloadedOperator() const
Whether this function declaration represents an C++ overloaded operator, e.g., "operator+".
Definition Decl.h:3063
bool isConsteval() const
Definition Decl.h:2608
unsigned getNumParams() const
Return the number of parameters this function must have based on its FunctionType.
Definition Decl.cpp:3870
DeclarationNameInfo getNameInfo() const
Definition Decl.h:2324
bool hasBody(const FunctionDecl *&Definition) const
Returns true if the function has a body.
Definition Decl.cpp:3188
Represents a C11 generic selection.
Definition Expr.h:6232
Expr * getResultExpr()
Return the result expression of this controlling expression.
Definition Expr.h:6518
One of these records is kept for each identifier that is lexed.
StringRef getName() const
Return the actual identifier string.
A simple pair of identifier info and location.
static IntegerLiteral * Create(const ASTContext &C, const llvm::APInt &V, QualType type, SourceLocation l)
Returns a new integer literal with value 'V' and type 'type'.
Definition Expr.cpp:981
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
static std::optional< Token > findNextToken(SourceLocation Loc, const SourceManager &SM, const LangOptions &LangOpts, bool IncludeComments=false)
Finds the token that comes right after the given location.
Definition Lexer.cpp:1376
static unsigned MeasureTokenLength(SourceLocation Loc, const SourceManager &SM, const LangOptions &LangOpts)
MeasureTokenLength - Relex the token at the specified location and return its length in bytes in the ...
Definition Lexer.cpp:509
static SourceLocation getLocForEndOfToken(SourceLocation Loc, unsigned Offset, const SourceManager &SM, const LangOptions &LangOpts)
Computes the source location just past the end of the token at this source location.
Definition Lexer.cpp:882
IdentifierInfo * getIdentifier() const
Get the identifier that names this declaration, if there is one.
Definition Decl.h:295
StringRef getName() const
Get the name of identifier for this declaration as a StringRef.
Definition Decl.h:301
DeclarationName getDeclName() const
Get the actual, stored name of the declaration, which may be a special name.
Definition Decl.h:340
std::string getNameAsString() const
Get a human-readable name for the declaration, even if it is one of the special kinds of names (C++ c...
Definition Decl.h:317
SourceLocation getBeginLoc() const
Retrieve the location of the beginning of this nested-name-specifier.
A single parameter index whose accessors require each use to make explicit the parameter index encodi...
Definition Attr.h:279
bool isValid() const
Is this parameter index valid?
Definition Attr.h:343
unsigned getASTIndex() const
Get the parameter index as it would normally be encoded at the AST level of representation: zero-orig...
Definition Attr.h:362
Represents a parameter to a function.
Definition Decl.h:1819
bool hasDefaultArg() const
Determines whether this parameter has a default argument, either parsed or not.
Definition Decl.cpp:3046
SourceRange getSourceRange() const override LLVM_READONLY
Source range that this declaration covers.
Definition Decl.cpp:2968
PointerType - C99 6.7.5.1 - Pointer Declarators.
Definition TypeBase.h:3408
A (possibly-)qualified type.
Definition TypeBase.h:938
bool hasQualifiers() const
Determine whether this type has any qualifiers.
Definition TypeBase.h:8591
bool isNull() const
Return true if this QualType doesn't point to a type yet.
Definition TypeBase.h:1005
Qualifiers getQualifiers() const
Retrieve the set of qualifiers applied to this type.
Definition TypeBase.h:8542
QualType getCanonicalType() const
Definition TypeBase.h:8554
bool isConstQualified() const
Determine whether this type is const-qualified.
Definition TypeBase.h:8575
std::string getAsString() const
Represents a struct/union/class.
Definition Decl.h:4459
Encodes a location in the source.
bool isValid() const
Return true if this is a valid SourceLocation object.
SourceLocation getLocWithOffset(IntTy Offset) const
Return a source location with the specified offset from this SourceLocation.
This class handles loading and caching of source files into memory.
bool isBeforeInTranslationUnit(SourceLocation LHS, SourceLocation RHS) const
Determines the order of 2 source locations in the translation unit.
A trivial tuple used to represent a source range.
SourceLocation getEnd() const
Stmt - This represents one statement.
Definition Stmt.h:85
StmtClass getStmtClass() const
Definition Stmt.h:1505
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
Definition Stmt.cpp:343
const char * getStmtClassName() const
Definition Stmt.cpp:86
SourceLocation getBeginLoc() const LLVM_READONLY
Definition Stmt.cpp:355
Exposes information about the current target.
Definition TargetInfo.h:226
A template argument list.
unsigned size() const
Retrieve the number of template arguments in this template argument list.
Represents a template argument.
QualType getAsType() const
Retrieve the type for a type template argument.
@ Type
The template argument is a type.
ArgKind getKind() const
Return the kind of stored template argument.
The base class of the type hierarchy.
Definition TypeBase.h:1879
bool isConstantSizeType() const
Return true if this is not a variable sized type, according to the rules of C99 6....
Definition Type.cpp:2549
bool isArrayType() const
Definition TypeBase.h:8838
bool isPointerType() const
Definition TypeBase.h:8739
bool isIntegerType() const
isIntegerType() does not include complex integers (a GCC extension).
Definition TypeBase.h:9155
const T * castAs() const
Member-template castAs<specific type>.
Definition TypeBase.h:9405
QualType getPointeeType() const
If this is a pointer, ObjC object pointer, or block pointer, this returns the respective pointee.
Definition Type.cpp:789
bool isAnyCharacterType() const
Determine whether this type is any of the built-in character types.
Definition Type.cpp:2259
bool isUnsignedIntegerType() const
Return true if this is an integer type that is unsigned, according to C99 6.2.5p6 [which returns true...
Definition Type.cpp:2364
bool isAnyPointerType() const
Definition TypeBase.h:8747
const Type * getUnqualifiedDesugaredType() const
Return the specified type with any "sugar" removed from the type, removing any typedefs,...
Definition Type.cpp:690
UnaryExprOrTypeTraitExpr - expression with either a type or (unevaluated) expression operand.
Definition Expr.h:2669
UnaryOperator - This represents the unary-expression's (except sizeof and alignof),...
Definition Expr.h:2288
Expr * getSubExpr() const
Definition Expr.h:2329
Opcode getOpcode() const
Definition Expr.h:2324
static bool isIncrementOp(Opcode Op)
Definition Expr.h:2370
SourceLocation getBeginLoc() const LLVM_READONLY
Definition Expr.h:2406
static bool isDecrementOp(Opcode Op)
Definition Expr.h:2377
static StringRef getOpcodeStr(Opcode Op)
getOpcodeStr - Turn an Opcode enum value into the punctuation char it corresponds to,...
Definition Expr.cpp:1434
The interface that lets the caller handle unsafe buffer usage analysis results by overriding this cla...
virtual void handleUnsafeUniquePtrArrayAccess(const DynTypedNode &Node, bool IsRelatedToDecl, ASTContext &Ctx)=0
void addDebugNoteForVar(const VarDecl *VD, SourceLocation Loc, std::string Text)
virtual std::string getUnsafeBufferUsageAttributeTextAt(SourceLocation Loc, StringRef WSSuffix="") const =0
virtual bool isSafeBufferOptOut(const SourceLocation &Loc) const =0
virtual bool ignoreUnsafeBufferInContainer(const SourceLocation &Loc) const =0
virtual void handleUnsafeOperation(const Stmt *Operation, bool IsRelatedToDecl, ASTContext &Ctx)=0
Invoked when an unsafe operation over raw pointers is found.
virtual void handleUnsafeOperationInStringView(const Stmt *Operation, bool IsRelatedToDecl, ASTContext &Ctx)=0
virtual void handleUnsafeVariableGroup(const VarDecl *Variable, const VariableGroupsManager &VarGrpMgr, FixItList &&Fixes, const Decl *D, const FixitStrategy &VarTargetTypes)=0
Invoked when a fix is suggested against a variable.
virtual void handleUnsafeOperationInContainer(const Stmt *Operation, bool IsRelatedToDecl, ASTContext &Ctx)=0
Invoked when an unsafe operation with a std container is found.
virtual bool ignoreUnsafeBufferInStaticSizedArray(const SourceLocation &Loc) const =0
virtual bool ignoreUnsafeBufferInLibcCall(const SourceLocation &Loc) const =0
virtual void handleUnsafeLibcCall(const CallExpr *Call, unsigned PrintfInfo, ASTContext &Ctx, const Expr *UnsafeArg=nullptr)=0
Invoked when a call to an unsafe libc function is found.
QualType getType() const
Definition Decl.h:723
Represents a variable declaration or definition.
Definition Decl.h:932
bool isConstexpr() const
Whether this variable is (C++11) constexpr.
Definition Decl.h:1593
SourceRange getSourceRange() const override LLVM_READONLY
Source range that this declaration covers.
Definition Decl.cpp:2172
VarDecl * getCanonicalDecl() override
Retrieves the "canonical" declaration of the given declaration.
Definition Decl.cpp:2239
bool isInlineSpecified() const
Definition Decl.h:1578
bool hasConstantInitialization() const
Determine whether this variable has constant initialization.
Definition Decl.cpp:2641
const Expr * getInit() const
Definition Decl.h:1391
bool hasLocalStorage() const
Returns true if a variable with function scope is a non-static local variable.
Definition Decl.h:1190
bool isLocalVarDecl() const
Returns true for local variable declarations other than parameters.
Definition Decl.h:1274
const Expr * getAnyInitializer() const
Get the initializer for this variable, no matter which declaration it is attached to.
Definition Decl.h:1381
virtual VarGrpRef getGroupOfVar(const VarDecl *Var, bool *HasParm=nullptr) const =0
Returns the set of variables (including Var) that need to be fixed together in one step.
virtual VarGrpRef getGroupOfParms() const =0
Returns the non-empty group of variables that include parameters of the analyzing function,...
const LengthModifier & getLengthModifier() const
const OptionalAmount & getPrecision() const
const PrintfConversionSpecifier & getConversionSpecifier() const
bool ParsePrintfString(FormatStringHandler &H, const char *beg, const char *end, const LangOptions &LO, const TargetInfo &Target, bool isFreeBSDKPrintf)
SmallVector< BoundNodes, 1 > match(MatcherT Matcher, const NodeT &Node, ASTContext &Context)
Returns the results of matching Matcher on Node.
void matchEachArgumentWithParamType(const CallExpr &Node, llvm::function_ref< void(QualType, const Expr *)> OnParamAndArg)
bool anyConflict(const llvm::SmallVectorImpl< FixItHint > &FixIts, const SourceManager &SM)
bool matches(const til::SExpr *E1, const til::SExpr *E2)
Top level wrappers for InstallAPI frontend operations.
bool isa(CodeGen::Address addr)
Definition Address.h:330
bool matchUnsafePointers(const DynTypedNode &N, ASTContext &Ctx, std::set< const Expr * > &UnsafePointers)
if(T->getSizeExpr()) TRY_TO(TraverseStmt(const_cast< Expr * >(T -> getSizeExpr())))
void checkUnsafeBufferUsage(const Decl *D, UnsafeBufferUsageHandler &Handler, bool EmitSuggestions)
SourceLocation getVarDeclIdentifierLoc(const DeclaratorDecl *VD)
static bool classof(const OMPClause *T)
std::vector< const VarDecl * > VarGrpTy
std::optional< StringRef > getExprText(const Expr *E, const SourceManager &SM, const LangOptions &LangOpts)
Expr * Cond
};
@ Result
The result type of a method or function.
Definition TypeBase.h:906
const FunctionProtoType * T
std::optional< std::string > getPointeeTypeText(const DeclaratorDecl *VD, const SourceManager &SM, const LangOptions &LangOpts, std::optional< Qualifiers > *QualifiersToAppend)
Definition FixitUtil.cpp:22
std::optional< StringRef > getRangeText(SourceRange SR, const SourceManager &SM, const LangOptions &LangOpts)
std::optional< StringRef > getVarDeclIdentifierText(const DeclaratorDecl *VD, const SourceManager &SM, const LangOptions &LangOpts)
std::optional< SourceLocation > getPastLoc(const NodeTy *Node, const SourceManager &SM, const LangOptions &LangOpts)
Definition FixitUtil.h:54
DynamicRecursiveASTVisitorBase< false > DynamicRecursiveASTVisitor
U cast(CodeGen::Address addr)
Definition Address.h:327
ArrayRef< const VarDecl * > VarGrpRef
static StringRef matchLibcNameOrBuiltinChk(StringRef Name)
static bool hasUnsafePrintfStringArg(const CallExpr &Node, ASTContext &Ctx, MatchResult &Result, llvm::StringRef Tag)
static bool isPredefinedUnsafeLibcFunc(const FunctionDecl &Node)
static bool hasUnsafeSnprintfBuffer(const CallExpr &Node, ASTContext &Ctx)
static bool isUnsafeVaListPrintfFunc(const FunctionDecl &Node)
static bool isUnsafeSprintfFunc(const FunctionDecl &Node)
static bool hasUnsafeFormatOrSArg(ASTContext &Ctx, const CallExpr *Call, const Expr *&UnsafeArg, const unsigned FmtIdx, std::optional< const unsigned > FmtArgIdx=std::nullopt, bool isKprintf=false)
static StringRef matchLibcName(StringRef Name)
static bool isUnsafeMemset(const CallExpr &Node, ASTContext &Ctx)
static StringRef matchName(StringRef FunName, bool isBuiltin)
static bool isNormalPrintfFunc(const FunctionDecl &Node)
#define false
Definition stdbool.h:26
bool operator()(const NodeTy *N1, const NodeTy *N2) const
std::map< const VarDecl *, std::set< const FixableGadget * >, CompareNode< VarDecl > > byVar
std::map< const VarDecl *, std::set< const WarningGadget * >, CompareNode< VarDecl > > byVar
llvm::SmallVector< const WarningGadget *, 16 > noVar
SourceLocation getBeginLoc() const
getBeginLoc - Retrieve the location of the first token.
SourceLocation getEndLoc() const LLVM_READONLY
EvalResult is a struct with detailed info about an evaluated expression.
Definition Expr.h:666
APValue Val
Val - This is the value the expression can be folded to.
Definition Expr.h:668
const BoundNodes Nodes
Contains the nodes bound on the current match.