@@ -453,22 +453,110 @@ static bool areEqualIntegers(const Expr *E1, const Expr *E2, ASTContext &Ctx) {
453
453
}
454
454
}
455
455
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
+
456
546
// Given a two-param std::span construct call, matches iff the call has the
457
547
// following forms:
458
548
// 1. `std::span<T>{new T[n], n}`, where `n` is a literal or a DRE
459
549
// 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
465
551
// `f` is a function with attribute `alloc_size(N, M)`;
466
552
// `args` represents the list of arguments;
467
553
// `N, M` are parameter indexes to the allocating element number and size.
468
554
// Sometimes, there is only one parameter index representing the total
469
555
// 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
471
557
// SIZED_CONTAINER_OR_VIEW_LIST.
558
+ // 5. `isPtrBufferSafe` returns true for the two arguments of the span
559
+ // constructor
472
560
static bool isSafeSpanTwoParamConstruct (const CXXConstructExpr &Node,
473
561
ASTContext &Ctx) {
474
562
assert (Node.getNumArgs () == 2 &&
@@ -495,7 +583,7 @@ static bool isSafeSpanTwoParamConstruct(const CXXConstructExpr &Node,
495
583
// Check form 5:
496
584
return true ;
497
585
498
- // Check forms 1-3 :
586
+ // Check forms 1-2 :
499
587
switch (Arg0->getStmtClass ()) {
500
588
case Stmt::CXXNewExprClass:
501
589
if (auto Size = cast<CXXNewExpr>(Arg0)->getArraySize ()) {
@@ -509,35 +597,11 @@ static bool isSafeSpanTwoParamConstruct(const CXXConstructExpr &Node,
509
597
return Arg1CV && Arg1CV->isOne ();
510
598
}
511
599
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 ;
528
600
default :
529
601
break ;
530
602
}
531
603
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:
541
605
if (auto CCast = dyn_cast<CStyleCastExpr>(Arg0)) {
542
606
if (!CCast->getType ()->isPointerType ())
543
607
return false ;
@@ -566,7 +630,7 @@ static bool isSafeSpanTwoParamConstruct(const CXXConstructExpr &Node,
566
630
}
567
631
}
568
632
}
569
- // Check form 7 :
633
+ // Check form 4 :
570
634
auto IsMethodCallToSizedObject = [](const Stmt *Node, StringRef MethodName) {
571
635
if (const auto *MC = dyn_cast<CXXMemberCallExpr>(Node)) {
572
636
const auto *MD = MC->getMethodDecl ();
@@ -592,7 +656,9 @@ static bool isSafeSpanTwoParamConstruct(const CXXConstructExpr &Node,
592
656
cast<CXXMemberCallExpr>(Arg1)
593
657
->getImplicitObjectArgument ()
594
658
->IgnoreParenImpCasts ());
595
- return false ;
659
+
660
+ // Check 5:
661
+ return isPtrBufferSafe (Arg0, Arg1, Ctx);
596
662
}
597
663
598
664
static bool isSafeArraySubscript (const ArraySubscriptExpr &Node,
@@ -1052,24 +1118,11 @@ static bool hasUnsafePrintfStringArg(const CallExpr &Node, ASTContext &Ctx,
1052
1118
return false ;
1053
1119
}
1054
1120
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) {
1073
1126
const FunctionDecl *FD = Node.getDirectCallee ();
1074
1127
1075
1128
assert (FD && " It should have been checked that FD is non-null." );
@@ -1085,57 +1138,12 @@ static bool hasUnsafeSnprintfBuffer(const CallExpr &Node,
1085
1138
QualType FirstPteTy = FirstParmTy->castAs <PointerType>()->getPointeeType ();
1086
1139
const Expr *Buf = Node.getArg (0 ), *Size = Node.getArg (1 );
1087
1140
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 ())
1090
1144
return false ; // not an snprintf call
1091
1145
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);
1139
1147
}
1140
1148
} // namespace libc_func_matchers
1141
1149
0 commit comments