In this case Clang is complaining about memset(complicated-expr, 0, 0);
which means that transposing the last two arguments would not "fix" anything. Clang ought to not complain in this case. It doesn't have anything to do with coming from a macro; it's just that *both* arguments are zero. (I would also expect that `memset(complicated-expr, (unsigned char)fill, (size_t)0)` shouldn't warn, either. But we had some disagreement that casts to `(unsigned char)` resp. `(size_t)` should be the right way to shut up the warning in the first place...) Basically Clang should only be suggesting a swap (and thus complaining at all) in the case that swapping the arguments would actually improve the situation. –Arthur On Sat, Jul 21, 2018 at 1:33 PM, Nico Weber via cfe-commits < cfe-commits@lists.llvm.org> wrote: > This has a false positive on ffmpeg: > > ../../third_party/ffmpeg/libavcodec/options.c:273:64: error: 'size' > argument to memset is '0'; did you mean to transpose the last two > arguments? [-Werror,-Wmemset-transposed-args] > alloc_and_copy_or_fail(intra_matrix, 64 * sizeof(int16_t), 0); > > With: > > #define alloc_and_copy_or_fail(obj, size, pad) \ > if (src->obj && size > 0) { \ > dest->obj = av_malloc(size + pad); \ > if (!dest->obj) \ > goto fail; \ > memcpy(dest->obj, src->obj, size); \ > if (pad) \ > memset(((uint8_t *) dest->obj) + size, 0, pad); \ > } > > (https://cs.chromium.org/chromium/src/third_party/ > ffmpeg/libavcodec/options.c?q=alloc_and_copy_or_fail&sq= > package:chromium&g=0&l=261 ; https://bugs.chromium.org/p/ > chromium/issues/detail?id=866202) > > Maybe the warning shouldn't fire if the memset is from a macro? > > On Thu, Jul 19, 2018 at 12:51 PM Erik Pilkington via cfe-commits < > cfe-commits@lists.llvm.org> wrote: > >> Author: epilk >> Date: Thu Jul 19 09:46:15 2018 >> New Revision: 337470 >> >> URL: http://llvm.org/viewvc/llvm-project?rev=337470&view=rev >> Log: >> [Sema] Add a new warning, -Wmemset-transposed-args >> >> This diagnoses calls to memset that have the second and third arguments >> transposed, for example: >> >> memset(buf, sizeof(buf), 0); >> >> This is done by checking if the third argument is a literal 0, or if the >> second >> is a sizeof expression (and the third isn't). The first check is also >> done for >> calls to bzero. >> >> Differential revision: https://reviews.llvm.org/D49112 >> >> Added: >> cfe/trunk/test/Sema/transpose-memset.c >> Modified: >> cfe/trunk/include/clang/Basic/DiagnosticGroups.td >> cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td >> cfe/trunk/lib/Sema/SemaChecking.cpp >> >> Modified: cfe/trunk/include/clang/Basic/DiagnosticGroups.td >> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/ >> clang/Basic/DiagnosticGroups.td?rev=337470&r1=337469&r2=337470&view=diff >> ============================================================ >> ================== >> --- cfe/trunk/include/clang/Basic/DiagnosticGroups.td (original) >> +++ cfe/trunk/include/clang/Basic/DiagnosticGroups.td Thu Jul 19 >> 09:46:15 2018 >> @@ -443,6 +443,13 @@ def : DiagGroup<"synth">; >> def SizeofArrayArgument : DiagGroup<"sizeof-array-argument">; >> def SizeofArrayDecay : DiagGroup<"sizeof-array-decay">; >> def SizeofPointerMemaccess : DiagGroup<"sizeof-pointer-memaccess">; >> +def MemsetTransposedArgs : DiagGroup<"memset-transposed-args">; >> +def DynamicClassMemaccess : DiagGroup<"dynamic-class-memaccess">; >> +def NonTrivialMemaccess : DiagGroup<"nontrivial-memaccess">; >> +def SuspiciousBzero : DiagGroup<"suspicious-bzero">; >> +def SuspiciousMemaccess : DiagGroup<"suspicious-memaccess", >> + [SizeofPointerMemaccess, DynamicClassMemaccess, >> + NonTrivialMemaccess, MemsetTransposedArgs, SuspiciousBzero]>; >> def StaticInInline : DiagGroup<"static-in-inline">; >> def StaticLocalInInline : DiagGroup<"static-local-in-inline">; >> def GNUStaticFloatInit : DiagGroup<"gnu-static-float-init">; >> >> Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td >> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/ >> DiagnosticSemaKinds.td?rev=337470&r1=337469&r2=337470&view=diff >> ============================================================ >> ================== >> --- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original) >> +++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Thu Jul 19 >> 09:46:15 2018 >> @@ -619,14 +619,14 @@ 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">>; >> + InGroup<NonTrivialMemaccess>; >> 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; " >> "vtable pointer will be %select{overwritten|copied|moved|compared}4">, >> - InGroup<DiagGroup<"dynamic-class-memaccess">>; >> + InGroup<DynamicClassMemaccess>; >> def note_bad_memaccess_silence : Note< >> "explicitly cast the pointer to silence this warning">; >> def warn_sizeof_pointer_expr_memaccess : Warning< >> @@ -655,7 +655,19 @@ def note_memsize_comparison_paren : Note >> "did you mean to compare the result of %0 instead?">; >> def note_memsize_comparison_cast_silence : Note< >> "explicitly cast the argument to size_t to silence this warning">; >> - >> +def warn_suspicious_sizeof_memset : Warning< >> + "%select{'size' argument to memset is '0'|" >> + "setting buffer to a 'sizeof' expression}0" >> + "; did you mean to transpose the last two arguments?">, >> + InGroup<MemsetTransposedArgs>; >> +def note_suspicious_sizeof_memset_silence : Note< >> + "%select{parenthesize the third argument|" >> + "cast the second argument to 'int'}0 to silence">; >> +def warn_suspicious_bzero_size : Warning<"'size' argument to bzero is >> '0'">, >> + InGroup<SuspiciousBzero>; >> +def note_suspicious_bzero_size_silence : Note< >> + "parenthesize the second argument to silence">; >> + >> def warn_strncat_large_size : Warning< >> "the value of the size argument in 'strncat' is too large, might lead >> to a " >> "buffer overflow">, InGroup<StrncatSize>; >> >> Modified: cfe/trunk/lib/Sema/SemaChecking.cpp >> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/ >> SemaChecking.cpp?rev=337470&r1=337469&r2=337470&view=diff >> ============================================================ >> ================== >> --- cfe/trunk/lib/Sema/SemaChecking.cpp (original) >> +++ cfe/trunk/lib/Sema/SemaChecking.cpp Thu Jul 19 09:46:15 2018 >> @@ -8629,24 +8629,26 @@ static const CXXRecordDecl *getContained >> return nullptr; >> } >> >> +static const UnaryExprOrTypeTraitExpr *getAsSizeOfExpr(const Expr *E) { >> + if (const auto *Unary = dyn_cast<UnaryExprOrTypeTraitExpr>(E)) >> + if (Unary->getKind() == UETT_SizeOf) >> + return Unary; >> + return nullptr; >> +} >> + >> /// If E is a sizeof expression, returns its argument expression, >> /// otherwise returns NULL. >> static const Expr *getSizeOfExprArg(const Expr *E) { >> - if (const UnaryExprOrTypeTraitExpr *SizeOf = >> - dyn_cast<UnaryExprOrTypeTraitExpr>(E)) >> - if (SizeOf->getKind() == UETT_SizeOf && !SizeOf->isArgumentType()) >> + if (const UnaryExprOrTypeTraitExpr *SizeOf = getAsSizeOfExpr(E)) >> + if (!SizeOf->isArgumentType()) >> return SizeOf->getArgumentExpr()->IgnoreParenImpCasts(); >> - >> return nullptr; >> } >> >> /// If E is a sizeof expression, returns its argument type. >> static QualType getSizeOfArgType(const Expr *E) { >> - if (const UnaryExprOrTypeTraitExpr *SizeOf = >> - dyn_cast<UnaryExprOrTypeTraitExpr>(E)) >> - if (SizeOf->getKind() == UETT_SizeOf) >> - return SizeOf->getTypeOfArgument(); >> - >> + if (const UnaryExprOrTypeTraitExpr *SizeOf = getAsSizeOfExpr(E)) >> + return SizeOf->getTypeOfArgument(); >> return QualType(); >> } >> >> @@ -8742,6 +8744,86 @@ struct SearchNonTrivialToCopyField >> >> } >> >> +/// Detect if \c SizeofExpr is likely to calculate the sizeof an object. >> +static bool doesExprLikelyComputeSize(const Expr *SizeofExpr) { >> + SizeofExpr = SizeofExpr->IgnoreParenImpCasts(); >> + >> + if (const auto *BO = dyn_cast<BinaryOperator>(SizeofExpr)) { >> + if (BO->getOpcode() != BO_Mul && BO->getOpcode() != BO_Add) >> + return false; >> + >> + return doesExprLikelyComputeSize(BO->getLHS()) || >> + doesExprLikelyComputeSize(BO->getRHS()); >> + } >> + >> + return getAsSizeOfExpr(SizeofExpr) != nullptr; >> +} >> + >> +/// Check if the ArgLoc originated from a macro passed to the call at >> CallLoc. >> +/// >> +/// \code >> +/// #define MACRO 0 >> +/// foo(MACRO); >> +/// foo(0); >> +/// \endcode >> +/// >> +/// This should return true for the first call to foo, but not for the >> second >> +/// (regardless of whether foo is a macro or function). >> +static bool isArgumentExpandedFromMacro(SourceManager &SM, >> + SourceLocation CallLoc, >> + SourceLocation ArgLoc) { >> + if (!CallLoc.isMacroID()) >> + return SM.getFileID(CallLoc) != SM.getFileID(ArgLoc); >> + >> + return SM.getFileID(SM.getImmediateMacroCallerLoc(CallLoc)) != >> + SM.getFileID(SM.getImmediateMacroCallerLoc(ArgLoc)); >> +} >> + >> +/// Diagnose cases like 'memset(buf, sizeof(buf), 0)', which should have >> the >> +/// last two arguments transposed. >> +static void CheckMemaccessSize(Sema &S, unsigned BId, const CallExpr >> *Call) { >> + if (BId != Builtin::BImemset && BId != Builtin::BIbzero) >> + return; >> + >> + const Expr *SizeArg = >> + Call->getArg(BId == Builtin::BImemset ? 2 : 1)->IgnoreImpCasts(); >> + >> + // If we're memsetting or bzeroing 0 bytes, then this is likely an >> error. >> + SourceLocation CallLoc = Call->getRParenLoc(); >> + SourceManager &SM = S.getSourceManager(); >> + if (isa<IntegerLiteral>(SizeArg) && >> + cast<IntegerLiteral>(SizeArg)->getValue() == 0 && >> + !isArgumentExpandedFromMacro(SM, CallLoc, SizeArg->getExprLoc())) >> { >> + >> + SourceLocation DiagLoc = SizeArg->getExprLoc(); >> + >> + // Some platforms #define bzero to __builtin_memset. See if this is >> the >> + // case, and if so, emit a better diagnostic. >> + if (BId == Builtin::BIbzero || >> + (CallLoc.isMacroID() && Lexer::getImmediateMacroName( >> + CallLoc, SM, S.getLangOpts()) == >> "bzero")) { >> + S.Diag(DiagLoc, diag::warn_suspicious_bzero_size); >> + S.Diag(DiagLoc, diag::note_suspicious_bzero_size_silence); >> + } else { >> + S.Diag(DiagLoc, diag::warn_suspicious_sizeof_memset) << 0; >> + S.Diag(DiagLoc, diag::note_suspicious_sizeof_memset_silence) << 0; >> + } >> + return; >> + } >> + >> + // If the second argument to a memset is a sizeof expression and the >> third >> + // isn't, this is also likely an error. This should catch >> + // 'memset(buf, sizeof(buf), 0xff)'. >> + if (BId == Builtin::BImemset && >> + doesExprLikelyComputeSize(Call->getArg(1)) && >> + !doesExprLikelyComputeSize(Call->getArg(2))) { >> + SourceLocation DiagLoc = Call->getArg(1)->getExprLoc(); >> + S.Diag(DiagLoc, diag::warn_suspicious_sizeof_memset) << 1; >> + S.Diag(DiagLoc, diag::note_suspicious_sizeof_memset_silence) << 1; >> + return; >> + } >> +} >> + >> /// Check for dangerous or invalid arguments to memset(). >> /// >> /// This issues warnings on known problematic, dangerous or unspecified >> @@ -8771,6 +8853,9 @@ void Sema::CheckMemaccessArguments(const >> Call->getLocStart(), >> Call->getRParenLoc())) >> return; >> >> + // Catch cases like 'memset(buf, sizeof(buf), 0)'. >> + CheckMemaccessSize(*this, BId, Call); >> + >> // We have special checking when the length is a sizeof expression. >> QualType SizeOfArgTy = getSizeOfArgType(LenExpr); >> const Expr *SizeOfArg = getSizeOfExprArg(LenExpr); >> >> Added: cfe/trunk/test/Sema/transpose-memset.c >> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Sema/ >> transpose-memset.c?rev=337470&view=auto >> ============================================================ >> ================== >> --- cfe/trunk/test/Sema/transpose-memset.c (added) >> +++ cfe/trunk/test/Sema/transpose-memset.c Thu Jul 19 09:46:15 2018 >> @@ -0,0 +1,60 @@ >> +// RUN: %clang_cc1 -Wmemset-transposed-args -verify %s >> +// RUN: %clang_cc1 -xc++ -Wmemset-transposed-args -verify %s >> + >> +#define memset(...) __builtin_memset(__VA_ARGS__) >> +#define bzero(x,y) __builtin_memset(x, 0, y) >> +#define real_bzero(x,y) __builtin_bzero(x,y) >> + >> +int array[10]; >> +int *ptr; >> + >> +int main() { >> + memset(array, sizeof(array), 0); // expected-warning{{'size' argument >> to memset is '0'; did you mean to transpose the last two arguments?}} >> expected-note{{parenthesize the third argument to silence}} >> + memset(array, sizeof(array), 0xff); // expected-warning{{setting >> buffer to a 'sizeof' expression; did you mean to transpose the last two >> arguments?}} expected-note{{cast the second argument to 'int' to silence}} >> + memset(ptr, sizeof(ptr), 0); // expected-warning{{'size' argument to >> memset is '0'; did you mean to transpose the last two arguments?}} >> expected-note{{parenthesize the third argument to silence}} >> + memset(ptr, sizeof(*ptr) * 10, 1); // expected-warning{{setting buffer >> to a 'sizeof' expression; did you mean to transpose the last two >> arguments?}} expected-note{{cast the second argument to 'int' to silence}} >> + memset(ptr, 10 * sizeof(int *), 1); // expected-warning{{setting >> buffer to a 'sizeof' expression; did you mean to transpose the last two >> arguments?}} expected-note{{cast the second argument to 'int' to silence}} >> + memset(ptr, 10 * sizeof(int *) + 10, 0xff); // >> expected-warning{{setting buffer to a 'sizeof' expression; did you mean to >> transpose the last two arguments?}} expected-note{{cast the second argument >> to 'int' to silence}} >> + memset(ptr, sizeof(char) * sizeof(int *), 0xff); // >> expected-warning{{setting buffer to a 'sizeof' expression; did you mean to >> transpose the last two arguments?}} expected-note{{cast the second argument >> to 'int' to silence}} >> + memset(array, sizeof(array), sizeof(array)); // Uh... fine I guess. >> + memset(array, 0, sizeof(array)); >> + memset(ptr, 0, sizeof(int *) * 10); >> + memset(array, (int)sizeof(array), (0)); // no warning >> + memset(array, (int)sizeof(array), 32); // no warning >> + memset(array, 32, (0)); // no warning >> + >> + bzero(ptr, 0); // expected-warning{{'size' argument to bzero is '0'}} >> expected-note{{parenthesize the second argument to silence}} >> + real_bzero(ptr, 0); // expected-warning{{'size' argument to bzero is >> '0'}} expected-note{{parenthesize the second argument to silence}} >> +} >> + >> +void macros() { >> +#define ZERO 0 >> + int array[10]; >> + memset(array, 0xff, ZERO); // no warning >> + // Still emit a diagnostic for memsetting a sizeof expression: >> + memset(array, sizeof(array), ZERO); // expected-warning{{'sizeof'}} >> expected-note{{cast}} >> + bzero(array, ZERO); // no warning >> + real_bzero(array, ZERO); // no warning >> +#define NESTED_DONT_DIAG \ >> + memset(array, 0xff, ZERO); \ >> + real_bzero(array, ZERO); >> + >> + NESTED_DONT_DIAG; >> + >> +#define NESTED_DO_DIAG \ >> + memset(array, 0xff, 0); \ >> + real_bzero(array, 0) >> + >> + NESTED_DO_DIAG; // expected-warning{{'size' argument to memset}} >> expected-warning{{'size' argument to bzero}} expected-note2{{parenthesize}} >> + >> +#define FN_MACRO(p) \ >> + memset(array, 0xff, p) >> + >> + FN_MACRO(ZERO); >> + FN_MACRO(0); // FIXME: should we diagnose this? >> + >> + __builtin_memset(array, 0, ZERO); // no warning >> + __builtin_bzero(array, ZERO); >> + __builtin_memset(array, 0, 0); // expected-warning{{'size' argument to >> memset}} // expected-note{{parenthesize}} >> + __builtin_bzero(array, 0); // expected-warning{{'size' argument to >> bzero}} // expected-note{{parenthesize}} >> +} >> >> >> _______________________________________________ >> cfe-commits mailing list >> cfe-commits@lists.llvm.org >> http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits >> > > _______________________________________________ > cfe-commits mailing list > cfe-commits@lists.llvm.org > http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits > >
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits