@@ -453,22 +453,110 @@ static bool areEqualIntegers(const Expr *E1, const Expr *E2, ASTContext &Ctx) {
453453 }
454454}
455455
456+ // Providing that `Ptr` is a pointer and `Size` is an unsigned-integral
457+ // expression, returns true iff they follow one of the following safe
458+ // patterns:
459+ // 1. Ptr is `DRE.data()` and Size is `DRE.size()`, where DRE is a hardened
460+ // container or view;
461+ //
462+ // 2. Ptr is `a` and Size is `n`, where `a` is of an array-of-T with constant
463+ // size `n`;
464+ //
465+ // 3. Ptr is `&var` and Size is `1`; or
466+ // Ptr is `std::addressof(...)` and Size is `1`;
467+ //
468+ // 4. Size is `0`;
469+ static bool isPtrBufferSafe (const Expr *Ptr, const Expr *Size,
470+ ASTContext &Ctx) {
471+ // Pattern 1:
472+ if (auto *MCEPtr = dyn_cast<CXXMemberCallExpr>(Ptr->IgnoreParenImpCasts ()))
473+ if (auto *MCESize =
474+ dyn_cast<CXXMemberCallExpr>(Size->IgnoreParenImpCasts ())) {
475+ auto *DREOfPtr = dyn_cast<DeclRefExpr>(
476+ MCEPtr->getImplicitObjectArgument ()->IgnoreParenImpCasts ());
477+ auto *DREOfSize = dyn_cast<DeclRefExpr>(
478+ MCESize->getImplicitObjectArgument ()->IgnoreParenImpCasts ());
479+
480+ if (!DREOfPtr || !DREOfSize)
481+ return false ; // not in safe pattern
482+ // We need to make sure 'a' is identical to 'b' for 'a.data()' and
483+ // 'b.size()' otherwise we do not know they match:
484+ if (DREOfPtr->getDecl () != DREOfSize->getDecl ())
485+ return false ;
486+ if (MCEPtr->getMethodDecl ()->getName () != " data" )
487+ return false ;
488+ // `MCEPtr->getRecordDecl()` must be non-null as `DREOfPtr` is non-null:
489+ if (!MCEPtr->getRecordDecl ()->isInStdNamespace ())
490+ return false ;
491+
492+ auto *ObjII = MCEPtr->getRecordDecl ()->getIdentifier ();
493+
494+ if (!ObjII)
495+ return false ;
496+
497+ bool AcceptSizeBytes = Ptr->getType ()->getPointeeType ()->isCharType ();
498+
499+ if (!((AcceptSizeBytes &&
500+ MCESize->getMethodDecl ()->getName () == " size_bytes" ) ||
501+ // Note here the pointer must be a pointer-to-char type unless there
502+ // is explicit casting. If there is explicit casting, this branch
503+ // is unreachable. Thus, at this branch "size" and "size_bytes" are
504+ // equivalent as the pointer is a char pointer:
505+ MCESize->getMethodDecl ()->getName () == " size" ))
506+ return false ;
507+
508+ return llvm::is_contained ({SIZED_CONTAINER_OR_VIEW_LIST},
509+ ObjII->getName ());
510+ }
511+
512+ Expr::EvalResult ER;
513+
514+ // Pattern 2-4:
515+ if (Size->EvaluateAsInt (ER, Ctx)) {
516+ // Pattern 2:
517+ if (auto *DRE = dyn_cast<DeclRefExpr>(Ptr->IgnoreParenImpCasts ())) {
518+ if (auto *CAT = Ctx.getAsConstantArrayType (DRE->getType ())) {
519+ llvm::APSInt SizeInt = ER.Val .getInt ();
520+
521+ return llvm::APSInt::compareValues (
522+ SizeInt, llvm::APSInt (CAT->getSize (), true )) == 0 ;
523+ }
524+ return false ;
525+ }
526+
527+ // Pattern 3:
528+ if (ER.Val .getInt ().isOne ()) {
529+ if (auto *UO = dyn_cast<UnaryOperator>(Ptr->IgnoreParenImpCasts ()))
530+ return UO && UO->getOpcode () == UnaryOperator::Opcode::UO_AddrOf;
531+ if (auto *CE = dyn_cast<CallExpr>(Ptr->IgnoreParenImpCasts ())) {
532+ auto *FnDecl = CE->getDirectCallee ();
533+
534+ return FnDecl && FnDecl->getNameAsString () == " addressof" &&
535+ FnDecl->isInStdNamespace ();
536+ }
537+ return false ;
538+ }
539+ // Pattern 4:
540+ if (ER.Val .getInt ().isZero ())
541+ return true ;
542+ }
543+ return false ;
544+ }
545+
456546// Given a two-param std::span construct call, matches iff the call has the
457547// following forms:
458548// 1. `std::span<T>{new T[n], n}`, where `n` is a literal or a DRE
459549// 2. `std::span<T>{new T, 1}`
460- // 3. `std::span<T>{&var, 1}` or `std::span<T>{std::addressof(...), 1}`
461- // 4. `std::span<T>{a, n}`, where `a` is of an array-of-T with constant size
462- // `n`
463- // 5. `std::span<T>{any, 0}`
464- // 6. `std::span<T>{ (char *)f(args), args[N] * arg*[M]}`, where
550+ // 3. `std::span<T>{ (char *)f(args), args[N] * arg*[M]}`, where
465551// `f` is a function with attribute `alloc_size(N, M)`;
466552// `args` represents the list of arguments;
467553// `N, M` are parameter indexes to the allocating element number and size.
468554// Sometimes, there is only one parameter index representing the total
469555// size.
470- // 7 . `std::span<T>{x.begin(), x.end()}` where `x` is an object in the
556+ // 4 . `std::span<T>{x.begin(), x.end()}` where `x` is an object in the
471557// SIZED_CONTAINER_OR_VIEW_LIST.
558+ // 5. `isPtrBufferSafe` returns true for the two arguments of the span
559+ // constructor
472560static bool isSafeSpanTwoParamConstruct (const CXXConstructExpr &Node,
473561 ASTContext &Ctx) {
474562 assert (Node.getNumArgs () == 2 &&
@@ -495,7 +583,7 @@ static bool isSafeSpanTwoParamConstruct(const CXXConstructExpr &Node,
495583 // Check form 5:
496584 return true ;
497585
498- // Check forms 1-3 :
586+ // Check forms 1-2 :
499587 switch (Arg0->getStmtClass ()) {
500588 case Stmt::CXXNewExprClass:
501589 if (auto Size = cast<CXXNewExpr>(Arg0)->getArraySize ()) {
@@ -509,35 +597,11 @@ static bool isSafeSpanTwoParamConstruct(const CXXConstructExpr &Node,
509597 return Arg1CV && Arg1CV->isOne ();
510598 }
511599 break ;
512- case Stmt::UnaryOperatorClass:
513- if (cast<UnaryOperator>(Arg0)->getOpcode () ==
514- UnaryOperator::Opcode::UO_AddrOf)
515- // Check form 3:
516- return Arg1CV && Arg1CV->isOne ();
517- break ;
518- case Stmt::CallExprClass:
519- // Check form 3:
520- if (const auto *CE = dyn_cast<CallExpr>(Arg0)) {
521- const auto FnDecl = CE->getDirectCallee ();
522- if (FnDecl && FnDecl->getNameAsString () == " addressof" &&
523- FnDecl->isInStdNamespace ()) {
524- return Arg1CV && Arg1CV->isOne ();
525- }
526- }
527- break ;
528600 default :
529601 break ;
530602 }
531603
532- QualType Arg0Ty = Arg0->IgnoreImplicit ()->getType ();
533-
534- if (auto *ConstArrTy = Ctx.getAsConstantArrayType (Arg0Ty)) {
535- const llvm::APSInt ConstArrSize = llvm::APSInt (ConstArrTy->getSize ());
536-
537- // Check form 4:
538- return Arg1CV && llvm::APSInt::compareValues (ConstArrSize, *Arg1CV) == 0 ;
539- }
540- // Check form 6:
604+ // Check form 3:
541605 if (auto CCast = dyn_cast<CStyleCastExpr>(Arg0)) {
542606 if (!CCast->getType ()->isPointerType ())
543607 return false ;
@@ -566,7 +630,7 @@ static bool isSafeSpanTwoParamConstruct(const CXXConstructExpr &Node,
566630 }
567631 }
568632 }
569- // Check form 7 :
633+ // Check form 4 :
570634 auto IsMethodCallToSizedObject = [](const Stmt *Node, StringRef MethodName) {
571635 if (const auto *MC = dyn_cast<CXXMemberCallExpr>(Node)) {
572636 const auto *MD = MC->getMethodDecl ();
@@ -592,7 +656,9 @@ static bool isSafeSpanTwoParamConstruct(const CXXConstructExpr &Node,
592656 cast<CXXMemberCallExpr>(Arg1)
593657 ->getImplicitObjectArgument ()
594658 ->IgnoreParenImpCasts ());
595- return false ;
659+
660+ // Check 5:
661+ return isPtrBufferSafe (Arg0, Arg1, Ctx);
596662}
597663
598664static bool isSafeArraySubscript (const ArraySubscriptExpr &Node,
@@ -1052,24 +1118,11 @@ static bool hasUnsafePrintfStringArg(const CallExpr &Node, ASTContext &Ctx,
10521118 return false ;
10531119}
10541120
1055- // This matcher requires that it is known that the callee `isNormalPrintf`.
1056- // Then it matches if the first two arguments of the call is a pointer and an
1057- // integer and they are not in a safe pattern.
1058- //
1059- // For the first two arguments: `ptr` and `size`, they are safe if in the
1060- // following patterns:
1061- //
1062- // Pattern 1:
1063- // ptr := DRE.data();
1064- // size:= DRE.size()/DRE.size_bytes()
1065- // And DRE is a hardened container or view.
1066- //
1067- // Pattern 2:
1068- // ptr := Constant-Array-DRE;
1069- // size:= any expression that has compile-time constant value equivalent to
1070- // sizeof (Constant-Array-DRE)
1071- static bool hasUnsafeSnprintfBuffer (const CallExpr &Node,
1072- const ASTContext &Ctx) {
1121+ // This function requires that it is known that the callee `isNormalPrintf`.
1122+ // It returns true iff the first two arguments of the call is a pointer
1123+ // `Ptr` and an unsigned integer `Size` and they are NOT safe, i.e.,
1124+ // `!isPtrBufferSafe(Ptr, Size)`.
1125+ static bool hasUnsafeSnprintfBuffer (const CallExpr &Node, ASTContext &Ctx) {
10731126 const FunctionDecl *FD = Node.getDirectCallee ();
10741127
10751128 assert (FD && " It should have been checked that FD is non-null." );
@@ -1085,57 +1138,12 @@ static bool hasUnsafeSnprintfBuffer(const CallExpr &Node,
10851138 QualType FirstPteTy = FirstParmTy->castAs <PointerType>()->getPointeeType ();
10861139 const Expr *Buf = Node.getArg (0 ), *Size = Node.getArg (1 );
10871140
1088- if (FirstPteTy.isConstQualified () || !Buf->getType ()->isPointerType () ||
1089- !Size->getType ()->isIntegerType ())
1141+ if (FirstPteTy.isConstQualified () || !FirstPteTy->isAnyCharacterType () ||
1142+ !Buf->getType ()->isPointerType () ||
1143+ !Size->getType ()->isUnsignedIntegerType ())
10901144 return false ; // not an snprintf call
10911145
1092- // Pattern 1:
1093- static StringRef SizedObjs[] = {SIZED_CONTAINER_OR_VIEW_LIST};
1094- Buf = Buf->IgnoreParenImpCasts ();
1095- Size = Size->IgnoreParenImpCasts ();
1096- if (auto *MCEPtr = dyn_cast<CXXMemberCallExpr>(Buf))
1097- if (auto *MCESize = dyn_cast<CXXMemberCallExpr>(Size)) {
1098- auto *DREOfPtr = dyn_cast<DeclRefExpr>(
1099- MCEPtr->getImplicitObjectArgument ()->IgnoreParenImpCasts ());
1100- auto *DREOfSize = dyn_cast<DeclRefExpr>(
1101- MCESize->getImplicitObjectArgument ()->IgnoreParenImpCasts ());
1102-
1103- if (!DREOfPtr || !DREOfSize)
1104- return true ; // not in safe pattern
1105- if (DREOfPtr->getDecl () != DREOfSize->getDecl ())
1106- return true ; // not in safe pattern
1107- if (MCEPtr->getMethodDecl ()->getName () != " data" )
1108- return true ; // not in safe pattern
1109-
1110- if (MCESize->getMethodDecl ()->getName () == " size_bytes" ||
1111- // Note here the pointer must be a pointer-to-char type unless there
1112- // is explicit casting. If there is explicit casting, this branch
1113- // is unreachable. Thus, at this branch "size" and "size_bytes" are
1114- // equivalent as the pointer is a char pointer:
1115- MCESize->getMethodDecl ()->getName () == " size" )
1116- for (StringRef SizedObj : SizedObjs)
1117- if (MCEPtr->getRecordDecl ()->isInStdNamespace () &&
1118- MCEPtr->getRecordDecl ()->getCanonicalDecl ()->getName () ==
1119- SizedObj)
1120- return false ; // It is in fact safe
1121- }
1122-
1123- // Pattern 2:
1124- if (auto *DRE = dyn_cast<DeclRefExpr>(Buf->IgnoreParenImpCasts ())) {
1125- if (auto *CAT = Ctx.getAsConstantArrayType (DRE->getType ())) {
1126- Expr::EvalResult ER;
1127- // The array element type must be compatible with `char` otherwise an
1128- // explicit cast will be needed, which will make this check unreachable.
1129- // Therefore, the array extent is same as its' bytewise size.
1130- if (Size->EvaluateAsInt (ER, Ctx)) {
1131- llvm::APSInt EVal = ER.Val .getInt (); // Size must have integer type
1132-
1133- return llvm::APSInt::compareValues (
1134- EVal, llvm::APSInt (CAT->getSize (), true )) != 0 ;
1135- }
1136- }
1137- }
1138- return true ; // ptr and size are not in safe pattern
1146+ return !isPtrBufferSafe (Buf, Size, Ctx);
11391147}
11401148} // namespace libc_func_matchers
11411149
0 commit comments