77// ===----------------------------------------------------------------------===//
88
99#include " clang/Analysis/Analyses/UnsafeBufferUsage.h"
10+ #include " clang/AST/APValue.h"
1011#include " clang/AST/ASTContext.h"
12+ #include " clang/AST/Attr.h"
1113#include " clang/AST/Decl.h"
1214#include " clang/AST/DynamicRecursiveASTVisitor.h"
1315#include " clang/AST/Expr.h"
@@ -353,23 +355,90 @@ isInUnspecifiedUntypedContext(internal::Matcher<Stmt> InnerMatcher) {
353355 return stmt (anyOf (CompStmt, IfStmtThen, IfStmtElse));
354356}
355357
358+ // Returns true iff integer E1 is equivalent to integer E2.
359+ //
360+ // For now we only support such expressions:
361+ // expr := DRE | const-value | expr BO expr
362+ // BO := '*' | '+'
363+ //
364+ // FIXME: We can reuse the expression comparator of the interop analysis after
365+ // it has been upstreamed.
366+ static bool areEqualIntegers (const Expr *E1 , const Expr *E2 , ASTContext &Ctx);
367+ static bool areEqualIntegralBinaryOperators (const BinaryOperator *E1 ,
368+ const Expr *E2_LHS,
369+ BinaryOperatorKind BOP,
370+ const Expr *E2_RHS,
371+ ASTContext &Ctx) {
372+ if (E1 ->getOpcode () == BOP) {
373+ switch (BOP) {
374+ // Commutative operators:
375+ case BO_Mul:
376+ case BO_Add:
377+ return (areEqualIntegers (E1 ->getLHS (), E2_LHS, Ctx) &&
378+ areEqualIntegers (E1 ->getRHS (), E2_RHS, Ctx)) ||
379+ (areEqualIntegers (E1 ->getLHS (), E2_RHS, Ctx) &&
380+ areEqualIntegers (E1 ->getRHS (), E2_LHS, Ctx));
381+ default :
382+ return false ;
383+ }
384+ }
385+ return false ;
386+ }
387+
388+ static bool areEqualIntegers (const Expr *E1 , const Expr *E2 , ASTContext &Ctx) {
389+ E1 = E1 ->IgnoreParenImpCasts ();
390+ E2 = E2 ->IgnoreParenImpCasts ();
391+ if (!E1 ->getType ()->isIntegerType () || E1 ->getType () != E2 ->getType ())
392+ return false ;
393+
394+ Expr::EvalResult ER1, ER2;
395+
396+ // If both are constants:
397+ if (E1 ->EvaluateAsInt (ER1, Ctx) &&
398+ E2 ->EvaluateAsInt (ER2, Ctx))
399+ return ER1.Val .getInt () == ER2.Val .getInt ();
400+
401+ // Otherwise, they should have identical stmt kind:
402+ if (E1 ->getStmtClass () != E2 ->getStmtClass ())
403+ return false ;
404+ switch (E1 ->getStmtClass ()) {
405+ case Stmt::DeclRefExprClass:
406+ return cast<DeclRefExpr>(E1 )->getDecl () == cast<DeclRefExpr>(E2 )->getDecl ();
407+ case Stmt::BinaryOperatorClass: {
408+ auto BO2 = cast<BinaryOperator>(E2 );
409+ return areEqualIntegralBinaryOperators (cast<BinaryOperator>(E1 ),
410+ BO2->getLHS (), BO2->getOpcode (),
411+ BO2->getRHS (), Ctx);
412+ }
413+ default :
414+ return false ;
415+ }
416+ }
417+
356418// Given a two-param std::span construct call, matches iff the call has the
357419// following forms:
358420// 1. `std::span<T>{new T[n], n}`, where `n` is a literal or a DRE
359421// 2. `std::span<T>{new T, 1}`
360- // 3. `std::span<T>{&var, 1}`
422+ // 3. `std::span<T>{&var, 1}` or `std::span<T>{std::addressof(...), 1}`
361423// 4. `std::span<T>{a, n}`, where `a` is of an array-of-T with constant size
362424// `n`
363425// 5. `std::span<T>{any, 0}`
364- // 6. `std::span<T>{std::addressof(...), 1}`
426+ // 6. `std::span<T>{ (char *)f(args), args[N] * arg*[M]}`, where
427+ // `f` is a function with attribute `alloc_size(N, M)`;
428+ // `args` represents the list of arguments;
429+ // `N, M` are parameter indexes to the allocating element number and size.
430+ // Sometimes, there is only one parameter index representing the total
431+ // size.
365432AST_MATCHER (CXXConstructExpr, isSafeSpanTwoParamConstruct) {
366433 assert (Node.getNumArgs () == 2 &&
367434 " expecting a two-parameter std::span constructor" );
368- const Expr *Arg0 = Node.getArg (0 )->IgnoreImplicit ();
369- const Expr *Arg1 = Node.getArg (1 )->IgnoreImplicit ();
370- auto HaveEqualConstantValues = [&Finder](const Expr *E0 , const Expr *E1 ) {
371- if (auto E0CV = E0 ->getIntegerConstantExpr (Finder->getASTContext ()))
372- if (auto E1CV = E1 ->getIntegerConstantExpr (Finder->getASTContext ())) {
435+ const Expr *Arg0 = Node.getArg (0 )->IgnoreParenImpCasts ();
436+ const Expr *Arg1 = Node.getArg (1 )->IgnoreParenImpCasts ();
437+ ASTContext &Ctx = Finder->getASTContext ();
438+
439+ auto HaveEqualConstantValues = [&Ctx](const Expr *E0 , const Expr *E1 ) {
440+ if (auto E0CV = E0 ->getIntegerConstantExpr (Ctx))
441+ if (auto E1CV = E1 ->getIntegerConstantExpr (Ctx)) {
373442 return APSInt::compareValues (*E0CV, *E1CV) == 0 ;
374443 }
375444 return false ;
@@ -381,13 +450,14 @@ AST_MATCHER(CXXConstructExpr, isSafeSpanTwoParamConstruct) {
381450 }
382451 return false ;
383452 };
384- std::optional<APSInt> Arg1CV =
385- Arg1->getIntegerConstantExpr (Finder->getASTContext ());
453+ std::optional<APSInt> Arg1CV = Arg1->getIntegerConstantExpr (Ctx);
386454
387455 if (Arg1CV && Arg1CV->isZero ())
388456 // Check form 5:
389457 return true ;
390- switch (Arg0->IgnoreImplicit ()->getStmtClass ()) {
458+
459+ // Check forms 1-3:
460+ switch (Arg0->getStmtClass ()) {
391461 case Stmt::CXXNewExprClass:
392462 if (auto Size = cast<CXXNewExpr>(Arg0)->getArraySize ()) {
393463 // Check form 1:
@@ -407,6 +477,7 @@ AST_MATCHER(CXXConstructExpr, isSafeSpanTwoParamConstruct) {
407477 return Arg1CV && Arg1CV->isOne ();
408478 break ;
409479 case Stmt::CallExprClass:
480+ // Check form 3:
410481 if (const auto *CE = dyn_cast<CallExpr>(Arg0)) {
411482 const auto FnDecl = CE->getDirectCallee ();
412483 if (FnDecl && FnDecl->getNameAsString () == " addressof" &&
@@ -421,13 +492,41 @@ AST_MATCHER(CXXConstructExpr, isSafeSpanTwoParamConstruct) {
421492
422493 QualType Arg0Ty = Arg0->IgnoreImplicit ()->getType ();
423494
424- if (auto *ConstArrTy =
425- Finder->getASTContext ().getAsConstantArrayType (Arg0Ty)) {
495+ if (auto *ConstArrTy = Ctx.getAsConstantArrayType (Arg0Ty)) {
426496 const APSInt ConstArrSize = APSInt (ConstArrTy->getSize ());
427497
428498 // Check form 4:
429499 return Arg1CV && APSInt::compareValues (ConstArrSize, *Arg1CV) == 0 ;
430500 }
501+ // Check form 6:
502+ if (auto CCast = dyn_cast<CStyleCastExpr>(Arg0)) {
503+ if (!CCast->getType ()->isPointerType ())
504+ return false ;
505+
506+ QualType PteTy = CCast->getType ()->getPointeeType ();
507+
508+ if (!(PteTy->isConstantSizeType () && Ctx.getTypeSizeInChars (PteTy).isOne ()))
509+ return false ;
510+
511+ if (const auto *Call = dyn_cast<CallExpr>(CCast->getSubExpr ())) {
512+ if (const FunctionDecl *FD = Call->getDirectCallee ())
513+ if (auto *AllocAttr = FD->getAttr <AllocSizeAttr>()) {
514+ const Expr *EleSizeExpr =
515+ Call->getArg (AllocAttr->getElemSizeParam ().getASTIndex ());
516+ // NumElemIdx is invalid if AllocSizeAttr has 1 argument:
517+ ParamIdx NumElemIdx = AllocAttr->getNumElemsParam ();
518+
519+ if (!NumElemIdx.isValid ())
520+ return areEqualIntegers (Arg1, EleSizeExpr, Ctx);
521+
522+ const Expr *NumElesExpr = Call->getArg (NumElemIdx.getASTIndex ());
523+
524+ if (auto BO = dyn_cast<BinaryOperator>(Arg1))
525+ return areEqualIntegralBinaryOperators (BO, NumElesExpr, BO_Mul,
526+ EleSizeExpr, Ctx);
527+ }
528+ }
529+ }
431530 return false ;
432531}
433532
0 commit comments