Commit 76fa37bc authored by Maya Lekova's avatar Maya Lekova Committed by Commit Bot

[gcmole] Make gcmole aware of DisallowHeapAllocation

This should help reduce the number of false positives detected
by dead variable analysis.

Bug: v8:9680, chromium:1000635
Change-Id: Id2893dd5f26cad230dede96930a5caacc0272b64
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1924359
Commit-Queue: Maya Lekova <mslekova@chromium.org>
Reviewed-by: 's avatarMichael Starzinger <mstarzinger@chromium.org>
Reviewed-by: 's avatarClemens Backes <clemensb@chromium.org>
Cr-Commit-Position: refs/heads/master@{#65186}
parent 2174ba9f
...@@ -161,5 +161,57 @@ void TestDeadVarAnalysis(Isolate* isolate) { ...@@ -161,5 +161,57 @@ void TestDeadVarAnalysis(Isolate* isolate) {
raw_obj.Print(); raw_obj.Print();
} }
void TestGuardedDeadVarAnalysis(Isolate* isolate) {
JSObject raw_obj = *isolate->factory()->NewJSObjectWithNullProto();
// Note: having DisallowHeapAllocation with the same function as CauseGC
// normally doesn't make sense, but we want to test whether the gurads
// are recognized by GCMole.
DisallowHeapAllocation no_gc;
CauseGCRaw(raw_obj, isolate);
// Shouldn't cause warning.
raw_obj.Print();
}
void TestGuardedDeadVarAnalysisNotOnStack(Isolate* isolate) {
JSObject raw_obj = *isolate->factory()->NewJSObjectWithNullProto();
// {DisallowHeapAccess} has a {DisallowHeapAllocation} embedded as a member
// field, so both are treated equally by gcmole.
DisallowHeapAccess no_gc;
CauseGCRaw(raw_obj, isolate);
// Shouldn't cause warning.
raw_obj.Print();
}
void TestGuardedDeadVarAnalysisNested(JSObject raw_obj, Isolate* isolate) {
CauseGCRaw(raw_obj, isolate);
// Shouldn't cause warning.
raw_obj.Print();
}
void TestGuardedDeadVarAnalysisCaller(Isolate* isolate) {
DisallowHeapAccess no_gc;
JSObject raw_obj = *isolate->factory()->NewJSObjectWithNullProto();
TestGuardedDeadVarAnalysisNested(raw_obj, isolate);
}
JSObject GuardedAllocation(Isolate* isolate) {
DisallowHeapAllocation no_gc;
return *isolate->factory()->NewJSObjectWithNullProto();
}
void TestNestedDeadVarAnalysis(Isolate* isolate) {
JSObject raw_obj = GuardedAllocation(isolate);
CauseGCRaw(raw_obj, isolate);
// Should cause warning.
raw_obj.Print();
}
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
...@@ -137,11 +137,73 @@ struct Resolver { ...@@ -137,11 +137,73 @@ struct Resolver {
clang::DeclContext::lookup_iterator end = result.end(); clang::DeclContext::lookup_iterator end = result.end();
for (clang::DeclContext::lookup_iterator i = result.begin(); i != end; for (clang::DeclContext::lookup_iterator i = result.begin(); i != end;
i++) { i++) {
if (llvm::isa<T>(*i)) return llvm::cast<T>(*i); if (llvm::isa<T>(*i)) {
return llvm::cast<T>(*i);
} else {
llvm::errs() << "Didn't match declaration template against "
<< (*i)->getNameAsString() << "\n";
}
}
return NULL;
}
clang::CXXRecordDecl* ResolveTemplate(const char* n) {
clang::NamedDecl* initial_template = Resolve<clang::NamedDecl>(n);
if (!initial_template) return NULL;
clang::NamedDecl* underlying_template =
initial_template->getUnderlyingDecl();
if (!underlying_template) {
llvm::errs() << "Couldn't resolve underlying template\n";
return NULL;
}
const clang::TypeAliasDecl* type_alias_decl =
llvm::dyn_cast_or_null<clang::TypeAliasDecl>(underlying_template);
if (!type_alias_decl) {
llvm::errs() << "Couldn't resolve TypeAliasDecl\n";
return NULL;
}
const clang::Type* type = type_alias_decl->getTypeForDecl();
if (!type) {
llvm::errs() << "Couldn't resolve TypeAliasDecl to Type\n";
return NULL;
}
const clang::TypedefType* typedef_type =
llvm::dyn_cast_or_null<clang::TypedefType>(type);
if (!typedef_type) {
llvm::errs() << "Couldn't resolve TypedefType\n";
return NULL;
}
const clang::TypedefNameDecl* typedef_name_decl = typedef_type->getDecl();
if (!typedef_name_decl) {
llvm::errs() << "Couldn't resolve TypedefType to TypedefNameDecl\n";
return NULL;
}
clang::QualType underlying_type = typedef_name_decl->getUnderlyingType();
if (!llvm::isa<clang::TemplateSpecializationType>(underlying_type)) {
llvm::errs() << "Couldn't resolve TemplateSpecializationType\n";
return NULL;
}
const clang::TemplateSpecializationType* templ_specialization_type =
llvm::cast<clang::TemplateSpecializationType>(underlying_type);
if (!llvm::isa<clang::RecordType>(templ_specialization_type->desugar())) {
llvm::errs() << "Couldn't resolve RecordType\n";
return NULL;
} }
const clang::RecordType* record_type =
llvm::cast<clang::RecordType>(templ_specialization_type->desugar());
clang::CXXRecordDecl* record_decl =
llvm::dyn_cast_or_null<clang::CXXRecordDecl>(record_type->getDecl());
if (!record_decl) {
llvm::errs() << "Couldn't resolve CXXRecordDecl\n";
return NULL; return NULL;
} }
return record_decl;
}
private: private:
clang::ASTContext& ctx_; clang::ASTContext& ctx_;
...@@ -454,7 +516,7 @@ class Environment { ...@@ -454,7 +516,7 @@ class Environment {
std::cout << e.first; std::cout << e.first;
comma = true; comma = true;
} }
std::cout << "}"; std::cout << "}" << std::endl;
} }
static Environment* Allocate(const Environment& env) { static Environment* Allocate(const Environment& env) {
...@@ -610,15 +672,19 @@ static std::string THIS ("this"); ...@@ -610,15 +672,19 @@ static std::string THIS ("this");
class FunctionAnalyzer { class FunctionAnalyzer {
public: public:
FunctionAnalyzer(clang::MangleContext* ctx, FunctionAnalyzer(clang::MangleContext* ctx, clang::CXXRecordDecl* object_decl,
clang::CXXRecordDecl* object_decl,
clang::CXXRecordDecl* maybe_object_decl, clang::CXXRecordDecl* maybe_object_decl,
clang::CXXRecordDecl* smi_decl, clang::DiagnosticsEngine& d, clang::CXXRecordDecl* smi_decl,
clang::SourceManager& sm, bool dead_vars_analysis) clang::CXXRecordDecl* no_gc_decl,
clang::CXXRecordDecl* no_heap_access_decl,
clang::DiagnosticsEngine& d, clang::SourceManager& sm,
bool dead_vars_analysis)
: ctx_(ctx), : ctx_(ctx),
object_decl_(object_decl), object_decl_(object_decl),
maybe_object_decl_(maybe_object_decl), maybe_object_decl_(maybe_object_decl),
smi_decl_(smi_decl), smi_decl_(smi_decl),
no_gc_decl_(no_gc_decl),
no_heap_access_decl_(no_heap_access_decl),
d_(d), d_(d),
sm_(sm), sm_(sm),
block_(NULL), block_(NULL),
...@@ -911,13 +977,14 @@ class FunctionAnalyzer { ...@@ -911,13 +977,14 @@ class FunctionAnalyzer {
const clang::QualType& var_type, const clang::QualType& var_type,
const std::string& var_name, const std::string& var_name,
const Environment& env) { const Environment& env) {
if (RepresentsRawPointerType(var_type)) {
// We currently care only about our internal pointer types and not about // We currently care only about our internal pointer types and not about
// raw C++ pointers, because normally special care is taken when storing // raw C++ pointers, because normally special care is taken when storing
// raw pointers to the managed heap. Furthermore, checking for raw // raw pointers to the managed heap. Furthermore, checking for raw
// pointers produces too many false positives in the dead variable // pointers produces too many false positives in the dead variable
// analysis. // analysis.
if (IsInternalPointerType(var_type)) { if (IsInternalPointerType(var_type) && !env.IsAlive(var_name) &&
if (!env.IsAlive(var_name) && dead_vars_analysis_) { !HasActiveGuard() && dead_vars_analysis_) {
ReportUnsafe(parent, DEAD_VAR_MSG); ReportUnsafe(parent, DEAD_VAR_MSG);
} }
return ExprEffect::RawUse(); return ExprEffect::RawUse();
...@@ -1168,6 +1235,7 @@ class FunctionAnalyzer { ...@@ -1168,6 +1235,7 @@ class FunctionAnalyzer {
} }
DECL_VISIT_STMT(CompoundStmt) { DECL_VISIT_STMT(CompoundStmt) {
scopes_.push_back(GCGuard(stmt, false));
Environment out = env; Environment out = env;
clang::CompoundStmt::body_iterator end = stmt->body_end(); clang::CompoundStmt::body_iterator end = stmt->body_end();
for (clang::CompoundStmt::body_iterator s = stmt->body_begin(); for (clang::CompoundStmt::body_iterator s = stmt->body_begin();
...@@ -1175,6 +1243,7 @@ class FunctionAnalyzer { ...@@ -1175,6 +1243,7 @@ class FunctionAnalyzer {
++s) { ++s) {
out = VisitStmt(*s, out); out = VisitStmt(*s, out);
} }
scopes_.pop_back();
return out; return out;
} }
...@@ -1314,13 +1383,36 @@ class FunctionAnalyzer { ...@@ -1314,13 +1383,36 @@ class FunctionAnalyzer {
} }
} }
Environment VisitDecl(clang::Decl* decl, const Environment& env) { bool IsGCGuard(clang::QualType qtype) {
if (qtype.isNull()) {
return false;
}
if (qtype->isNullPtrType()) {
return false;
}
const clang::CXXRecordDecl* record = qtype->getAsCXXRecordDecl();
const clang::CXXRecordDecl* definition = GetDefinitionOrNull(record);
if (!definition) {
return false;
}
return (no_gc_decl_ && IsDerivedFrom(definition, no_gc_decl_)) ||
(no_heap_access_decl_ &&
IsDerivedFrom(definition, no_heap_access_decl_));
}
Environment VisitDecl(clang::Decl* decl, Environment& env) {
if (clang::VarDecl* var = llvm::dyn_cast<clang::VarDecl>(decl)) { if (clang::VarDecl* var = llvm::dyn_cast<clang::VarDecl>(decl)) {
Environment out = var->hasInit() ? VisitStmt(var->getInit(), env) : env; Environment out = var->hasInit() ? VisitStmt(var->getInit(), env) : env;
if (RepresentsRawPointerType(var->getType())) { if (RepresentsRawPointerType(var->getType())) {
out = out.Define(var->getNameAsString()); out = out.Define(var->getNameAsString());
} }
if (IsGCGuard(var->getType())) {
scopes_.back().has_guard = true;
}
return out; return out;
} }
...@@ -1372,6 +1464,13 @@ class FunctionAnalyzer { ...@@ -1372,6 +1464,13 @@ class FunctionAnalyzer {
block_ = block; block_ = block;
} }
bool HasActiveGuard() {
for (auto s : scopes_) {
if (s.has_guard) return true;
}
return false;
}
private: private:
void ReportUnsafe(const clang::Expr* expr, const std::string& msg) { void ReportUnsafe(const clang::Expr* expr, const std::string& msg) {
d_.Report(clang::FullSourceLoc(expr->getExprLoc(), sm_), d_.Report(clang::FullSourceLoc(expr->getExprLoc(), sm_),
...@@ -1384,14 +1483,24 @@ class FunctionAnalyzer { ...@@ -1384,14 +1483,24 @@ class FunctionAnalyzer {
clang::CXXRecordDecl* object_decl_; clang::CXXRecordDecl* object_decl_;
clang::CXXRecordDecl* maybe_object_decl_; clang::CXXRecordDecl* maybe_object_decl_;
clang::CXXRecordDecl* smi_decl_; clang::CXXRecordDecl* smi_decl_;
clang::CXXRecordDecl* no_gc_decl_;
clang::CXXRecordDecl* no_heap_access_decl_;
clang::DiagnosticsEngine& d_; clang::DiagnosticsEngine& d_;
clang::SourceManager& sm_; clang::SourceManager& sm_;
Block* block_; Block* block_;
bool dead_vars_analysis_; bool dead_vars_analysis_;
};
struct GCGuard {
clang::CompoundStmt* stmt = NULL;
bool has_guard = false;
GCGuard(clang::CompoundStmt* stmt_, bool has_guard_)
: stmt(stmt_), has_guard(has_guard_) {}
};
std::vector<GCGuard> scopes_;
};
class ProblemsFinder : public clang::ASTConsumer, class ProblemsFinder : public clang::ASTConsumer,
public clang::RecursiveASTVisitor<ProblemsFinder> { public clang::RecursiveASTVisitor<ProblemsFinder> {
...@@ -1412,6 +1521,19 @@ class ProblemsFinder : public clang::ASTConsumer, ...@@ -1412,6 +1521,19 @@ class ProblemsFinder : public clang::ASTConsumer,
virtual void HandleTranslationUnit(clang::ASTContext &ctx) { virtual void HandleTranslationUnit(clang::ASTContext &ctx) {
Resolver r(ctx); Resolver r(ctx);
// It is a valid situation that no_gc_decl == NULL when the
// DisallowHeapAllocation is not included and can't be resolved.
// This is gracefully handled in the FunctionAnalyzer later.
clang::CXXRecordDecl* no_gc_decl =
r.ResolveNamespace("v8")
.ResolveNamespace("internal")
.ResolveTemplate("DisallowHeapAllocation");
clang::CXXRecordDecl* no_heap_access_decl =
r.ResolveNamespace("v8")
.ResolveNamespace("internal")
.Resolve<clang::CXXRecordDecl>("DisallowHeapAccess");
clang::CXXRecordDecl* object_decl = clang::CXXRecordDecl* object_decl =
r.ResolveNamespace("v8").ResolveNamespace("internal"). r.ResolveNamespace("v8").ResolveNamespace("internal").
Resolve<clang::CXXRecordDecl>("Object"); Resolve<clang::CXXRecordDecl>("Object");
...@@ -1432,10 +1554,14 @@ class ProblemsFinder : public clang::ASTConsumer, ...@@ -1432,10 +1554,14 @@ class ProblemsFinder : public clang::ASTConsumer,
if (smi_decl != NULL) smi_decl = smi_decl->getDefinition(); if (smi_decl != NULL) smi_decl = smi_decl->getDefinition();
if (no_heap_access_decl != NULL)
no_heap_access_decl = no_heap_access_decl->getDefinition();
if (object_decl != NULL && smi_decl != NULL && maybe_object_decl != NULL) { if (object_decl != NULL && smi_decl != NULL && maybe_object_decl != NULL) {
function_analyzer_ = new FunctionAnalyzer( function_analyzer_ = new FunctionAnalyzer(
clang::ItaniumMangleContext::create(ctx, d_), object_decl, clang::ItaniumMangleContext::create(ctx, d_), object_decl,
maybe_object_decl, smi_decl, d_, sm_, dead_vars_analysis_); maybe_object_decl, smi_decl, no_gc_decl, no_heap_access_decl, d_, sm_,
dead_vars_analysis_);
TraverseDecl(ctx.getTranslationUnitDecl()); TraverseDecl(ctx.getTranslationUnitDecl());
} else { } else {
if (object_decl == NULL) { if (object_decl == NULL) {
......
...@@ -475,7 +475,8 @@ end ...@@ -475,7 +475,8 @@ end
local function TestRun() local function TestRun()
local errors, output = SafeCheckCorrectnessForArch('x64', true) local errors, output = SafeCheckCorrectnessForArch('x64', true)
if not errors then if not errors then
log("** Test file should produce errors, but none were found.") log("** Test file should produce errors, but none were found. Output:")
log(output)
return false return false
end end
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment