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 CXXConstructExpr *CE, ASTContext &Ctx,
1881 const auto *CDecl = CE->getConstructor();
1882 const auto *CRecordDecl = CDecl->getParent();
1883 auto HasTwoParamSpanCtorDecl =
1884 CRecordDecl->isInStdNamespace() &&
1885 CDecl->getDeclName().getAsString() == "span" && CE->getNumArgs() == 2;
1886 if (!HasTwoParamSpanCtorDecl || isSafeSpanTwoParamConstruct(*CE, Ctx))
1887 return false;
1888 Result.addNode(SpanTwoParamConstructorTag, DynTypedNode::create(*CE));
1889 return true;
1890 }
1891
1892 static bool matches(const Stmt *S, ASTContext &Ctx,
1893 const UnsafeBufferUsageHandler *Handler,
1895 const auto *CE = dyn_cast<CXXConstructExpr>(S);
1896 if (!CE)
1897 return false;
1898 if (ignoreUnsafeBufferInContainer(*S, Handler))
1899 return false;
1900 return matches(CE, Ctx, Result);
1901 }
1902
1903 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
1904 bool IsRelatedToDecl,
1905 ASTContext &Ctx) const override {
1906 Handler.handleUnsafeOperationInContainer(Ctor, IsRelatedToDecl, Ctx);
1907 }
1908 SourceLocation getSourceLoc() const override { return Ctor->getBeginLoc(); }
1909
1910 DeclUseList getClaimedVarUseSites() const override {
1911 // If the constructor call is of the form `std::span{var, n}`, `var` is
1912 // considered an unsafe variable.
1913 if (auto *DRE = dyn_cast<DeclRefExpr>(Ctor->getArg(0))) {
1914 if (isa<VarDecl>(DRE->getDecl()))
1915 return {DRE};
1916 }
1917 return {};
1918 }
1919
1920 SmallVector<const Expr *, 1> getUnsafePtrs() const override { return {}; }
1921};
1922
1923class StringViewTwoParamConstructorGadget : public WarningGadget {
1924 static constexpr const char *const StringViewTwoParamConstructorTag =
1925 "stringViewTwoParamConstructor";
1926 const CXXConstructExpr *Ctor; // the string_view constructor expression
1927
1928public:
1929 StringViewTwoParamConstructorGadget(const MatchResult &Result)
1930 : WarningGadget(Kind::StringViewTwoParamConstructor),
1931 Ctor(Result.getNodeAs<CXXConstructExpr>(
1932 StringViewTwoParamConstructorTag)) {}
1933
1934 static bool classof(const Gadget *G) {
1935 return G->getKind() == Kind::StringViewTwoParamConstructor;
1936 }
1937
1938 static bool matches(const CXXConstructExpr *CE, ASTContext &Ctx,
1940 const auto *CDecl = CE->getConstructor();
1941 const auto *CRecordDecl = CDecl->getParent();
1942
1943 // MATCH: std::basic_string_view
1944 bool IsStringView =
1945 CRecordDecl->isInStdNamespace() &&
1946 CDecl->getDeclName().getAsString() == "basic_string_view" &&
1947 CE->getNumArgs() == 2;
1948
1949 if (!IsStringView || isSafeStringViewTwoParamConstruct(*CE, Ctx))
1950 return false;
1951
1952 Result.addNode(StringViewTwoParamConstructorTag, DynTypedNode::create(*CE));
1953 return true;
1954 }
1955
1956 static bool matches(const Stmt *S, ASTContext &Ctx,
1957 const UnsafeBufferUsageHandler *Handler,
1959 const auto *CE = dyn_cast<CXXConstructExpr>(S);
1960 if (!CE)
1961 return false;
1962 if (ignoreUnsafeBufferInContainer(*S, Handler))
1963 return false;
1964 return matches(CE, Ctx, Result);
1965 }
1966
1967 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
1968 bool IsRelatedToDecl,
1969 ASTContext &Ctx) const override {
1970 Handler.handleUnsafeOperationInStringView(Ctor, IsRelatedToDecl, Ctx);
1971 }
1972
1973 SourceLocation getSourceLoc() const override { return Ctor->getBeginLoc(); }
1974
1975 DeclUseList getClaimedVarUseSites() const override {
1976 // If the constructor call is of the form `std::string_view{var, n}`, `var`
1977 // is considered an unsafe variable.
1978 if (auto *DRE = dyn_cast<DeclRefExpr>(Ctor->getArg(0))) {
1979 if (isa<VarDecl>(DRE->getDecl()))
1980 return {DRE};
1981 }
1982 return {};
1983 }
1984
1985 SmallVector<const Expr *, 1> getUnsafePtrs() const override { return {}; }
1986};
1987
1988/// A pointer initialization expression of the form:
1989/// \code
1990/// int *p = q;
1991/// \endcode
1992class PointerInitGadget : public FixableGadget {
1993private:
1994 static constexpr const char *const PointerInitLHSTag = "ptrInitLHS";
1995 static constexpr const char *const PointerInitRHSTag = "ptrInitRHS";
1996 const VarDecl *PtrInitLHS; // the LHS pointer expression in `PI`
1997 const DeclRefExpr *PtrInitRHS; // the RHS pointer expression in `PI`
1998
1999public:
2000 PointerInitGadget(const MatchResult &Result)
2001 : FixableGadget(Kind::PointerInit),
2002 PtrInitLHS(Result.getNodeAs<VarDecl>(PointerInitLHSTag)),
2003 PtrInitRHS(Result.getNodeAs<DeclRefExpr>(PointerInitRHSTag)) {}
2004
2005 static bool classof(const Gadget *G) {
2006 return G->getKind() == Kind::PointerInit;
2007 }
2008
2009 static bool matches(const Stmt *S,
2010 llvm::SmallVectorImpl<MatchResult> &Results) {
2011 const DeclStmt *DS = dyn_cast<DeclStmt>(S);
2012 if (!DS || !DS->isSingleDecl())
2013 return false;
2014 const VarDecl *VD = dyn_cast<VarDecl>(DS->getSingleDecl());
2015 if (!VD)
2016 return false;
2017 const Expr *Init = VD->getAnyInitializer();
2018 if (!Init)
2019 return false;
2020 const auto *DRE = dyn_cast<DeclRefExpr>(Init->IgnoreImpCasts());
2021 if (!DRE || !hasPointerType(*DRE) || !isSupportedVariable(*DRE)) {
2022 return false;
2023 }
2024 MatchResult R;
2025 R.addNode(PointerInitLHSTag, DynTypedNode::create(*VD));
2026 R.addNode(PointerInitRHSTag, DynTypedNode::create(*DRE));
2027 Results.emplace_back(std::move(R));
2028 return true;
2029 }
2030
2031 virtual std::optional<FixItList>
2032 getFixits(const FixitStrategy &S) const override;
2033 SourceLocation getSourceLoc() const override {
2034 return PtrInitRHS->getBeginLoc();
2035 }
2036
2037 virtual DeclUseList getClaimedVarUseSites() const override {
2038 return DeclUseList{PtrInitRHS};
2039 }
2040
2041 virtual std::optional<std::pair<const VarDecl *, const VarDecl *>>
2042 getStrategyImplications() const override {
2043 return std::make_pair(PtrInitLHS, cast<VarDecl>(PtrInitRHS->getDecl()));
2044 }
2045};
2046
2047/// A pointer assignment expression of the form:
2048/// \code
2049/// p = q;
2050/// \endcode
2051/// where both `p` and `q` are pointers.
2052class PtrToPtrAssignmentGadget : public FixableGadget {
2053private:
2054 static constexpr const char *const PointerAssignLHSTag = "ptrLHS";
2055 static constexpr const char *const PointerAssignRHSTag = "ptrRHS";
2056 const DeclRefExpr *PtrLHS; // the LHS pointer expression in `PA`
2057 const DeclRefExpr *PtrRHS; // the RHS pointer expression in `PA`
2058
2059public:
2060 PtrToPtrAssignmentGadget(const MatchResult &Result)
2061 : FixableGadget(Kind::PtrToPtrAssignment),
2062 PtrLHS(Result.getNodeAs<DeclRefExpr>(PointerAssignLHSTag)),
2063 PtrRHS(Result.getNodeAs<DeclRefExpr>(PointerAssignRHSTag)) {}
2064
2065 static bool classof(const Gadget *G) {
2066 return G->getKind() == Kind::PtrToPtrAssignment;
2067 }
2068
2069 static bool matches(const Stmt *S,
2070 llvm::SmallVectorImpl<MatchResult> &Results) {
2071 size_t SizeBefore = Results.size();
2072 findStmtsInUnspecifiedUntypedContext(S, [&Results](const Stmt *S) {
2073 const auto *BO = dyn_cast<BinaryOperator>(S);
2074 if (!BO || BO->getOpcode() != BO_Assign)
2075 return;
2076 const auto *RHS = BO->getRHS()->IgnoreParenImpCasts();
2077 if (const auto *RHSRef = dyn_cast<DeclRefExpr>(RHS);
2078 !RHSRef || !hasPointerType(*RHSRef) ||
2079 !isSupportedVariable(*RHSRef)) {
2080 return;
2081 }
2082 const auto *LHS = BO->getLHS();
2083 if (const auto *LHSRef = dyn_cast<DeclRefExpr>(LHS);
2084 !LHSRef || !hasPointerType(*LHSRef) ||
2085 !isSupportedVariable(*LHSRef)) {
2086 return;
2087 }
2088 MatchResult R;
2089 R.addNode(PointerAssignLHSTag, DynTypedNode::create(*LHS));
2090 R.addNode(PointerAssignRHSTag, DynTypedNode::create(*RHS));
2091 Results.emplace_back(std::move(R));
2092 });
2093 return SizeBefore != Results.size();
2094 }
2095
2096 virtual std::optional<FixItList>
2097 getFixits(const FixitStrategy &S) const override;
2098 SourceLocation getSourceLoc() const override { return PtrLHS->getBeginLoc(); }
2099
2100 virtual DeclUseList getClaimedVarUseSites() const override {
2101 return DeclUseList{PtrLHS, PtrRHS};
2102 }
2103
2104 virtual std::optional<std::pair<const VarDecl *, const VarDecl *>>
2105 getStrategyImplications() const override {
2106 return std::make_pair(cast<VarDecl>(PtrLHS->getDecl()),
2107 cast<VarDecl>(PtrRHS->getDecl()));
2108 }
2109};
2110
2111/// An assignment expression of the form:
2112/// \code
2113/// ptr = array;
2114/// \endcode
2115/// where `p` is a pointer and `array` is a constant size array.
2116class CArrayToPtrAssignmentGadget : public FixableGadget {
2117private:
2118 static constexpr const char *const PointerAssignLHSTag = "ptrLHS";
2119 static constexpr const char *const PointerAssignRHSTag = "ptrRHS";
2120 const DeclRefExpr *PtrLHS; // the LHS pointer expression in `PA`
2121 const DeclRefExpr *PtrRHS; // the RHS pointer expression in `PA`
2122
2123public:
2124 CArrayToPtrAssignmentGadget(const MatchResult &Result)
2125 : FixableGadget(Kind::CArrayToPtrAssignment),
2126 PtrLHS(Result.getNodeAs<DeclRefExpr>(PointerAssignLHSTag)),
2127 PtrRHS(Result.getNodeAs<DeclRefExpr>(PointerAssignRHSTag)) {}
2128
2129 static bool classof(const Gadget *G) {
2130 return G->getKind() == Kind::CArrayToPtrAssignment;
2131 }
2132
2133 static bool matches(const Stmt *S,
2134 llvm::SmallVectorImpl<MatchResult> &Results) {
2135 size_t SizeBefore = Results.size();
2136 findStmtsInUnspecifiedUntypedContext(S, [&Results](const Stmt *S) {
2137 const auto *BO = dyn_cast<BinaryOperator>(S);
2138 if (!BO || BO->getOpcode() != BO_Assign)
2139 return;
2140 const auto *RHS = BO->getRHS()->IgnoreParenImpCasts();
2141 if (const auto *RHSRef = dyn_cast<DeclRefExpr>(RHS);
2142 !RHSRef ||
2143 !isa<ConstantArrayType>(RHSRef->getType().getCanonicalType()) ||
2144 !isSupportedVariable(*RHSRef)) {
2145 return;
2146 }
2147 const auto *LHS = BO->getLHS();
2148 if (const auto *LHSRef = dyn_cast<DeclRefExpr>(LHS);
2149 !LHSRef || !hasPointerType(*LHSRef) ||
2150 !isSupportedVariable(*LHSRef)) {
2151 return;
2152 }
2153 MatchResult R;
2154 R.addNode(PointerAssignLHSTag, DynTypedNode::create(*LHS));
2155 R.addNode(PointerAssignRHSTag, DynTypedNode::create(*RHS));
2156 Results.emplace_back(std::move(R));
2157 });
2158 return SizeBefore != Results.size();
2159 }
2160
2161 virtual std::optional<FixItList>
2162 getFixits(const FixitStrategy &S) const override;
2163 SourceLocation getSourceLoc() const override { return PtrLHS->getBeginLoc(); }
2164
2165 virtual DeclUseList getClaimedVarUseSites() const override {
2166 return DeclUseList{PtrLHS, PtrRHS};
2167 }
2168
2169 virtual std::optional<std::pair<const VarDecl *, const VarDecl *>>
2170 getStrategyImplications() const override {
2171 return {};
2172 }
2173};
2174
2175/// A call of a function or method that performs unchecked buffer operations
2176/// over one of its pointer parameters.
2177class UnsafeBufferUsageAttrGadget : public WarningGadget {
2178 constexpr static const char *const OpTag = "attr_expr";
2179 const Expr *Op;
2180
2181public:
2182 UnsafeBufferUsageAttrGadget(const MatchResult &Result)
2183 : WarningGadget(Kind::UnsafeBufferUsageAttr),
2184 Op(Result.getNodeAs<Expr>(OpTag)) {}
2185
2186 static bool classof(const Gadget *G) {
2187 return G->getKind() == Kind::UnsafeBufferUsageAttr;
2188 }
2189
2190 static bool matches(const Stmt *S, const ASTContext &Ctx,
2192 if (auto *CE = dyn_cast<CallExpr>(S)) {
2193 if (CE->getDirectCallee() &&
2194 CE->getDirectCallee()->hasAttr<UnsafeBufferUsageAttr>()) {
2195 Result.addNode(OpTag, DynTypedNode::create(*CE));
2196 return true;
2197 }
2198 }
2199 if (auto *ME = dyn_cast<MemberExpr>(S)) {
2200 if (!isa<FieldDecl>(ME->getMemberDecl()))
2201 return false;
2202 if (ME->getMemberDecl()->hasAttr<UnsafeBufferUsageAttr>()) {
2203 Result.addNode(OpTag, DynTypedNode::create(*ME));
2204 return true;
2205 }
2206 }
2207 return false;
2208 }
2209
2210 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
2211 bool IsRelatedToDecl,
2212 ASTContext &Ctx) const override {
2213 Handler.handleUnsafeOperation(Op, IsRelatedToDecl, Ctx);
2214 }
2215 SourceLocation getSourceLoc() const override { return Op->getBeginLoc(); }
2216
2217 DeclUseList getClaimedVarUseSites() const override { return {}; }
2218
2219 SmallVector<const Expr *, 1> getUnsafePtrs() const override { return {}; }
2220};
2221
2222/// A call of a constructor that performs unchecked buffer operations
2223/// over one of its pointer parameters, or constructs a class object that will
2224/// perform buffer operations that depend on the correctness of the parameters.
2225class UnsafeBufferUsageCtorAttrGadget : public WarningGadget {
2226 constexpr static const char *const OpTag = "cxx_construct_expr";
2227 const CXXConstructExpr *Op;
2228
2229public:
2230 UnsafeBufferUsageCtorAttrGadget(const MatchResult &Result)
2231 : WarningGadget(Kind::UnsafeBufferUsageCtorAttr),
2232 Op(Result.getNodeAs<CXXConstructExpr>(OpTag)) {}
2233
2234 static bool classof(const Gadget *G) {
2235 return G->getKind() == Kind::UnsafeBufferUsageCtorAttr;
2236 }
2237
2238 static bool matches(const Stmt *S, ASTContext &Ctx, MatchResult &Result) {
2239 const auto *CE = dyn_cast<CXXConstructExpr>(S);
2240 if (!CE || !CE->getConstructor()->hasAttr<UnsafeBufferUsageAttr>())
2241 return false;
2242 // std::span(ptr, size) ctor is handled by SpanTwoParamConstructorGadget.
2243 MatchResult Tmp;
2244 if (SpanTwoParamConstructorGadget::matches(CE, Ctx, Tmp))
2245 return false;
2246 Result.addNode(OpTag, DynTypedNode::create(*CE));
2247 return true;
2248 }
2249
2250 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
2251 bool IsRelatedToDecl,
2252 ASTContext &Ctx) const override {
2253 Handler.handleUnsafeOperation(Op, IsRelatedToDecl, Ctx);
2254 }
2255 SourceLocation getSourceLoc() const override { return Op->getBeginLoc(); }
2256
2257 DeclUseList getClaimedVarUseSites() const override { return {}; }
2258
2259 SmallVector<const Expr *, 1> getUnsafePtrs() const override { return {}; }
2260};
2261
2262// Warning gadget for unsafe invocation of span::data method.
2263// Triggers when the pointer returned by the invocation is immediately
2264// cast to a larger type.
2265
2266class DataInvocationGadget : public WarningGadget {
2267 constexpr static const char *const OpTag = "data_invocation_expr";
2268 const ExplicitCastExpr *Op;
2269
2270public:
2271 DataInvocationGadget(const MatchResult &Result)
2272 : WarningGadget(Kind::DataInvocation),
2273 Op(Result.getNodeAs<ExplicitCastExpr>(OpTag)) {}
2274
2275 static bool classof(const Gadget *G) {
2276 return G->getKind() == Kind::DataInvocation;
2277 }
2278
2279 static bool matches(const Stmt *S, const ASTContext &Ctx,
2281 auto *CE = dyn_cast<ExplicitCastExpr>(S);
2282 if (!CE)
2283 return false;
2284 for (auto *Child : CE->children()) {
2285 if (auto *MCE = dyn_cast<CXXMemberCallExpr>(Child);
2286 MCE && isDataFunction(MCE)) {
2287 Result.addNode(OpTag, DynTypedNode::create(*CE));
2288 return true;
2289 }
2290 if (auto *Paren = dyn_cast<ParenExpr>(Child)) {
2291 if (auto *MCE = dyn_cast<CXXMemberCallExpr>(Paren->getSubExpr());
2292 MCE && isDataFunction(MCE)) {
2293 Result.addNode(OpTag, DynTypedNode::create(*CE));
2294 return true;
2295 }
2296 }
2297 }
2298 return false;
2299 }
2300
2301 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
2302 bool IsRelatedToDecl,
2303 ASTContext &Ctx) const override {
2304 Handler.handleUnsafeOperation(Op, IsRelatedToDecl, Ctx);
2305 }
2306 SourceLocation getSourceLoc() const override { return Op->getBeginLoc(); }
2307
2308 DeclUseList getClaimedVarUseSites() const override { return {}; }
2309
2310private:
2311 static bool isDataFunction(const CXXMemberCallExpr *call) {
2312 if (!call)
2313 return false;
2314 auto *callee = call->getDirectCallee();
2315 if (!callee || !isa<CXXMethodDecl>(callee))
2316 return false;
2317 auto *method = cast<CXXMethodDecl>(callee);
2318 if (method->getNameAsString() == "data" &&
2319 method->getParent()->isInStdNamespace() &&
2320 llvm::is_contained({SIZED_CONTAINER_OR_VIEW_LIST},
2321 method->getParent()->getName()))
2322 return true;
2323 return false;
2324 }
2325
2326 SmallVector<const Expr *, 1> getUnsafePtrs() const override { return {}; }
2327};
2328
2329class UnsafeLibcFunctionCallGadget : public WarningGadget {
2330 const CallExpr *const Call;
2331 const Expr *UnsafeArg = nullptr;
2332 constexpr static const char *const Tag = "UnsafeLibcFunctionCall";
2333 // Extra tags for additional information:
2334 constexpr static const char *const UnsafeSprintfTag =
2335 "UnsafeLibcFunctionCall_sprintf";
2336 constexpr static const char *const UnsafeSizedByTag =
2337 "UnsafeLibcFunctionCall_sized_by";
2338 constexpr static const char *const UnsafeStringTag =
2339 "UnsafeLibcFunctionCall_string";
2340 constexpr static const char *const UnsafeVaListTag =
2341 "UnsafeLibcFunctionCall_va_list";
2342
2343public:
2344 enum UnsafeKind {
2345 OTHERS = 0, // no specific information, the callee function is unsafe
2346 SPRINTF = 1, // never call `-sprintf`s, call `-snprintf`s instead.
2347 SIZED_BY =
2348 2, // the first two arguments of `snprintf` function have
2349 // "__sized_by" relation but they do not conform to safe patterns
2350 STRING = 3, // an argument is a pointer-to-char-as-string but does not
2351 // guarantee null-termination
2352 VA_LIST = 4, // one of the `-printf`s function that take va_list, which is
2353 // considered unsafe as it is not compile-time check
2354 FORMAT_ATTR = 8, // flag: the callee has the format attribute
2355 } WarnedFunKind = OTHERS;
2356
2357 UnsafeLibcFunctionCallGadget(const MatchResult &Result)
2358 : WarningGadget(Kind::UnsafeLibcFunctionCall),
2359 Call(Result.getNodeAs<CallExpr>(Tag)) {
2360 if (Result.getNodeAs<Decl>(UnsafeSprintfTag))
2361 WarnedFunKind = SPRINTF;
2362 else if (auto *E = Result.getNodeAs<Expr>(UnsafeStringTag)) {
2363 WarnedFunKind = STRING;
2364 UnsafeArg = E;
2365 } else if (Result.getNodeAs<CallExpr>(UnsafeSizedByTag)) {
2366 WarnedFunKind = SIZED_BY;
2367 UnsafeArg = Call->getArg(0);
2368 } else if (Result.getNodeAs<Decl>(UnsafeVaListTag))
2369 WarnedFunKind = VA_LIST;
2370 }
2371
2372 static bool matches(const Stmt *S, ASTContext &Ctx,
2373 const UnsafeBufferUsageHandler *Handler,
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 if (ignoreUnsafeLibcCall(Ctx, *S, Handler))
2382 return false;
2383
2384 const bool IsGlobalAndNotInAnyNamespace =
2385 FD->isGlobal() && !FD->getEnclosingNamespaceContext()->isNamespace();
2386
2387 // A libc function must either be in the std:: namespace or a global
2388 // function that is not in any namespace:
2389 if (!FD->isInStdNamespace() && !IsGlobalAndNotInAnyNamespace)
2390 return false;
2391 // If the call has a sole null-terminated argument, e.g., strlen,
2392 // printf, atoi, we consider it safe:
2393 if (CE->getNumArgs() == 1 && isNullTermPointer(CE->getArg(0), Ctx))
2394 return false;
2395
2396 const bool isSingleStringLiteralArg =
2397 CE->getNumArgs() == 1 &&
2399 if (!isSingleStringLiteralArg) {
2400 // (unless the call has a sole string literal argument):
2402 Result.addNode(Tag, DynTypedNode::create(*CE));
2403 return true;
2404 }
2405 if (libc_func_matchers::isUnsafeMemset(*CE, Ctx)) {
2406 Result.addNode(Tag, DynTypedNode::create(*CE));
2407 return true;
2408 }
2410 Result.addNode(Tag, DynTypedNode::create(*CE));
2411 Result.addNode(UnsafeVaListTag, DynTypedNode::create(*FD));
2412 return true;
2413 }
2415 Result.addNode(Tag, DynTypedNode::create(*CE));
2416 Result.addNode(UnsafeSprintfTag, DynTypedNode::create(*FD));
2417 return true;
2418 }
2419 }
2422 Result.addNode(Tag, DynTypedNode::create(*CE));
2423 Result.addNode(UnsafeSizedByTag, DynTypedNode::create(*CE));
2424 return true;
2425 }
2427 UnsafeStringTag)) {
2428 Result.addNode(Tag, DynTypedNode::create(*CE));
2429 return true;
2430 }
2431 }
2432 return false;
2433 }
2434
2435 const Stmt *getBaseStmt() const { return Call; }
2436
2437 SourceLocation getSourceLoc() const override { return Call->getBeginLoc(); }
2438
2439 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
2440 bool IsRelatedToDecl,
2441 ASTContext &Ctx) const override {
2442 Handler.handleUnsafeLibcCall(Call, WarnedFunKind, Ctx, UnsafeArg);
2443 }
2444
2445 DeclUseList getClaimedVarUseSites() const override { return {}; }
2446
2447 SmallVector<const Expr *, 1> getUnsafePtrs() const override { return {}; }
2448};
2449
2450class UnsafeFormatAttributedFunctionCallGadget : public WarningGadget {
2451 const CallExpr *const Call;
2452 const Expr *UnsafeArg = nullptr;
2453 constexpr static const char *const Tag = "UnsafeFormatAttributedFunctionCall";
2454 constexpr static const char *const UnsafeStringTag =
2455 "UnsafeFormatAttributedFunctionCall_string";
2456
2457public:
2458 UnsafeFormatAttributedFunctionCallGadget(const MatchResult &Result)
2459 : WarningGadget(Kind::UnsafeLibcFunctionCall),
2460 Call(Result.getNodeAs<CallExpr>(Tag)),
2461 UnsafeArg(Result.getNodeAs<Expr>(UnsafeStringTag)) {}
2462
2463 static bool matches(const Stmt *S, ASTContext &Ctx,
2464 const UnsafeBufferUsageHandler *Handler,
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 if (ignoreUnsafeLibcCall(Ctx, *S, Handler))
2473 return false;
2474
2475 const FormatAttr *Attr = nullptr;
2476 bool IsPrintf = false;
2477 bool AnyAttr = llvm::any_of(
2478 FD->specific_attrs<FormatAttr>(),
2479 [&Attr, &IsPrintf](const FormatAttr *FA) -> bool {
2480 if (const auto *II = FA->getType()) {
2481 if (II->getName() == "printf" || II->getName() == "scanf") {
2482 Attr = FA;
2483 IsPrintf = II->getName() == "printf";
2484 return true;
2485 }
2486 }
2487 return false;
2488 });
2489 const Expr *UnsafeArg;
2490
2491 if (!AnyAttr)
2492 return false;
2493
2494 // FormatAttribute indexes are 1-based:
2495 unsigned FmtIdx = Attr->getFormatIdx() - 1;
2496 std::optional<unsigned> FmtArgIdx = Attr->getFirstArg() - 1;
2497
2498 if (isa<CXXMemberCallExpr>(CE)) {
2499 // For CXX member calls, attribute parameters are specified as if there is
2500 // an implicit "this". The implicit "this" is invisible through CallExpr
2501 // `CE`. (What makes it even less ergonomic is that the
2502 // implicit "this" is visible through CallExpr `CE` for CXX operator
2503 // calls!)
2504 --FmtIdx;
2505 --*FmtArgIdx;
2506 } else if (CE->getStmtClass() != Stmt::CallExprClass &&
2508 return false; // Ignore unsupported CallExpr subclasses
2509 if (*FmtArgIdx >= CE->getNumArgs())
2510 // Format arguments are allowed to be absent when variadic parameter is
2511 // used. So we need to check if those arguments exist. Moreover, when
2512 // variadic parameter is NOT used, `Attr->getFirstArg()` could be an
2513 // out-of-bound value. E.g.,
2514 // clang does not complain about `__attribute__((__format__(__printf__, 2,
2515 // 99))) void f(int, char *);`.
2516 FmtArgIdx = std::nullopt;
2517
2518 if (AnyAttr && !IsPrintf && FmtArgIdx) {
2519 // For scanf-like functions, any format argument is considered unsafe:
2520 Result.addNode(Tag, DynTypedNode::create(*CE));
2521 return true;
2522 }
2523 // For printf-like functions:
2525 Ctx, CE, UnsafeArg, FmtIdx, FmtArgIdx)) {
2526 Result.addNode(Tag, DynTypedNode::create(*CE));
2527 Result.addNode(UnsafeStringTag, DynTypedNode::create(*UnsafeArg));
2528 return true;
2529 }
2530 return false;
2531 }
2532
2533 const Stmt *getBaseStmt() const { return Call; }
2534
2535 SourceLocation getSourceLoc() const override { return Call->getBeginLoc(); }
2536
2537 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
2538 bool IsRelatedToDecl,
2539 ASTContext &Ctx) const override {
2540 if (UnsafeArg)
2541 Handler.handleUnsafeLibcCall(
2542 Call,
2543 UnsafeLibcFunctionCallGadget::UnsafeKind::STRING |
2544 UnsafeLibcFunctionCallGadget::UnsafeKind::FORMAT_ATTR,
2545 Ctx, UnsafeArg);
2546 else
2547 Handler.handleUnsafeLibcCall(
2548 Call,
2549 UnsafeLibcFunctionCallGadget::UnsafeKind::OTHERS |
2550 UnsafeLibcFunctionCallGadget::UnsafeKind::FORMAT_ATTR,
2551 Ctx);
2552 }
2553
2554 DeclUseList getClaimedVarUseSites() const override { return {}; }
2555
2556 SmallVector<const Expr *, 1> getUnsafePtrs() const override { return {}; }
2557};
2558
2559// Represents expressions of the form `DRE[*]` in the Unspecified Lvalue
2560// Context (see `findStmtsInUnspecifiedLvalueContext`).
2561// Note here `[]` is the built-in subscript operator.
2562class ULCArraySubscriptGadget : public FixableGadget {
2563private:
2564 static constexpr const char *const ULCArraySubscriptTag =
2565 "ArraySubscriptUnderULC";
2566 const ArraySubscriptExpr *Node;
2567
2568public:
2569 ULCArraySubscriptGadget(const MatchResult &Result)
2570 : FixableGadget(Kind::ULCArraySubscript),
2571 Node(Result.getNodeAs<ArraySubscriptExpr>(ULCArraySubscriptTag)) {
2572 assert(Node != nullptr && "Expecting a non-null matching result");
2573 }
2574
2575 static bool classof(const Gadget *G) {
2576 return G->getKind() == Kind::ULCArraySubscript;
2577 }
2578
2579 static bool matches(const Stmt *S,
2580 llvm::SmallVectorImpl<MatchResult> &Results) {
2581 size_t SizeBefore = Results.size();
2582 findStmtsInUnspecifiedLvalueContext(S, [&Results](const Expr *E) {
2583 const auto *ASE = dyn_cast<ArraySubscriptExpr>(E);
2584 if (!ASE)
2585 return;
2586 const auto *DRE =
2587 dyn_cast<DeclRefExpr>(ASE->getBase()->IgnoreParenImpCasts());
2588 if (!DRE || !(hasPointerType(*DRE) || hasArrayType(*DRE)) ||
2589 !isSupportedVariable(*DRE))
2590 return;
2591 MatchResult R;
2592 R.addNode(ULCArraySubscriptTag, DynTypedNode::create(*ASE));
2593 Results.emplace_back(std::move(R));
2594 });
2595 return SizeBefore != Results.size();
2596 }
2597
2598 virtual std::optional<FixItList>
2599 getFixits(const FixitStrategy &S) const override;
2600 SourceLocation getSourceLoc() const override { return Node->getBeginLoc(); }
2601
2602 virtual DeclUseList getClaimedVarUseSites() const override {
2603 if (const auto *DRE =
2604 dyn_cast<DeclRefExpr>(Node->getBase()->IgnoreImpCasts())) {
2605 return {DRE};
2606 }
2607 return {};
2608 }
2609};
2610
2611// Fixable gadget to handle stand alone pointers of the form `UPC(DRE)` in the
2612// unspecified pointer context (findStmtsInUnspecifiedPointerContext). The
2613// gadget emits fixit of the form `UPC(DRE.data())`.
2614class UPCStandalonePointerGadget : public FixableGadget {
2615private:
2616 static constexpr const char *const DeclRefExprTag = "StandalonePointer";
2617 const DeclRefExpr *Node;
2618
2619public:
2620 UPCStandalonePointerGadget(const MatchResult &Result)
2621 : FixableGadget(Kind::UPCStandalonePointer),
2622 Node(Result.getNodeAs<DeclRefExpr>(DeclRefExprTag)) {
2623 assert(Node != nullptr && "Expecting a non-null matching result");
2624 }
2625
2626 static bool classof(const Gadget *G) {
2627 return G->getKind() == Kind::UPCStandalonePointer;
2628 }
2629
2630 static bool matches(const Stmt *S,
2631 llvm::SmallVectorImpl<MatchResult> &Results) {
2632 size_t SizeBefore = Results.size();
2633 findStmtsInUnspecifiedPointerContext(S, [&Results](const Stmt *S) {
2634 auto *E = dyn_cast<Expr>(S);
2635 if (!E)
2636 return;
2637 const auto *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts());
2638 if (!DRE || (!hasPointerType(*DRE) && !hasArrayType(*DRE)) ||
2639 !isSupportedVariable(*DRE))
2640 return;
2641 MatchResult R;
2642 R.addNode(DeclRefExprTag, DynTypedNode::create(*DRE));
2643 Results.emplace_back(std::move(R));
2644 });
2645 return SizeBefore != Results.size();
2646 }
2647
2648 virtual std::optional<FixItList>
2649 getFixits(const FixitStrategy &S) const override;
2650 SourceLocation getSourceLoc() const override { return Node->getBeginLoc(); }
2651
2652 virtual DeclUseList getClaimedVarUseSites() const override { return {Node}; }
2653};
2654
2655class PointerDereferenceGadget : public FixableGadget {
2656 static constexpr const char *const BaseDeclRefExprTag = "BaseDRE";
2657 static constexpr const char *const OperatorTag = "op";
2658
2659 const DeclRefExpr *BaseDeclRefExpr = nullptr;
2660 const UnaryOperator *Op = nullptr;
2661
2662public:
2663 PointerDereferenceGadget(const MatchResult &Result)
2664 : FixableGadget(Kind::PointerDereference),
2665 BaseDeclRefExpr(Result.getNodeAs<DeclRefExpr>(BaseDeclRefExprTag)),
2666 Op(Result.getNodeAs<UnaryOperator>(OperatorTag)) {}
2667
2668 static bool classof(const Gadget *G) {
2669 return G->getKind() == Kind::PointerDereference;
2670 }
2671
2672 static bool matches(const Stmt *S,
2673 llvm::SmallVectorImpl<MatchResult> &Results) {
2674 size_t SizeBefore = Results.size();
2675 findStmtsInUnspecifiedLvalueContext(S, [&Results](const Stmt *S) {
2676 const auto *UO = dyn_cast<UnaryOperator>(S);
2677 if (!UO || UO->getOpcode() != UO_Deref)
2678 return;
2679 const Expr *CE = UO->getSubExpr();
2680 if (!CE)
2681 return;
2682 CE = CE->IgnoreParenImpCasts();
2683 const auto *DRE = dyn_cast<DeclRefExpr>(CE);
2684 if (!DRE || !isSupportedVariable(*DRE))
2685 return;
2686 MatchResult R;
2687 R.addNode(BaseDeclRefExprTag, DynTypedNode::create(*DRE));
2688 R.addNode(OperatorTag, DynTypedNode::create(*UO));
2689 Results.emplace_back(std::move(R));
2690 });
2691 return SizeBefore != Results.size();
2692 }
2693
2694 DeclUseList getClaimedVarUseSites() const override {
2695 return {BaseDeclRefExpr};
2696 }
2697
2698 virtual std::optional<FixItList>
2699 getFixits(const FixitStrategy &S) const override;
2700 SourceLocation getSourceLoc() const override { return Op->getBeginLoc(); }
2701};
2702
2703// Represents expressions of the form `&DRE[any]` in the Unspecified Pointer
2704// Context (see `findStmtsInUnspecifiedPointerContext`).
2705// Note here `[]` is the built-in subscript operator.
2706class UPCAddressofArraySubscriptGadget : public FixableGadget {
2707private:
2708 static constexpr const char *const UPCAddressofArraySubscriptTag =
2709 "AddressofArraySubscriptUnderUPC";
2710 const UnaryOperator *Node; // the `&DRE[any]` node
2711
2712public:
2713 UPCAddressofArraySubscriptGadget(const MatchResult &Result)
2714 : FixableGadget(Kind::ULCArraySubscript),
2715 Node(Result.getNodeAs<UnaryOperator>(UPCAddressofArraySubscriptTag)) {
2716 assert(Node != nullptr && "Expecting a non-null matching result");
2717 }
2718
2719 static bool classof(const Gadget *G) {
2720 return G->getKind() == Kind::UPCAddressofArraySubscript;
2721 }
2722
2723 static bool matches(const Stmt *S,
2724 llvm::SmallVectorImpl<MatchResult> &Results) {
2725 size_t SizeBefore = Results.size();
2726 findStmtsInUnspecifiedPointerContext(S, [&Results](const Stmt *S) {
2727 auto *E = dyn_cast<Expr>(S);
2728 if (!E)
2729 return;
2730 const auto *UO = dyn_cast<UnaryOperator>(E->IgnoreImpCasts());
2731 if (!UO || UO->getOpcode() != UO_AddrOf)
2732 return;
2733 const auto *ASE = dyn_cast<ArraySubscriptExpr>(UO->getSubExpr());
2734 if (!ASE)
2735 return;
2736 const auto *DRE =
2737 dyn_cast<DeclRefExpr>(ASE->getBase()->IgnoreParenImpCasts());
2738 if (!DRE || !isSupportedVariable(*DRE))
2739 return;
2740 MatchResult R;
2741 R.addNode(UPCAddressofArraySubscriptTag, DynTypedNode::create(*UO));
2742 Results.emplace_back(std::move(R));
2743 });
2744 return SizeBefore != Results.size();
2745 }
2746
2747 virtual std::optional<FixItList>
2748 getFixits(const FixitStrategy &) const override;
2749 SourceLocation getSourceLoc() const override { return Node->getBeginLoc(); }
2750
2751 virtual DeclUseList getClaimedVarUseSites() const override {
2752 const auto *ArraySubst = cast<ArraySubscriptExpr>(Node->getSubExpr());
2753 const auto *DRE =
2754 cast<DeclRefExpr>(ArraySubst->getBase()->IgnoreParenImpCasts());
2755 return {DRE};
2756 }
2757};
2758} // namespace
2759
2760namespace {
2761// An auxiliary tracking facility for the fixit analysis. It helps connect
2762// declarations to its uses and make sure we've covered all uses with our
2763// analysis before we try to fix the declaration.
2764class DeclUseTracker {
2765 using UseSetTy = llvm::SmallPtrSet<const DeclRefExpr *, 16>;
2766 using DefMapTy = llvm::DenseMap<const VarDecl *, const DeclStmt *>;
2767
2768 // Allocate on the heap for easier move.
2769 std::unique_ptr<UseSetTy> Uses{std::make_unique<UseSetTy>()};
2770 DefMapTy Defs{};
2771
2772public:
2773 DeclUseTracker() = default;
2774 DeclUseTracker(const DeclUseTracker &) = delete; // Let's avoid copies.
2775 DeclUseTracker &operator=(const DeclUseTracker &) = delete;
2776 DeclUseTracker(DeclUseTracker &&) = default;
2777 DeclUseTracker &operator=(DeclUseTracker &&) = default;
2778
2779 // Start tracking a freshly discovered DRE.
2780 void discoverUse(const DeclRefExpr *DRE) { Uses->insert(DRE); }
2781
2782 // Stop tracking the DRE as it's been fully figured out.
2783 void claimUse(const DeclRefExpr *DRE) {
2784 assert(Uses->count(DRE) &&
2785 "DRE not found or claimed by multiple matchers!");
2786 Uses->erase(DRE);
2787 }
2788
2789 // A variable is unclaimed if at least one use is unclaimed.
2790 bool hasUnclaimedUses(const VarDecl *VD) const {
2791 // FIXME: Can this be less linear? Maybe maintain a map from VDs to DREs?
2792 return any_of(*Uses, [VD](const DeclRefExpr *DRE) {
2793 return DRE->getDecl()->getCanonicalDecl() == VD->getCanonicalDecl();
2794 });
2795 }
2796
2797 UseSetTy getUnclaimedUses(const VarDecl *VD) const {
2798 UseSetTy ReturnSet;
2799 for (auto use : *Uses) {
2800 if (use->getDecl()->getCanonicalDecl() == VD->getCanonicalDecl()) {
2801 ReturnSet.insert(use);
2802 }
2803 }
2804 return ReturnSet;
2805 }
2806
2807 void discoverDecl(const DeclStmt *DS) {
2808 for (const Decl *D : DS->decls()) {
2809 if (const auto *VD = dyn_cast<VarDecl>(D)) {
2810 // FIXME: Assertion temporarily disabled due to a bug in
2811 // ASTMatcher internal behavior in presence of GNU
2812 // statement-expressions. We need to properly investigate this
2813 // because it can screw up our algorithm in other ways.
2814 // assert(Defs.count(VD) == 0 && "Definition already discovered!");
2815 Defs[VD] = DS;
2816 }
2817 }
2818 }
2819
2820 const DeclStmt *lookupDecl(const VarDecl *VD) const {
2821 return Defs.lookup(VD);
2822 }
2823};
2824} // namespace
2825
2826// Representing a pointer type expression of the form `++Ptr` in an Unspecified
2827// Pointer Context (UPC):
2828class UPCPreIncrementGadget : public FixableGadget {
2829private:
2830 static constexpr const char *const UPCPreIncrementTag =
2831 "PointerPreIncrementUnderUPC";
2832 const UnaryOperator *Node; // the `++Ptr` node
2833
2834public:
2835 UPCPreIncrementGadget(const MatchResult &Result)
2836 : FixableGadget(Kind::UPCPreIncrement),
2837 Node(Result.getNodeAs<UnaryOperator>(UPCPreIncrementTag)) {
2838 assert(Node != nullptr && "Expecting a non-null matching result");
2839 }
2840
2841 static bool classof(const Gadget *G) {
2842 return G->getKind() == Kind::UPCPreIncrement;
2843 }
2844
2845 static bool matches(const Stmt *S,
2847 // Note here we match `++Ptr` for any expression `Ptr` of pointer type.
2848 // Although currently we can only provide fix-its when `Ptr` is a DRE, we
2849 // can have the matcher be general, so long as `getClaimedVarUseSites` does
2850 // things right.
2851 size_t SizeBefore = Results.size();
2852 findStmtsInUnspecifiedPointerContext(S, [&Results](const Stmt *S) {
2853 auto *E = dyn_cast<Expr>(S);
2854 if (!E)
2855 return;
2856 const auto *UO = dyn_cast<UnaryOperator>(E->IgnoreImpCasts());
2857 if (!UO || UO->getOpcode() != UO_PreInc)
2858 return;
2859 const auto *DRE = dyn_cast<DeclRefExpr>(UO->getSubExpr());
2860 if (!DRE || !isSupportedVariable(*DRE))
2861 return;
2862 MatchResult R;
2863 R.addNode(UPCPreIncrementTag, DynTypedNode::create(*UO));
2864 Results.emplace_back(std::move(R));
2865 });
2866 return SizeBefore != Results.size();
2867 }
2868
2869 virtual std::optional<FixItList>
2870 getFixits(const FixitStrategy &S) const override;
2871 SourceLocation getSourceLoc() const override { return Node->getBeginLoc(); }
2872
2873 virtual DeclUseList getClaimedVarUseSites() const override {
2874 return {dyn_cast<DeclRefExpr>(Node->getSubExpr())};
2875 }
2876};
2877
2878// Representing a pointer type expression of the form `Ptr += n` in an
2879// Unspecified Untyped Context (UUC):
2880class UUCAddAssignGadget : public FixableGadget {
2881private:
2882 static constexpr const char *const UUCAddAssignTag =
2883 "PointerAddAssignUnderUUC";
2884 static constexpr const char *const OffsetTag = "Offset";
2885
2886 const BinaryOperator *Node; // the `Ptr += n` node
2887 const Expr *Offset = nullptr;
2888
2889public:
2890 UUCAddAssignGadget(const MatchResult &Result)
2891 : FixableGadget(Kind::UUCAddAssign),
2892 Node(Result.getNodeAs<BinaryOperator>(UUCAddAssignTag)),
2893 Offset(Result.getNodeAs<Expr>(OffsetTag)) {
2894 assert(Node != nullptr && "Expecting a non-null matching result");
2895 }
2896
2897 static bool classof(const Gadget *G) {
2898 return G->getKind() == Kind::UUCAddAssign;
2899 }
2900
2901 static bool matches(const Stmt *S,
2903 size_t SizeBefore = Results.size();
2904 findStmtsInUnspecifiedUntypedContext(S, [&Results](const Stmt *S) {
2905 const auto *E = dyn_cast<Expr>(S);
2906 if (!E)
2907 return;
2908 const auto *BO = dyn_cast<BinaryOperator>(E->IgnoreImpCasts());
2909 if (!BO || BO->getOpcode() != BO_AddAssign)
2910 return;
2911 const auto *DRE = dyn_cast<DeclRefExpr>(BO->getLHS());
2912 if (!DRE || !hasPointerType(*DRE) || !isSupportedVariable(*DRE))
2913 return;
2914 MatchResult R;
2915 R.addNode(UUCAddAssignTag, DynTypedNode::create(*BO));
2916 R.addNode(OffsetTag, DynTypedNode::create(*BO->getRHS()));
2917 Results.emplace_back(std::move(R));
2918 });
2919 return SizeBefore != Results.size();
2920 }
2921
2922 virtual std::optional<FixItList>
2923 getFixits(const FixitStrategy &S) const override;
2924 SourceLocation getSourceLoc() const override { return Node->getBeginLoc(); }
2925
2926 virtual DeclUseList getClaimedVarUseSites() const override {
2927 return {dyn_cast<DeclRefExpr>(Node->getLHS())};
2928 }
2929};
2930
2931// Representing a fixable expression of the form `*(ptr + 123)` or `*(123 +
2932// ptr)`:
2933class DerefSimplePtrArithFixableGadget : public FixableGadget {
2934 static constexpr const char *const BaseDeclRefExprTag = "BaseDRE";
2935 static constexpr const char *const DerefOpTag = "DerefOp";
2936 static constexpr const char *const AddOpTag = "AddOp";
2937 static constexpr const char *const OffsetTag = "Offset";
2938
2939 const DeclRefExpr *BaseDeclRefExpr = nullptr;
2940 const UnaryOperator *DerefOp = nullptr;
2941 const BinaryOperator *AddOp = nullptr;
2942 const IntegerLiteral *Offset = nullptr;
2943
2944public:
2946 : FixableGadget(Kind::DerefSimplePtrArithFixable),
2947 BaseDeclRefExpr(Result.getNodeAs<DeclRefExpr>(BaseDeclRefExprTag)),
2948 DerefOp(Result.getNodeAs<UnaryOperator>(DerefOpTag)),
2949 AddOp(Result.getNodeAs<BinaryOperator>(AddOpTag)),
2950 Offset(Result.getNodeAs<IntegerLiteral>(OffsetTag)) {}
2951
2952 static bool matches(const Stmt *S,
2954 auto IsPtr = [](const Expr *E, MatchResult &R) {
2955 if (!E || !hasPointerType(*E))
2956 return false;
2957 const auto *DRE = dyn_cast<DeclRefExpr>(E->IgnoreImpCasts());
2958 if (!DRE || !isSupportedVariable(*DRE))
2959 return false;
2960 R.addNode(BaseDeclRefExprTag, DynTypedNode::create(*DRE));
2961 return true;
2962 };
2963 const auto IsPlusOverPtrAndInteger = [&IsPtr](const Expr *E,
2964 MatchResult &R) {
2965 const auto *BO = dyn_cast<BinaryOperator>(E);
2966 if (!BO || BO->getOpcode() != BO_Add)
2967 return false;
2968
2969 const auto *LHS = BO->getLHS();
2970 const auto *RHS = BO->getRHS();
2971 if (isa<IntegerLiteral>(RHS) && IsPtr(LHS, R)) {
2972 R.addNode(OffsetTag, DynTypedNode::create(*RHS));
2973 R.addNode(AddOpTag, DynTypedNode::create(*BO));
2974 return true;
2975 }
2976 if (isa<IntegerLiteral>(LHS) && IsPtr(RHS, R)) {
2977 R.addNode(OffsetTag, DynTypedNode::create(*LHS));
2978 R.addNode(AddOpTag, DynTypedNode::create(*BO));
2979 return true;
2980 }
2981 return false;
2982 };
2983 size_t SizeBefore = Results.size();
2984 const auto InnerMatcher = [&IsPlusOverPtrAndInteger,
2985 &Results](const Expr *E) {
2986 const auto *UO = dyn_cast<UnaryOperator>(E);
2987 if (!UO || UO->getOpcode() != UO_Deref)
2988 return;
2989
2990 const auto *Operand = UO->getSubExpr()->IgnoreParens();
2991 MatchResult R;
2992 if (IsPlusOverPtrAndInteger(Operand, R)) {
2993 R.addNode(DerefOpTag, DynTypedNode::create(*UO));
2994 Results.emplace_back(std::move(R));
2995 }
2996 };
2997 findStmtsInUnspecifiedLvalueContext(S, InnerMatcher);
2998 return SizeBefore != Results.size();
2999 }
3000
3001 virtual std::optional<FixItList>
3002 getFixits(const FixitStrategy &s) const final;
3003 SourceLocation getSourceLoc() const override {
3004 return DerefOp->getBeginLoc();
3005 }
3006
3007 virtual DeclUseList getClaimedVarUseSites() const final {
3008 return {BaseDeclRefExpr};
3009 }
3010};
3011
3012class WarningGadgetMatcher : public FastMatcher {
3013
3014public:
3015 WarningGadgetMatcher(WarningGadgetList &WarningGadgets)
3016 : WarningGadgets(WarningGadgets) {}
3017
3018 bool matches(const DynTypedNode &DynNode, ASTContext &Ctx,
3019 const UnsafeBufferUsageHandler &Handler) override {
3020 const Stmt *S = DynNode.get<Stmt>();
3021 if (!S)
3022 return false;
3023
3024 MatchResult Result;
3025#define WARNING_GADGET(name) \
3026 if (name##Gadget::matches(S, Ctx, Result) && \
3027 notInSafeBufferOptOut(*S, &Handler)) { \
3028 WarningGadgets.push_back(std::make_unique<name##Gadget>(Result)); \
3029 return true; \
3030 }
3031#define WARNING_OPTIONAL_GADGET(name) \
3032 if (name##Gadget::matches(S, Ctx, &Handler, Result) && \
3033 notInSafeBufferOptOut(*S, &Handler)) { \
3034 WarningGadgets.push_back(std::make_unique<name##Gadget>(Result)); \
3035 return true; \
3036 }
3037#include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
3038 return false;
3039 }
3040
3041private:
3042 WarningGadgetList &WarningGadgets;
3043};
3044
3045class FixableGadgetMatcher : public FastMatcher {
3046
3047public:
3048 FixableGadgetMatcher(FixableGadgetList &FixableGadgets,
3049 DeclUseTracker &Tracker)
3050 : FixableGadgets(FixableGadgets), Tracker(Tracker) {}
3051
3052 bool matches(const DynTypedNode &DynNode, ASTContext &Ctx,
3053 const UnsafeBufferUsageHandler &Handler) override {
3054 bool matchFound = false;
3055 const Stmt *S = DynNode.get<Stmt>();
3056 if (!S) {
3057 return matchFound;
3058 }
3059
3061#define FIXABLE_GADGET(name) \
3062 if (name##Gadget::matches(S, Results)) { \
3063 for (const auto &R : Results) { \
3064 FixableGadgets.push_back(std::make_unique<name##Gadget>(R)); \
3065 matchFound = true; \
3066 } \
3067 Results = {}; \
3068 }
3069#include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
3070 // In parallel, match all DeclRefExprs so that to find out
3071 // whether there are any uncovered by gadgets.
3072 if (auto *DRE = findDeclRefExpr(S); DRE) {
3073 Tracker.discoverUse(DRE);
3074 matchFound = true;
3075 }
3076 // Also match DeclStmts because we'll need them when fixing
3077 // their underlying VarDecls that otherwise don't have
3078 // any backreferences to DeclStmts.
3079 if (auto *DS = findDeclStmt(S); DS) {
3080 Tracker.discoverDecl(DS);
3081 matchFound = true;
3082 }
3083 return matchFound;
3084 }
3085
3086private:
3087 const DeclRefExpr *findDeclRefExpr(const Stmt *S) {
3088 const auto *DRE = dyn_cast<DeclRefExpr>(S);
3089 if (!DRE || (!hasPointerType(*DRE) && !hasArrayType(*DRE)))
3090 return nullptr;
3091 const Decl *D = DRE->getDecl();
3092 if (!D || (!isa<VarDecl>(D) && !isa<BindingDecl>(D)))
3093 return nullptr;
3094 return DRE;
3095 }
3096 const DeclStmt *findDeclStmt(const Stmt *S) {
3097 const auto *DS = dyn_cast<DeclStmt>(S);
3098 if (!DS)
3099 return nullptr;
3100 return DS;
3101 }
3102 FixableGadgetList &FixableGadgets;
3103 DeclUseTracker &Tracker;
3104};
3105
3106// Scan the function and return a list of gadgets found with provided kits.
3107static void findGadgets(const Stmt *S, ASTContext &Ctx,
3108 const UnsafeBufferUsageHandler &Handler,
3109 bool EmitSuggestions, FixableGadgetList &FixableGadgets,
3110 WarningGadgetList &WarningGadgets,
3111 DeclUseTracker &Tracker) {
3112 WarningGadgetMatcher WMatcher{WarningGadgets};
3113 forEachDescendantEvaluatedStmt(S, Ctx, Handler, WMatcher);
3114 if (EmitSuggestions) {
3115 FixableGadgetMatcher FMatcher{FixableGadgets, Tracker};
3116 forEachDescendantStmt(S, Ctx, Handler, FMatcher);
3117 }
3118}
3119
3120// Compares AST nodes by source locations.
3121template <typename NodeTy> struct CompareNode {
3122 bool operator()(const NodeTy *N1, const NodeTy *N2) const {
3123 return N1->getBeginLoc().getRawEncoding() <
3124 N2->getBeginLoc().getRawEncoding();
3125 }
3126};
3127
3128// Populate `Stmts` with the body/initializer Stmt of `D`, if `D` is one of the
3129// followings:
3130// VarDecl
3131// FieldDecl
3132// FunctionDecl
3133// BlockDecl
3134// ObjCMethodDecl
3136 const Decl *D) {
3137 auto AddStmt = [&Stmts](const Stmt *S) {
3138 if (S)
3139 Stmts.push_back(S);
3140 };
3141 if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
3142 AddStmt(FD->getBody());
3143 for (const auto *PD : FD->parameters())
3144 if (PD->hasDefaultArg() && !PD->hasUninstantiatedDefaultArg())
3145 AddStmt(PD->getDefaultArg());
3146 if (const auto *CtorD = dyn_cast<CXXConstructorDecl>(FD))
3147 llvm::append_range(
3148 Stmts, llvm::map_range(CtorD->inits(),
3149 std::mem_fn(&CXXCtorInitializer::getInit)));
3150 } else if (isa<BlockDecl>(D) || isa<ObjCMethodDecl>(D)) {
3151 AddStmt(D->getBody());
3152 } else if (const auto *VD = dyn_cast<VarDecl>(D)) {
3153 AddStmt(VD->getInit()); // FIXME: default arg for ParmVarDecl?
3154 } else if (const auto *FD = dyn_cast<FieldDecl>(D)) {
3155 AddStmt(FD->getInClassInitializer());
3156 }
3157}
3158
3160 std::map<const VarDecl *, std::set<const WarningGadget *>,
3161 // To keep keys sorted by their locations in the map so that the
3162 // order is deterministic:
3165 // These Gadgets are not related to pointer variables (e. g. temporaries).
3167};
3168
3169static WarningGadgetSets
3170groupWarningGadgetsByVar(const WarningGadgetList &AllUnsafeOperations) {
3171 WarningGadgetSets result;
3172 // If some gadgets cover more than one
3173 // variable, they'll appear more than once in the map.
3174 for (auto &G : AllUnsafeOperations) {
3175 DeclUseList ClaimedVarUseSites = G->getClaimedVarUseSites();
3176
3177 bool AssociatedWithVarDecl = false;
3178 for (const DeclRefExpr *DRE : ClaimedVarUseSites) {
3179 if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
3180 result.byVar[VD].insert(G.get());
3181 AssociatedWithVarDecl = true;
3182 }
3183 }
3184
3185 if (!AssociatedWithVarDecl) {
3186 result.noVar.push_back(G.get());
3187 continue;
3188 }
3189 }
3190 return result;
3191}
3192
3194 std::map<const VarDecl *, std::set<const FixableGadget *>,
3195 // To keep keys sorted by their locations in the map so that the
3196 // order is deterministic:
3199};
3200
3201static FixableGadgetSets
3202groupFixablesByVar(FixableGadgetList &&AllFixableOperations) {
3203 FixableGadgetSets FixablesForUnsafeVars;
3204 for (auto &F : AllFixableOperations) {
3205 DeclUseList DREs = F->getClaimedVarUseSites();
3206
3207 for (const DeclRefExpr *DRE : DREs) {
3208 if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
3209 FixablesForUnsafeVars.byVar[VD].insert(F.get());
3210 }
3211 }
3212 }
3213 return FixablesForUnsafeVars;
3214}
3215
3217 const SourceManager &SM) {
3218 // A simple interval overlap detection algorithm. Sorts all ranges by their
3219 // begin location then finds the first overlap in one pass.
3220 std::vector<const FixItHint *> All; // a copy of `FixIts`
3221
3222 for (const FixItHint &H : FixIts)
3223 All.push_back(&H);
3224 std::sort(All.begin(), All.end(),
3225 [&SM](const FixItHint *H1, const FixItHint *H2) {
3226 return SM.isBeforeInTranslationUnit(H1->RemoveRange.getBegin(),
3227 H2->RemoveRange.getBegin());
3228 });
3229
3230 const FixItHint *CurrHint = nullptr;
3231
3232 for (const FixItHint *Hint : All) {
3233 if (!CurrHint ||
3235 Hint->RemoveRange.getBegin())) {
3236 // Either to initialize `CurrHint` or `CurrHint` does not
3237 // overlap with `Hint`:
3238 CurrHint = Hint;
3239 } else
3240 // In case `Hint` overlaps the `CurrHint`, we found at least one
3241 // conflict:
3242 return true;
3243 }
3244 return false;
3245}
3246
3247std::optional<FixItList>
3248PtrToPtrAssignmentGadget::getFixits(const FixitStrategy &S) const {
3249 const auto *LeftVD = cast<VarDecl>(PtrLHS->getDecl());
3250 const auto *RightVD = cast<VarDecl>(PtrRHS->getDecl());
3251 switch (S.lookup(LeftVD)) {
3253 if (S.lookup(RightVD) == FixitStrategy::Kind::Span)
3254 return FixItList{};
3255 return std::nullopt;
3257 return std::nullopt;
3260 return std::nullopt;
3262 llvm_unreachable("unsupported strategies for FixableGadgets");
3263 }
3264 return std::nullopt;
3265}
3266
3267/// \returns fixit that adds .data() call after \DRE.
3268static inline std::optional<FixItList> createDataFixit(const ASTContext &Ctx,
3269 const DeclRefExpr *DRE);
3270
3271std::optional<FixItList>
3272CArrayToPtrAssignmentGadget::getFixits(const FixitStrategy &S) const {
3273 const auto *LeftVD = cast<VarDecl>(PtrLHS->getDecl());
3274 const auto *RightVD = cast<VarDecl>(PtrRHS->getDecl());
3275 // TLDR: Implementing fixits for non-Wontfix strategy on both LHS and RHS is
3276 // non-trivial.
3277 //
3278 // CArrayToPtrAssignmentGadget doesn't have strategy implications because
3279 // constant size array propagates its bounds. Because of that LHS and RHS are
3280 // addressed by two different fixits.
3281 //
3282 // At the same time FixitStrategy S doesn't reflect what group a fixit belongs
3283 // to and can't be generally relied on in multi-variable Fixables!
3284 //
3285 // E. g. If an instance of this gadget is fixing variable on LHS then the
3286 // variable on RHS is fixed by a different fixit and its strategy for LHS
3287 // fixit is as if Wontfix.
3288 //
3289 // The only exception is Wontfix strategy for a given variable as that is
3290 // valid for any fixit produced for the given input source code.
3291 if (S.lookup(LeftVD) == FixitStrategy::Kind::Span) {
3292 if (S.lookup(RightVD) == FixitStrategy::Kind::Wontfix) {
3293 return FixItList{};
3294 }
3295 } else if (S.lookup(LeftVD) == FixitStrategy::Kind::Wontfix) {
3296 if (S.lookup(RightVD) == FixitStrategy::Kind::Array) {
3297 return createDataFixit(RightVD->getASTContext(), PtrRHS);
3298 }
3299 }
3300 return std::nullopt;
3301}
3302
3303std::optional<FixItList>
3304PointerInitGadget::getFixits(const FixitStrategy &S) const {
3305 const auto *LeftVD = PtrInitLHS;
3306 const auto *RightVD = cast<VarDecl>(PtrInitRHS->getDecl());
3307 switch (S.lookup(LeftVD)) {
3308 case FixitStrategy::Kind::Span:
3309 if (S.lookup(RightVD) == FixitStrategy::Kind::Span)
3310 return FixItList{};
3311 return std::nullopt;
3312 case FixitStrategy::Kind::Wontfix:
3313 return std::nullopt;
3314 case FixitStrategy::Kind::Iterator:
3315 case FixitStrategy::Kind::Array:
3316 return std::nullopt;
3317 case FixitStrategy::Kind::Vector:
3318 llvm_unreachable("unsupported strategies for FixableGadgets");
3319 }
3320 return std::nullopt;
3321}
3322
3323static bool isNonNegativeIntegerExpr(const Expr *Expr, const VarDecl *VD,
3324 const ASTContext &Ctx) {
3325 if (auto ConstVal = Expr->getIntegerConstantExpr(Ctx)) {
3326 if (ConstVal->isNegative())
3327 return false;
3328 } else if (!Expr->getType()->isUnsignedIntegerType())
3329 return false;
3330 return true;
3331}
3332
3333std::optional<FixItList>
3334ULCArraySubscriptGadget::getFixits(const FixitStrategy &S) const {
3335 if (const auto *DRE =
3336 dyn_cast<DeclRefExpr>(Node->getBase()->IgnoreImpCasts()))
3337 if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
3338 switch (S.lookup(VD)) {
3339 case FixitStrategy::Kind::Span: {
3340
3341 // If the index has a negative constant value, we give up as no valid
3342 // fix-it can be generated:
3343 const ASTContext &Ctx = // FIXME: we need ASTContext to be passed in!
3344 VD->getASTContext();
3345 if (!isNonNegativeIntegerExpr(Node->getIdx(), VD, Ctx))
3346 return std::nullopt;
3347 // no-op is a good fix-it, otherwise
3348 return FixItList{};
3349 }
3350 case FixitStrategy::Kind::Array:
3351 return FixItList{};
3352 case FixitStrategy::Kind::Wontfix:
3353 case FixitStrategy::Kind::Iterator:
3354 case FixitStrategy::Kind::Vector:
3355 llvm_unreachable("unsupported strategies for FixableGadgets");
3356 }
3357 }
3358 return std::nullopt;
3359}
3360
3361static std::optional<FixItList> // forward declaration
3362fixUPCAddressofArraySubscriptWithSpan(const UnaryOperator *Node);
3363
3364std::optional<FixItList>
3365UPCAddressofArraySubscriptGadget::getFixits(const FixitStrategy &S) const {
3366 auto DREs = getClaimedVarUseSites();
3367 const auto *VD = cast<VarDecl>(DREs.front()->getDecl());
3368
3369 switch (S.lookup(VD)) {
3370 case FixitStrategy::Kind::Span:
3372 case FixitStrategy::Kind::Wontfix:
3373 case FixitStrategy::Kind::Iterator:
3374 case FixitStrategy::Kind::Array:
3375 return std::nullopt;
3376 case FixitStrategy::Kind::Vector:
3377 llvm_unreachable("unsupported strategies for FixableGadgets");
3378 }
3379 return std::nullopt; // something went wrong, no fix-it
3380}
3381
3382// FIXME: this function should be customizable through format
3383static StringRef getEndOfLine() {
3384 static const char *const EOL = "\n";
3385 return EOL;
3386}
3387
3388// Returns the text indicating that the user needs to provide input there:
3389static std::string
3390getUserFillPlaceHolder(StringRef HintTextToUser = "placeholder") {
3391 std::string s = std::string("<# ");
3392 s += HintTextToUser;
3393 s += " #>";
3394 return s;
3395}
3396
3397// Return the source location of the last character of the AST `Node`.
3398template <typename NodeTy>
3399static std::optional<SourceLocation>
3400getEndCharLoc(const NodeTy *Node, const SourceManager &SM,
3401 const LangOptions &LangOpts) {
3402 if (unsigned TkLen =
3403 Lexer::MeasureTokenLength(Node->getEndLoc(), SM, LangOpts)) {
3404 SourceLocation Loc = Node->getEndLoc().getLocWithOffset(TkLen - 1);
3405
3406 if (Loc.isValid())
3407 return Loc;
3408 }
3409 return std::nullopt;
3410}
3411
3412// We cannot fix a variable declaration if it has some other specifiers than the
3413// type specifier. Because the source ranges of those specifiers could overlap
3414// with the source range that is being replaced using fix-its. Especially when
3415// we often cannot obtain accurate source ranges of cv-qualified type
3416// specifiers.
3417// FIXME: also deal with type attributes
3418static bool hasUnsupportedSpecifiers(const VarDecl *VD,
3419 const SourceManager &SM) {
3420 // AttrRangeOverlapping: true if at least one attribute of `VD` overlaps the
3421 // source range of `VD`:
3422 bool AttrRangeOverlapping = llvm::any_of(VD->attrs(), [&](Attr *At) -> bool {
3423 return !(SM.isBeforeInTranslationUnit(At->getRange().getEnd(),
3424 VD->getBeginLoc())) &&
3425 !(SM.isBeforeInTranslationUnit(VD->getEndLoc(),
3426 At->getRange().getBegin()));
3427 });
3428 return VD->isInlineSpecified() || VD->isConstexpr() ||
3430 AttrRangeOverlapping;
3431}
3432
3433// Returns the `SourceRange` of `D`. The reason why this function exists is
3434// that `D->getSourceRange()` may return a range where the end location is the
3435// starting location of the last token. The end location of the source range
3436// returned by this function is the last location of the last token.
3438 const SourceManager &SM,
3439 const LangOptions &LangOpts) {
3440 SourceLocation Begin = D->getBeginLoc();
3442 End = // `D->getEndLoc` should always return the starting location of the
3443 // last token, so we should get the end of the token
3444 Lexer::getLocForEndOfToken(D->getEndLoc(), 0, SM, LangOpts);
3445
3446 return SourceRange(Begin, End);
3447}
3448
3449// Returns the text of the name (with qualifiers) of a `FunctionDecl`.
3450static std::optional<StringRef> getFunNameText(const FunctionDecl *FD,
3451 const SourceManager &SM,
3452 const LangOptions &LangOpts) {
3453 SourceLocation BeginLoc = FD->getQualifier()
3455 : FD->getNameInfo().getBeginLoc();
3456 // Note that `FD->getNameInfo().getEndLoc()` returns the begin location of the
3457 // last token:
3459 FD->getNameInfo().getEndLoc(), 0, SM, LangOpts);
3460 SourceRange NameRange{BeginLoc, EndLoc};
3461
3462 return getRangeText(NameRange, SM, LangOpts);
3463}
3464
3465// Returns the text representing a `std::span` type where the element type is
3466// represented by `EltTyText`.
3467//
3468// Note the optional parameter `Qualifiers`: one needs to pass qualifiers
3469// explicitly if the element type needs to be qualified.
3470static std::string
3471getSpanTypeText(StringRef EltTyText,
3472 std::optional<Qualifiers> Quals = std::nullopt) {
3473 const char *const SpanOpen = "std::span<";
3474
3475 if (Quals)
3476 return SpanOpen + EltTyText.str() + ' ' + Quals->getAsString() + '>';
3477 return SpanOpen + EltTyText.str() + '>';
3478}
3479
3480std::optional<FixItList>
3482 const VarDecl *VD = dyn_cast<VarDecl>(BaseDeclRefExpr->getDecl());
3483
3484 if (VD && s.lookup(VD) == FixitStrategy::Kind::Span) {
3485 ASTContext &Ctx = VD->getASTContext();
3486 // std::span can't represent elements before its begin()
3487 if (auto ConstVal = Offset->getIntegerConstantExpr(Ctx))
3488 if (ConstVal->isNegative())
3489 return std::nullopt;
3490
3491 // note that the expr may (oddly) has multiple layers of parens
3492 // example:
3493 // *((..(pointer + 123)..))
3494 // goal:
3495 // pointer[123]
3496 // Fix-It:
3497 // remove '*('
3498 // replace ' + ' with '['
3499 // replace ')' with ']'
3500
3501 // example:
3502 // *((..(123 + pointer)..))
3503 // goal:
3504 // 123[pointer]
3505 // Fix-It:
3506 // remove '*('
3507 // replace ' + ' with '['
3508 // replace ')' with ']'
3509
3510 const Expr *LHS = AddOp->getLHS(), *RHS = AddOp->getRHS();
3511 const SourceManager &SM = Ctx.getSourceManager();
3512 const LangOptions &LangOpts = Ctx.getLangOpts();
3513 CharSourceRange StarWithTrailWhitespace =
3514 clang::CharSourceRange::getCharRange(DerefOp->getOperatorLoc(),
3515 LHS->getBeginLoc());
3516
3517 std::optional<SourceLocation> LHSLocation = getPastLoc(LHS, SM, LangOpts);
3518 if (!LHSLocation)
3519 return std::nullopt;
3520
3521 CharSourceRange PlusWithSurroundingWhitespace =
3522 clang::CharSourceRange::getCharRange(*LHSLocation, RHS->getBeginLoc());
3523
3524 std::optional<SourceLocation> AddOpLocation =
3525 getPastLoc(AddOp, SM, LangOpts);
3526 std::optional<SourceLocation> DerefOpLocation =
3527 getPastLoc(DerefOp, SM, LangOpts);
3528
3529 if (!AddOpLocation || !DerefOpLocation)
3530 return std::nullopt;
3531
3532 CharSourceRange ClosingParenWithPrecWhitespace =
3533 clang::CharSourceRange::getCharRange(*AddOpLocation, *DerefOpLocation);
3534
3535 return FixItList{
3536 {FixItHint::CreateRemoval(StarWithTrailWhitespace),
3537 FixItHint::CreateReplacement(PlusWithSurroundingWhitespace, "["),
3538 FixItHint::CreateReplacement(ClosingParenWithPrecWhitespace, "]")}};
3539 }
3540 return std::nullopt; // something wrong or unsupported, give up
3541}
3542
3543std::optional<FixItList>
3544PointerDereferenceGadget::getFixits(const FixitStrategy &S) const {
3545 const VarDecl *VD = cast<VarDecl>(BaseDeclRefExpr->getDecl());
3546 switch (S.lookup(VD)) {
3548 ASTContext &Ctx = VD->getASTContext();
3549 SourceManager &SM = Ctx.getSourceManager();
3550 // Required changes: *(ptr); => (ptr[0]); and *ptr; => ptr[0]
3551 // Deletes the *operand
3553 Op->getBeginLoc(), Op->getBeginLoc().getLocWithOffset(1));
3554 // Inserts the [0]
3555 if (auto LocPastOperand =
3556 getPastLoc(BaseDeclRefExpr, SM, Ctx.getLangOpts())) {
3557 return FixItList{{FixItHint::CreateRemoval(derefRange),
3558 FixItHint::CreateInsertion(*LocPastOperand, "[0]")}};
3559 }
3560 break;
3561 }
3562 case FixitStrategy::Kind::Iterator:
3563 case FixitStrategy::Kind::Array:
3564 return std::nullopt;
3565 case FixitStrategy::Kind::Vector:
3566 llvm_unreachable("FixitStrategy not implemented yet!");
3567 case FixitStrategy::Kind::Wontfix:
3568 llvm_unreachable("Invalid strategy!");
3569 }
3570
3571 return std::nullopt;
3572}
3573
3574static inline std::optional<FixItList> createDataFixit(const ASTContext &Ctx,
3575 const DeclRefExpr *DRE) {
3576 const SourceManager &SM = Ctx.getSourceManager();
3577 // Inserts the .data() after the DRE
3578 std::optional<SourceLocation> EndOfOperand =
3579 getPastLoc(DRE, SM, Ctx.getLangOpts());
3580
3581 if (EndOfOperand)
3582 return FixItList{{FixItHint::CreateInsertion(*EndOfOperand, ".data()")}};
3583
3584 return std::nullopt;
3585}
3586
3587// Generates fix-its replacing an expression of the form UPC(DRE) with
3588// `DRE.data()`
3589std::optional<FixItList>
3590UPCStandalonePointerGadget::getFixits(const FixitStrategy &S) const {
3591 const auto VD = cast<VarDecl>(Node->getDecl());
3592 switch (S.lookup(VD)) {
3593 case FixitStrategy::Kind::Array:
3594 case FixitStrategy::Kind::Span: {
3595 return createDataFixit(VD->getASTContext(), Node);
3596 // FIXME: Points inside a macro expansion.
3597 break;
3598 }
3599 case FixitStrategy::Kind::Wontfix:
3600 case FixitStrategy::Kind::Iterator:
3601 return std::nullopt;
3602 case FixitStrategy::Kind::Vector:
3603 llvm_unreachable("unsupported strategies for FixableGadgets");
3604 }
3605
3606 return std::nullopt;
3607}
3608
3609// Generates fix-its replacing an expression of the form `&DRE[e]` with
3610// `&DRE.data()[e]`:
3611static std::optional<FixItList>
3613 const auto *ArraySub = cast<ArraySubscriptExpr>(Node->getSubExpr());
3614 const auto *DRE = cast<DeclRefExpr>(ArraySub->getBase()->IgnoreImpCasts());
3615 // FIXME: this `getASTContext` call is costly, we should pass the
3616 // ASTContext in:
3617 const ASTContext &Ctx = DRE->getDecl()->getASTContext();
3618 const Expr *Idx = ArraySub->getIdx();
3619 const SourceManager &SM = Ctx.getSourceManager();
3620 const LangOptions &LangOpts = Ctx.getLangOpts();
3621 std::stringstream SS;
3622 bool IdxIsLitZero = false;
3623
3624 if (auto ICE = Idx->getIntegerConstantExpr(Ctx))
3625 if ((*ICE).isZero())
3626 IdxIsLitZero = true;
3627 std::optional<StringRef> DreString = getExprText(DRE, SM, LangOpts);
3628 if (!DreString)
3629 return std::nullopt;
3630
3631 if (IdxIsLitZero) {
3632 // If the index is literal zero, we produce the most concise fix-it:
3633 SS << (*DreString).str() << ".data()";
3634 } else {
3635 std::optional<StringRef> IndexString = getExprText(Idx, SM, LangOpts);
3636 if (!IndexString)
3637 return std::nullopt;
3638
3639 SS << "&" << (*DreString).str() << ".data()"
3640 << "[" << (*IndexString).str() << "]";
3641 }
3642 return FixItList{
3643 FixItHint::CreateReplacement(Node->getSourceRange(), SS.str())};
3644}
3645
3646std::optional<FixItList>
3648 DeclUseList DREs = getClaimedVarUseSites();
3649
3650 if (DREs.size() != 1)
3651 return std::nullopt; // In cases of `Ptr += n` where `Ptr` is not a DRE, we
3652 // give up
3653 if (const VarDecl *VD = dyn_cast<VarDecl>(DREs.front()->getDecl())) {
3654 if (S.lookup(VD) == FixitStrategy::Kind::Span) {
3655 FixItList Fixes;
3656
3657 const Stmt *AddAssignNode = Node;
3658 StringRef varName = VD->getName();
3659 const ASTContext &Ctx = VD->getASTContext();
3660
3661 if (!isNonNegativeIntegerExpr(Offset, VD, Ctx))
3662 return std::nullopt;
3663
3664 // To transform UUC(p += n) to UUC(p = p.subspan(..)):
3665 bool NotParenExpr =
3666 (Offset->IgnoreParens()->getBeginLoc() == Offset->getBeginLoc());
3667 std::string SS = varName.str() + " = " + varName.str() + ".subspan";
3668 if (NotParenExpr)
3669 SS += "(";
3670
3671 std::optional<SourceLocation> AddAssignLocation = getEndCharLoc(
3672 AddAssignNode, Ctx.getSourceManager(), Ctx.getLangOpts());
3673 if (!AddAssignLocation)
3674 return std::nullopt;
3675
3676 Fixes.push_back(FixItHint::CreateReplacement(
3677 SourceRange(AddAssignNode->getBeginLoc(), Node->getOperatorLoc()),
3678 SS));
3679 if (NotParenExpr)
3680 Fixes.push_back(FixItHint::CreateInsertion(
3681 Offset->getEndLoc().getLocWithOffset(1), ")"));
3682 return Fixes;
3683 }
3684 }
3685 return std::nullopt; // Not in the cases that we can handle for now, give up.
3686}
3687
3688std::optional<FixItList>
3690 DeclUseList DREs = getClaimedVarUseSites();
3691
3692 if (DREs.size() != 1)
3693 return std::nullopt; // In cases of `++Ptr` where `Ptr` is not a DRE, we
3694 // give up
3695 if (const VarDecl *VD = dyn_cast<VarDecl>(DREs.front()->getDecl())) {
3696 if (S.lookup(VD) == FixitStrategy::Kind::Span) {
3697 FixItList Fixes;
3698 std::stringstream SS;
3699 StringRef varName = VD->getName();
3700 const ASTContext &Ctx = VD->getASTContext();
3701
3702 // To transform UPC(++p) to UPC((p = p.subspan(1)).data()):
3703 SS << "(" << varName.data() << " = " << varName.data()
3704 << ".subspan(1)).data()";
3705 std::optional<SourceLocation> PreIncLocation =
3706 getEndCharLoc(Node, Ctx.getSourceManager(), Ctx.getLangOpts());
3707 if (!PreIncLocation)
3708 return std::nullopt;
3709
3710 Fixes.push_back(FixItHint::CreateReplacement(
3711 SourceRange(Node->getBeginLoc(), *PreIncLocation), SS.str()));
3712 return Fixes;
3713 }
3714 }
3715 return std::nullopt; // Not in the cases that we can handle for now, give up.
3716}
3717
3718// For a non-null initializer `Init` of `T *` type, this function returns
3719// `FixItHint`s producing a list initializer `{Init, S}` as a part of a fix-it
3720// to output stream.
3721// In many cases, this function cannot figure out the actual extent `S`. It
3722// then will use a place holder to replace `S` to ask users to fill `S` in. The
3723// initializer shall be used to initialize a variable of type `std::span<T>`.
3724// In some cases (e. g. constant size array) the initializer should remain
3725// unchanged and the function returns empty list. In case the function can't
3726// provide the right fixit it will return nullopt.
3727//
3728// FIXME: Support multi-level pointers
3729//
3730// Parameters:
3731// `Init` a pointer to the initializer expression
3732// `Ctx` a reference to the ASTContext
3733static std::optional<FixItList>
3735 const StringRef UserFillPlaceHolder) {
3736 const SourceManager &SM = Ctx.getSourceManager();
3737 const LangOptions &LangOpts = Ctx.getLangOpts();
3738
3739 // If `Init` has a constant value that is (or equivalent to) a
3740 // NULL pointer, we use the default constructor to initialize the span
3741 // object, i.e., a `std:span` variable declaration with no initializer.
3742 // So the fix-it is just to remove the initializer.
3743 if (Init->isNullPointerConstant(
3744 Ctx,
3745 // FIXME: Why does this function not ask for `const ASTContext
3746 // &`? It should. Maybe worth an NFC patch later.
3748 NPC_ValueDependentIsNotNull)) {
3749 std::optional<SourceLocation> InitLocation =
3750 getEndCharLoc(Init, SM, LangOpts);
3751 if (!InitLocation)
3752 return std::nullopt;
3753
3754 SourceRange SR(Init->getBeginLoc(), *InitLocation);
3755
3756 return FixItList{FixItHint::CreateRemoval(SR)};
3757 }
3758
3759 FixItList FixIts{};
3760 std::string ExtentText = UserFillPlaceHolder.data();
3761 StringRef One = "1";
3762
3763 // Insert `{` before `Init`:
3764 FixIts.push_back(FixItHint::CreateInsertion(Init->getBeginLoc(), "{"));
3765 // Try to get the data extent. Break into different cases:
3766 if (auto CxxNew = dyn_cast<CXXNewExpr>(Init->IgnoreImpCasts())) {
3767 // In cases `Init` is `new T[n]` and there is no explicit cast over
3768 // `Init`, we know that `Init` must evaluates to a pointer to `n` objects
3769 // of `T`. So the extent is `n` unless `n` has side effects. Similar but
3770 // simpler for the case where `Init` is `new T`.
3771 if (const Expr *Ext = CxxNew->getArraySize().value_or(nullptr)) {
3772 if (!Ext->HasSideEffects(Ctx)) {
3773 std::optional<StringRef> ExtentString = getExprText(Ext, SM, LangOpts);
3774 if (!ExtentString)
3775 return std::nullopt;
3776 ExtentText = *ExtentString;
3777 }
3778 } else if (!CxxNew->isArray())
3779 // Although the initializer is not allocating a buffer, the pointer
3780 // variable could still be used in buffer access operations.
3781 ExtentText = One;
3782 } else if (Ctx.getAsConstantArrayType(Init->IgnoreImpCasts()->getType())) {
3783 // std::span has a single parameter constructor for initialization with
3784 // constant size array. The size is auto-deduced as the constructor is a
3785 // function template. The correct fixit is empty - no changes should happen.
3786 return FixItList{};
3787 } else {
3788 // In cases `Init` is of the form `&Var` after stripping of implicit
3789 // casts, where `&` is the built-in operator, the extent is 1.
3790 if (auto AddrOfExpr = dyn_cast<UnaryOperator>(Init->IgnoreImpCasts()))
3791 if (AddrOfExpr->getOpcode() == UnaryOperatorKind::UO_AddrOf &&
3792 isa_and_present<DeclRefExpr>(AddrOfExpr->getSubExpr()))
3793 ExtentText = One;
3794 // TODO: we can handle more cases, e.g., `&a[0]`, `&a`, `std::addressof`,
3795 // and explicit casting, etc. etc.
3796 }
3797
3798 SmallString<32> StrBuffer{};
3799 std::optional<SourceLocation> LocPassInit = getPastLoc(Init, SM, LangOpts);
3800
3801 if (!LocPassInit)
3802 return std::nullopt;
3803
3804 StrBuffer.append(", ");
3805 StrBuffer.append(ExtentText);
3806 StrBuffer.append("}");
3807 FixIts.push_back(FixItHint::CreateInsertion(*LocPassInit, StrBuffer.str()));
3808 return FixIts;
3809}
3810
3811#ifndef NDEBUG
3812#define DEBUG_NOTE_DECL_FAIL(D, Msg) \
3813 Handler.addDebugNoteForVar((D), (D)->getBeginLoc(), \
3814 "failed to produce fixit for declaration '" + \
3815 (D)->getNameAsString() + "'" + (Msg))
3816#else
3817#define DEBUG_NOTE_DECL_FAIL(D, Msg)
3818#endif
3819
3820// For the given variable declaration with a pointer-to-T type, returns the text
3821// `std::span<T>`. If it is unable to generate the text, returns
3822// `std::nullopt`.
3823static std::optional<std::string>
3825 assert(VD->getType()->isPointerType());
3826
3827 std::optional<Qualifiers> PteTyQualifiers = std::nullopt;
3828 std::optional<std::string> PteTyText = getPointeeTypeText(
3829 VD, Ctx.getSourceManager(), Ctx.getLangOpts(), &PteTyQualifiers);
3830
3831 if (!PteTyText)
3832 return std::nullopt;
3833
3834 std::string SpanTyText = "std::span<";
3835
3836 SpanTyText.append(*PteTyText);
3837 // Append qualifiers to span element type if any:
3838 if (PteTyQualifiers) {
3839 SpanTyText.append(" ");
3840 SpanTyText.append(PteTyQualifiers->getAsString());
3841 }
3842 SpanTyText.append(">");
3843 return SpanTyText;
3844}
3845
3846// For a `VarDecl` of the form `T * var (= Init)?`, this
3847// function generates fix-its that
3848// 1) replace `T * var` with `std::span<T> var`; and
3849// 2) change `Init` accordingly to a span constructor, if it exists.
3850//
3851// FIXME: support Multi-level pointers
3852//
3853// Parameters:
3854// `D` a pointer the variable declaration node
3855// `Ctx` a reference to the ASTContext
3856// `UserFillPlaceHolder` the user-input placeholder text
3857// Returns:
3858// the non-empty fix-it list, if fix-its are successfuly generated; empty
3859// list otherwise.
3860static FixItList fixLocalVarDeclWithSpan(const VarDecl *D, ASTContext &Ctx,
3861 const StringRef UserFillPlaceHolder,
3862 UnsafeBufferUsageHandler &Handler) {
3864 return {};
3865
3866 FixItList FixIts{};
3867 std::optional<std::string> SpanTyText = createSpanTypeForVarDecl(D, Ctx);
3868
3869 if (!SpanTyText) {
3870 DEBUG_NOTE_DECL_FAIL(D, " : failed to generate 'std::span' type");
3871 return {};
3872 }
3873
3874 // Will hold the text for `std::span<T> Ident`:
3875 std::stringstream SS;
3876
3877 SS << *SpanTyText;
3878 // Fix the initializer if it exists:
3879 if (const Expr *Init = D->getInit()) {
3880 std::optional<FixItList> InitFixIts =
3881 FixVarInitializerWithSpan(Init, Ctx, UserFillPlaceHolder);
3882 if (!InitFixIts)
3883 return {};
3884 FixIts.insert(FixIts.end(), std::make_move_iterator(InitFixIts->begin()),
3885 std::make_move_iterator(InitFixIts->end()));
3886 }
3887 // For declaration of the form `T * ident = init;`, we want to replace
3888 // `T * ` with `std::span<T>`.
3889 // We ignore CV-qualifiers so for `T * const ident;` we also want to replace
3890 // just `T *` with `std::span<T>`.
3891 const SourceLocation EndLocForReplacement = D->getTypeSpecEndLoc();
3892 if (!EndLocForReplacement.isValid()) {
3893 DEBUG_NOTE_DECL_FAIL(D, " : failed to locate the end of the declaration");
3894 return {};
3895 }
3896 // The only exception is that for `T *ident` we'll add a single space between
3897 // "std::span<T>" and "ident".
3898 // FIXME: The condition is false for identifiers expended from macros.
3899 if (EndLocForReplacement.getLocWithOffset(1) == getVarDeclIdentifierLoc(D))
3900 SS << " ";
3901
3902 FixIts.push_back(FixItHint::CreateReplacement(
3903 SourceRange(D->getBeginLoc(), EndLocForReplacement), SS.str()));
3904 return FixIts;
3905}
3906
3907static bool hasConflictingOverload(const FunctionDecl *FD) {
3908 return !FD->getDeclContext()->lookup(FD->getDeclName()).isSingleResult();
3909}
3910
3911// For a `FunctionDecl`, whose `ParmVarDecl`s are being changed to have new
3912// types, this function produces fix-its to make the change self-contained. Let
3913// 'F' be the entity defined by the original `FunctionDecl` and "NewF" be the
3914// entity defined by the `FunctionDecl` after the change to the parameters.
3915// Fix-its produced by this function are
3916// 1. Add the `[[clang::unsafe_buffer_usage]]` attribute to each declaration
3917// of 'F';
3918// 2. Create a declaration of "NewF" next to each declaration of `F`;
3919// 3. Create a definition of "F" (as its original definition is now belongs
3920// to "NewF") next to its original definition. The body of the creating
3921// definition calls to "NewF".
3922//
3923// Example:
3924//
3925// void f(int *p); // original declaration
3926// void f(int *p) { // original definition
3927// p[5];
3928// }
3929//
3930// To change the parameter `p` to be of `std::span<int>` type, we
3931// also add overloads:
3932//
3933// [[clang::unsafe_buffer_usage]] void f(int *p); // original decl
3934// void f(std::span<int> p); // added overload decl
3935// void f(std::span<int> p) { // original def where param is changed
3936// p[5];
3937// }
3938// [[clang::unsafe_buffer_usage]] void f(int *p) { // added def
3939// return f(std::span(p, <# size #>));
3940// }
3941//
3942static std::optional<FixItList>
3944 const ASTContext &Ctx,
3945 UnsafeBufferUsageHandler &Handler) {
3946 // FIXME: need to make this conflict checking better:
3947 if (hasConflictingOverload(FD))
3948 return std::nullopt;
3949
3950 const SourceManager &SM = Ctx.getSourceManager();
3951 const LangOptions &LangOpts = Ctx.getLangOpts();
3952 const unsigned NumParms = FD->getNumParams();
3953 std::vector<std::string> NewTysTexts(NumParms);
3954 std::vector<bool> ParmsMask(NumParms, false);
3955 bool AtLeastOneParmToFix = false;
3956
3957 for (unsigned i = 0; i < NumParms; i++) {
3958 const ParmVarDecl *PVD = FD->getParamDecl(i);
3959
3961 continue;
3962 if (S.lookup(PVD) != FixitStrategy::Kind::Span)
3963 // Not supported, not suppose to happen:
3964 return std::nullopt;
3965
3966 std::optional<Qualifiers> PteTyQuals = std::nullopt;
3967 std::optional<std::string> PteTyText =
3968 getPointeeTypeText(PVD, SM, LangOpts, &PteTyQuals);
3969
3970 if (!PteTyText)
3971 // something wrong in obtaining the text of the pointee type, give up
3972 return std::nullopt;
3973 // FIXME: whether we should create std::span type depends on the
3974 // FixitStrategy.
3975 NewTysTexts[i] = getSpanTypeText(*PteTyText, PteTyQuals);
3976 ParmsMask[i] = true;
3977 AtLeastOneParmToFix = true;
3978 }
3979 if (!AtLeastOneParmToFix)
3980 // No need to create function overloads:
3981 return {};
3982 // FIXME Respect indentation of the original code.
3983
3984 // A lambda that creates the text representation of a function declaration
3985 // with the new type signatures:
3986 const auto NewOverloadSignatureCreator =
3987 [&SM, &LangOpts, &NewTysTexts,
3988 &ParmsMask](const FunctionDecl *FD) -> std::optional<std::string> {
3989 std::stringstream SS;
3990
3991 SS << ";";
3992 SS << getEndOfLine().str();
3993 // Append: ret-type func-name "("
3994 if (auto Prefix = getRangeText(
3995 SourceRange(FD->getBeginLoc(), (*FD->param_begin())->getBeginLoc()),
3996 SM, LangOpts))
3997 SS << Prefix->str();
3998 else
3999 return std::nullopt; // give up
4000 // Append: parameter-type-list
4001 const unsigned NumParms = FD->getNumParams();
4002
4003 for (unsigned i = 0; i < NumParms; i++) {
4004 const ParmVarDecl *Parm = FD->getParamDecl(i);
4005
4006 if (Parm->isImplicit())
4007 continue;
4008 if (ParmsMask[i]) {
4009 // This `i`-th parameter will be fixed with `NewTysTexts[i]` being its
4010 // new type:
4011 SS << NewTysTexts[i];
4012 // print parameter name if provided:
4013 if (IdentifierInfo *II = Parm->getIdentifier())
4014 SS << ' ' << II->getName().str();
4015 } else if (auto ParmTypeText =
4016 getRangeText(getSourceRangeToTokenEnd(Parm, SM, LangOpts),
4017 SM, LangOpts)) {
4018 // print the whole `Parm` without modification:
4019 SS << ParmTypeText->str();
4020 } else
4021 return std::nullopt; // something wrong, give up
4022 if (i != NumParms - 1)
4023 SS << ", ";
4024 }
4025 SS << ")";
4026 return SS.str();
4027 };
4028
4029 // A lambda that creates the text representation of a function definition with
4030 // the original signature:
4031 const auto OldOverloadDefCreator =
4032 [&Handler, &SM, &LangOpts, &NewTysTexts,
4033 &ParmsMask](const FunctionDecl *FD) -> std::optional<std::string> {
4034 std::stringstream SS;
4035
4036 SS << getEndOfLine().str();
4037 // Append: attr-name ret-type func-name "(" param-list ")" "{"
4038 if (auto FDPrefix = getRangeText(
4039 SourceRange(FD->getBeginLoc(), FD->getBody()->getBeginLoc()), SM,
4040 LangOpts))
4041 SS << Handler.getUnsafeBufferUsageAttributeTextAt(FD->getBeginLoc(), " ")
4042 << FDPrefix->str() << "{";
4043 else
4044 return std::nullopt;
4045 // Append: "return" func-name "("
4046 if (auto FunQualName = getFunNameText(FD, SM, LangOpts))
4047 SS << "return " << FunQualName->str() << "(";
4048 else
4049 return std::nullopt;
4050
4051 // Append: arg-list
4052 const unsigned NumParms = FD->getNumParams();
4053 for (unsigned i = 0; i < NumParms; i++) {
4054 const ParmVarDecl *Parm = FD->getParamDecl(i);
4055
4056 if (Parm->isImplicit())
4057 continue;
4058 // FIXME: If a parameter has no name, it is unused in the
4059 // definition. So we could just leave it as it is.
4060 if (!Parm->getIdentifier())
4061 // If a parameter of a function definition has no name:
4062 return std::nullopt;
4063 if (ParmsMask[i])
4064 // This is our spanified paramter!
4065 SS << NewTysTexts[i] << "(" << Parm->getIdentifier()->getName().str()
4066 << ", " << getUserFillPlaceHolder("size") << ")";
4067 else
4068 SS << Parm->getIdentifier()->getName().str();
4069 if (i != NumParms - 1)
4070 SS << ", ";
4071 }
4072 // finish call and the body
4073 SS << ");}" << getEndOfLine().str();
4074 // FIXME: 80-char line formatting?
4075 return SS.str();
4076 };
4077
4078 FixItList FixIts{};
4079 for (FunctionDecl *FReDecl : FD->redecls()) {
4080 std::optional<SourceLocation> Loc = getPastLoc(FReDecl, SM, LangOpts);
4081
4082 if (!Loc)
4083 return {};
4084 if (FReDecl->isThisDeclarationADefinition()) {
4085 assert(FReDecl == FD && "inconsistent function definition");
4086 // Inserts a definition with the old signature to the end of
4087 // `FReDecl`:
4088 if (auto OldOverloadDef = OldOverloadDefCreator(FReDecl))
4089 FixIts.emplace_back(FixItHint::CreateInsertion(*Loc, *OldOverloadDef));
4090 else
4091 return {}; // give up
4092 } else {
4093 // Adds the unsafe-buffer attribute (if not already there) to `FReDecl`:
4094 if (!FReDecl->hasAttr<UnsafeBufferUsageAttr>()) {
4095 FixIts.emplace_back(FixItHint::CreateInsertion(
4096 FReDecl->getBeginLoc(), Handler.getUnsafeBufferUsageAttributeTextAt(
4097 FReDecl->getBeginLoc(), " ")));
4098 }
4099 // Inserts a declaration with the new signature to the end of `FReDecl`:
4100 if (auto NewOverloadDecl = NewOverloadSignatureCreator(FReDecl))
4101 FixIts.emplace_back(FixItHint::CreateInsertion(*Loc, *NewOverloadDecl));
4102 else
4103 return {};
4104 }
4105 }
4106 return FixIts;
4107}
4108
4109// To fix a `ParmVarDecl` to be of `std::span` type.
4110static FixItList fixParamWithSpan(const ParmVarDecl *PVD, const ASTContext &Ctx,
4111 UnsafeBufferUsageHandler &Handler) {
4113 DEBUG_NOTE_DECL_FAIL(PVD, " : has unsupport specifier(s)");
4114 return {};
4115 }
4116 if (PVD->hasDefaultArg()) {
4117 // FIXME: generate fix-its for default values:
4118 DEBUG_NOTE_DECL_FAIL(PVD, " : has default arg");
4119 return {};
4120 }
4121
4122 std::optional<Qualifiers> PteTyQualifiers = std::nullopt;
4123 std::optional<std::string> PteTyText = getPointeeTypeText(
4124 PVD, Ctx.getSourceManager(), Ctx.getLangOpts(), &PteTyQualifiers);
4125
4126 if (!PteTyText) {
4127 DEBUG_NOTE_DECL_FAIL(PVD, " : invalid pointee type");
4128 return {};
4129 }
4130
4131 std::optional<StringRef> PVDNameText = PVD->getIdentifier()->getName();
4132
4133 if (!PVDNameText) {
4134 DEBUG_NOTE_DECL_FAIL(PVD, " : invalid identifier name");
4135 return {};
4136 }
4137
4138 std::stringstream SS;
4139 std::optional<std::string> SpanTyText = createSpanTypeForVarDecl(PVD, Ctx);
4140
4141 if (PteTyQualifiers)
4142 // Append qualifiers if they exist:
4143 SS << getSpanTypeText(*PteTyText, PteTyQualifiers);
4144 else
4145 SS << getSpanTypeText(*PteTyText);
4146 // Append qualifiers to the type of the parameter:
4147 if (PVD->getType().hasQualifiers())
4148 SS << ' ' << PVD->getType().getQualifiers().getAsString();
4149 // Append parameter's name:
4150 SS << ' ' << PVDNameText->str();
4151 // Add replacement fix-it:
4152 return {FixItHint::CreateReplacement(PVD->getSourceRange(), SS.str())};
4153}
4154
4155static FixItList fixVariableWithSpan(const VarDecl *VD,
4156 const DeclUseTracker &Tracker,
4157 ASTContext &Ctx,
4158 UnsafeBufferUsageHandler &Handler) {
4159 const DeclStmt *DS = Tracker.lookupDecl(VD);
4160 if (!DS) {
4162 " : variables declared this way not implemented yet");
4163 return {};
4164 }
4165 if (!DS->isSingleDecl()) {
4166 // FIXME: to support handling multiple `VarDecl`s in a single `DeclStmt`
4167 DEBUG_NOTE_DECL_FAIL(VD, " : multiple VarDecls");
4168 return {};
4169 }
4170 // Currently DS is an unused variable but we'll need it when
4171 // non-single decls are implemented, where the pointee type name
4172 // and the '*' are spread around the place.
4173 (void)DS;
4174
4175 // FIXME: handle cases where DS has multiple declarations
4176 return fixLocalVarDeclWithSpan(VD, Ctx, getUserFillPlaceHolder(), Handler);
4177}
4178
4179static FixItList fixVarDeclWithArray(const VarDecl *D, const ASTContext &Ctx,
4180 UnsafeBufferUsageHandler &Handler) {
4181 FixItList FixIts{};
4182
4183 // Note: the code below expects the declaration to not use any type sugar like
4184 // typedef.
4185 if (auto CAT = Ctx.getAsConstantArrayType(D->getType())) {
4186 const QualType &ArrayEltT = CAT->getElementType();
4187 assert(!ArrayEltT.isNull() && "Trying to fix a non-array type variable!");
4188 // FIXME: support multi-dimensional arrays
4189 if (isa<clang::ArrayType>(ArrayEltT.getCanonicalType()))
4190 return {};
4191
4193
4194 // Get the spelling of the element type as written in the source file
4195 // (including macros, etc.).
4196 auto MaybeElemTypeTxt =
4198 Ctx.getLangOpts());
4199 if (!MaybeElemTypeTxt)
4200 return {};
4201 const llvm::StringRef ElemTypeTxt = MaybeElemTypeTxt->trim();
4202
4203 // Find the '[' token.
4204 std::optional<Token> NextTok = Lexer::findNextToken(
4206 while (NextTok && !NextTok->is(tok::l_square) &&
4207 NextTok->getLocation() <= D->getSourceRange().getEnd())
4208 NextTok = Lexer::findNextToken(NextTok->getLocation(),
4209 Ctx.getSourceManager(), Ctx.getLangOpts());
4210 if (!NextTok)
4211 return {};
4212 const SourceLocation LSqBracketLoc = NextTok->getLocation();
4213
4214 // Get the spelling of the array size as written in the source file
4215 // (including macros, etc.).
4216 auto MaybeArraySizeTxt = getRangeText(
4217 {LSqBracketLoc.getLocWithOffset(1), D->getTypeSpecEndLoc()},
4218 Ctx.getSourceManager(), Ctx.getLangOpts());
4219 if (!MaybeArraySizeTxt)
4220 return {};
4221 const llvm::StringRef ArraySizeTxt = MaybeArraySizeTxt->trim();
4222 if (ArraySizeTxt.empty()) {
4223 // FIXME: Support array size getting determined from the initializer.
4224 // Examples:
4225 // int arr1[] = {0, 1, 2};
4226 // int arr2{3, 4, 5};
4227 // We might be able to preserve the non-specified size with `auto` and
4228 // `std::to_array`:
4229 // auto arr1 = std::to_array<int>({0, 1, 2});
4230 return {};
4231 }
4232
4233 std::optional<StringRef> IdentText =
4235
4236 if (!IdentText) {
4237 DEBUG_NOTE_DECL_FAIL(D, " : failed to locate the identifier");
4238 return {};
4239 }
4240
4241 SmallString<32> Replacement;
4242 llvm::raw_svector_ostream OS(Replacement);
4243 OS << "std::array<" << ElemTypeTxt << ", " << ArraySizeTxt << "> "
4244 << IdentText->str();
4245
4246 FixIts.push_back(FixItHint::CreateReplacement(
4247 SourceRange{D->getBeginLoc(), D->getTypeSpecEndLoc()}, OS.str()));
4248 }
4249
4250 return FixIts;
4251}
4252
4253static FixItList fixVariableWithArray(const VarDecl *VD,
4254 const DeclUseTracker &Tracker,
4255 const ASTContext &Ctx,
4256 UnsafeBufferUsageHandler &Handler) {
4257 const DeclStmt *DS = Tracker.lookupDecl(VD);
4258 assert(DS && "Fixing non-local variables not implemented yet!");
4259 if (!DS->isSingleDecl()) {
4260 // FIXME: to support handling multiple `VarDecl`s in a single `DeclStmt`
4261 return {};
4262 }
4263 // Currently DS is an unused variable but we'll need it when
4264 // non-single decls are implemented, where the pointee type name
4265 // and the '*' are spread around the place.
4266 (void)DS;
4267
4268 // FIXME: handle cases where DS has multiple declarations
4269 return fixVarDeclWithArray(VD, Ctx, Handler);
4270}
4271
4272// TODO: we should be consistent to use `std::nullopt` to represent no-fix due
4273// to any unexpected problem.
4274static FixItList
4276 /* The function decl under analysis */ const Decl *D,
4277 const DeclUseTracker &Tracker, ASTContext &Ctx,
4278 UnsafeBufferUsageHandler &Handler) {
4279 if (const auto *PVD = dyn_cast<ParmVarDecl>(VD)) {
4280 auto *FD = dyn_cast<clang::FunctionDecl>(PVD->getDeclContext());
4281 if (!FD || FD != D) {
4282 // `FD != D` means that `PVD` belongs to a function that is not being
4283 // analyzed currently. Thus `FD` may not be complete.
4284 DEBUG_NOTE_DECL_FAIL(VD, " : function not currently analyzed");
4285 return {};
4286 }
4287
4288 // TODO If function has a try block we can't change params unless we check
4289 // also its catch block for their use.
4290 // FIXME We might support static class methods, some select methods,
4291 // operators and possibly lamdas.
4292 if (FD->isMain() || FD->isConstexpr() ||
4294 FD->isVariadic() ||
4295 // also covers call-operator of lamdas
4296 isa<CXXMethodDecl>(FD) ||
4297 // skip when the function body is a try-block
4298 isa_and_nonnull<CXXTryStmt>(FD->getBody()) ||
4299 FD->isOverloadedOperator()) {
4300 DEBUG_NOTE_DECL_FAIL(VD, " : unsupported function decl");
4301 return {}; // TODO test all these cases
4302 }
4303 }
4304
4305 switch (K) {
4307 if (VD->getType()->isPointerType()) {
4308 if (const auto *PVD = dyn_cast<ParmVarDecl>(VD))
4309 return fixParamWithSpan(PVD, Ctx, Handler);
4310
4311 if (VD->isLocalVarDecl())
4312 return fixVariableWithSpan(VD, Tracker, Ctx, Handler);
4313 }
4314 DEBUG_NOTE_DECL_FAIL(VD, " : not a pointer");
4315 return {};
4316 }
4318 if (VD->isLocalVarDecl() && Ctx.getAsConstantArrayType(VD->getType()))
4319 return fixVariableWithArray(VD, Tracker, Ctx, Handler);
4320
4321 DEBUG_NOTE_DECL_FAIL(VD, " : not a local const-size array");
4322 return {};
4323 }
4326 llvm_unreachable("FixitStrategy not implemented yet!");
4328 llvm_unreachable("Invalid strategy!");
4329 }
4330 llvm_unreachable("Unknown strategy!");
4331}
4332
4333// Returns true iff there exists a `FixItHint` 'h' in `FixIts` such that the
4334// `RemoveRange` of 'h' overlaps with a macro use.
4335static bool overlapWithMacro(const FixItList &FixIts) {
4336 // FIXME: For now we only check if the range (or the first token) is (part of)
4337 // a macro expansion. Ideally, we want to check for all tokens in the range.
4338 return llvm::any_of(FixIts, [](const FixItHint &Hint) {
4339 auto Range = Hint.RemoveRange;
4340 if (Range.getBegin().isMacroID() || Range.getEnd().isMacroID())
4341 // If the range (or the first token) is (part of) a macro expansion:
4342 return true;
4343 return false;
4344 });
4345}
4346
4347// Returns true iff `VD` is a parameter of the declaration `D`:
4348static bool isParameterOf(const VarDecl *VD, const Decl *D) {
4349 return isa<ParmVarDecl>(VD) &&
4350 VD->getDeclContext() == dyn_cast<DeclContext>(D);
4351}
4352
4353// Erases variables in `FixItsForVariable`, if such a variable has an unfixable
4354// group mate. A variable `v` is unfixable iff `FixItsForVariable` does not
4355// contain `v`.
4357 std::map<const VarDecl *, FixItList> &FixItsForVariable,
4358 const VariableGroupsManager &VarGrpMgr) {
4359 // Variables will be removed from `FixItsForVariable`:
4361
4362 for (const auto &[VD, Ignore] : FixItsForVariable) {
4363 VarGrpRef Grp = VarGrpMgr.getGroupOfVar(VD);
4364 if (llvm::any_of(Grp,
4365 [&FixItsForVariable](const VarDecl *GrpMember) -> bool {
4366 return !FixItsForVariable.count(GrpMember);
4367 })) {
4368 // At least one group member cannot be fixed, so we have to erase the
4369 // whole group:
4370 for (const VarDecl *Member : Grp)
4371 ToErase.push_back(Member);
4372 }
4373 }
4374 for (auto *VarToErase : ToErase)
4375 FixItsForVariable.erase(VarToErase);
4376}
4377
4378// Returns the fix-its that create bounds-safe function overloads for the
4379// function `D`, if `D`'s parameters will be changed to safe-types through
4380// fix-its in `FixItsForVariable`.
4381//
4382// NOTE: In case `D`'s parameters will be changed but bounds-safe function
4383// overloads cannot created, the whole group that contains the parameters will
4384// be erased from `FixItsForVariable`.
4386 std::map<const VarDecl *, FixItList> &FixItsForVariable /* mutable */,
4387 const VariableGroupsManager &VarGrpMgr, const FunctionDecl *FD,
4388 const FixitStrategy &S, ASTContext &Ctx,
4389 UnsafeBufferUsageHandler &Handler) {
4390 FixItList FixItsSharedByParms{};
4391
4392 std::optional<FixItList> OverloadFixes =
4393 createOverloadsForFixedParams(S, FD, Ctx, Handler);
4394
4395 if (OverloadFixes) {
4396 FixItsSharedByParms.append(*OverloadFixes);
4397 } else {
4398 // Something wrong in generating `OverloadFixes`, need to remove the
4399 // whole group, where parameters are in, from `FixItsForVariable` (Note
4400 // that all parameters should be in the same group):
4401 for (auto *Member : VarGrpMgr.getGroupOfParms())
4402 FixItsForVariable.erase(Member);
4403 }
4404 return FixItsSharedByParms;
4405}
4406
4407// Constructs self-contained fix-its for each variable in `FixablesForAllVars`.
4408static std::map<const VarDecl *, FixItList>
4409getFixIts(FixableGadgetSets &FixablesForAllVars, const FixitStrategy &S,
4410 ASTContext &Ctx,
4411 /* The function decl under analysis */ const Decl *D,
4412 const DeclUseTracker &Tracker, UnsafeBufferUsageHandler &Handler,
4413 const VariableGroupsManager &VarGrpMgr) {
4414 // `FixItsForVariable` will map each variable to a set of fix-its directly
4415 // associated to the variable itself. Fix-its of distinct variables in
4416 // `FixItsForVariable` are disjoint.
4417 std::map<const VarDecl *, FixItList> FixItsForVariable;
4418
4419 // Populate `FixItsForVariable` with fix-its directly associated with each
4420 // variable. Fix-its directly associated to a variable 'v' are the ones
4421 // produced by the `FixableGadget`s whose claimed variable is 'v'.
4422 for (const auto &[VD, Fixables] : FixablesForAllVars.byVar) {
4423 FixItsForVariable[VD] =
4424 fixVariable(VD, S.lookup(VD), D, Tracker, Ctx, Handler);
4425 // If we fail to produce Fix-It for the declaration we have to skip the
4426 // variable entirely.
4427 if (FixItsForVariable[VD].empty()) {
4428 FixItsForVariable.erase(VD);
4429 continue;
4430 }
4431 for (const auto &F : Fixables) {
4432 std::optional<FixItList> Fixits = F->getFixits(S);
4433
4434 if (Fixits) {
4435 FixItsForVariable[VD].insert(FixItsForVariable[VD].end(),
4436 Fixits->begin(), Fixits->end());
4437 continue;
4438 }
4439#ifndef NDEBUG
4440 Handler.addDebugNoteForVar(
4441 VD, F->getSourceLoc(),
4442 ("gadget '" + F->getDebugName() + "' refused to produce a fix")
4443 .str());
4444#endif
4445 FixItsForVariable.erase(VD);
4446 break;
4447 }
4448 }
4449
4450 // `FixItsForVariable` now contains only variables that can be
4451 // fixed. A variable can be fixed if its declaration and all Fixables
4452 // associated to it can all be fixed.
4453
4454 // To further remove from `FixItsForVariable` variables whose group mates
4455 // cannot be fixed...
4456 eraseVarsForUnfixableGroupMates(FixItsForVariable, VarGrpMgr);
4457 // Now `FixItsForVariable` gets further reduced: a variable is in
4458 // `FixItsForVariable` iff it can be fixed and all its group mates can be
4459 // fixed.
4460
4461 // Fix-its of bounds-safe overloads of `D` are shared by parameters of `D`.
4462 // That is, when fixing multiple parameters in one step, these fix-its will
4463 // be applied only once (instead of being applied per parameter).
4464 FixItList FixItsSharedByParms{};
4465
4466 if (auto *FD = dyn_cast<FunctionDecl>(D))
4467 FixItsSharedByParms = createFunctionOverloadsForParms(
4468 FixItsForVariable, VarGrpMgr, FD, S, Ctx, Handler);
4469
4470 // The map that maps each variable `v` to fix-its for the whole group where
4471 // `v` is in:
4472 std::map<const VarDecl *, FixItList> FinalFixItsForVariable{
4473 FixItsForVariable};
4474
4475 for (auto &[Var, Ignore] : FixItsForVariable) {
4476 bool AnyParm = false;
4477 const auto VarGroupForVD = VarGrpMgr.getGroupOfVar(Var, &AnyParm);
4478
4479 for (const VarDecl *GrpMate : VarGroupForVD) {
4480 if (Var == GrpMate)
4481 continue;
4482 if (FixItsForVariable.count(GrpMate))
4483 FinalFixItsForVariable[Var].append(FixItsForVariable[GrpMate]);
4484 }
4485 if (AnyParm) {
4486 // This assertion should never fail. Otherwise we have a bug.
4487 assert(!FixItsSharedByParms.empty() &&
4488 "Should not try to fix a parameter that does not belong to a "
4489 "FunctionDecl");
4490 FinalFixItsForVariable[Var].append(FixItsSharedByParms);
4491 }
4492 }
4493 // Fix-its that will be applied in one step shall NOT:
4494 // 1. overlap with macros or/and templates; or
4495 // 2. conflict with each other.
4496 // Otherwise, the fix-its will be dropped.
4497 for (auto Iter = FinalFixItsForVariable.begin();
4498 Iter != FinalFixItsForVariable.end();)
4499 if (overlapWithMacro(Iter->second) ||
4500 clang::internal::anyConflict(Iter->second, Ctx.getSourceManager())) {
4501 Iter = FinalFixItsForVariable.erase(Iter);
4502 } else
4503 Iter++;
4504 return FinalFixItsForVariable;
4505}
4506
4507template <typename VarDeclIterTy>
4508static FixitStrategy
4509getNaiveStrategy(llvm::iterator_range<VarDeclIterTy> UnsafeVars) {
4510 FixitStrategy S;
4511 for (const VarDecl *VD : UnsafeVars) {
4514 else
4516 }
4517 return S;
4518}
4519
4520// Manages variable groups:
4522 const std::vector<VarGrpTy> &Groups;
4523 const std::map<const VarDecl *, unsigned> &VarGrpMap;
4524 const llvm::SetVector<const VarDecl *> &GrpsUnionForParms;
4525
4526public:
4528 const std::vector<VarGrpTy> &Groups,
4529 const std::map<const VarDecl *, unsigned> &VarGrpMap,
4530 const llvm::SetVector<const VarDecl *> &GrpsUnionForParms)
4531 : Groups(Groups), VarGrpMap(VarGrpMap),
4532 GrpsUnionForParms(GrpsUnionForParms) {}
4533
4534 VarGrpRef getGroupOfVar(const VarDecl *Var, bool *HasParm) const override {
4535 if (GrpsUnionForParms.contains(Var)) {
4536 if (HasParm)
4537 *HasParm = true;
4538 return GrpsUnionForParms.getArrayRef();
4539 }
4540 if (HasParm)
4541 *HasParm = false;
4542
4543 auto It = VarGrpMap.find(Var);
4544
4545 if (It == VarGrpMap.end())
4546 return {};
4547 return Groups[It->second];
4548 }
4549
4550 VarGrpRef getGroupOfParms() const override {
4551 return GrpsUnionForParms.getArrayRef();
4552 }
4553};
4554
4555static void applyGadgets(const Decl *D, FixableGadgetList FixableGadgets,
4556 WarningGadgetList WarningGadgets,
4557 DeclUseTracker Tracker,
4558 UnsafeBufferUsageHandler &Handler,
4559 bool EmitSuggestions) {
4560 if (!EmitSuggestions) {
4561 // Our job is very easy without suggestions. Just warn about
4562 // every problematic operation and consider it done. No need to deal
4563 // with fixable gadgets, no need to group operations by variable.
4564 for (const auto &G : WarningGadgets) {
4565 G->handleUnsafeOperation(Handler, /*IsRelatedToDecl=*/false,
4566 D->getASTContext());
4567 }
4568
4569 // This return guarantees that most of the machine doesn't run when
4570 // suggestions aren't requested.
4571 assert(FixableGadgets.empty() &&
4572 "Fixable gadgets found but suggestions not requested!");
4573 return;
4574 }
4575
4576 // If no `WarningGadget`s ever matched, there is no unsafe operations in the
4577 // function under the analysis. No need to fix any Fixables.
4578 if (!WarningGadgets.empty()) {
4579 // Gadgets "claim" variables they're responsible for. Once this loop
4580 // finishes, the tracker will only track DREs that weren't claimed by any
4581 // gadgets, i.e. not understood by the analysis.
4582 for (const auto &G : FixableGadgets) {
4583 for (const auto *DRE : G->getClaimedVarUseSites()) {
4584 Tracker.claimUse(DRE);
4585 }
4586 }
4587 }
4588
4589 // If no `WarningGadget`s ever matched, there is no unsafe operations in the
4590 // function under the analysis. Thus, it early returns here as there is
4591 // nothing needs to be fixed.
4592 //
4593 // Note this claim is based on the assumption that there is no unsafe
4594 // variable whose declaration is invisible from the analyzing function.
4595 // Otherwise, we need to consider if the uses of those unsafe varuables needs
4596 // fix.
4597 // So far, we are not fixing any global variables or class members. And,
4598 // lambdas will be analyzed along with the enclosing function. So this early
4599 // return is correct for now.
4600 if (WarningGadgets.empty())
4601 return;
4602
4603 WarningGadgetSets UnsafeOps =
4604 groupWarningGadgetsByVar(std::move(WarningGadgets));
4605 FixableGadgetSets FixablesForAllVars =
4606 groupFixablesByVar(std::move(FixableGadgets));
4607
4608 std::map<const VarDecl *, FixItList> FixItsForVariableGroup;
4609
4610 // Filter out non-local vars and vars with unclaimed DeclRefExpr-s.
4611 for (auto it = FixablesForAllVars.byVar.cbegin();
4612 it != FixablesForAllVars.byVar.cend();) {
4613 // FIXME: need to deal with global variables later
4614 if ((!it->first->isLocalVarDecl() && !isa<ParmVarDecl>(it->first))) {
4615#ifndef NDEBUG
4616 Handler.addDebugNoteForVar(it->first, it->first->getBeginLoc(),
4617 ("failed to produce fixit for '" +
4618 it->first->getNameAsString() +
4619 "' : neither local nor a parameter"));
4620#endif
4621 it = FixablesForAllVars.byVar.erase(it);
4622 } else if (it->first->getType().getCanonicalType()->isReferenceType()) {
4623#ifndef NDEBUG
4624 Handler.addDebugNoteForVar(it->first, it->first->getBeginLoc(),
4625 ("failed to produce fixit for '" +
4626 it->first->getNameAsString() +
4627 "' : has a reference type"));
4628#endif
4629 it = FixablesForAllVars.byVar.erase(it);
4630 } else if (Tracker.hasUnclaimedUses(it->first)) {
4631 it = FixablesForAllVars.byVar.erase(it);
4632 } else if (it->first->isInitCapture()) {
4633#ifndef NDEBUG
4634 Handler.addDebugNoteForVar(it->first, it->first->getBeginLoc(),
4635 ("failed to produce fixit for '" +
4636 it->first->getNameAsString() +
4637 "' : init capture"));
4638#endif
4639 it = FixablesForAllVars.byVar.erase(it);
4640 } else {
4641 ++it;
4642 }
4643 }
4644
4645#ifndef NDEBUG
4646 for (const auto &it : UnsafeOps.byVar) {
4647 const VarDecl *const UnsafeVD = it.first;
4648 auto UnclaimedDREs = Tracker.getUnclaimedUses(UnsafeVD);
4649 if (UnclaimedDREs.empty())
4650 continue;
4651 const auto UnfixedVDName = UnsafeVD->getNameAsString();
4652 for (const clang::DeclRefExpr *UnclaimedDRE : UnclaimedDREs) {
4653 std::string UnclaimedUseTrace =
4654 getDREAncestorString(UnclaimedDRE, D->getASTContext());
4655
4656 Handler.addDebugNoteForVar(
4657 UnsafeVD, UnclaimedDRE->getBeginLoc(),
4658 ("failed to produce fixit for '" + UnfixedVDName +
4659 "' : has an unclaimed use\nThe unclaimed DRE trace: " +
4660 UnclaimedUseTrace));
4661 }
4662 }
4663#endif
4664
4665 // Fixpoint iteration for pointer assignments
4666 using DepMapTy =
4667 llvm::DenseMap<const VarDecl *, llvm::SetVector<const VarDecl *>>;
4668 DepMapTy DependenciesMap{};
4669 DepMapTy PtrAssignmentGraph{};
4670
4671 for (const auto &it : FixablesForAllVars.byVar) {
4672 for (const FixableGadget *fixable : it.second) {
4673 std::optional<std::pair<const VarDecl *, const VarDecl *>> ImplPair =
4674 fixable->getStrategyImplications();
4675 if (ImplPair) {
4676 std::pair<const VarDecl *, const VarDecl *> Impl = std::move(*ImplPair);
4677 PtrAssignmentGraph[Impl.first].insert(Impl.second);
4678 }
4679 }
4680 }
4681
4682 /*
4683 The following code does a BFS traversal of the `PtrAssignmentGraph`
4684 considering all unsafe vars as starting nodes and constructs an undirected
4685 graph `DependenciesMap`. Constructing the `DependenciesMap` in this manner
4686 elimiates all variables that are unreachable from any unsafe var. In other
4687 words, this removes all dependencies that don't include any unsafe variable
4688 and consequently don't need any fixit generation.
4689 Note: A careful reader would observe that the code traverses
4690 `PtrAssignmentGraph` using `CurrentVar` but adds edges between `Var` and
4691 `Adj` and not between `CurrentVar` and `Adj`. Both approaches would
4692 achieve the same result but the one used here dramatically cuts the
4693 amount of hoops the second part of the algorithm needs to jump, given that
4694 a lot of these connections become "direct". The reader is advised not to
4695 imagine how the graph is transformed because of using `Var` instead of
4696 `CurrentVar`. The reader can continue reading as if `CurrentVar` was used,
4697 and think about why it's equivalent later.
4698 */
4699 std::set<const VarDecl *> VisitedVarsDirected{};
4700 for (const auto &[Var, ignore] : UnsafeOps.byVar) {
4701 if (VisitedVarsDirected.find(Var) == VisitedVarsDirected.end()) {
4702
4703 std::queue<const VarDecl *> QueueDirected{};
4704 QueueDirected.push(Var);
4705 while (!QueueDirected.empty()) {
4706 const VarDecl *CurrentVar = QueueDirected.front();
4707 QueueDirected.pop();
4708 VisitedVarsDirected.insert(CurrentVar);
4709 auto AdjacentNodes = PtrAssignmentGraph[CurrentVar];
4710 for (const VarDecl *Adj : AdjacentNodes) {
4711 if (VisitedVarsDirected.find(Adj) == VisitedVarsDirected.end()) {
4712 QueueDirected.push(Adj);
4713 }
4714 DependenciesMap[Var].insert(Adj);
4715 DependenciesMap[Adj].insert(Var);
4716 }
4717 }
4718 }
4719 }
4720
4721 // `Groups` stores the set of Connected Components in the graph.
4722 std::vector<VarGrpTy> Groups;
4723 // `VarGrpMap` maps variables that need fix to the groups (indexes) that the
4724 // variables belong to. Group indexes refer to the elements in `Groups`.
4725 // `VarGrpMap` is complete in that every variable that needs fix is in it.
4726 std::map<const VarDecl *, unsigned> VarGrpMap;
4727 // The union group over the ones in "Groups" that contain parameters of `D`:
4728 llvm::SetVector<const VarDecl *>
4729 GrpsUnionForParms; // these variables need to be fixed in one step
4730
4731 // Group Connected Components for Unsafe Vars
4732 // (Dependencies based on pointer assignments)
4733 std::set<const VarDecl *> VisitedVars{};
4734 for (const auto &[Var, ignore] : UnsafeOps.byVar) {
4735 if (VisitedVars.find(Var) == VisitedVars.end()) {
4736 VarGrpTy &VarGroup = Groups.emplace_back();
4737 std::queue<const VarDecl *> Queue{};
4738
4739 Queue.push(Var);
4740 while (!Queue.empty()) {
4741 const VarDecl *CurrentVar = Queue.front();
4742 Queue.pop();
4743 VisitedVars.insert(CurrentVar);
4744 VarGroup.push_back(CurrentVar);
4745 auto AdjacentNodes = DependenciesMap[CurrentVar];
4746 for (const VarDecl *Adj : AdjacentNodes) {
4747 if (VisitedVars.find(Adj) == VisitedVars.end()) {
4748 Queue.push(Adj);
4749 }
4750 }
4751 }
4752
4753 bool HasParm = false;
4754 unsigned GrpIdx = Groups.size() - 1;
4755
4756 for (const VarDecl *V : VarGroup) {
4757 VarGrpMap[V] = GrpIdx;
4758 if (!HasParm && isParameterOf(V, D))
4759 HasParm = true;
4760 }
4761 if (HasParm)
4762 GrpsUnionForParms.insert_range(VarGroup);
4763 }
4764 }
4765
4766 // Remove a `FixableGadget` if the associated variable is not in the graph
4767 // computed above. We do not want to generate fix-its for such variables,
4768 // since they are neither warned nor reachable from a warned one.
4769 //
4770 // Note a variable is not warned if it is not directly used in any unsafe
4771 // operation. A variable `v` is NOT reachable from an unsafe variable, if it
4772 // does not exist another variable `u` such that `u` is warned and fixing `u`
4773 // (transitively) implicates fixing `v`.
4774 //
4775 // For example,
4776 // ```
4777 // void f(int * p) {
4778 // int * a = p; *p = 0;
4779 // }
4780 // ```
4781 // `*p = 0` is a fixable gadget associated with a variable `p` that is neither
4782 // warned nor reachable from a warned one. If we add `a[5] = 0` to the end of
4783 // the function above, `p` becomes reachable from a warned variable.
4784 for (auto I = FixablesForAllVars.byVar.begin();
4785 I != FixablesForAllVars.byVar.end();) {
4786 // Note `VisitedVars` contain all the variables in the graph:
4787 if (!VisitedVars.count((*I).first)) {
4788 // no such var in graph:
4789 I = FixablesForAllVars.byVar.erase(I);
4790 } else
4791 ++I;
4792 }
4793
4794 // We assign strategies to variables that are 1) in the graph and 2) can be
4795 // fixed. Other variables have the default "Won't fix" strategy.
4796 FixitStrategy NaiveStrategy = getNaiveStrategy(llvm::make_filter_range(
4797 VisitedVars, [&FixablesForAllVars](const VarDecl *V) {
4798 // If a warned variable has no "Fixable", it is considered unfixable:
4799 return FixablesForAllVars.byVar.count(V);
4800 }));
4801 VariableGroupsManagerImpl VarGrpMgr(Groups, VarGrpMap, GrpsUnionForParms);
4802
4803 if (isa<NamedDecl>(D))
4804 // The only case where `D` is not a `NamedDecl` is when `D` is a
4805 // `BlockDecl`. Let's not fix variables in blocks for now
4806 FixItsForVariableGroup =
4807 getFixIts(FixablesForAllVars, NaiveStrategy, D->getASTContext(), D,
4808 Tracker, Handler, VarGrpMgr);
4809
4810 for (const auto &G : UnsafeOps.noVar) {
4811 G->handleUnsafeOperation(Handler, /*IsRelatedToDecl=*/false,
4812 D->getASTContext());
4813 }
4814
4815 for (const auto &[VD, WarningGadgets] : UnsafeOps.byVar) {
4816 auto FixItsIt = FixItsForVariableGroup.find(VD);
4817 Handler.handleUnsafeVariableGroup(VD, VarGrpMgr,
4818 FixItsIt != FixItsForVariableGroup.end()
4819 ? std::move(FixItsIt->second)
4820 : FixItList{},
4821 D, NaiveStrategy);
4822 for (const auto &G : WarningGadgets) {
4823 G->handleUnsafeOperation(Handler, /*IsRelatedToDecl=*/true,
4824 D->getASTContext());
4825 }
4826 }
4827}
4828
4830 UnsafeBufferUsageHandler &Handler,
4831 bool EmitSuggestions) {
4832#ifndef NDEBUG
4833 Handler.clearDebugNotes();
4834#endif
4835
4836 assert(D);
4837 if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
4838 // Consteval functions are free of UB by the spec, so we don't need to
4839 // visit them or produce diagnostics.
4840 if (FD->isConsteval())
4841 return;
4842 // We do not want to visit a Lambda expression defined inside a method
4843 // independently. Instead, it should be visited along with the outer method.
4844 // FIXME: do we want to do the same thing for `BlockDecl`s?
4845 if (const auto *MD = dyn_cast<CXXMethodDecl>(D)) {
4846 if (MD->getParent()->isLambda() && MD->getParent()->isLocalClass())
4847 return;
4848 }
4849
4850 for (FunctionDecl *FReDecl : FD->redecls()) {
4851 if (FReDecl->isExternC()) {
4852 // Do not emit fixit suggestions for functions declared in an
4853 // extern "C" block.
4854 EmitSuggestions = false;
4855 break;
4856 }
4857 }
4858 }
4859
4861
4863
4864 assert(!Stmts.empty());
4865
4866 FixableGadgetList FixableGadgets;
4867 WarningGadgetList WarningGadgets;
4868 DeclUseTracker Tracker;
4869 for (const Stmt *S : Stmts) {
4870 findGadgets(S, D->getASTContext(), Handler, EmitSuggestions, FixableGadgets,
4871 WarningGadgets, Tracker);
4872 }
4873 applyGadgets(D, std::move(FixableGadgets), std::move(WarningGadgets),
4874 std::move(Tracker), Handler, EmitSuggestions);
4875}
4876
4878 std::set<const Expr *> &UnsafePointers) {
4879 class MockReporter : public UnsafeBufferUsageHandler {
4880 public:
4881 MockReporter() {}
4882 void handleUnsafeOperation(const Stmt *, bool, ASTContext &) override {}
4883 void handleUnsafeLibcCall(const CallExpr *, unsigned, ASTContext &,
4884 const Expr *UnsafeArg = nullptr) override {}
4885 void handleUnsafeOperationInContainer(const Stmt *, bool,
4886 ASTContext &) override {}
4887 void handleUnsafeOperationInStringView(const Stmt *, bool,
4888 ASTContext &) override {}
4889 void handleUnsafeVariableGroup(const VarDecl *,
4890 const VariableGroupsManager &, FixItList &&,
4891 const Decl *,
4892 const FixitStrategy &) override {}
4893 void handleUnsafeUniquePtrArrayAccess(const DynTypedNode &Node,
4894 bool IsRelatedToDecl,
4895 ASTContext &Ctx) override {}
4896 bool ignoreUnsafeBufferInContainer(const SourceLocation &) const override {
4897 return false;
4898 }
4899 bool isSafeBufferOptOut(const SourceLocation &) const override {
4900 return false;
4901 }
4902 bool ignoreUnsafeBufferInLibcCall(const SourceLocation &) const override {
4903 return false;
4904 }
4905 bool ignoreUnsafeBufferInStaticSizedArray(
4906 const SourceLocation &Loc) const override {
4907 return false;
4908 }
4909 std::string getUnsafeBufferUsageAttributeTextAt(
4910 SourceLocation, StringRef WSSuffix = "") const override {
4911 return "";
4912 }
4913 } Handler;
4914
4915 const Stmt *S = N.get<Stmt>();
4916 if (!S)
4917 return false;
4918
4919 MatchResult Result;
4920 WarningGadgetList WarningGadgets;
4921 bool Matched = false;
4922
4923 // FIXME: By design, we don't need MockReporter, and we are supposed to
4924 // only define WARNING_GADGET when we want to treat WARNING_OPTIONAL_GADGET
4925 // the same as WARNING_GADGET. The reason we have to do it this way now is
4926 // that some WARNING_OPTIONAL_GADGETs do not have the 3-argument `matches`
4927 // overload. We need to fix this problem in a separate patch.
4928
4929#define WARNING_GADGET(name) \
4930 if (name##Gadget::matches(S, Ctx, Result)) \
4931 WarningGadgets.push_back(std::make_unique<name##Gadget>(Result));
4932#define WARNING_OPTIONAL_GADGET(name) \
4933 if (name##Gadget::matches(S, Ctx, &Handler, Result)) \
4934 WarningGadgets.push_back(std::make_unique<name##Gadget>(Result));
4935#include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
4936
4937 for (auto &WG : WarningGadgets)
4938 for (auto *E : WG->getUnsafePtrs()) {
4939 UnsafePointers.insert(E);
4940 Matched = true;
4941 }
4942 return Matched;
4943}
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:566
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition ASTContext.h:239
SourceManager & getSourceManager()
Definition ASTContext.h:911
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
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:969
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:2167
Expr * getRHS() const
Definition Expr.h:4134
Opcode getOpcode() const
Definition Expr.h:4127
Represents a call to a C++ constructor.
Definition ExprCXX.h:1553
child_range children()
Definition ExprCXX.h:1743
Expr * getArg(unsigned Arg)
Return the specified argument.
Definition ExprCXX.h:1696
CXXConstructorDecl * getConstructor() const
Get the constructor that this expression will (ultimately) call.
Definition ExprCXX.h:1616
unsigned getNumArgs() const
Return the number of arguments to the constructor call.
Definition ExprCXX.h:1693
Expr * getInit() const
Get the initializer.
Definition DeclCXX.h:2609
A use of a default initializer in a constructor or in aggregate initialization.
Definition ExprCXX.h:1382
Expr * getExpr()
Get the initialization expression that will be used.
Definition ExprCXX.cpp:1137
Represents a static or instance method of a struct/union/class.
Definition DeclCXX.h:2150
const CXXRecordDecl * getParent() const
Return the parent of this method declaration, which is the class in which this method is defined.
Definition DeclCXX.h:2293
Represents a C++11 noexcept expression (C++ [expr.unary.noexcept]).
Definition ExprCXX.h:4363
OverloadedOperatorKind getOperator() const
Returns the kind of overloaded operator that this expression refers to.
Definition ExprCXX.h:116
Represents a C++ struct/union/class.
Definition DeclCXX.h:258
CXXRecordDecl * getCanonicalDecl() override
Retrieves the "canonical" declaration of the given declaration.
Definition DeclCXX.h:523
A C++ typeid expression (C++ [expr.typeid]), which gets the type_info that corresponds to the supplie...
Definition ExprCXX.h:853
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:1984
Represents a byte-granular source range.
static CharSourceRange getCharRange(SourceRange R)
SourceLocation getEnd() const
bool isOne() const
Test whether the quantity equals one.
Definition CharUnits.h:104
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
bool hasAttr() const
Definition DeclBase.h:585
virtual Decl * getCanonicalDecl()
Retrieves the "canonical" declaration of the given declaration.
Definition DeclBase.h:995
SourceLocation getTypeSpecEndLoc() const
Definition Decl.cpp:2010
SourceLocation getBeginLoc() const LLVM_READONLY
Definition Decl.h:832
NestedNameSpecifierLoc getQualifierLoc() const
Retrieve the nested-name-specifier (with source-location information) that qualifies the name of this...
Definition Decl.h:846
NestedNameSpecifier getQualifier() const
Retrieve the nested-name-specifier that qualifies the name of this declaration, if it was present in ...
Definition Decl.h:838
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:3126
Expr * IgnoreParens() LLVM_READONLY
Skip past any parentheses which might surround this expression until reaching a fixed point.
Definition Expr.cpp:3122
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:3106
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:79
CharSourceRange RemoveRange
Code that should be replaced to correct the error.
Definition Diagnostic.h:83
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:140
static FixItHint CreateRemoval(CharSourceRange RemoveRange)
Create a code modification hint that removes the given source range.
Definition Diagnostic.h:129
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:103
Kind lookup(const VarDecl *VD) const
void set(const VarDecl *VD, Kind K)
Represents a function declaration or definition.
Definition Decl.h:2059
const ParmVarDecl * getParamDecl(unsigned i) const
Definition Decl.h:2928
Stmt * getBody(const FunctionDecl *&Definition) const
Retrieve the body (definition) of the function.
Definition Decl.cpp:3271
unsigned getBuiltinID(bool ConsiderWrapperFunctions=false) const
Returns a value indicating whether this function corresponds to a builtin function.
Definition Decl.cpp:3809
ArrayRef< ParmVarDecl * > parameters() const
Definition Decl.h:2905
param_iterator param_begin()
Definition Decl.h:2917
bool isVariadic() const
Whether this function is variadic.
Definition Decl.cpp:3124
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:2597
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:3415
bool isOverloadedOperator() const
Whether this function declaration represents an C++ overloaded operator, e.g., "operator+".
Definition Decl.h:3064
bool isConsteval() const
Definition Decl.h:2609
unsigned getNumParams() const
Return the number of parameters this function must have based on its FunctionType.
Definition Decl.cpp:3873
DeclarationNameInfo getNameInfo() const
Definition Decl.h:2325
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:985
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:1381
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:296
StringRef getName() const
Get the name of identifier for this declaration as a StringRef.
Definition Decl.h:302
DeclarationName getDeclName() const
Get the actual, stored name of the declaration, which may be a special name.
Definition Decl.h:341
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:318
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:1820
bool hasDefaultArg() const
Determines whether this parameter has a default argument, either parsed or not.
Definition Decl.cpp:3049
SourceRange getSourceRange() const override LLVM_READONLY
Source range that this declaration covers.
Definition Decl.cpp:2971
PointerType - C99 6.7.5.1 - Pointer Declarators.
Definition TypeBase.h:3403
A (possibly-)qualified type.
Definition TypeBase.h:938
bool hasQualifiers() const
Determine whether this type has any qualifiers.
Definition TypeBase.h:8535
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:8486
QualType getCanonicalType() const
Definition TypeBase.h:8498
bool isConstQualified() const
Determine whether this type is const-qualified.
Definition TypeBase.h:8519
std::string getAsString() const
Represents a struct/union/class.
Definition Decl.h:4460
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:2645
bool isArrayType() const
Definition TypeBase.h:8782
bool isPointerType() const
Definition TypeBase.h:8683
bool isIntegerType() const
isIntegerType() does not include complex integers (a GCC extension).
Definition TypeBase.h:9116
const T * castAs() const
Member-template castAs<specific type>.
Definition TypeBase.h:9366
QualType getPointeeType() const
If this is a pointer, ObjC object pointer, or block pointer, this returns the respective pointee.
Definition Type.cpp:885
bool isAnyCharacterType() const
Determine whether this type is any of the built-in character types.
Definition Type.cpp:2355
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:2460
bool isAnyPointerType() const
Definition TypeBase.h:8691
const Type * getUnqualifiedDesugaredType() const
Return the specified type with any "sugar" removed from the type, removing any typedefs,...
Definition Type.cpp:786
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:1438
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:724
Represents a variable declaration or definition.
Definition Decl.h:933
bool isConstexpr() const
Whether this variable is (C++11) constexpr.
Definition Decl.h:1594
SourceRange getSourceRange() const override LLVM_READONLY
Source range that this declaration covers.
Definition Decl.cpp:2170
VarDecl * getCanonicalDecl() override
Retrieves the "canonical" declaration of the given declaration.
Definition Decl.cpp:2237
bool isInlineSpecified() const
Definition Decl.h:1579
bool hasConstantInitialization() const
Determine whether this variable has constant initialization.
Definition Decl.cpp:2644
const Expr * getInit() const
Definition Decl.h:1392
bool hasLocalStorage() const
Returns true if a variable with function scope is a non-static local variable.
Definition Decl.h:1191
bool isLocalVarDecl() const
Returns true for local variable declarations other than parameters.
Definition Decl.h:1275
const Expr * getAnyInitializer() const
Get the initializer for this variable, no matter which declaration it is attached to.
Definition Decl.h:1382
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)
@ 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.