Skip to content

Commit a1e0a3b

Browse files
authored
Merge pull request #5840 from ziqingluo-90/stable/20221013-cherrypick-2
static analysis related cherry-picks to stable/20221013 #2
2 parents 76968be + 399ca50 commit a1e0a3b

38 files changed

+1685
-120
lines changed

clang-tools-extra/clang-doc/Generators.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ void Generator::addInfoToIndex(Index &Idx, const doc::Info *Info) {
6565
for (const auto &R : llvm::reverse(Info->Namespace)) {
6666
// Look for the current namespace in the children of the index I is
6767
// pointing.
68-
auto It = std::find(I->Children.begin(), I->Children.end(), R.USR);
68+
auto It = llvm::find(I->Children, R.USR);
6969
if (It != I->Children.end()) {
7070
// If it is found, just change I to point the namespace reference found.
7171
I = &*It;
@@ -79,7 +79,7 @@ void Generator::addInfoToIndex(Index &Idx, const doc::Info *Info) {
7979
// Look for Info in the vector where it is supposed to be; it could already
8080
// exist if it is a parent namespace of an Info already passed to this
8181
// function.
82-
auto It = std::find(I->Children.begin(), I->Children.end(), Info->USR);
82+
auto It = llvm::find(I->Children, Info->USR);
8383
if (It == I->Children.end()) {
8484
// If it is not in the vector it is inserted
8585
I->Children.emplace_back(Info->USR, Info->extractName(), Info->IT,

clang-tools-extra/clang-tidy/misc/ConfusableIdentifierCheck.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,7 @@ std::string ConfusableIdentifierCheck::skeleton(StringRef Name) {
7878
UTF8 *BufferStart = std::begin(Buffer);
7979
UTF8 *IBuffer = BufferStart;
8080
const UTF32 *ValuesStart = std::begin(Where->values);
81-
const UTF32 *ValuesEnd =
82-
std::find(std::begin(Where->values), std::end(Where->values), '\0');
81+
const UTF32 *ValuesEnd = llvm::find(Where->values, '\0');
8382
if (ConvertUTF32toUTF8(&ValuesStart, ValuesEnd, &IBuffer,
8483
std::end(Buffer),
8584
strictConversion) != conversionOK) {

clang-tools-extra/clang-tidy/misc/MisleadingBidirectional.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ static bool containsMisleadingBidi(StringRef Buffer,
7575
BidiContexts.push_back(PDI);
7676
// Close a PDI Context.
7777
else if (CodePoint == PDI) {
78-
auto R = std::find(BidiContexts.rbegin(), BidiContexts.rend(), PDI);
78+
auto R = llvm::find(llvm::reverse(BidiContexts), PDI);
7979
if (R != BidiContexts.rend())
8080
BidiContexts.resize(BidiContexts.rend() - R - 1);
8181
}

clang-tools-extra/clang-tidy/performance/UnnecessaryValueParamCheck.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,7 @@ void UnnecessaryValueParamCheck::check(const MatchFinder::MatchResult &Result) {
120120
}
121121
}
122122

123-
const size_t Index = std::find(Function->parameters().begin(),
124-
Function->parameters().end(), Param) -
123+
const size_t Index = llvm::find(Function->parameters(), Param) -
125124
Function->parameters().begin();
126125

127126
auto Diag =

clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -519,9 +519,9 @@ int clangTidyMain(int argc, const char **argv) {
519519
std::vector<clang::tidy::ClangTidyOptionsProvider::OptionsSource>
520520
RawOptions = OptionsProvider->getRawOptions(FilePath);
521521
for (const std::string &Check : EnabledChecks) {
522-
for (auto It = RawOptions.rbegin(); It != RawOptions.rend(); ++It) {
523-
if (It->first.Checks && GlobList(*It->first.Checks).contains(Check)) {
524-
llvm::outs() << "'" << Check << "' is enabled in the " << It->second
522+
for (const auto &[Opts, Source] : llvm::reverse(RawOptions)) {
523+
if (Opts.Checks && GlobList(*Opts.Checks).contains(Check)) {
524+
llvm::outs() << "'" << Check << "' is enabled in the " << Source
525525
<< ".\n";
526526
break;
527527
}
@@ -557,20 +557,16 @@ int clangTidyMain(int argc, const char **argv) {
557557
NamesAndOptions Valid =
558558
getAllChecksAndOptions(AllowEnablingAnalyzerAlphaCheckers);
559559
bool AnyInvalid = false;
560-
for (const std::pair<ClangTidyOptions, std::string> &OptionWithSource :
561-
RawOptions) {
562-
const ClangTidyOptions &Opts = OptionWithSource.first;
560+
for (const auto &[Opts, Source] : RawOptions) {
563561
if (Opts.Checks)
564-
AnyInvalid |=
565-
verifyChecks(Valid.Names, *Opts.Checks, OptionWithSource.second);
562+
AnyInvalid |= verifyChecks(Valid.Names, *Opts.Checks, Source);
566563

567564
for (auto Key : Opts.CheckOptions.keys()) {
568565
if (Valid.Options.contains(Key))
569566
continue;
570567
AnyInvalid = true;
571-
auto &Output =
572-
llvm::WithColor::warning(llvm::errs(), OptionWithSource.second)
573-
<< "unknown check option '" << Key << '\'';
568+
auto &Output = llvm::WithColor::warning(llvm::errs(), Source)
569+
<< "unknown check option '" << Key << '\'';
574570
llvm::StringRef Closest = closest(Key, Valid.Options);
575571
if (!Closest.empty())
576572
Output << "; did you mean '" << Closest << '\'';

clang-tools-extra/clangd/InlayHints.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -581,9 +581,8 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
581581
static const ParmVarDecl *getParamDefinition(const ParmVarDecl *P) {
582582
if (auto *Callee = dyn_cast<FunctionDecl>(P->getDeclContext())) {
583583
if (auto *Def = Callee->getDefinition()) {
584-
auto I = std::distance(
585-
Callee->param_begin(),
586-
std::find(Callee->param_begin(), Callee->param_end(), P));
584+
auto I = std::distance(Callee->param_begin(),
585+
llvm::find(Callee->parameters(), P));
587586
if (I < Callee->getNumParams()) {
588587
return Def->getParamDecl(I);
589588
}

clang/docs/ReleaseNotes.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,11 @@ Attribute Changes in Clang
418418
used ``201904L`` (the date the proposal was seen by the committee) by mistake.
419419
There were no other changes to the attribute behavior.
420420

421+
- Introduced a new record declaration attribute ``__attribute__((enforce_read_only_placement))``
422+
to support analysis of instances of a given type focused on read-only program
423+
memory placement. It emits a warning if something in the code provably prevents
424+
an instance from a read-only memory placement.
425+
421426
Windows Support
422427
---------------
423428
- For the MinGW driver, added the options ``-mguard=none``, ``-mguard=cf`` and

clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,16 @@
3232
namespace clang {
3333
namespace dataflow {
3434

35+
template <typename AnalysisT, typename LatticeT, typename = std::void_t<>>
36+
struct HasTransferBranchFor : std::false_type {};
37+
38+
template <typename AnalysisT, typename LatticeT>
39+
struct HasTransferBranchFor<
40+
AnalysisT, LatticeT,
41+
std::void_t<decltype(std::declval<AnalysisT>().transferBranch(
42+
std::declval<bool>(), std::declval<const Stmt *>(),
43+
std::declval<LatticeT &>(), std::declval<Environment &>()))>>
44+
: std::true_type {};
3545
/// Base class template for dataflow analyses built on a single lattice type.
3646
///
3747
/// Requirements:
@@ -101,6 +111,17 @@ class DataflowAnalysis : public TypeErasedDataflowAnalysis {
101111
static_cast<Derived *>(this)->transfer(Element, L, Env);
102112
}
103113

114+
void transferBranchTypeErased(bool Branch, const Stmt *Stmt,
115+
TypeErasedLattice &E, Environment &Env) final {
116+
if constexpr (HasTransferBranchFor<Derived, LatticeT>::value) {
117+
Lattice &L = llvm::any_cast<Lattice &>(E.Value);
118+
static_cast<Derived *>(this)->transferBranch(Branch, Stmt, L, Env);
119+
}
120+
// Silence unused parameter warnings.
121+
(void)Branch;
122+
(void)Stmt;
123+
}
124+
104125
private:
105126
ASTContext &Context;
106127
};

clang/include/clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,14 @@ class TypeErasedDataflowAnalysis : public Environment::ValueModel {
8484
virtual void transferTypeErased(const CFGElement *, TypeErasedLattice &,
8585
Environment &) = 0;
8686

87+
/// Applies the analysis transfer function for a given edge from a CFG block
88+
/// of a conditional statement.
89+
/// @param Stmt The condition which is responsible for the split in the CFG.
90+
/// @param Branch True if the edge goes to the basic block where the
91+
/// condition is true.
92+
virtual void transferBranchTypeErased(bool Branch, const Stmt *,
93+
TypeErasedLattice &, Environment &) = 0;
94+
8795
/// If the built-in transfer functions (which model the heap and stack in the
8896
/// `Environment`) are to be applied, returns the options to be passed to
8997
/// them. Otherwise returns empty.

clang/include/clang/Basic/Attr.td

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4145,3 +4145,8 @@ def FunctionReturnThunks : InheritableAttr,
41454145
let Subjects = SubjectList<[Function]>;
41464146
let Documentation = [FunctionReturnThunksDocs];
41474147
}
4148+
def ReadOnlyPlacement : InheritableAttr {
4149+
let Spellings = [Clang<"enforce_read_only_placement">];
4150+
let Subjects = SubjectList<[Record]>;
4151+
let Documentation = [ReadOnlyPlacementDocs];
4152+
}

clang/include/clang/Basic/AttrDocs.td

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6910,3 +6910,42 @@ The symbol used for ``thunk-extern`` is target specific:
69106910
As such, this function attribute is currently only supported on X86 targets.
69116911
}];
69126912
}
6913+
6914+
def ReadOnlyPlacementDocs : Documentation {
6915+
let Category = DocCatType;
6916+
let Content = [{This attribute is attached to a structure, class or union declaration.
6917+
When attached to a record declaration/definition, it checks if all instances
6918+
of this type can be placed in the read-only data segment of the program. If it
6919+
finds an instance that can not be placed in a read-only segment, the compiler
6920+
emits a warning at the source location where the type was used.
6921+
6922+
Examples:
6923+
* ``struct __attribute__((enforce_read_only_placement)) Foo;``
6924+
* ``struct __attribute__((enforce_read_only_placement)) Bar { ... };``
6925+
6926+
Both ``Foo`` and ``Bar`` types have the ``enforce_read_only_placement`` attribute.
6927+
6928+
The goal of introducing this attribute is to assist developers with writing secure
6929+
code. A ``const``-qualified global is generally placed in the read-only section
6930+
of the memory that has additional run time protection from malicious writes. By
6931+
attaching this attribute to a declaration, the developer can express the intent
6932+
to place all instances of the annotated type in the read-only program memory.
6933+
6934+
Note 1: The attribute doesn't guarantee that the object will be placed in the
6935+
read-only data segment as it does not instruct the compiler to ensure such
6936+
a placement. It emits a warning if something in the code can be proven to prevent
6937+
an instance from being placed in the read-only data segment.
6938+
6939+
Note 2: Currently, clang only checks if all global declarations of a given type 'T'
6940+
are ``const``-qualified. The following conditions would also prevent the data to be
6941+
put into read only segment, but the corresponding warnings are not yet implemented.
6942+
6943+
1. An instance of type ``T`` is allocated on the heap/stack.
6944+
2. Type ``T`` defines/inherits a mutable field.
6945+
3. Type ``T`` defines/inherits non-constexpr constructor(s) for initialization.
6946+
4. A field of type ``T`` is defined by type ``Q``, which does not bear the
6947+
``enforce_read_only_placement`` attribute.
6948+
5. A type ``Q`` inherits from type ``T`` and it does not have the
6949+
``enforce_read_only_placement`` attribute.
6950+
}];
6951+
}

clang/include/clang/Basic/DiagnosticGroups.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1396,3 +1396,6 @@ def BranchProtection : DiagGroup<"branch-protection">;
13961396
// HLSL diagnostic groups
13971397
// Warnings for HLSL Clang extensions
13981398
def HLSLExtension : DiagGroup<"hlsl-extensions">;
1399+
1400+
// Warnings and notes related to const_var_decl_type attribute checks
1401+
def ReadOnlyPlacementChecks : DiagGroup<"read-only-types">;

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5744,6 +5744,12 @@ def err_new_abi_tag_on_redeclaration : Error<
57445744
def note_use_ifdef_guards : Note<
57455745
"unguarded header; consider using #ifdef guards or #pragma once">;
57465746

5747+
def warn_var_decl_not_read_only : Warning<
5748+
"object of type %0 cannot be placed in read-only memory">,
5749+
InGroup<ReadOnlyPlacementChecks>;
5750+
def note_enforce_read_only_placement : Note<"type was declared read-only here">;
5751+
5752+
57475753
def note_deleted_dtor_no_operator_delete : Note<
57485754
"virtual destructor requires an unambiguous, accessible 'operator delete'">;
57495755
def note_deleted_special_member_class_subobject : Note<

clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp

Lines changed: 40 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -69,54 +69,63 @@ static int blockIndexInPredecessor(const CFGBlock &Pred,
6969
return BlockPos - Pred.succ_begin();
7070
}
7171

72+
// The return type of the visit functions in TerminatorVisitor. The first
73+
// element represents the terminator expression (that is the conditional
74+
// expression in case of a path split in the CFG). The second element
75+
// represents whether the condition was true or false.
76+
using TerminatorVisitorRetTy = std::pair<const Expr *, bool>;
77+
7278
/// Extends the flow condition of an environment based on a terminator
7379
/// statement.
74-
class TerminatorVisitor : public ConstStmtVisitor<TerminatorVisitor> {
80+
class TerminatorVisitor
81+
: public ConstStmtVisitor<TerminatorVisitor, TerminatorVisitorRetTy> {
7582
public:
7683
TerminatorVisitor(const StmtToEnvMap &StmtToEnv, Environment &Env,
7784
int BlockSuccIdx, TransferOptions TransferOpts)
78-
: StmtToEnv(StmtToEnv), Env(Env), BlockSuccIdx(BlockSuccIdx),
79-
TransferOpts(TransferOpts) {}
85+
: StmtToEnv(StmtToEnv), Env(Env),
86+
BlockSuccIdx(BlockSuccIdx), TransferOpts(TransferOpts) {}
8087

81-
void VisitIfStmt(const IfStmt *S) {
88+
TerminatorVisitorRetTy VisitIfStmt(const IfStmt *S) {
8289
auto *Cond = S->getCond();
8390
assert(Cond != nullptr);
84-
extendFlowCondition(*Cond);
91+
return extendFlowCondition(*Cond);
8592
}
8693

87-
void VisitWhileStmt(const WhileStmt *S) {
94+
TerminatorVisitorRetTy VisitWhileStmt(const WhileStmt *S) {
8895
auto *Cond = S->getCond();
8996
assert(Cond != nullptr);
90-
extendFlowCondition(*Cond);
97+
return extendFlowCondition(*Cond);
9198
}
9299

93-
void VisitDoStmt(const DoStmt *S) {
100+
TerminatorVisitorRetTy VisitDoStmt(const DoStmt *S) {
94101
auto *Cond = S->getCond();
95102
assert(Cond != nullptr);
96-
extendFlowCondition(*Cond);
103+
return extendFlowCondition(*Cond);
97104
}
98105

99-
void VisitForStmt(const ForStmt *S) {
106+
TerminatorVisitorRetTy VisitForStmt(const ForStmt *S) {
100107
auto *Cond = S->getCond();
101108
if (Cond != nullptr)
102-
extendFlowCondition(*Cond);
109+
return extendFlowCondition(*Cond);
110+
return {nullptr, false};
103111
}
104112

105-
void VisitBinaryOperator(const BinaryOperator *S) {
113+
TerminatorVisitorRetTy VisitBinaryOperator(const BinaryOperator *S) {
106114
assert(S->getOpcode() == BO_LAnd || S->getOpcode() == BO_LOr);
107115
auto *LHS = S->getLHS();
108116
assert(LHS != nullptr);
109-
extendFlowCondition(*LHS);
117+
return extendFlowCondition(*LHS);
110118
}
111119

112-
void VisitConditionalOperator(const ConditionalOperator *S) {
120+
TerminatorVisitorRetTy
121+
VisitConditionalOperator(const ConditionalOperator *S) {
113122
auto *Cond = S->getCond();
114123
assert(Cond != nullptr);
115-
extendFlowCondition(*Cond);
124+
return extendFlowCondition(*Cond);
116125
}
117126

118127
private:
119-
void extendFlowCondition(const Expr &Cond) {
128+
TerminatorVisitorRetTy extendFlowCondition(const Expr &Cond) {
120129
// The terminator sub-expression might not be evaluated.
121130
if (Env.getStorageLocation(Cond, SkipPast::None) == nullptr)
122131
transfer(StmtToEnv, Cond, Env, TransferOpts);
@@ -140,12 +149,16 @@ class TerminatorVisitor : public ConstStmtVisitor<TerminatorVisitor> {
140149
Env.setValue(*Loc, *Val);
141150
}
142151

152+
bool ConditionValue = true;
143153
// The condition must be inverted for the successor that encompasses the
144154
// "else" branch, if such exists.
145-
if (BlockSuccIdx == 1)
155+
if (BlockSuccIdx == 1) {
146156
Val = &Env.makeNot(*Val);
157+
ConditionValue = false;
158+
}
147159

148160
Env.addToFlowCondition(*Val);
161+
return {&Cond, ConditionValue};
149162
}
150163

151164
const StmtToEnvMap &StmtToEnv;
@@ -239,10 +252,16 @@ computeBlockInputState(const CFGBlock &Block, AnalysisContext &AC) {
239252
if (BuiltinTransferOpts) {
240253
if (const Stmt *PredTerminatorStmt = Pred->getTerminatorStmt()) {
241254
const StmtToEnvMapImpl StmtToEnv(AC.CFCtx, AC.BlockStates);
242-
TerminatorVisitor(StmtToEnv, PredState.Env,
243-
blockIndexInPredecessor(*Pred, Block),
244-
*BuiltinTransferOpts)
245-
.Visit(PredTerminatorStmt);
255+
auto [Cond, CondValue] =
256+
TerminatorVisitor(StmtToEnv, PredState.Env,
257+
blockIndexInPredecessor(*Pred, Block),
258+
*BuiltinTransferOpts)
259+
.Visit(PredTerminatorStmt);
260+
if (Cond != nullptr)
261+
// FIXME: Call transferBranchTypeErased even if BuiltinTransferOpts
262+
// are not set.
263+
Analysis.transferBranchTypeErased(CondValue, Cond, PredState.Lattice,
264+
PredState.Env);
246265
}
247266
}
248267

clang/lib/Sema/SemaDecl.cpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7380,6 +7380,36 @@ static void copyAttrFromTypedefToDecl(Sema &S, Decl *D, const TypedefType *TT) {
73807380
}
73817381
}
73827382

7383+
// This function emits warning and a corresponding note based on the
7384+
// ReadOnlyPlacementAttr attribute. The warning checks that all global variable
7385+
// declarations of an annotated type must be const qualified.
7386+
void emitReadOnlyPlacementAttrWarning(Sema &S, const VarDecl *VD) {
7387+
QualType VarType = VD->getType().getCanonicalType();
7388+
7389+
// Ignore local declarations (for now) and those with const qualification.
7390+
// TODO: Local variables should not be allowed if their type declaration has
7391+
// ReadOnlyPlacementAttr attribute. To be handled in follow-up patch.
7392+
if (!VD || VD->hasLocalStorage() || VD->getType().isConstQualified())
7393+
return;
7394+
7395+
if (VarType->isArrayType()) {
7396+
// Retrieve element type for array declarations.
7397+
VarType = S.getASTContext().getBaseElementType(VarType);
7398+
}
7399+
7400+
const RecordDecl *RD = VarType->getAsRecordDecl();
7401+
7402+
// Check if the record declaration is present and if it has any attributes.
7403+
if (RD == nullptr)
7404+
return;
7405+
7406+
if (const auto *ConstDecl = RD->getAttr<ReadOnlyPlacementAttr>()) {
7407+
S.Diag(VD->getLocation(), diag::warn_var_decl_not_read_only) << RD;
7408+
S.Diag(ConstDecl->getLocation(), diag::note_enforce_read_only_placement);
7409+
return;
7410+
}
7411+
}
7412+
73837413
NamedDecl *Sema::ActOnVariableDeclarator(
73847414
Scope *S, Declarator &D, DeclContext *DC, TypeSourceInfo *TInfo,
73857415
LookupResult &Previous, MultiTemplateParamsArg TemplateParamLists,
@@ -8044,6 +8074,8 @@ NamedDecl *Sema::ActOnVariableDeclarator(
80448074
if (IsMemberSpecialization && !NewVD->isInvalidDecl())
80458075
CompleteMemberSpecialization(NewVD, Previous);
80468076

8077+
emitReadOnlyPlacementAttrWarning(*this, NewVD);
8078+
80478079
return NewVD;
80488080
}
80498081

0 commit comments

Comments
 (0)