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