Author: ahatanak Date: Tue Apr 17 12:13:41 2018 New Revision: 330202 URL: http://llvm.org/viewvc/llvm-project?rev=330202&view=rev Log: [Sema] Warn about memcpy'ing non-trivial C structs.
Issue a warning when non-trivial C structs are copied or initialized by calls to memset, bzero, memcpy, or memmove. rdar://problem/36124208 Differential Revision: https://reviews.llvm.org/D45310 Added: cfe/trunk/test/SemaObjC/warn-nontrivial-struct-memaccess.m Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td cfe/trunk/lib/Sema/SemaChecking.cpp Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=330202&r1=330201&r2=330202&view=diff ============================================================================== --- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original) +++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Tue Apr 17 12:13:41 2018 @@ -613,6 +613,13 @@ def err_function_needs_feature "'%2'">; def warn_builtin_unknown : Warning<"use of unknown builtin %0">, InGroup<ImplicitFunctionDeclare>, DefaultError; +def warn_cstruct_memaccess : Warning< + "%select{destination for|source of|first operand of|second operand of}0 this " + "%1 call is a pointer to record %2 that is not trivial to " + "%select{primitive-default-initialize|primitive-copy}3">, + InGroup<DiagGroup<"nontrivial-memaccess">>; +def note_nontrivial_field : Note< + "field is non-trivial to %select{copy|default-initialize}0">; def warn_dyn_class_memaccess : Warning< "%select{destination for|source of|first operand of|second operand of}0 this " "%1 call is a pointer to %select{|class containing a }2dynamic class %3; " Modified: cfe/trunk/lib/Sema/SemaChecking.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaChecking.cpp?rev=330202&r1=330201&r2=330202&view=diff ============================================================================== --- cfe/trunk/lib/Sema/SemaChecking.cpp (original) +++ cfe/trunk/lib/Sema/SemaChecking.cpp Tue Apr 17 12:13:41 2018 @@ -28,6 +28,7 @@ #include "clang/AST/ExprObjC.h" #include "clang/AST/ExprOpenMP.h" #include "clang/AST/NSAPI.h" +#include "clang/AST/NonTrivialTypeVisitor.h" #include "clang/AST/OperationKinds.h" #include "clang/AST/Stmt.h" #include "clang/AST/TemplateBase.h" @@ -7378,6 +7379,98 @@ static QualType getSizeOfArgType(const E return QualType(); } +namespace { + +struct SearchNonTrivialToInitializeField + : DefaultInitializedTypeVisitor<SearchNonTrivialToInitializeField> { + using Super = + DefaultInitializedTypeVisitor<SearchNonTrivialToInitializeField>; + + SearchNonTrivialToInitializeField(const Expr *E, Sema &S) : E(E), S(S) {} + + void visitWithKind(QualType::PrimitiveDefaultInitializeKind PDIK, QualType FT, + SourceLocation SL) { + if (const auto *AT = asDerived().getContext().getAsArrayType(FT)) { + asDerived().visitArray(PDIK, AT, SL); + return; + } + + Super::visitWithKind(PDIK, FT, SL); + } + + void visitARCStrong(QualType FT, SourceLocation SL) { + S.DiagRuntimeBehavior(SL, E, S.PDiag(diag::note_nontrivial_field) << 1); + } + void visitARCWeak(QualType FT, SourceLocation SL) { + S.DiagRuntimeBehavior(SL, E, S.PDiag(diag::note_nontrivial_field) << 1); + } + void visitStruct(QualType FT, SourceLocation SL) { + for (const FieldDecl *FD : FT->castAs<RecordType>()->getDecl()->fields()) + visit(FD->getType(), FD->getLocation()); + } + void visitArray(QualType::PrimitiveDefaultInitializeKind PDIK, + const ArrayType *AT, SourceLocation SL) { + visit(getContext().getBaseElementType(AT), SL); + } + void visitTrivial(QualType FT, SourceLocation SL) {} + + static void diag(QualType RT, const Expr *E, Sema &S) { + SearchNonTrivialToInitializeField(E, S).visitStruct(RT, SourceLocation()); + } + + ASTContext &getContext() { return S.getASTContext(); } + + const Expr *E; + Sema &S; +}; + +struct SearchNonTrivialToCopyField + : CopiedTypeVisitor<SearchNonTrivialToCopyField, false> { + using Super = CopiedTypeVisitor<SearchNonTrivialToCopyField, false>; + + SearchNonTrivialToCopyField(const Expr *E, Sema &S) : E(E), S(S) {} + + void visitWithKind(QualType::PrimitiveCopyKind PCK, QualType FT, + SourceLocation SL) { + if (const auto *AT = asDerived().getContext().getAsArrayType(FT)) { + asDerived().visitArray(PCK, AT, SL); + return; + } + + Super::visitWithKind(PCK, FT, SL); + } + + void visitARCStrong(QualType FT, SourceLocation SL) { + S.DiagRuntimeBehavior(SL, E, S.PDiag(diag::note_nontrivial_field) << 0); + } + void visitARCWeak(QualType FT, SourceLocation SL) { + S.DiagRuntimeBehavior(SL, E, S.PDiag(diag::note_nontrivial_field) << 0); + } + void visitStruct(QualType FT, SourceLocation SL) { + for (const FieldDecl *FD : FT->castAs<RecordType>()->getDecl()->fields()) + visit(FD->getType(), FD->getLocation()); + } + void visitArray(QualType::PrimitiveCopyKind PCK, const ArrayType *AT, + SourceLocation SL) { + visit(getContext().getBaseElementType(AT), SL); + } + void preVisit(QualType::PrimitiveCopyKind PCK, QualType FT, + SourceLocation SL) {} + void visitTrivial(QualType FT, SourceLocation SL) {} + void visitVolatileTrivial(QualType FT, SourceLocation SL) {} + + static void diag(QualType RT, const Expr *E, Sema &S) { + SearchNonTrivialToCopyField(E, S).visitStruct(RT, SourceLocation()); + } + + ASTContext &getContext() { return S.getASTContext(); } + + const Expr *E; + Sema &S; +}; + +} + /// \brief Check for dangerous or invalid arguments to memset(). /// /// This issues warnings on known problematic, dangerous or unspecified @@ -7543,7 +7636,23 @@ void Sema::CheckMemaccessArguments(const PDiag(diag::warn_arc_object_memaccess) << ArgIdx << FnName << PointeeTy << Call->getCallee()->getSourceRange()); - else + else if (const auto *RT = PointeeTy->getAs<RecordType>()) { + if ((BId == Builtin::BImemset || BId == Builtin::BIbzero) && + RT->getDecl()->isNonTrivialToPrimitiveDefaultInitialize()) { + DiagRuntimeBehavior(Dest->getExprLoc(), Dest, + PDiag(diag::warn_cstruct_memaccess) + << ArgIdx << FnName << PointeeTy << 0); + SearchNonTrivialToInitializeField::diag(PointeeTy, Dest, *this); + } else if ((BId == Builtin::BImemcpy || BId == Builtin::BImemmove) && + RT->getDecl()->isNonTrivialToPrimitiveCopy()) { + DiagRuntimeBehavior(Dest->getExprLoc(), Dest, + PDiag(diag::warn_cstruct_memaccess) + << ArgIdx << FnName << PointeeTy << 1); + SearchNonTrivialToCopyField::diag(PointeeTy, Dest, *this); + } else { + continue; + } + } else continue; DiagRuntimeBehavior( Added: cfe/trunk/test/SemaObjC/warn-nontrivial-struct-memaccess.m URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaObjC/warn-nontrivial-struct-memaccess.m?rev=330202&view=auto ============================================================================== --- cfe/trunk/test/SemaObjC/warn-nontrivial-struct-memaccess.m (added) +++ cfe/trunk/test/SemaObjC/warn-nontrivial-struct-memaccess.m Tue Apr 17 12:13:41 2018 @@ -0,0 +1,39 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-runtime-has-weak -x objective-c -fobjc-arc -verify %s + +void *memset(void *, int, __SIZE_TYPE__); +void bzero(void *, __SIZE_TYPE__); +void *memcpy(void *, const void *, __SIZE_TYPE__); +void *memmove(void *, const void *, __SIZE_TYPE__); + +struct Trivial { + int f0; + volatile int f1; +}; + +struct NonTrivial0 { + int f0; + __weak id f1; // expected-note 2 {{non-trivial to default-initialize}} expected-note 2 {{non-trivial to copy}} + volatile int f2; + id f3[10]; // expected-note 2 {{non-trivial to default-initialize}} expected-note 2 {{non-trivial to copy}} +}; + +struct NonTrivial1 { + id f0; // expected-note 2 {{non-trivial to default-initialize}} expected-note 2 {{non-trivial to copy}} + int f1; + struct NonTrivial0 f2; +}; + +void testTrivial(struct Trivial *d, struct Trivial *s) { + memset(d, 0, sizeof(struct Trivial)); + bzero(d, sizeof(struct Trivial)); + memcpy(d, s, sizeof(struct Trivial)); + memmove(d, s, sizeof(struct Trivial)); +} + +void testNonTrivial1(struct NonTrivial1 *d, struct NonTrivial1 *s) { + memset(d, 0, sizeof(struct NonTrivial1)); // expected-warning {{that is not trivial to primitive-default-initialize}} expected-note {{explicitly cast the pointer to silence}} + memset((void *)d, 0, sizeof(struct NonTrivial1)); + bzero(d, sizeof(struct NonTrivial1)); // expected-warning {{that is not trivial to primitive-default-initialize}} expected-note {{explicitly cast the pointer to silence}} + memcpy(d, s, sizeof(struct NonTrivial1)); // expected-warning {{that is not trivial to primitive-copy}} expected-note {{explicitly cast the pointer to silence}} + memmove(d, s, sizeof(struct NonTrivial1)); // expected-warning {{that is not trivial to primitive-copy}} expected-note {{explicitly cast the pointer to silence}} +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits