Index: clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def =================================================================== --- clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def +++ clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def @@ -21,6 +21,7 @@ UNSAFE_GADGET(Increment) UNSAFE_GADGET(Decrement) UNSAFE_GADGET(ArraySubscript) +UNSAFE_GADGET(UnsafeBufferUsageAttr) #undef SAFE_GADGET #undef UNSAFE_GADGET Index: clang/include/clang/Basic/Attr.td =================================================================== --- clang/include/clang/Basic/Attr.td +++ clang/include/clang/Basic/Attr.td @@ -3940,6 +3940,12 @@ let Documentation = [ReleaseHandleDocs]; } +def UnsafeBufferUsage : InheritableAttr { + let Spellings = [Clang<"unsafe_buffer_usage">]; + let Subjects = SubjectList<[Function]>; + let Documentation = [UnsafeBufferUsageDocs]; +} + def DiagnoseAsBuiltin : InheritableAttr { let Spellings = [Clang<"diagnose_as_builtin">]; let Args = [DeclArgument, Index: clang/include/clang/Basic/AttrDocs.td =================================================================== --- clang/include/clang/Basic/AttrDocs.td +++ clang/include/clang/Basic/AttrDocs.td @@ -6171,6 +6171,12 @@ }]; } +def UnsafeBufferDocs : DocumentationCategory<"Unsafe Buffer Usage Attributes"> { + let Content = [{ +Unsafe Buffer usage attributes TODO: add documentation. + }]; +} + def HandleDocs : DocumentationCategory<"Handle Attributes"> { let Content = [{ Handles are a way to identify resources like files, sockets, and processes. @@ -6236,6 +6242,21 @@ }]; } +def UnsafeBufferUsageDocs : Documentation { + let Category = UnsafeBufferDocs; + let Content = [{ +If a function parameter is accessed in an unsafe manner, +the corresponding function declaration is annotated with ``unsafe_buffer_usage``. + +.. code-block:: c++ + + [[clang::unsafe_buffer_usage]] + void foo(int* buf, int size) { + buf++; + } + }]; +} + def DiagnoseAsBuiltinDocs : Documentation { let Category = DocCatFunction; let Content = [{ Index: clang/lib/Analysis/UnsafeBufferUsage.cpp =================================================================== --- clang/lib/Analysis/UnsafeBufferUsage.cpp +++ clang/lib/Analysis/UnsafeBufferUsage.cpp @@ -330,6 +330,35 @@ return {}; } }; + +/// A call of a function or method that performs unchecked buffer operations +/// over one of its pointer parameters. +class UnsafeBufferUsageAttrGadget : public UnsafeGadget { + const CallExpr *Op; + +public: + UnsafeBufferUsageAttrGadget(const MatchFinder::MatchResult &Result) + : UnsafeGadget(Kind::UnsafeBufferUsageAttr), + Op(Result.Nodes.getNodeAs("call_expr")) {} + + static bool classof(const Gadget *G) { + return G->getKind() == Kind::UnsafeBufferUsageAttr; + } + + static Matcher matcher() { + return stmt(callExpr(callee(functionDecl(hasAttr(attr::UnsafeBufferUsage)))) + .bind("call_expr")); + } + + const Stmt *getBaseStmt() const override { return Op; } + + DeclUseList getClaimedVarUseSites() const override { + // FIXME: Not implemented yet. Returning {} is safe as it causes the gadget + // to block any attempts to fix variables that it could have otherwise + // claimed as known. + return {}; + } +}; } // namespace namespace { Index: clang/lib/Sema/SemaDeclAttr.cpp =================================================================== --- clang/lib/Sema/SemaDeclAttr.cpp +++ clang/lib/Sema/SemaDeclAttr.cpp @@ -8335,6 +8335,15 @@ D->addAttr(Attr::Create(S.Context, Argument, AL)); } +template +static void handleUnsafeBufferUsage(Sema &S, Decl *D, const ParsedAttr &AL) { +// StringRef Argument; +// if (!S.checkStringLiteralArgumentAttr(AL, 0, Argument)) +// return; +// D->addAttr(Attr::Create(S.Context, Argument, AL)); + D->addAttr(Attr::Create(S.Context, AL)); +} + static void handleCFGuardAttr(Sema &S, Decl *D, const ParsedAttr &AL) { // The guard attribute takes a single identifier argument. @@ -9210,6 +9219,10 @@ case ParsedAttr::AT_ReleaseHandle: handleHandleAttr(S, D, AL); break; + + case ParsedAttr::AT_UnsafeBufferUsage: + handleUnsafeBufferUsage(S, D, AL); + break; case ParsedAttr::AT_UseHandle: handleHandleAttr(S, D, AL); Index: clang/test/Misc/pragma-attribute-supported-attributes-list.test =================================================================== --- clang/test/Misc/pragma-attribute-supported-attributes-list.test +++ clang/test/Misc/pragma-attribute-supported-attributes-list.test @@ -185,6 +185,7 @@ // CHECK-NEXT: TestTypestate (SubjectMatchRule_function_is_member) // CHECK-NEXT: TrivialABI (SubjectMatchRule_record) // CHECK-NEXT: Uninitialized (SubjectMatchRule_variable_is_local) +// CHECK-NEXT: UnsafeBufferUsage (SubjectMatchRule_function) // CHECK-NEXT: UseHandle (SubjectMatchRule_variable_is_parameter) // CHECK-NEXT: VecReturn (SubjectMatchRule_record) // CHECK-NEXT: VecTypeHint (SubjectMatchRule_function) Index: clang/test/Sema/attr-unsafe_buffer_usage.cpp =================================================================== --- /dev/null +++ clang/test/Sema/attr-unsafe_buffer_usage.cpp @@ -0,0 +1,6 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +// Function annotations. +[[clang::unsafe_buffer_usage]] +void f(int *buf, int size); +void g(int *buffer [[clang::unsafe_buffer_usage("buffer")]], int size); // expected-warning {{'unsafe_buffer_usage' attribute only applies to functions}} Index: clang/test/SemaCXX/warn-unsafe-buffer-usage-function-attr.cpp =================================================================== --- /dev/null +++ clang/test/SemaCXX/warn-unsafe-buffer-usage-function-attr.cpp @@ -0,0 +1,31 @@ +// RUN: %clang_cc1 -std=c++20 -Wno-deprecated-declarations -Wunsafe-buffer-usage -verify %s + +[[clang::unsafe_buffer_usage]] +void deprecatedFunction3(); + +void deprecatedFunction4(int z); + +void someFunction(); + +[[clang::unsafe_buffer_usage]] +void overloading(int* x); + +void overloading(char c[]); + +void overloading(int* x, int size); + +[[clang::unsafe_buffer_usage]] +void deprecatedFunction4(int z); + +void caller(int z, int* x, int size, char c[]) { + deprecatedFunction3(); // expected-warning{{unchecked operation on raw buffer in expression}} + deprecatedFunction4(z); // expected-warning{{unchecked operation on raw buffer in expression}} + someFunction(); + + overloading(x); // expected-warning{{unchecked operation on raw buffer in expression}} + overloading(x, size); + overloading(c); +} + +[[clang::unsafe_buffer_usage]] +void overloading(char c[]);