Skip to content

Commit ed69c23

Browse files
authored
Merge branch 'llvm:main' into main
2 parents 96a0469 + 8aeab8f commit ed69c23

File tree

59 files changed

+4090
-380
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+4090
-380
lines changed

clang/lib/Analysis/UnsafeBufferUsage.cpp

Lines changed: 109 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -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
472560
static 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

598664
static 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

clang/lib/Driver/ToolChains/MinGW.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,9 @@ void tools::MinGW::Linker::ConstructJob(Compilation &C, const JobAction &JA,
132132
CmdArgs.push_back("thumb2pe");
133133
break;
134134
case llvm::Triple::aarch64:
135-
if (TC.getEffectiveTriple().isWindowsArm64EC())
135+
if (Args.hasArg(options::OPT_marm64x))
136+
CmdArgs.push_back("arm64xpe");
137+
else if (TC.getEffectiveTriple().isWindowsArm64EC())
136138
CmdArgs.push_back("arm64ecpe");
137139
else
138140
CmdArgs.push_back("arm64pe");

clang/lib/StaticAnalyzer/Core/MemRegion.cpp

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1022,6 +1022,22 @@ getStackOrCaptureRegionForDeclContext(const LocationContext *LC,
10221022
return (const StackFrameContext *)nullptr;
10231023
}
10241024

1025+
static bool isStdStreamVar(const VarDecl *D) {
1026+
const IdentifierInfo *II = D->getIdentifier();
1027+
if (!II)
1028+
return false;
1029+
if (!D->getDeclContext()->isTranslationUnit())
1030+
return false;
1031+
StringRef N = II->getName();
1032+
QualType FILETy = D->getASTContext().getFILEType();
1033+
if (FILETy.isNull())
1034+
return false;
1035+
FILETy = FILETy.getCanonicalType();
1036+
QualType Ty = D->getType().getCanonicalType();
1037+
return Ty->isPointerType() && Ty->getPointeeType() == FILETy &&
1038+
(N == "stdin" || N == "stdout" || N == "stderr");
1039+
}
1040+
10251041
const VarRegion *MemRegionManager::getVarRegion(const VarDecl *D,
10261042
const LocationContext *LC) {
10271043
const auto *PVD = dyn_cast<ParmVarDecl>(D);
@@ -1054,10 +1070,18 @@ const VarRegion *MemRegionManager::getVarRegion(const VarDecl *D,
10541070
assert(!Ty.isNull());
10551071
if (Ty.isConstQualified()) {
10561072
sReg = getGlobalsRegion(MemRegion::GlobalImmutableSpaceRegionKind);
1057-
} else if (Ctx.getSourceManager().isInSystemHeader(D->getLocation())) {
1058-
sReg = getGlobalsRegion(MemRegion::GlobalSystemSpaceRegionKind);
10591073
} else {
1060-
sReg = getGlobalsRegion(MemRegion::GlobalInternalSpaceRegionKind);
1074+
// Pointer value of C standard streams is usually not modified by calls
1075+
// to functions declared in system headers. This means that they should
1076+
// not get invalidated by calls to functions declared in system headers,
1077+
// so they are placed in the global internal space, which is not
1078+
// invalidated by calls to functions declared in system headers.
1079+
if (Ctx.getSourceManager().isInSystemHeader(D->getLocation()) &&
1080+
!isStdStreamVar(D)) {
1081+
sReg = getGlobalsRegion(MemRegion::GlobalSystemSpaceRegionKind);
1082+
} else {
1083+
sReg = getGlobalsRegion(MemRegion::GlobalInternalSpaceRegionKind);
1084+
}
10611085
}
10621086

10631087
// Finally handle static locals.

clang/test/Analysis/stream.c

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -519,14 +519,36 @@ void reopen_std_stream(void) {
519519
if (!fp) return;
520520

521521
stdout = fp; // Let's make them alias.
522-
clang_analyzer_eval(fp == oldStdout); // expected-warning {{UNKNOWN}}
523-
clang_analyzer_eval(fp == stdout); // expected-warning {{TRUE}} no-FALSE
524-
clang_analyzer_eval(oldStdout == stdout); // expected-warning {{UNKNOWN}}
522+
clang_analyzer_eval(fp == oldStdout); // expected-warning {{FALSE}}
523+
clang_analyzer_eval(fp == stdout); // expected-warning {{TRUE}}
524+
clang_analyzer_eval(oldStdout == stdout); // expected-warning {{FALSE}}
525525
}
526526

527527
void only_success_path_does_not_alias_with_stdout(void) {
528528
if (stdout) return;
529529
FILE *f = fopen("/tmp/foof", "r"); // no-crash
530+
clang_analyzer_eval(f == 0);// expected-warning {{TRUE}} expected-warning {{FALSE}}
530531
if (!f) return;
531532
fclose(f);
532533
}
534+
535+
extern void do_something();
536+
537+
void test_no_invalidate_at_system_call() {
538+
FILE *old_stdin = stdin;
539+
if ((stdin = fopen("x/y/z", "r")) == NULL)
540+
return;
541+
clang_analyzer_eval(old_stdin == stdin); // expected-warning{{FALSE}}
542+
free(malloc(100));
543+
clang_analyzer_eval(old_stdin == stdin); // expected-warning{{FALSE}}
544+
fclose(stdin);
545+
}
546+
547+
void test_invalidate_at_non_system_call() {
548+
FILE *old_stdin = stdin;
549+
if ((stdin = fopen("x/y/z", "r")) == NULL)
550+
return;
551+
clang_analyzer_eval(old_stdin == stdin); // expected-warning{{FALSE}}
552+
do_something();
553+
clang_analyzer_eval(old_stdin == stdin); // expected-warning{{UNKNOWN}}
554+
}

clang/test/Driver/mingw.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,10 @@
8585
// RUN: | FileCheck %s --check-prefix CHECK_MINGW_EC_LINK
8686
// CHECK_MINGW_EC_LINK: "-m" "arm64ecpe"
8787

88+
// RUN: %clang --target=aarch64-windows-gnu -marm64x -### -o /dev/null %s 2>&1 \
89+
// RUN: | FileCheck %s --check-prefix CHECK_MINGW_A64X_LINK
90+
// CHECK_MINGW_A64X_LINK: "-m" "arm64xpe"
91+
8892
// RUN: %clang --target=mipsel-windows-gnu -### -o /dev/null %s 2>&1 \
8993
// RUN: | FileCheck %s --check-prefix CHECK_MINGW_MIPSPE
9094
// CHECK_MINGW_MIPSPE: "-m" "mipspe"

clang/test/SemaCXX/warn-unsafe-buffer-usage-libc-functions.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,10 +125,13 @@ void safe_examples(std::string s1, int *p) {
125125
fprintf((FILE*)0, s1.c_str(), __PRETTY_FUNCTION__, *p, "hello", s1.c_str()); // no warn
126126

127127
char a[10];
128+
char c = 'c';
128129

129130
snprintf(a, sizeof a, "%s%d%s%p%s", __PRETTY_FUNCTION__, *p, "hello", s1.c_str()); // no warn
130131
snprintf(a, sizeof(decltype(a)), "%s%d%s%p%s", __PRETTY_FUNCTION__, *p, "hello", s1.c_str()); // no warn
131132
snprintf(a, 10, "%s%d%s%p%s", __PRETTY_FUNCTION__, *p, "hello", s1.c_str()); // no warn
133+
snprintf(&c, 1, "%s%d%s%p%s", __PRETTY_FUNCTION__, *p, "hello", s1.c_str()); // no warn
134+
snprintf(nullptr, 0, "%s%d%s%p%s", __PRETTY_FUNCTION__, *p, "hello", s1.c_str()); // no warn
132135
}
133136

134137

0 commit comments

Comments
 (0)