[clang] 2345796 - Revert "[-Wunsafe-buffer-usage] Warning Libc functions (#101583)"
via cfe-commits
cfe-commits at lists.llvm.org
Wed Sep 4 16:27:58 PDT 2024
Author: ziqingluo-90
Date: 2024-09-04T16:26:44-07:00
New Revision: 23457964392d00fc872fa6021763859024fb38da
URL: https://github.com/llvm/llvm-project/commit/23457964392d00fc872fa6021763859024fb38da
DIFF: https://github.com/llvm/llvm-project/commit/23457964392d00fc872fa6021763859024fb38da.diff
LOG: Revert "[-Wunsafe-buffer-usage] Warning Libc functions (#101583)"
This reverts commit 0fffdeb5f46078ddcc61e112cd38856b1165f050.
Will re-land this commit soon with a way to opt-out
Added:
Modified:
clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/lib/Analysis/UnsafeBufferUsage.cpp
clang/lib/Sema/AnalysisBasedWarnings.cpp
clang/test/SemaCXX/warn-unsafe-buffer-usage-test-unreachable.cpp
Removed:
clang/test/SemaCXX/warn-unsafe-buffer-usage-libc-functions-inline-namespace.cpp
clang/test/SemaCXX/warn-unsafe-buffer-usage-libc-functions.cpp
################################################################################
diff --git a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
index aa2c01ad10d45d..228b4ae1e3e115 100644
--- a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
+++ b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
@@ -15,7 +15,6 @@
#define LLVM_CLANG_ANALYSIS_ANALYSES_UNSAFEBUFFERUSAGE_H
#include "clang/AST/Decl.h"
-#include "clang/AST/Expr.h"
#include "clang/AST/Stmt.h"
#include "clang/Basic/SourceLocation.h"
#include "llvm/Support/Debug.h"
@@ -107,20 +106,6 @@ class UnsafeBufferUsageHandler {
virtual void handleUnsafeOperation(const Stmt *Operation,
bool IsRelatedToDecl, ASTContext &Ctx) = 0;
- /// Invoked when a call to an unsafe libc function is found.
- /// \param PrintfInfo
- /// is 0 if the callee function is not a member of the printf family;
- /// is 1 if the callee is `sprintf`;
- /// is 2 if arguments of the call have `__size_by` relation but are not in a
- /// safe pattern;
- /// is 3 if string arguments do not guarantee null-termination
- /// is 4 if the callee takes va_list
- /// \param UnsafeArg one of the actual arguments that is unsafe, non-null
- /// only when `2 <= PrintfInfo <= 3`
- virtual void handleUnsafeLibcCall(const CallExpr *Call, unsigned PrintfInfo,
- ASTContext &Ctx,
- const Expr *UnsafeArg = nullptr) = 0;
-
/// Invoked when an unsafe operation with a std container is found.
virtual void handleUnsafeOperationInContainer(const Stmt *Operation,
bool IsRelatedToDecl,
diff --git a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
index ac01b285ae833b..242ad763ba62b9 100644
--- a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
+++ b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
@@ -38,7 +38,6 @@ WARNING_GADGET(PointerArithmetic)
WARNING_GADGET(UnsafeBufferUsageAttr)
WARNING_GADGET(UnsafeBufferUsageCtorAttr)
WARNING_GADGET(DataInvocation)
-WARNING_GADGET(UnsafeLibcFunctionCall)
WARNING_CONTAINER_GADGET(SpanTwoParamConstructor) // Uses of `std::span(arg0, arg1)`
FIXABLE_GADGET(ULCArraySubscript) // `DRE[any]` in an Unspecified Lvalue Context
FIXABLE_GADGET(DerefSimplePtrArithFixable)
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 35f68f51dfb356..dcb49d8a67604a 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -12412,13 +12412,6 @@ def warn_unsafe_buffer_operation : Warning<
"unsafe buffer access|function introduces unsafe buffer manipulation|unsafe invocation of span::data|"
"field %1 prone to unsafe buffer manipulation}0">,
InGroup<UnsafeBufferUsage>, DefaultIgnore;
-def warn_unsafe_buffer_libc_call : Warning<
- "function %0 is unsafe">,
- InGroup<UnsafeBufferUsage>, DefaultIgnore;
-def note_unsafe_buffer_printf_call : Note<
- "%select{|change to 'snprintf' for explicit bounds checking | buffer pointer and size may not match"
- "|string argument is not guaranteed to be null-terminated"
- "|'va_list' is unsafe}0">;
def note_unsafe_buffer_operation : Note<
"used%select{| in pointer arithmetic| in buffer access}0 here">;
def note_unsafe_buffer_variable_fixit_group : Note<
diff --git a/clang/lib/Analysis/UnsafeBufferUsage.cpp b/clang/lib/Analysis/UnsafeBufferUsage.cpp
index f0d072643f8ff0..da7446913f7c87 100644
--- a/clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -10,12 +10,12 @@
#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
#include "clang/AST/Expr.h"
-#include "clang/AST/FormatString.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/AST/Stmt.h"
#include "clang/AST/StmtVisitor.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Basic/CharInfo.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Lex/Lexer.h"
#include "clang/Lex/Preprocessor.h"
@@ -443,426 +443,6 @@ AST_MATCHER(ArraySubscriptExpr, isSafeArraySubscript) {
return false;
}
-AST_MATCHER_P(CallExpr, hasNumArgs, unsigned, Num) {
- return Node.getNumArgs() == Num;
-}
-
-namespace libc_func_matchers {
-// Under `libc_func_matchers`, define a set of matchers that match unsafe
-// functions in libc and unsafe calls to them.
-
-// A tiny parser to strip off common prefix and suffix of libc function names
-// in real code.
-//
-// Given a function name, `matchName` returns `CoreName` according to the
-// following grammar:
-//
-// LibcName := CoreName | CoreName + "_s"
-// MatchingName := "__builtin_" + LibcName |
-// "__builtin___" + LibcName + "_chk" |
-// "__asan_" + LibcName
-//
-struct LibcFunNamePrefixSuffixParser {
- StringRef matchName(StringRef FunName, bool isBuiltin) {
- // Try to match __builtin_:
- if (isBuiltin && FunName.starts_with("__builtin_"))
- // Then either it is __builtin_LibcName or __builtin___LibcName_chk or
- // no match:
- return matchLibcNameOrBuiltinChk(
- FunName.drop_front(10 /* truncate "__builtin_" */));
- // Try to match __asan_:
- if (FunName.starts_with("__asan_"))
- return matchLibcName(FunName.drop_front(7 /* truncate of "__asan_" */));
- return matchLibcName(FunName);
- }
-
- // Parameter `Name` is the substring after stripping off the prefix
- // "__builtin_".
- StringRef matchLibcNameOrBuiltinChk(StringRef Name) {
- if (Name.starts_with("__") && Name.ends_with("_chk"))
- return matchLibcName(
- Name.drop_front(2).drop_back(4) /* truncate "__" and "_chk" */);
- return matchLibcName(Name);
- }
-
- StringRef matchLibcName(StringRef Name) {
- if (Name.ends_with("_s"))
- return Name.drop_back(2 /* truncate "_s" */);
- return Name;
- }
-};
-
-// A pointer type expression is known to be null-terminated, if it has the
-// form: E.c_str(), for any expression E of `std::string` type.
-static bool isNullTermPointer(const Expr *Ptr) {
- if (isa<StringLiteral>(Ptr->IgnoreParenImpCasts()))
- return true;
- if (isa<PredefinedExpr>(Ptr->IgnoreParenImpCasts()))
- return true;
- if (auto *MCE = dyn_cast<CXXMemberCallExpr>(Ptr->IgnoreParenImpCasts())) {
- const CXXMethodDecl *MD = MCE->getMethodDecl();
- const CXXRecordDecl *RD = MCE->getRecordDecl()->getCanonicalDecl();
-
- if (MD && RD && RD->isInStdNamespace())
- if (MD->getName() == "c_str" && RD->getName() == "basic_string")
- return true;
- }
- return false;
-}
-
-// Return true iff at least one of following cases holds:
-// 1. Format string is a literal and there is an unsafe pointer argument
-// corresponding to an `s` specifier;
-// 2. Format string is not a literal and there is least an unsafe pointer
-// argument (including the formatter argument).
-//
-// `UnsafeArg` is the output argument that will be set only if this function
-// returns true.
-static bool hasUnsafeFormatOrSArg(const CallExpr *Call, const Expr *&UnsafeArg,
- const unsigned FmtArgIdx, ASTContext &Ctx,
- bool isKprintf = false) {
- class StringFormatStringHandler
- : public analyze_format_string::FormatStringHandler {
- const CallExpr *Call;
- unsigned FmtArgIdx;
- const Expr *&UnsafeArg;
-
- public:
- StringFormatStringHandler(const CallExpr *Call, unsigned FmtArgIdx,
- const Expr *&UnsafeArg)
- : Call(Call), FmtArgIdx(FmtArgIdx), UnsafeArg(UnsafeArg) {}
-
- bool HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier &FS,
- const char *startSpecifier,
- unsigned specifierLen,
- const TargetInfo &Target) override {
- if (FS.getConversionSpecifier().getKind() ==
- analyze_printf::PrintfConversionSpecifier::sArg) {
- unsigned ArgIdx = FS.getPositionalArgIndex() + FmtArgIdx;
-
- if (0 < ArgIdx && ArgIdx < Call->getNumArgs())
- if (!isNullTermPointer(Call->getArg(ArgIdx))) {
- UnsafeArg = Call->getArg(ArgIdx); // output
- // returning false stops parsing immediately
- return false;
- }
- }
- return true; // continue parsing
- }
- };
-
- const Expr *Fmt = Call->getArg(FmtArgIdx);
-
- if (auto *SL = dyn_cast<StringLiteral>(Fmt->IgnoreParenImpCasts())) {
- StringRef FmtStr = SL->getString();
- StringFormatStringHandler Handler(Call, FmtArgIdx, UnsafeArg);
-
- return analyze_format_string::ParsePrintfString(
- Handler, FmtStr.begin(), FmtStr.end(), Ctx.getLangOpts(),
- Ctx.getTargetInfo(), isKprintf);
- }
- // If format is not a string literal, we cannot analyze the format string.
- // In this case, this call is considered unsafe if at least one argument
- // (including the format argument) is unsafe pointer.
- return llvm::any_of(
- llvm::make_range(Call->arg_begin() + FmtArgIdx, Call->arg_end()),
- [&UnsafeArg](const Expr *Arg) -> bool {
- if (Arg->getType()->isPointerType() && !isNullTermPointer(Arg)) {
- UnsafeArg = Arg;
- return true;
- }
- return false;
- });
-}
-
-// Matches a FunctionDecl node such that
-// 1. It's name, after stripping off predefined prefix and suffix, is
-// `CoreName`; and
-// 2. `CoreName` or `CoreName[str/wcs]` is one of the `PredefinedNames`, which
-// is a set of libc function names.
-//
-// Note: For predefined prefix and suffix, see `LibcFunNamePrefixSuffixParser`.
-// The notation `CoreName[str/wcs]` means a new name obtained from replace
-// string "wcs" with "str" in `CoreName`.
-AST_MATCHER(FunctionDecl, isPredefinedUnsafeLibcFunc) {
- static std::unique_ptr<std::set<StringRef>> PredefinedNames = nullptr;
- if (!PredefinedNames)
- PredefinedNames =
- std::make_unique<std::set<StringRef>, std::set<StringRef>>({
- // numeric conversion:
- "atof",
- "atoi",
- "atol",
- "atoll",
- "strtol",
- "strtoll",
- "strtoul",
- "strtoull",
- "strtof",
- "strtod",
- "strtold",
- "strtoimax",
- "strtoumax",
- // "strfromf", "strfromd", "strfroml", // C23?
- // string manipulation:
- "strcpy",
- "strncpy",
- "strlcpy",
- "strcat",
- "strncat",
- "strlcat",
- "strxfrm",
- "strdup",
- "strndup",
- // string examination:
- "strlen",
- "strnlen",
- "strcmp",
- "strncmp",
- "stricmp",
- "strcasecmp",
- "strcoll",
- "strchr",
- "strrchr",
- "strspn",
- "strcspn",
- "strpbrk",
- "strstr",
- "strtok",
- // "mem-" functions
- "memchr",
- "wmemchr",
- "memcmp",
- "wmemcmp",
- "memcpy",
- "memccpy",
- "mempcpy",
- "wmemcpy",
- "memmove",
- "wmemmove",
- "memset",
- "wmemset",
- // IO:
- "fread",
- "fwrite",
- "fgets",
- "fgetws",
- "gets",
- "fputs",
- "fputws",
- "puts",
- // others
- "strerror_s",
- "strerror_r",
- "bcopy",
- "bzero",
- "bsearch",
- "qsort",
- });
-
- auto *II = Node.getIdentifier();
-
- if (!II)
- return false;
-
- StringRef Name = LibcFunNamePrefixSuffixParser().matchName(
- II->getName(), Node.getBuiltinID());
-
- // Match predefined names:
- if (PredefinedNames->find(Name) != PredefinedNames->end())
- return true;
-
- std::string NameWCS = Name.str();
- size_t WcsPos = NameWCS.find("wcs");
-
- while (WcsPos != std::string::npos) {
- NameWCS[WcsPos++] = 's';
- NameWCS[WcsPos++] = 't';
- NameWCS[WcsPos++] = 'r';
- WcsPos = NameWCS.find("wcs", WcsPos);
- }
- if (PredefinedNames->find(NameWCS) != PredefinedNames->end())
- return true;
- // All `scanf` functions are unsafe (including `sscanf`, `vsscanf`, etc.. They
- // all should end with "scanf"):
- return Name.ends_with("scanf");
-}
-
-// Match a call to one of the `v*printf` functions taking `va_list`. We cannot
-// check safety for these functions so they should be changed to their
-// non-va_list versions.
-AST_MATCHER(FunctionDecl, isUnsafeVaListPrintfFunc) {
- auto *II = Node.getIdentifier();
-
- if (!II)
- return false;
-
- StringRef Name = LibcFunNamePrefixSuffixParser().matchName(
- II->getName(), Node.getBuiltinID());
-
- if (!Name.ends_with("printf"))
- return false; // neither printf nor scanf
- return Name.starts_with("v");
-}
-
-// Matches a call to one of the `sprintf` functions as they are always unsafe
-// and should be changed to `snprintf`.
-AST_MATCHER(FunctionDecl, isUnsafeSprintfFunc) {
- auto *II = Node.getIdentifier();
-
- if (!II)
- return false;
-
- StringRef Name = LibcFunNamePrefixSuffixParser().matchName(
- II->getName(), Node.getBuiltinID());
-
- if (!Name.ends_with("printf") ||
- // Let `isUnsafeVaListPrintfFunc` check for cases with va-list:
- Name.starts_with("v"))
- return false;
-
- StringRef Prefix = Name.drop_back(6);
-
- if (Prefix.ends_with("w"))
- Prefix = Prefix.drop_back(1);
- return Prefix == "s";
-}
-
-// Match function declarations of `printf`, `fprintf`, `snprintf` and their wide
-// character versions. Calls to these functions can be safe if their arguments
-// are carefully made safe.
-AST_MATCHER(FunctionDecl, isNormalPrintfFunc) {
- auto *II = Node.getIdentifier();
-
- if (!II)
- return false;
-
- StringRef Name = LibcFunNamePrefixSuffixParser().matchName(
- II->getName(), Node.getBuiltinID());
-
- if (!Name.ends_with("printf") || Name.starts_with("v"))
- return false;
-
- StringRef Prefix = Name.drop_back(6);
-
- if (Prefix.ends_with("w"))
- Prefix = Prefix.drop_back(1);
-
- return Prefix.empty() || Prefix == "k" || Prefix == "f" || Prefix == "sn";
-}
-
-// This matcher requires that it is known that the callee `isNormalPrintf`.
-// Then if the format string is a string literal, this matcher matches when at
-// least one string argument is unsafe. If the format is not a string literal,
-// this matcher matches when at least one pointer type argument is unsafe.
-AST_MATCHER_P(CallExpr, hasUnsafePrintfStringArg,
- clang::ast_matchers::internal::Matcher<Expr>,
- UnsafeStringArgMatcher) {
- // Determine what printf it is:
- const Expr *FirstArg = Node.getArg(0);
- ASTContext &Ctx = Finder->getASTContext();
-
- if (isa<StringLiteral>(FirstArg->IgnoreParenImpCasts())) {
- // It is a printf/kprintf. And, the format is a string literal:
- bool isKprintf = false;
- const Expr *UnsafeArg;
-
- if (auto *Callee = Node.getDirectCallee())
- if (auto *II = Node.getDirectCallee()->getIdentifier())
- isKprintf = II->getName() == "kprintf";
- if (hasUnsafeFormatOrSArg(&Node, UnsafeArg, 0, Ctx, isKprintf))
- return UnsafeStringArgMatcher.matches(*UnsafeArg, Finder, Builder);
- return false;
- }
-
- QualType PtrTy = FirstArg->getType();
-
- assert(PtrTy->isPointerType());
-
- QualType PteTy = (cast<PointerType>(PtrTy))->getPointeeType();
-
- if (!Ctx.getFILEType().isNull() /* If `FILE *` is not ever in the ASTContext,
- there can't be any file pointer then */
- && PteTy.getCanonicalType() == Ctx.getFILEType().getCanonicalType()) {
- // It is a fprintf:
- const Expr *UnsafeArg;
-
- if (hasUnsafeFormatOrSArg(&Node, UnsafeArg, 1, Ctx, false))
- return UnsafeStringArgMatcher.matches(*UnsafeArg, Finder, Builder);
- return false;
- }
-
- const Expr *SecondArg = Node.getArg(1);
-
- if (SecondArg->getType()->isIntegerType()) {
- // It is a snprintf:
- const Expr *UnsafeArg;
-
- if (unsigned UnsafeArgIdx =
- hasUnsafeFormatOrSArg(&Node, UnsafeArg, 2, Ctx, false))
- return UnsafeStringArgMatcher.matches(*UnsafeArg, Finder, Builder);
- return false;
- }
- // It is printf but the format string is passed by pointer. The only thing we
- // can do is to require all pointers to be null-terminated:
- for (auto Arg : Node.arguments())
- if (Arg->getType()->isPointerType() && !isNullTermPointer(Arg))
- if (UnsafeStringArgMatcher.matches(*Arg, Finder, Builder))
- return true;
- return false;
-}
-
-// This matcher requires that it is known that the callee `isNormalPrintf`.
-// Then it matches if the first two arguments of the call is a pointer and an
-// integer and they are not in a safe pattern.
-//
-// For the first two arguments: `ptr` and `size`, they are safe if in the
-// following patterns:
-// ptr := DRE.data();
-// size:= DRE.size()/DRE.size_bytes()
-// And DRE is a hardened container or view.
-AST_MATCHER(CallExpr, hasUnsafeSnprintfBuffer) {
- if (Node.getNumArgs() < 3)
- return false; // not an snprintf call
-
- const Expr *Buf = Node.getArg(0), *Size = Node.getArg(1);
-
- if (!Buf->getType()->isPointerType() || !Size->getType()->isIntegerType())
- return false; // not an snprintf call
-
- static StringRef SizedObjs[] = {"span", "array", "vector",
- "basic_string_view", "basic_string"};
- Buf = Buf->IgnoreParenImpCasts();
- Size = Size->IgnoreParenImpCasts();
- if (auto *MCEPtr = dyn_cast<CXXMemberCallExpr>(Buf))
- if (auto *MCESize = dyn_cast<CXXMemberCallExpr>(Size)) {
- auto *DREOfPtr = dyn_cast<DeclRefExpr>(
- MCEPtr->getImplicitObjectArgument()->IgnoreParenImpCasts());
- auto *DREOfSize = dyn_cast<DeclRefExpr>(
- MCESize->getImplicitObjectArgument()->IgnoreParenImpCasts());
-
- if (!DREOfPtr || !DREOfSize)
- return true; // not in safe pattern
- if (DREOfPtr->getDecl() != DREOfSize->getDecl())
- return true; // not in safe pattern
- if (MCEPtr->getMethodDecl()->getName() != "data")
- return true; // not in safe pattern
-
- if (MCESize->getMethodDecl()->getName() == "size_bytes" ||
- // Note here the pointer must be a pointer-to-char type unless there
- // is explicit casting. If there is explicit casting, this branch
- // is unreachable. Thus, at this branch "size" and "size_bytes" are
- // equivalent as the pointer is a char pointer:
- MCESize->getMethodDecl()->getName() == "size")
- for (StringRef SizedObj : SizedObjs)
- if (MCEPtr->getRecordDecl()->isInStdNamespace() &&
- MCEPtr->getRecordDecl()->getCanonicalDecl()->getName() ==
- SizedObj)
- return false; // It is in fact safe
- }
- return true; // ptr and size are not in safe pattern
-}
-} // namespace libc_func_matchers
} // namespace clang::ast_matchers
namespace {
@@ -1450,97 +1030,6 @@ class DataInvocationGadget : public WarningGadget {
DeclUseList getClaimedVarUseSites() const override { return {}; }
};
-class UnsafeLibcFunctionCallGadget : public WarningGadget {
- const CallExpr *const Call;
- const Expr *UnsafeArg = nullptr;
- constexpr static const char *const Tag = "UnsafeLibcFunctionCall";
- // Extra tags for additional information:
- constexpr static const char *const UnsafeSprintfTag =
- "UnsafeLibcFunctionCall_sprintf";
- constexpr static const char *const UnsafeSizedByTag =
- "UnsafeLibcFunctionCall_sized_by";
- constexpr static const char *const UnsafeStringTag =
- "UnsafeLibcFunctionCall_string";
- constexpr static const char *const UnsafeVaListTag =
- "UnsafeLibcFunctionCall_va_list";
-
- enum UnsafeKind {
- OTHERS = 0, // no specific information, the callee function is unsafe
- SPRINTF = 1, // never call `-sprintf`s, call `-snprintf`s instead.
- SIZED_BY =
- 2, // the first two arguments of `snprintf` function have
- // "__sized_by" relation but they do not conform to safe patterns
- STRING = 3, // an argument is a pointer-to-char-as-string but does not
- // guarantee null-termination
- VA_LIST = 4, // one of the `-printf`s function that take va_list, which is
- // considered unsafe as it is not compile-time check
- } WarnedFunKind = OTHERS;
-
-public:
- UnsafeLibcFunctionCallGadget(const MatchFinder::MatchResult &Result)
- : WarningGadget(Kind::UnsafeLibcFunctionCall),
- Call(Result.Nodes.getNodeAs<CallExpr>(Tag)) {
- if (Result.Nodes.getNodeAs<Decl>(UnsafeSprintfTag))
- WarnedFunKind = SPRINTF;
- else if (auto *E = Result.Nodes.getNodeAs<Expr>(UnsafeStringTag)) {
- WarnedFunKind = STRING;
- UnsafeArg = E;
- } else if (Result.Nodes.getNodeAs<CallExpr>(UnsafeSizedByTag)) {
- WarnedFunKind = SIZED_BY;
- UnsafeArg = Call->getArg(0);
- } else if (Result.Nodes.getNodeAs<Decl>(UnsafeVaListTag))
- WarnedFunKind = VA_LIST;
- }
-
- static Matcher matcher() {
- return stmt(anyOf(
- callExpr(
- callee(functionDecl(anyOf(
- // Match a predefined unsafe libc
- // function:
- functionDecl(libc_func_matchers::isPredefinedUnsafeLibcFunc()),
- // Match a call to one of the `v*printf` functions
- // taking va-list, which cannot be checked at
- // compile-time:
- functionDecl(libc_func_matchers::isUnsafeVaListPrintfFunc())
- .bind(UnsafeVaListTag),
- // Match a call to a `sprintf` function, which is never
- // safe:
- functionDecl(libc_func_matchers::isUnsafeSprintfFunc())
- .bind(UnsafeSprintfTag)))),
- // (unless the call has a sole string literal argument):
- unless(
- allOf(hasArgument(0, expr(stringLiteral())), hasNumArgs(1)))),
-
- // The following two cases require checking against actual
- // arguments of the call:
-
- // Match a call to an `snprintf` function. And first two
- // arguments of the call (that describe a buffer) are not in
- // safe patterns:
- callExpr(callee(functionDecl(libc_func_matchers::isNormalPrintfFunc())),
- libc_func_matchers::hasUnsafeSnprintfBuffer())
- .bind(UnsafeSizedByTag),
- // Match a call to a `printf` function, which can be safe if
- // all arguments are null-terminated:
- callExpr(callee(functionDecl(libc_func_matchers::isNormalPrintfFunc())),
- libc_func_matchers::hasUnsafePrintfStringArg(
- expr().bind(UnsafeStringTag)))));
- }
-
- const Stmt *getBaseStmt() const { return Call; }
-
- SourceLocation getSourceLoc() const override { return Call->getBeginLoc(); }
-
- void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
- bool IsRelatedToDecl,
- ASTContext &Ctx) const override {
- Handler.handleUnsafeLibcCall(Call, WarnedFunKind, Ctx, UnsafeArg);
- }
-
- DeclUseList getClaimedVarUseSites() const override { return {}; }
-};
-
// Represents expressions of the form `DRE[*]` in the Unspecified Lvalue
// Context (see `isInUnspecifiedLvalueContext`).
// Note here `[]` is the built-in subscript operator.
diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp
index 72078ae1534b07..e6ce89dc7ec406 100644
--- a/clang/lib/Sema/AnalysisBasedWarnings.cpp
+++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp
@@ -2304,20 +2304,6 @@ class UnsafeBufferUsageReporter : public UnsafeBufferUsageHandler {
}
}
- void handleUnsafeLibcCall(const CallExpr *Call, unsigned PrintfInfo,
- ASTContext &Ctx,
- const Expr *UnsafeArg = nullptr) override {
- S.Diag(Call->getBeginLoc(), diag::warn_unsafe_buffer_libc_call)
- << Call->getDirectCallee() // We've checked there is a direct callee
- << Call->getSourceRange();
- if (PrintfInfo > 0) {
- SourceRange R =
- UnsafeArg ? UnsafeArg->getSourceRange() : Call->getSourceRange();
- S.Diag(R.getBegin(), diag::note_unsafe_buffer_printf_call)
- << PrintfInfo << R;
- }
- }
-
void handleUnsafeOperationInContainer(const Stmt *Operation,
bool IsRelatedToDecl,
ASTContext &Ctx) override {
diff --git a/clang/test/SemaCXX/warn-unsafe-buffer-usage-libc-functions-inline-namespace.cpp b/clang/test/SemaCXX/warn-unsafe-buffer-usage-libc-functions-inline-namespace.cpp
deleted file mode 100644
index 2bd12db93fd526..00000000000000
--- a/clang/test/SemaCXX/warn-unsafe-buffer-usage-libc-functions-inline-namespace.cpp
+++ /dev/null
@@ -1,60 +0,0 @@
-// RUN: %clang_cc1 -std=c++20 -Wno-all -Wunsafe-buffer-usage \
-// RUN: -verify %s
-
-namespace std {
- inline namespace __1 {
- template< class InputIt, class OutputIt >
- OutputIt copy( InputIt first, InputIt last,
- OutputIt d_first );
-
- struct iterator{};
- template<typename T>
- struct span {
- T * ptr;
- T * data();
- unsigned size_bytes();
- unsigned size();
- iterator begin() const noexcept;
- iterator end() const noexcept;
- };
-
- template<typename T>
- struct basic_string {
- T* p;
- T *c_str();
- T *data();
- unsigned size_bytes();
- };
-
- typedef basic_string<char> string;
- typedef basic_string<wchar_t> wstring;
-
- // C function under std:
- void memcpy();
- void strcpy();
- int snprintf( char* buffer, unsigned buf_size, const char* format, ... );
- }
-}
-
-void f(char * p, char * q, std::span<char> s) {
- std::memcpy(); // expected-warning{{function 'memcpy' is unsafe}}
- std::strcpy(); // expected-warning{{function 'strcpy' is unsafe}}
- std::__1::memcpy(); // expected-warning{{function 'memcpy' is unsafe}}
- std::__1::strcpy(); // expected-warning{{function 'strcpy' is unsafe}}
-
- /* Test printfs */
- std::snprintf(s.data(), 10, "%s%d", "hello", *p); // expected-warning{{function 'snprintf' is unsafe}} expected-note{{buffer pointer and size may not match}}
- std::__1::snprintf(s.data(), 10, "%s%d", "hello", *p); // expected-warning{{function 'snprintf' is unsafe}} expected-note{{buffer pointer and size may not match}}
- std::snprintf(s.data(), s.size_bytes(), "%s%d", "hello", *p); // no warn
- std::__1::snprintf(s.data(), s.size_bytes(), "%s%d", "hello", *p); // no warn
-}
-
-void v(std::string s1) {
- std::snprintf(s1.data(), s1.size_bytes(), "%s%d", s1.c_str(), 0); // no warn
- std::__1::snprintf(s1.data(), s1.size_bytes(), "%s%d", s1.c_str(), 0); // no warn
-}
-
-void g(char *begin, char *end, char *p, std::span<char> s) {
- std::copy(begin, end, p); // no warn
- std::copy(s.begin(), s.end(), s.begin()); // no warn
-}
diff --git a/clang/test/SemaCXX/warn-unsafe-buffer-usage-libc-functions.cpp b/clang/test/SemaCXX/warn-unsafe-buffer-usage-libc-functions.cpp
deleted file mode 100644
index 1a29654f660c97..00000000000000
--- a/clang/test/SemaCXX/warn-unsafe-buffer-usage-libc-functions.cpp
+++ /dev/null
@@ -1,106 +0,0 @@
-// RUN: %clang_cc1 -std=c++20 -Wno-all -Wunsafe-buffer-usage \
-// RUN: -verify %s
-
-typedef struct {} FILE;
-void memcpy();
-void __asan_memcpy();
-void strcpy();
-void strcpy_s();
-void wcscpy_s();
-unsigned strlen( const char* str );
-int fprintf( FILE* stream, const char* format, ... );
-int printf( const char* format, ... );
-int sprintf( char* buffer, const char* format, ... );
-int swprintf( char* buffer, const char* format, ... );
-int snprintf( char* buffer, unsigned buf_size, const char* format, ... );
-int snwprintf( char* buffer, unsigned buf_size, const char* format, ... );
-int snwprintf_s( char* buffer, unsigned buf_size, const char* format, ... );
-int vsnprintf( char* buffer, unsigned buf_size, const char* format, ... );
-int sscanf_s(const char * buffer, const char * format, ...);
-int sscanf(const char * buffer, const char * format, ... );
-
-namespace std {
- template< class InputIt, class OutputIt >
- OutputIt copy( InputIt first, InputIt last,
- OutputIt d_first );
-
- struct iterator{};
- template<typename T>
- struct span {
- T * ptr;
- T * data();
- unsigned size_bytes();
- unsigned size();
- iterator begin() const noexcept;
- iterator end() const noexcept;
- };
-
- template<typename T>
- struct basic_string {
- T* p;
- T *c_str();
- T *data();
- unsigned size_bytes();
- };
-
- typedef basic_string<char> string;
- typedef basic_string<wchar_t> wstring;
-
- // C function under std:
- void memcpy();
- void strcpy();
-}
-
-void f(char * p, char * q, std::span<char> s, std::span<char> s2) {
- memcpy(); // expected-warning{{function 'memcpy' is unsafe}}
- std::memcpy(); // expected-warning{{function 'memcpy' is unsafe}}
- __builtin_memcpy(p, q, 64); // expected-warning{{function '__builtin_memcpy' is unsafe}}
- __builtin___memcpy_chk(p, q, 8, 64); // expected-warning{{function '__builtin___memcpy_chk' is unsafe}}
- __asan_memcpy(); // expected-warning{{function '__asan_memcpy' is unsafe}}
- strcpy(); // expected-warning{{function 'strcpy' is unsafe}}
- std::strcpy(); // expected-warning{{function 'strcpy' is unsafe}}
- strcpy_s(); // expected-warning{{function 'strcpy_s' is unsafe}}
- wcscpy_s(); // expected-warning{{function 'wcscpy_s' is unsafe}}
-
-
- /* Test printfs */
- fprintf((FILE*)p, "%s%d", p, *p); // expected-warning{{function 'fprintf' is unsafe}} expected-note{{string argument is not guaranteed to be null-terminated}}
- printf("%s%d", // expected-warning{{function 'printf' is unsafe}}
- p, // expected-note{{string argument is not guaranteed to be null-terminated}} note attached to the unsafe argument
- *p);
- sprintf(q, "%s%d", "hello", *p); // expected-warning{{function 'sprintf' is unsafe}} expected-note{{change to 'snprintf' for explicit bounds checking}}
- swprintf(q, "%s%d", "hello", *p); // expected-warning{{function 'swprintf' is unsafe}} expected-note{{change to 'snprintf' for explicit bounds checking}}
- snprintf(q, 10, "%s%d", "hello", *p); // expected-warning{{function 'snprintf' is unsafe}} expected-note{{buffer pointer and size may not match}}
- snprintf(s.data(), s2.size(), "%s%d", "hello", *p); // expected-warning{{function 'snprintf' is unsafe}} expected-note{{buffer pointer and size may not match}}
- snwprintf(s.data(), s2.size(), "%s%d", "hello", *p); // expected-warning{{function 'snwprintf' is unsafe}} expected-note{{buffer pointer and size may not match}}
- snwprintf_s( // expected-warning{{function 'snwprintf_s' is unsafe}}
- s.data(), // expected-note{{buffer pointer and size may not match}} // note attached to the buffer
- s2.size(),
- "%s%d", "hello", *p);
- vsnprintf(s.data(), s.size_bytes(), "%s%d", "hello", *p); // expected-warning{{function 'vsnprintf' is unsafe}} expected-note{{'va_list' is unsafe}}
- sscanf(p, "%s%d", "hello", *p); // expected-warning{{function 'sscanf' is unsafe}}
- sscanf_s(p, "%s%d", "hello", *p); // expected-warning{{function 'sscanf_s' is unsafe}}
- fprintf((FILE*)p, "%P%d%p%i hello world %32s", *p, *p, p, *p, p); // expected-warning{{function 'fprintf' is unsafe}} expected-note{{string argument is not guaranteed to be null-terminated}}
- fprintf((FILE*)p, "%P%d%p%i hello world %32s", *p, *p, p, *p, "hello"); // no warn
- printf("%s%d", "hello", *p); // no warn
- snprintf(s.data(), s.size_bytes(), "%s%d", "hello", *p); // no warn
- snprintf(s.data(), s.size_bytes(), "%s%d", __PRETTY_FUNCTION__, *p); // no warn
- snwprintf(s.data(), s.size_bytes(), "%s%d", __PRETTY_FUNCTION__, *p); // no warn
- snwprintf_s(s.data(), s.size_bytes(), "%s%d", __PRETTY_FUNCTION__, *p); // no warn
- strlen("hello");// no warn
-}
-
-void v(std::string s1, int *p) {
- snprintf(s1.data(), s1.size_bytes(), "%s%d%s%p%s", __PRETTY_FUNCTION__, *p, "hello", p, s1.c_str()); // no warn
- snprintf(s1.data(), s1.size_bytes(), s1.c_str(), __PRETTY_FUNCTION__, *p, "hello", s1.c_str()); // no warn
- printf("%s%d%s%p%s", __PRETTY_FUNCTION__, *p, "hello", p, s1.c_str()); // no warn
- printf(s1.c_str(), __PRETTY_FUNCTION__, *p, "hello", s1.c_str()); // no warn
- fprintf((FILE*)0, "%s%d%s%p%s", __PRETTY_FUNCTION__, *p, "hello", p, s1.c_str()); // no warn
- fprintf((FILE*)0, s1.c_str(), __PRETTY_FUNCTION__, *p, "hello", s1.c_str()); // no warn
-}
-
-
-void g(char *begin, char *end, char *p, std::span<char> s) {
- std::copy(begin, end, p); // no warn
- std::copy(s.begin(), s.end(), s.begin()); // no warn
-}
diff --git a/clang/test/SemaCXX/warn-unsafe-buffer-usage-test-unreachable.cpp b/clang/test/SemaCXX/warn-unsafe-buffer-usage-test-unreachable.cpp
index 989931e41c0cc1..844311c3a51a58 100644
--- a/clang/test/SemaCXX/warn-unsafe-buffer-usage-test-unreachable.cpp
+++ b/clang/test/SemaCXX/warn-unsafe-buffer-usage-test-unreachable.cpp
@@ -1,6 +1,8 @@
// RUN: %clang_cc1 -std=c++20 -Wunsafe-buffer-usage -fsafe-buffer-usage-suggestions -verify %s
+// expected-no-diagnostics
+
typedef unsigned __darwin_size_t;
typedef __darwin_size_t size_t;
#define bzero(s, n) __builtin_bzero(s, n)
-void __nosan_bzero(void *dst, size_t sz) { bzero(dst, sz); } // expected-warning{{function '__builtin_bzero' is unsafe}}
+void __nosan_bzero(void *dst, size_t sz) { bzero(dst, sz); }
More information about the cfe-commits
mailing list