Index: clang/include/clang/Basic/Builtins.def =================================================================== --- clang/include/clang/Basic/Builtins.def +++ clang/include/clang/Basic/Builtins.def @@ -913,6 +913,7 @@ LIBBUILTIN(_Exit, "vi", "fr", "stdlib.h", ALL_LANGUAGES) LIBBUILTIN(malloc, "v*z", "f", "stdlib.h", ALL_LANGUAGES) LIBBUILTIN(realloc, "v*v*z", "f", "stdlib.h", ALL_LANGUAGES) +LIBBUILTIN(free, "vv*", "f", "stdlib.h", ALL_LANGUAGES) LIBBUILTIN(strtod, "dcC*c**", "f", "stdlib.h", ALL_LANGUAGES) LIBBUILTIN(strtof, "fcC*c**", "f", "stdlib.h", ALL_LANGUAGES) LIBBUILTIN(strtold, "LdcC*c**", "f", "stdlib.h", ALL_LANGUAGES) Index: clang/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticSemaKinds.td +++ clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -7537,6 +7537,11 @@ def warn_condition_is_assignment : Warning<"using the result of an " "assignment as a condition without parentheses">, InGroup; +def warn_free_nonheap_object + : Warning<"attempt to call %0 on non-heap object %1">, + InGroup>, + DefaultIgnore; + // Completely identical except off by default. def warn_condition_is_idiomatic_assignment : Warning<"using the result " "of an assignment as a condition without parentheses">, Index: clang/include/clang/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -12396,6 +12396,14 @@ void CheckStrncatArguments(const CallExpr *Call, IdentifierInfo *FnName); + void CheckFreeArguments(const CallExpr *E, const IdentifierInfo *FnName); + void CheckFreeArgumentsAddressof(const CallExpr *E, + const UnaryOperator *UnaryExpr, + const IdentifierInfo *FnName); + void CheckFreeArgumentsStackArray(const CallExpr *E, + const ImplicitCastExpr *DecayExpr, + const IdentifierInfo *FnName); + void CheckReturnValExpr(Expr *RetValExp, QualType lhsType, SourceLocation ReturnLoc, bool isObjCMethod = false, Index: clang/lib/AST/Decl.cpp =================================================================== --- clang/lib/AST/Decl.cpp +++ clang/lib/AST/Decl.cpp @@ -3959,34 +3959,53 @@ case Builtin::BIbzero: return Builtin::BIbzero; + case Builtin::BIfree: + return Builtin::BIfree; + default: if (isExternC()) { - if (FnInfo->isStr("memset")) + if (FnInfo->isStr("memset")) { return Builtin::BImemset; - else if (FnInfo->isStr("memcpy")) + } + if (FnInfo->isStr("memcpy")) { return Builtin::BImemcpy; - else if (FnInfo->isStr("mempcpy")) + } + if (FnInfo->isStr("mempcpy")) { return Builtin::BImempcpy; - else if (FnInfo->isStr("memmove")) + } + if (FnInfo->isStr("memmove")) { return Builtin::BImemmove; - else if (FnInfo->isStr("memcmp")) + } + if (FnInfo->isStr("memcmp")) { return Builtin::BImemcmp; - else if (FnInfo->isStr("bcmp")) + } + if (FnInfo->isStr("bcmp")) { return Builtin::BIbcmp; - else if (FnInfo->isStr("strncpy")) + } + if (FnInfo->isStr("strncpy")) { return Builtin::BIstrncpy; - else if (FnInfo->isStr("strncmp")) + } + if (FnInfo->isStr("strncmp")) { return Builtin::BIstrncmp; - else if (FnInfo->isStr("strncasecmp")) + } + if (FnInfo->isStr("strncasecmp")) { return Builtin::BIstrncasecmp; - else if (FnInfo->isStr("strncat")) + } + if (FnInfo->isStr("strncat")) { return Builtin::BIstrncat; - else if (FnInfo->isStr("strndup")) + } + if (FnInfo->isStr("strndup")) { return Builtin::BIstrndup; - else if (FnInfo->isStr("strlen")) + } + if (FnInfo->isStr("strlen")) { return Builtin::BIstrlen; - else if (FnInfo->isStr("bzero")) + } + if (FnInfo->isStr("bzero")) { return Builtin::BIbzero; + } + if (FnInfo->isStr("free")) { + return Builtin::BIfree; + } } break; } Index: clang/lib/Sema/SemaChecking.cpp =================================================================== --- clang/lib/Sema/SemaChecking.cpp +++ clang/lib/Sema/SemaChecking.cpp @@ -4477,9 +4477,10 @@ ImplicitThis = Args[0]; ++Args; --NumArgs; - } else if (IsMemberFunction) + } else if (IsMemberFunction) { ImplicitThis = cast(TheCall)->getImplicitObjectArgument(); + } checkCall(FDecl, Proto, ImplicitThis, llvm::makeArrayRef(Args, NumArgs), IsMemberFunction, TheCall->getRParenLoc(), @@ -4488,26 +4489,32 @@ IdentifierInfo *FnInfo = FDecl->getIdentifier(); // None of the checks below are needed for functions that don't have // simple names (e.g., C++ conversion functions). - if (!FnInfo) + if (!FnInfo) { return false; + } CheckAbsoluteValueFunction(TheCall, FDecl); CheckMaxUnsignedZero(TheCall, FDecl); - if (getLangOpts().ObjC) + if (getLangOpts().ObjC) { DiagnoseCStringFormatDirectiveInCFAPI(*this, FDecl, Args, NumArgs); + } unsigned CMId = FDecl->getMemoryFunctionKind(); - if (CMId == 0) + if (CMId == 0) { return false; + } // Handle memory setting and copying functions. - if (CMId == Builtin::BIstrlcpy || CMId == Builtin::BIstrlcat) + if (CMId == Builtin::BIstrlcpy || CMId == Builtin::BIstrlcat) { CheckStrlcpycatArguments(TheCall, FnInfo); - else if (CMId == Builtin::BIstrncat) + } else if (CMId == Builtin::BIstrncat) { CheckStrncatArguments(TheCall, FnInfo); - else + } else if (CMId == Builtin::BIfree) { + CheckFreeArguments(TheCall, FnInfo); + } else { CheckMemaccessArguments(TheCall, CMId, FnInfo); + } return false; } @@ -10100,6 +10107,80 @@ << FixItHint::CreateReplacement(SR, OS.str()); } +void Sema::CheckFreeArgumentsAddressof(const CallExpr *E, + const UnaryOperator *UnaryExpr, + const IdentifierInfo *FnName) { + assert(E != nullptr); + assert(UnaryExpr != nullptr); + + if (UnaryExpr->getOpcode() != UnaryOperator::Opcode::UO_AddrOf) { + return; + } + + auto *Lvalue = dyn_cast(UnaryExpr->getSubExpr()); + if (Lvalue == nullptr) { + return; + } + + auto *Var = dyn_cast(Lvalue->getDecl()); + if (Var == nullptr) { + return; + } + + StorageClass Class = Var->getStorageClass(); + if (Class == StorageClass::SC_Extern || + Class == StorageClass::SC_PrivateExtern || + Var->getType()->isReferenceType()) { + return; + } + + Diag(UnaryExpr->getBeginLoc(), diag::warn_free_nonheap_object) + << FnName->getName() << Var->getIdentifier(); +} + +void Sema::CheckFreeArgumentsStackArray(const CallExpr *E, + const ImplicitCastExpr *DecayExpr, + const IdentifierInfo *FnName) { + assert(E != nullptr); + assert(DecayExpr != nullptr); + + auto *Lvalue = dyn_cast(DecayExpr->getSubExpr()); + if (Lvalue == nullptr) { + return; + } + + if (!Lvalue->getType()->isArrayType()) { + return; + } + + auto *Var = dyn_cast(Lvalue->getDecl()); + if (Var == nullptr) { + return; + } + + Diag(DecayExpr->getBeginLoc(), diag::warn_free_nonheap_object) + << FnName->getName() << Var->getIdentifier(); +} + +/// Alerts the user that they are attempting to free a non-malloc'd object. +void Sema::CheckFreeArguments(const CallExpr *E, const IdentifierInfo *FnName) { + assert(E != nullptr); + + auto *CastToVoidPtrExpr = dyn_cast(E->getArg(0)); + if (CastToVoidPtrExpr == nullptr) { + return; + } + + const Expr *VoidPtrExpr = CastToVoidPtrExpr->getSubExpr(); + if (auto *UnaryExpr = dyn_cast(VoidPtrExpr)) { + return CheckFreeArgumentsAddressof(E, UnaryExpr, FnName); + } + + if (auto *DecayExpr = dyn_cast(VoidPtrExpr)) { + return CheckFreeArgumentsStackArray(E, DecayExpr, FnName); + } +} + void Sema::CheckReturnValExpr(Expr *RetValExp, QualType lhsType, SourceLocation ReturnLoc, Index: clang/test/Sema/warn-free-nonheap-object.c =================================================================== --- /dev/null +++ clang/test/Sema/warn-free-nonheap-object.c @@ -0,0 +1,23 @@ +// RUN: %clang_cc1 -Wfree-nonheap-object -fsyntax-only %s + +typedef unsigned long size_t; +void *malloc(size_t) { (void *)0; } +void free(void *) {} + +int GI; +void test() { + free(&GI); // expected-warning {{attempt to call free on non-heap object 'GI'}} + + int I = 0; + free(&I); // expected-warning {{attempt to call free on non-heap object 'I'}} + + int *P = &I; + free(P); + + void *P2 = malloc(8); + free(P2); + + int A[] = {0, 1, 2, 3}; + free(A); // expected-warning {{attempt to call free on non-heap object 'A'}} + free(&A); // expected-warning {{attempt to call free on non-heap object 'A'}} +} Index: clang/test/Sema/warn-free-nonheap-object.cpp =================================================================== --- /dev/null +++ clang/test/Sema/warn-free-nonheap-object.cpp @@ -0,0 +1,34 @@ +// RUN: %clang_cc1 -Wfree-nonheap-object -std=c++11 -x c++ -fsyntax-only %s + +extern "C" void free(void *) {} + +namespace std { +using size_t = decltype(0); +void *malloc(size_t) { return nullptr; } +void free(void *p) { ::free(p); } +} // namespace std + +int GI; + +void test() { + free(&GI); // expected-warning {{attempt to call free on non-heap object 'GI'}} + std::free(&GI); // expected-warning {{attempt to call free on non-heap object 'GI'}} + + int I = 0; + free(&I); // expected-warning {{attempt to call free on non-heap object 'I'}} + std::free(&I); // expected-warning {{attempt to call free on non-heap object 'I'}} + + int *P = &I; + free(P); + std::free(P); + + void *P2 = std::malloc(8); + free(P2); + std::free(P2); + + int A[] = {0, 1, 2, 3}; + free(A); // expected-warning {{attempt to call free on non-heap object 'A'}} + std::free(A); // expected-warning {{attempt to call free on non-heap object 'A'}} + free(&A); // expected-warning {{attempt to call free on non-heap object 'A'}} + std::free(&A); // expected-warning {{attempt to call free on non-heap object 'A'}} +}