[analyzer] Fix yet-another-crash in body-farming std::call_once

Crash occurs when parameters to the callback and to std::call_once
mismatch, and C++ is supposed to auto-construct an argument.

Filed by Alexander Kornienko in
https://bugs.llvm.org/show_bug.cgi?id=36149

rdar://37034403

Differential Revision: https://reviews.llvm.org/D42777

llvm-svn: 324046
This commit is contained in:
George Karpenkov 2018-02-02 01:44:07 +00:00
parent 844ccca577
commit 59202324a5
2 changed files with 54 additions and 2 deletions

View File

@ -406,6 +406,16 @@ static Stmt *create_call_once(ASTContext &C, const FunctionDecl *D) {
// reference.
for (unsigned int ParamIdx = 2; ParamIdx < D->getNumParams(); ParamIdx++) {
const ParmVarDecl *PDecl = D->getParamDecl(ParamIdx);
if (PDecl &&
CallbackFunctionType->getParamType(ParamIdx - 2)
.getNonReferenceType()
.getCanonicalType() !=
PDecl->getType().getNonReferenceType().getCanonicalType()) {
DEBUG(llvm::dbgs() << "Types of params of the callback do not match "
<< "params passed to std::call_once, "
<< "ignoring the call\n");
return nullptr;
}
Expr *ParamExpr = M.makeDeclRefExpr(PDecl);
if (!CallbackFunctionType->getParamType(ParamIdx - 2)->isReferenceType()) {
QualType PTy = PDecl->getType().getNonReferenceType();
@ -816,4 +826,3 @@ Stmt *BodyFarm::getBody(const ObjCMethodDecl *D) {
return Val.getValue();
}

View File

@ -9,9 +9,26 @@
void clang_analyzer_eval(bool);
// Faking std::std::call_once implementation.
// Faking std::call_once implementation.
namespace std {
// Fake std::function implementation.
template <typename>
class function;
class function_base {
public:
long field;
};
template <typename R, typename... P>
class function<R(P...)> : function_base {
public:
R operator()(P...) const {
// Read from a super-class necessary to reproduce a crash.
bool a = field;
}
};
#ifndef EMULATE_LIBSTDCPP
typedef struct once_flag_s {
unsigned long __state_ = 0;
@ -360,3 +377,29 @@ void test_implicit_funcptr() {
clang_analyzer_eval(x == 42); // expected-warning{{TRUE}}
#endif
}
int param_passed(int *x) {
return *x; // no-warning, as std::function is not working yet.
}
void callback_taking_func_ok(std::function<void(int*)> &innerCallback) {
innerCallback(nullptr);
}
// The provided callback expects an std::function, but instead a pointer
// to a C++ function is provided.
void callback_with_implicit_cast_ok() {
std::once_flag flag;
call_once(flag, callback_taking_func_ok, &param_passed);
}
void callback_taking_func(std::function<void()> &innerCallback) {
innerCallback();
}
// The provided callback expects an std::function, but instead a C function
// name is provided, and C++ implicitly auto-constructs a pointer from it.
void callback_with_implicit_cast() {
std::once_flag flag;
call_once(flag, callback_taking_func, callback_with_implicit_cast);
}