s += " #>";
return s;
}
-
- /// Returns a reference to the `Preprocessor`:
- virtual const Preprocessor & getPP() const;
};
// This function invokes the analysis and allows the caller to react to it
}
-def err_pp_double_begin_pragma_unsafe_buffer_usage :
-Error<"already inside '#pragma unsafe_buffer_usage'">;
-
-def err_pp_unmatched_end_begin_pragma_unsafe_buffer_usage :
-Error<"not currently inside '#pragma unsafe_buffer_usage'">;
-
-def err_pp_unclosed_pragma_unsafe_buffer_usage :
-Error<"'#pragma unsafe_buffer_usage' was not ended">;
-
-def err_pp_pragma_unsafe_buffer_usage_syntax :
-Error<"Expected 'begin' or 'end'">;
}
void emitMacroDeprecationWarning(const Token &Identifier) const;
void emitRestrictExpansionWarning(const Token &Identifier) const;
void emitFinalMacroWarning(const Token &Identifier, bool IsUndef) const;
-
- /// This boolean state keeps track if the current scanned token (by this PP)
- /// is in an "-Wunsafe-buffer-usage" opt-out region. Assuming PP scans a
- /// translation unit in a linear order.
- bool InSafeBufferOptOutRegion = 0;
-
- /// Hold the start location of the current "-Wunsafe-buffer-usage" opt-out
- /// region if PP is currently in such a region. Hold undefined value
- /// otherwise.
- SourceLocation CurrentSafeBufferOptOutStart; // It is used to report the start location of an never-closed region.
-
- // An ordered sequence of "-Wunsafe-buffer-usage" opt-out regions in one
- // translation unit. Each region is represented by a pair of start and end
- // locations. A region is "open" if its' start and end locations are
- // identical.
- SmallVector<std::pair<SourceLocation, SourceLocation>, 8> SafeBufferOptOutMap;
-
-public:
- /// \return true iff the given `Loc` is in a "-Wunsafe-buffer-usage" opt-out
- /// region. This `Loc` must be a source location that has been pre-processed.
- bool isSafeBufferOptOut(const SourceManager&SourceMgr, const SourceLocation &Loc) const;
-
- /// Alter the state of whether this PP currently is in a
- /// "-Wunsafe-buffer-usage" opt-out region.
- ///
- /// \param isEnter: true if this PP is entering a region; otherwise, this PP
- /// is exiting a region
- /// \param Loc: the location of the entry or exit of a
- /// region
- /// \return true iff it is INVALID to enter or exit a region, i.e.,
- /// attempt to enter a region before exiting a previous region, or exiting a
- /// region that PP is not currently in.
- bool enterOrExitSafeBufferOptOutRegion(bool isEnter,
- const SourceLocation &Loc);
-
- /// \return true iff this PP is currently in a "-Wunsafe-buffer-usage"
- /// opt-out region
- bool isPPInSafeBufferOptOutRegion();
-
- /// \param StartLoc: output argument. It will be set to the start location of
- /// the current "-Wunsafe-buffer-usage" opt-out region iff this function
- /// returns true.
- /// \return true iff this PP is currently in a "-Wunsafe-buffer-usage"
- /// opt-out region
- bool isPPInSafeBufferOptOutRegion(SourceLocation &StartLoc);
};
/// Abstract base class that describes a handler that will receive
#include "clang/Analysis/Analyses/UnsafeBufferUsage.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
-#include "clang/Lex/Preprocessor.h"
#include "clang/Lex/Lexer.h"
#include "llvm/ADT/SmallVector.h"
#include <memory>
return Visitor.findMatch(DynTypedNode::create(Node));
}
-// Matches a `Stmt` node iff the node is in a safe-buffer opt-out region
-AST_MATCHER_P(Stmt, notInSafeBufferOptOut, const Preprocessor *, PP) {
- const SourceManager &SM = Finder->getASTContext().getSourceManager();
- return !PP->isSafeBufferOptOut(SM, Node.getBeginLoc());
-}
-
AST_MATCHER_P(CastExpr, castSubExpr, internal::Matcher<Expr>, innerMatcher) {
return innerMatcher.matches(*Node.getSubExpr(), Finder, Builder);
}
} // namespace
/// Scan the function and return a list of gadgets found with provided kits.
-static std::tuple<FixableGadgetList, WarningGadgetList, DeclUseTracker>
-findGadgets(const Decl *D, const Preprocessor &PP) {
+static std::tuple<FixableGadgetList, WarningGadgetList, DeclUseTracker> findGadgets(const Decl *D) {
struct GadgetFinderCallback : MatchFinder::MatchCallback {
FixableGadgetList FixableGadgets;
stmt(anyOf(
// Add Gadget::matcher() for every gadget in the registry.
#define WARNING_GADGET(x) \
- allOf(x ## Gadget::matcher().bind(#x), notInSafeBufferOptOut(&PP)),
+ x ## Gadget::matcher().bind(#x),
#include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
// In parallel, match all DeclRefExprs so that to find out
// whether there are any uncovered by gadgets.
DeclUseTracker Tracker;
{
- auto [FixableGadgets, WarningGadgets, TrackerRes] = findGadgets(D, Handler.getPP());
+ auto [FixableGadgets, WarningGadgets, TrackerRes] = findGadgets(D);
UnsafeOps = groupWarningGadgetsByVar(std::move(WarningGadgets));
FixablesForUnsafeVars = groupFixablesByVar(std::move(FixableGadgets));
Tracker = std::move(TrackerRes);
assert(!CurTokenLexer &&
"Ending a file when currently in a macro!");
- SourceLocation UnclosedSafeBufferOptOutLoc;
-
- if (IncludeMacroStack.empty() &&
- isPPInSafeBufferOptOutRegion(UnclosedSafeBufferOptOutLoc)) {
- // To warn if a "-Wunsafe-buffer-usage" opt-out region is still open by the
- // end of a file.
- Diag(UnclosedSafeBufferOptOutLoc,
- diag::err_pp_unclosed_pragma_unsafe_buffer_usage);
- }
// If we have an unclosed module region from a pragma at the end of a
// module, complain and close it now.
const bool LeavingSubmodule = CurLexer && CurLexerSubmodule;
#endif
};
-struct PragmaUnsafeBufferUsageHandler : public PragmaHandler {
- PragmaUnsafeBufferUsageHandler() : PragmaHandler("unsafe_buffer_usage") {}
- void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer,
- Token &FirstToken) override {
- Token Tok;
-
- PP.LexUnexpandedToken(Tok);
- if (Tok.isNot(tok::identifier)) {
- PP.Diag(Tok, diag::err_pp_pragma_unsafe_buffer_usage_syntax);
- return;
- }
-
- IdentifierInfo *II = Tok.getIdentifierInfo();
- SourceLocation Loc = Tok.getLocation();
-
- if (II->isStr("begin")) {
- if (PP.enterOrExitSafeBufferOptOutRegion(true, Loc))
- PP.Diag(Loc, diag::err_pp_double_begin_pragma_unsafe_buffer_usage);
- } else if (II->isStr("end")) {
- if (PP.enterOrExitSafeBufferOptOutRegion(false, Loc))
- PP.Diag(Loc, diag::err_pp_unmatched_end_begin_pragma_unsafe_buffer_usage);
- } else
- PP.Diag(Tok, diag::err_pp_pragma_unsafe_buffer_usage_syntax);
- }
-};
-
/// PragmaDiagnosticHandler - e.g. '\#pragma GCC diagnostic ignored "-Wformat"'
struct PragmaDiagnosticHandler : public PragmaHandler {
private:
ModuleHandler->AddPragma(new PragmaModuleBuildHandler());
ModuleHandler->AddPragma(new PragmaModuleLoadHandler());
- // Safe Buffers pragmas
- AddPragmaHandler("clang", new PragmaUnsafeBufferUsageHandler);
-
// Add region pragmas.
AddPragmaHandler(new PragmaRegionHandler("region"));
AddPragmaHandler(new PragmaRegionHandler("endregion"));
Diag(*A.FinalAnnotationLoc, diag::note_pp_macro_annotation) << 2;
}
-bool Preprocessor::isSafeBufferOptOut(const SourceManager &SourceMgr,
- const SourceLocation &Loc) const {
- // Try to find a region in `SafeBufferOptOutMap` where `Loc` is in:
- auto FirstRegionEndingAfterLoc = llvm::partition_point(
- SafeBufferOptOutMap,
- [&SourceMgr,
- &Loc](const std::pair<SourceLocation, SourceLocation> &Region) {
- return SourceMgr.isBeforeInTranslationUnit(Region.second, Loc);
- });
-
- if (FirstRegionEndingAfterLoc != SafeBufferOptOutMap.end()) {
- // To test if the start location of the found region precedes `Loc`:
- return SourceMgr.isBeforeInTranslationUnit(FirstRegionEndingAfterLoc->first,
- Loc);
- }
- // If we do not find a region whose end location passes `Loc`, we want to
- // check if the current region is still open:
- if (!SafeBufferOptOutMap.empty() &&
- SafeBufferOptOutMap.back().first == SafeBufferOptOutMap.back().second)
- return SourceMgr.isBeforeInTranslationUnit(SafeBufferOptOutMap.back().first,
- Loc);
- return false;
-}
-
-bool Preprocessor::enterOrExitSafeBufferOptOutRegion(
- bool isEnter, const SourceLocation &Loc) {
- if (isEnter) {
- if (isPPInSafeBufferOptOutRegion())
- return true; // invalid enter action
- InSafeBufferOptOutRegion = true;
- CurrentSafeBufferOptOutStart = Loc;
-
- // To set the start location of a new region:
-
- if (!SafeBufferOptOutMap.empty()) {
- auto *PrevRegion = &SafeBufferOptOutMap.back();
- assert(PrevRegion->first != PrevRegion->second &&
- "Shall not begin a safe buffer opt-out region before closing the "
- "previous one.");
- }
- // If the start location equals to the end location, we call the region a
- // open region or a unclosed region (i.e., end location has not been set
- // yet).
- SafeBufferOptOutMap.emplace_back(Loc, Loc);
- } else {
- if (!isPPInSafeBufferOptOutRegion())
- return true; // invalid enter action
- InSafeBufferOptOutRegion = false;
-
- // To set the end location of the current open region:
-
- assert(!SafeBufferOptOutMap.empty() &&
- "Misordered safe buffer opt-out regions");
- auto *CurrRegion = &SafeBufferOptOutMap.back();
- assert(CurrRegion->first == CurrRegion->second &&
- "Set end location to a closed safe buffer opt-out region");
- CurrRegion->second = Loc;
- }
- return false;
-}
-
-bool Preprocessor::isPPInSafeBufferOptOutRegion() {
- return InSafeBufferOptOutRegion;
-}
-bool Preprocessor::isPPInSafeBufferOptOutRegion(SourceLocation &StartLoc) {
- StartLoc = CurrentSafeBufferOptOutStart;
- return InSafeBufferOptOutRegion;
-}
-
ModuleLoader::~ModuleLoader() = default;
CommentHandler::~CommentHandler() = default;
FD << F;
}
}
-
- const clang::Preprocessor & getPP() const override {
- return S.getPreprocessor();
- }
};
} // namespace
+++ /dev/null
-// RUN: %clang_cc1 -std=c++20 -Wunsafe-buffer-usage -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
-
-void basic(int * x) {
- int tmp;
- int *p1 = new int[10]; // no fix
- // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
- int *p2 = new int[10];
- // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:12}:"std::span<int> p2"
- // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-2]]:13-[[@LINE-2]]:13}:"{"
- // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-3]]:24-[[@LINE-3]]:24}:", 10}"
-#pragma clang unsafe_buffer_usage begin
- tmp = p1[5];
-#pragma clang unsafe_buffer_usage end
- tmp = p2[5];
-}
-
-void withDiagnosticWarning() {
- int tmp;
- int *p1 = new int[10]; // no fix
- // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
- int *p2 = new int[10];
- // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:12}:"std::span<int> p2"
- // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-2]]:13-[[@LINE-2]]:13}:"{"
- // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-3]]:24-[[@LINE-3]]:24}:", 10}"
-
- // diagnostics in opt-out region
-#pragma clang unsafe_buffer_usage begin
- tmp = p1[5]; // not to warn
- tmp = p2[5]; // not to warn
-#pragma clang diagnostic push
-#pragma clang diagnostic warning "-Wunsafe-buffer-usage"
- tmp = p1[5]; // not to warn
- tmp = p2[5]; // not to warn
-#pragma clang diagnostic warning "-Weverything"
- tmp = p1[5]; // not to warn
- tmp = p2[5]; // not to warn
-#pragma clang diagnostic pop
-#pragma clang unsafe_buffer_usage end
-
- // opt-out region under diagnostic warning
-#pragma clang diagnostic push
-#pragma clang diagnostic warning "-Wunsafe-buffer-usage"
-#pragma clang unsafe_buffer_usage begin
- tmp = p1[5]; // not to warn
- tmp = p2[5]; // not to warn
-#pragma clang unsafe_buffer_usage end
-#pragma clang diagnostic pop
-
- tmp = p2[5];
-}
-
-
-void withDiagnosticIgnore() {
- int tmp;
- int *p1 = new int[10];
- // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
- int *p2 = new int[10];
- // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:12}:"std::span<int> p2"
- // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-2]]:13-[[@LINE-2]]:13}:"{"
- // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-3]]:24-[[@LINE-3]]:24}:", 10}"
- int *p3 = new int[10];
- // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:12}:"std::span<int> p3"
- // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-2]]:13-[[@LINE-2]]:13}:"{"
- // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-3]]:24-[[@LINE-3]]:24}:", 10}"
-
-#pragma clang unsafe_buffer_usage begin
- tmp = p1[5]; // not to warn
- tmp = p2[5]; // not to warn
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
- tmp = p1[5]; // not to warn
- tmp = p2[5]; // not to warn
-#pragma clang diagnostic ignored "-Weverything"
- tmp = p1[5]; // not to warn
- tmp = p2[5]; // not to warn
-#pragma clang diagnostic pop
-#pragma clang unsafe_buffer_usage end
-
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
-#pragma clang unsafe_buffer_usage begin
- tmp = p1[5]; // not to warn
- tmp = p2[5]; // not to warn
-#pragma clang unsafe_buffer_usage end
-#pragma clang diagnostic pop
-
- tmp = p2[5];
-
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
-#pragma clang unsafe_buffer_usage begin
- tmp = p1[5]; // not to warn
- tmp = p2[5]; // not to warn
-#pragma clang unsafe_buffer_usage end
- tmp = p3[5]; // expected-note{{used in buffer access here}}
-#pragma clang diagnostic pop
-}
-
-void noteGoesWithVarDeclWarning() {
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
- int *p = new int[10]; // not to warn
- // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
-#pragma clang diagnostic pop
-
- p[5]; // not to note since the associated warning is suppressed
-}
+++ /dev/null
-// RUN: %clang_cc1 -std=c++20 -Wunsafe-buffer-usage -verify %s
-
-void beginUnclosed(int * x) {
-#pragma clang unsafe_buffer_usage begin
-
-#pragma clang unsafe_buffer_usage begin // expected-error{{already inside '#pragma unsafe_buffer_usage'}}
- x++;
-#pragma clang unsafe_buffer_usage end
-}
-
-void endUnopened(int *x) {
-#pragma clang unsafe_buffer_usage end // expected-error{{not currently inside '#pragma unsafe_buffer_usage'}}
-
-#pragma clang unsafe_buffer_usage begin
- x++;
-#pragma clang unsafe_buffer_usage end
-}
-
-void wrongOption() {
-#pragma clang unsafe_buffer_usage start // expected-error{{Expected 'begin' or 'end'}}
-#pragma clang unsafe_buffer_usage close // expected-error{{Expected 'begin' or 'end'}}
-}
-
-void unclosed(int * p1) {
-#pragma clang unsafe_buffer_usage begin
-// End of the included file will not raise the unclosed region warning:
-#define _INCLUDE_NO_WARN
-#include "warn-unsafe-buffer-usage-pragma.h"
-#pragma clang unsafe_buffer_usage end
-
-// End of this file raises the warning:
-#pragma clang unsafe_buffer_usage begin // expected-error{{'#pragma unsafe_buffer_usage' was not ended}}
-}
+++ /dev/null
-// RUN: %clang_cc1 -std=c++20 -Wunsafe-buffer-usage -Wno-unused-value -verify %s
-
-void basic(int * x) { // expected-warning{{'x' is an unsafe pointer used for buffer access}}
- int *p1 = new int[10]; // not to warn
- int *p2 = new int[10]; // expected-warning{{'p2' is an unsafe pointer used for buffer access}}
-
-#pragma clang unsafe_buffer_usage begin
- p1[5]; // not to warn
-
-#define _INCLUDE_NO_WARN
-#include "warn-unsafe-buffer-usage-pragma.h" // increment p1 in header
-
- int *p3 = new int[10]; // expected-warning{{'p3' is an unsafe pointer used for buffer access}}
-
-#pragma clang unsafe_buffer_usage end
- p2[5]; //expected-note{{used in buffer access here}}
- p3[5]; //expected-note{{used in buffer access here}}
- x++; //expected-note{{used in pointer arithmetic here}}
-#define _INCLUDE_WARN
-#include "warn-unsafe-buffer-usage-pragma.h" // increment p2 in header
-}
-
-
-void withDiagnosticWarning() {
- int *p1 = new int[10]; // not to warn
- int *p2 = new int[10]; // expected-warning{{'p2' is an unsafe pointer used for buffer access}}
-
- // diagnostics in opt-out region
-#pragma clang unsafe_buffer_usage begin
- p1[5]; // not to warn
- p2[5]; // not to warn
-#pragma clang diagnostic push
-#pragma clang diagnostic warning "-Wunsafe-buffer-usage"
- p1[5]; // not to warn
- p2[5]; // not to warn
-#pragma clang diagnostic warning "-Weverything"
- p1[5]; // not to warn expected-warning{{expression result unused}}
- p2[5]; // not to warn expected-warning{{expression result unused}}
-#pragma clang diagnostic pop
-#pragma clang unsafe_buffer_usage end
-
- // opt-out region under diagnostic warning
-#pragma clang diagnostic push
-#pragma clang diagnostic warning "-Wunsafe-buffer-usage"
-#pragma clang unsafe_buffer_usage begin
- p1[5]; // not to warn
- p2[5]; // not to warn
-#pragma clang unsafe_buffer_usage end
-#pragma clang diagnostic pop
-
- p2[5]; // expected-note{{used in buffer access here}}
-}
-
-
-void withDiagnosticIgnore() {
- int *p1 = new int[10]; // not to warn
- int *p2 = new int[10]; // expected-warning{{'p2' is an unsafe pointer used for buffer access}}
- int *p3 = new int[10]; // expected-warning{{'p3' is an unsafe pointer used for buffer access}}
-
-#pragma clang unsafe_buffer_usage begin
- p1[5]; // not to warn
- p2[5]; // not to warn
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
- p1[5]; // not to warn
- p2[5]; // not to warn
-#pragma clang diagnostic ignored "-Weverything"
- p1[5]; // not to warn
- p2[5]; // not to warn
-#pragma clang diagnostic pop
-#pragma clang unsafe_buffer_usage end
-
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
-#pragma clang unsafe_buffer_usage begin
- p1[5]; // not to warn
- p2[5]; // not to warn
-#pragma clang unsafe_buffer_usage end
-#pragma clang diagnostic pop
-
- p2[5]; // expected-note{{used in buffer access here}}
-
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
-#pragma clang unsafe_buffer_usage begin
- p1[5]; // not to warn
- p2[5]; // not to warn
-#pragma clang unsafe_buffer_usage end
- p3[5]; // expected-note{{used in buffer access here}}
-#pragma clang diagnostic pop
-}
-
-void noteGoesWithVarDeclWarning() {
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
- int *p = new int[10]; // not to warn
-#pragma clang diagnostic pop
-
- p[5]; // not to note since the associated warning is suppressed
-}
+++ /dev/null
-#ifdef _INCLUDE_NO_WARN
-// the snippet will be included in an opt-out region
-p1++;
-
-#undef _INCLUDE_NO_WARN
-
-#elif defined(_INCLUDE_WARN)
-// the snippet will be included in a location where warnings are expected
-p2++; // expected-note{{used in pointer arithmetic here}}
-#undef _INCLUDE_WARN
-
-#else
-
-#endif