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)
def warn_condition_is_assignment : Warning<"using the result of an "
"assignment as a condition without parentheses">,
InGroup<Parentheses>;
+def warn_free_nonheap_object
+ : Warning<"attempt to call %0 on non-heap object %1">,
+ InGroup<DiagGroup<"free-nonheap-object">>,
+ DefaultIgnore; // FIXME: add to -Wall after sufficient testing
+
// Completely identical except off by default.
def warn_condition_is_idiomatic_assignment : Warning<"using the result "
"of an assignment as a condition without parentheses">,
void CheckStrncatArguments(const CallExpr *Call,
IdentifierInfo *FnName);
+ void CheckFreeArguments(const CallExpr *E);
+
void CheckReturnValExpr(Expr *RetValExp, QualType lhsType,
SourceLocation ReturnLoc,
bool isObjCMethod = false,
case Builtin::BIbzero:
return Builtin::BIbzero;
+ case Builtin::BIfree:
+ return Builtin::BIfree;
+
default:
if (isExternC()) {
if (FnInfo->isStr("memset"))
return Builtin::BIstrlen;
else if (FnInfo->isStr("bzero"))
return Builtin::BIbzero;
+ } else if (isInStdNamespace()) {
+ if (FnInfo->isStr("free"))
+ return Builtin::BIfree;
}
break;
}
DiagnoseCStringFormatDirectiveInCFAPI(*this, FDecl, Args, NumArgs);
unsigned CMId = FDecl->getMemoryFunctionKind();
- if (CMId == 0)
- return false;
// Handle memory setting and copying functions.
- if (CMId == Builtin::BIstrlcpy || CMId == Builtin::BIstrlcat)
+ switch (CMId) {
+ case 0:
+ return false;
+ case Builtin::BIstrlcpy: // fallthrough
+ case Builtin::BIstrlcat:
CheckStrlcpycatArguments(TheCall, FnInfo);
- else if (CMId == Builtin::BIstrncat)
+ break;
+ case Builtin::BIstrncat:
CheckStrncatArguments(TheCall, FnInfo);
- else
+ break;
+ case Builtin::BIfree:
+ CheckFreeArguments(TheCall);
+ break;
+ default:
CheckMemaccessArguments(TheCall, CMId, FnInfo);
+ }
return false;
}
<< FixItHint::CreateReplacement(SR, OS.str());
}
+namespace {
+void CheckFreeArgumentsAddressof(Sema &S, const std::string &CalleeName,
+ const UnaryOperator *UnaryExpr) {
+ if (UnaryExpr->getOpcode() != UnaryOperator::Opcode::UO_AddrOf)
+ return;
+
+ const auto *Lvalue = dyn_cast<DeclRefExpr>(UnaryExpr->getSubExpr());
+ if (Lvalue == nullptr)
+ return;
+
+ const auto *Var = dyn_cast<VarDecl>(Lvalue->getDecl());
+ if (Var == nullptr)
+ return;
+
+ StorageClass Class = Var->getStorageClass();
+ if (Class == StorageClass::SC_Extern ||
+ Class == StorageClass::SC_PrivateExtern ||
+ Var->getType()->isReferenceType())
+ return;
+
+ S.Diag(UnaryExpr->getBeginLoc(), diag::warn_free_nonheap_object)
+ << CalleeName << Var;
+}
+
+void CheckFreeArgumentsStackArray(Sema &S, const std::string &CalleeName,
+ const DeclRefExpr *Lvalue) {
+ if (!Lvalue->getType()->isArrayType())
+ return;
+
+ const auto *Var = dyn_cast<VarDecl>(Lvalue->getDecl());
+ if (Var == nullptr)
+ return;
+
+ S.Diag(Lvalue->getBeginLoc(), diag::warn_free_nonheap_object)
+ << CalleeName << Var;
+}
+} // namespace
+
+/// Alerts the user that they are attempting to free a non-malloc'd object.
+void Sema::CheckFreeArguments(const CallExpr *E) {
+ const Expr *Arg = E->getArg(0)->IgnoreParenCasts();
+ const std::string CalleeName =
+ dyn_cast<FunctionDecl>(E->getCalleeDecl())->getQualifiedNameAsString();
+
+ if (const auto *UnaryExpr = dyn_cast<UnaryOperator>(Arg))
+ return CheckFreeArgumentsAddressof(*this, CalleeName, UnaryExpr);
+
+ if (const auto *Lvalue = dyn_cast<DeclRefExpr>(Arg))
+ return CheckFreeArgumentsStackArray(*this, CalleeName, Lvalue);
+}
+
void
Sema::CheckReturnValExpr(Expr *RetValExp, QualType lhsType,
SourceLocation ReturnLoc,
--- /dev/null
+// RUN: %clang_cc1 -Wfree-nonheap-object -fsyntax-only -verify %s
+
+typedef __SIZE_TYPE__ size_t;
+void *malloc(size_t);
+void free(void *);
+
+int GI;
+void test() {
+ {
+ free(&GI); // expected-warning {{attempt to call free on non-heap object 'GI'}}
+ }
+ {
+ static int SI = 0;
+ free(&SI); // expected-warning {{attempt to call free on non-heap object 'SI'}}
+ }
+ {
+ int I = 0;
+ free(&I); // expected-warning {{attempt to call free on non-heap object 'I'}}
+ }
+ {
+ int I = 0;
+ int *P = &I;
+ free(P); // FIXME diagnosing this would require control flow analysis.
+ }
+ {
+ void *P = malloc(8);
+ free(P);
+ }
+ {
+ 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'}}
+ }
+}
--- /dev/null
+// RUN: %clang_cc1 -Wfree-nonheap-object -std=c++11 -x c++ -fsyntax-only -verify %s
+
+extern "C" void free(void *) {}
+
+namespace std {
+using size_t = decltype(sizeof(0));
+void *malloc(size_t);
+void free(void *p);
+} // namespace std
+
+int GI;
+
+struct S {
+ operator char *() { return ptr; }
+
+private:
+ char *ptr = (char *)std::malloc(10);
+};
+
+void test1() {
+ {
+ free(&GI); // expected-warning {{attempt to call free on non-heap object 'GI'}}
+ }
+ {
+ static int SI = 0;
+ free(&SI); // expected-warning {{attempt to call free on non-heap object 'SI'}}
+ }
+ {
+ int I = 0;
+ free(&I); // expected-warning {{attempt to call free on non-heap object 'I'}}
+ }
+ {
+ int I = 0;
+ int *P = &I;
+ free(P);
+ }
+ {
+ void *P = std::malloc(8);
+ free(P); // FIXME diagnosing this would require control flow analysis.
+ }
+ {
+ int A[] = {0, 1, 2, 3};
+ free(A); // expected-warning {{attempt to call free on non-heap object 'A'}}
+ }
+ {
+ int A[] = {0, 1, 2, 3};
+ free(&A); // expected-warning {{attempt to call free on non-heap object 'A'}}
+ }
+ {
+ S s;
+ free(s);
+ free(&s); // expected-warning {{attempt to call free on non-heap object 's'}}
+ }
+}
+
+void test2() {
+ {
+ std::free(&GI); // expected-warning {{attempt to call std::free on non-heap object 'GI'}}
+ }
+ {
+ static int SI = 0;
+ std::free(&SI); // expected-warning {{attempt to call std::free on non-heap object 'SI'}}
+ }
+ {
+ int I = 0;
+ std::free(&I); // expected-warning {{attempt to call std::free on non-heap object 'I'}}
+ }
+ {
+ int I = 0;
+ int *P = &I;
+ std::free(P); // FIXME diagnosing this would require control flow analysis.
+ }
+ {
+ void *P = std::malloc(8);
+ std::free(P);
+ }
+ {
+ int A[] = {0, 1, 2, 3};
+ std::free(A); // expected-warning {{attempt to call std::free on non-heap object 'A'}}
+ }
+ {
+ int A[] = {0, 1, 2, 3};
+ std::free(&A); // expected-warning {{attempt to call std::free on non-heap object 'A'}}
+ }
+ {
+ S s;
+ std::free(s);
+ std::free(&s); // expected-warning {{attempt to call std::free on non-heap object 's'}}
+ }
+}