#include <cstdint>
#include <memory>
#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
#include "clang/AST/Expr.h"
#include "clang/AST/OperationKinds.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/Analysis/FlowSensitive/AdornedCFG.h"
#include "clang/Analysis/FlowSensitive/DataflowAnalysis.h"
#include "clang/Analysis/FlowSensitive/DataflowLattice.h"
#include "clang/Analysis/FlowSensitive/Models/ChromiumCheckModel.h"
#include "clang/Analysis/FlowSensitive/NoopLattice.h"
#include "clang/Analysis/FlowSensitive/Value.h"
#include "clang/Analysis/FlowSensitive/WatchedLiteralsSolver.h"
#include "clang/Frontend/FrontendPluginRegistry.h"
#include "clang/Tooling/Transformer/Stencil.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/Support/TimeProfiler.h"
namespace {
const char kInvalidIteratorUsage[] =
"[iterator-checker] Potentially invalid iterator used.";
const char kInvalidIteratorComparison[] =
"[iterator-checker] Potentially invalid iterator comparison.";
const char kIteratorMismatch[] =
"[iterator-checker] Potentially iterator mismatch.";
enum AnnotationType : uint8_t {
kNone = 0,
kContainer = 1 << 0,
kEndContainer = 1 << 1,
kInvalidate = 1 << 2,
kIteratorPair = 1 << 3,
kSwap = 1 << 4,
};
struct Annotation {
Annotation(AnnotationType type, llvm::StringRef identifier)
: type(type), identifier(identifier) {}
Annotation(AnnotationType type) : type(type) {}
AnnotationType type;
std::string identifier;
};
using Annotations = std::vector<Annotation>;
struct GroupedFunctionAnnotation {
Annotations function_annotations;
Annotations return_annotations;
std::vector<Annotations> args_annotations;
GroupedFunctionAnnotation() = default;
GroupedFunctionAnnotation& Function(const Annotation& annotation) {
function_annotations.push_back(annotation);
return *this;
}
GroupedFunctionAnnotation& Return(const Annotation& annotation) {
return_annotations.push_back(annotation);
return *this;
}
GroupedFunctionAnnotation& Arg(const Annotations& annotations) {
args_annotations.push_back(annotations);
return *this;
}
};
Annotations::const_iterator FindAnnotation(const Annotations& annotations,
const uint8_t types) {
return std::find_if(
annotations.begin(), annotations.end(),
[&types](Annotation annotation) { return annotation.type & types; });
}
Annotations::const_iterator FindAnnotation(const Annotations& annotations,
const uint8_t types,
const llvm::StringRef& identifier) {
return std::find_if(annotations.begin(), annotations.end(),
[&types, &identifier](Annotation annotation) {
return (annotation.type & types) &&
(annotation.identifier == identifier);
});
}
GroupedFunctionAnnotation MergeGroupedFunctionAnnotations(
GroupedFunctionAnnotation first,
const GroupedFunctionAnnotation& second) {
first.function_annotations.insert(first.function_annotations.end(),
second.function_annotations.begin(),
second.function_annotations.end());
first.return_annotations.insert(first.return_annotations.end(),
second.return_annotations.begin(),
second.return_annotations.end());
for (size_t i = 0; i < second.args_annotations.size(); i++) {
if (i < first.args_annotations.size()) {
first.args_annotations[i].insert(first.args_annotations[i].end(),
second.args_annotations[i].begin(),
second.args_annotations[i].end());
} else {
first.args_annotations.push_back(second.args_annotations[i]);
}
}
return first;
}
static llvm::DenseMap<llvm::StringRef, AnnotationType> g_annotations = {
{"container", AnnotationType::kContainer},
{"end_container", AnnotationType::kEndContainer},
{"invalidate", AnnotationType::kInvalidate},
{"swap", AnnotationType::kSwap},
};
static llvm::DenseMap<llvm::StringRef, Annotations> g_types_annotations = {
{"__normal_iterator", {Annotation(AnnotationType::kContainer)}},
{"__wrap_iter", {Annotation(AnnotationType::kContainer)}},
{"reverse_iterator", {Annotation(AnnotationType::kContainer)}},
};
static llvm::DenseMap<llvm::StringRef, GroupedFunctionAnnotation>
g_functions_annotations = {
{
"std::begin",
{},
},
{
"std::cbegin",
{},
},
{
"std::end",
GroupedFunctionAnnotation().Return(
Annotation(AnnotationType::kEndContainer)),
},
{
"std::rend",
GroupedFunctionAnnotation().Return(
Annotation(AnnotationType::kEndContainer)),
},
{
"std::cend",
GroupedFunctionAnnotation().Return(
Annotation(AnnotationType::kEndContainer)),
},
{
"std::next",
{},
},
{
"std::prev",
{},
},
{
"std::find",
{},
},
{
"std::search",
{},
},
{
"std::swap",
GroupedFunctionAnnotation()
.Arg({Annotation(AnnotationType::kSwap)})
.Arg({Annotation(AnnotationType::kSwap)}),
},
};
static llvm::DenseMap<
llvm::StringRef,
llvm::DenseMap<llvm::StringRef, GroupedFunctionAnnotation>>
g_member_function_annotations = {
{
"std::vector",
{
{
"append_range",
GroupedFunctionAnnotation().Function(
Annotation(AnnotationType::kInvalidate)),
},
{
"assign",
GroupedFunctionAnnotation().Function(
Annotation(AnnotationType::kInvalidate)),
},
{
"assign_range",
GroupedFunctionAnnotation().Function(
Annotation(AnnotationType::kInvalidate)),
},
{
"back",
{},
},
{
"begin",
{},
},
{
"capacity",
{},
},
{
"cbegin",
{},
},
{
"cend",
GroupedFunctionAnnotation().Return(
Annotation(AnnotationType::kEndContainer)),
},
{
"clear",
GroupedFunctionAnnotation().Function(
Annotation(AnnotationType::kInvalidate)),
},
{
"crbegin",
{},
},
{
"crend",
GroupedFunctionAnnotation().Return(
Annotation(AnnotationType::kEndContainer)),
},
{
"data",
{},
},
{
"emplace",
GroupedFunctionAnnotation().Function(
Annotation(AnnotationType::kInvalidate)),
},
{
"emplace_back",
GroupedFunctionAnnotation().Function(
Annotation(AnnotationType::kInvalidate)),
},
{
"empty",
{},
},
{
"end",
GroupedFunctionAnnotation().Return(
Annotation(AnnotationType::kEndContainer)),
},
{"erase",
GroupedFunctionAnnotation()
.Function(Annotation(AnnotationType::kInvalidate))
.Function(Annotation(AnnotationType::kContainer, "a"))
.Arg({Annotation(AnnotationType::kContainer, "a")})
.Return(Annotation(AnnotationType::kEndContainer))},
{
"front",
{},
},
{
"insert",
GroupedFunctionAnnotation()
.Function(Annotation(AnnotationType::kInvalidate))
.Function(Annotation(AnnotationType::kContainer)),
},
{
"insert_range",
GroupedFunctionAnnotation().Function(
Annotation(AnnotationType::kInvalidate)),
},
{
"max_size",
{},
},
{
"pop_back",
GroupedFunctionAnnotation().Function(
Annotation(AnnotationType::kInvalidate)),
},
{
"push_back",
GroupedFunctionAnnotation().Function(
Annotation(AnnotationType::kInvalidate)),
},
{
"rbegin",
{},
},
{
"rend",
GroupedFunctionAnnotation().Return(
Annotation(AnnotationType::kEndContainer)),
},
{
"reserve",
GroupedFunctionAnnotation().Function(
Annotation(AnnotationType::kInvalidate)),
},
{
"resize",
GroupedFunctionAnnotation().Function(
Annotation(AnnotationType::kInvalidate)),
},
{
"shrink_to_fit",
GroupedFunctionAnnotation().Function(
Annotation(AnnotationType::kInvalidate)),
},
{
"size",
{},
},
{
"swap",
GroupedFunctionAnnotation()
.Function(Annotation(AnnotationType::kSwap))
.Arg({Annotation(AnnotationType::kSwap)}),
},
},
},
{
"std::unordered_set",
{
{
"begin",
{},
},
{
"cbegin",
{},
},
{
"end",
GroupedFunctionAnnotation().Return(
Annotation(AnnotationType::kEndContainer)),
},
{
"cend",
GroupedFunctionAnnotation().Return(
Annotation(AnnotationType::kEndContainer)),
},
{
"clear",
GroupedFunctionAnnotation().Function(
Annotation(AnnotationType::kInvalidate)),
},
{
"insert",
GroupedFunctionAnnotation()
.Function(Annotation(AnnotationType::kInvalidate))
.Return(Annotation(AnnotationType::kIteratorPair)),
},
{
"emplace",
GroupedFunctionAnnotation()
.Function(Annotation(AnnotationType::kInvalidate))
.Return(Annotation(AnnotationType::kIteratorPair)),
},
{
"emplace_hint",
GroupedFunctionAnnotation().Function(
Annotation(AnnotationType::kInvalidate)),
},
{
"erase",
GroupedFunctionAnnotation().Arg(
{Annotation(AnnotationType::kInvalidate)}),
},
{
"extract",
GroupedFunctionAnnotation().Arg(
{Annotation(AnnotationType::kInvalidate)}),
},
{
"find",
{},
},
},
},
{
"blink::Vector",
{
{
"begin",
{},
},
{
"rbegin",
{},
},
{
"end",
GroupedFunctionAnnotation().Return(
Annotation(AnnotationType::kEndContainer)),
},
{
"rend",
GroupedFunctionAnnotation().Return(
Annotation(AnnotationType::kEndContainer)),
},
{
"clear",
GroupedFunctionAnnotation().Function(
Annotation(AnnotationType::kInvalidate)),
},
{
"shrink_to_fit",
GroupedFunctionAnnotation().Function(
Annotation(AnnotationType::kInvalidate)),
},
{
"push_back",
GroupedFunctionAnnotation().Function(
Annotation(AnnotationType::kInvalidate)),
},
{
"emplace_back",
GroupedFunctionAnnotation().Function(
Annotation(AnnotationType::kInvalidate)),
},
{
"insert",
GroupedFunctionAnnotation().Function(
Annotation(AnnotationType::kInvalidate)),
},
{
"InsertAt",
GroupedFunctionAnnotation().Function(
Annotation(AnnotationType::kInvalidate)),
},
{
"InsertVector",
GroupedFunctionAnnotation().Function(
Annotation(AnnotationType::kInvalidate)),
},
{
"push_front",
GroupedFunctionAnnotation().Function(
Annotation(AnnotationType::kInvalidate)),
},
{
"PrependVector",
GroupedFunctionAnnotation().Function(
Annotation(AnnotationType::kInvalidate)),
},
{
"EraseAt",
GroupedFunctionAnnotation().Function(
Annotation(AnnotationType::kInvalidate)),
},
{
"erase",
GroupedFunctionAnnotation().Function(
Annotation(AnnotationType::kInvalidate)),
},
{
"pop_back",
{},
},
},
},
{
"std::deque",
{
{
"begin",
{},
},
{
"cbegin",
{},
},
{
"rbegin",
{},
},
{
"end",
GroupedFunctionAnnotation().Return(
Annotation(AnnotationType::kEndContainer)),
},
{
"cend",
GroupedFunctionAnnotation().Return(
Annotation(AnnotationType::kEndContainer)),
},
{
"rend",
GroupedFunctionAnnotation().Return(
Annotation(AnnotationType::kEndContainer)),
},
{
"clear",
GroupedFunctionAnnotation().Function(
Annotation(AnnotationType::kInvalidate)),
},
{
"shrink_to_fit",
GroupedFunctionAnnotation().Function(
Annotation(AnnotationType::kInvalidate)),
},
{
"insert",
GroupedFunctionAnnotation().Function(
Annotation(AnnotationType::kInvalidate)),
},
{
"emplace",
GroupedFunctionAnnotation().Function(
Annotation(AnnotationType::kInvalidate)),
},
{
"erase",
GroupedFunctionAnnotation().Function(
Annotation(AnnotationType::kInvalidate)),
},
{
"push_back",
GroupedFunctionAnnotation().Function(
Annotation(AnnotationType::kInvalidate)),
},
{
"emplace_back",
GroupedFunctionAnnotation().Function(
Annotation(AnnotationType::kInvalidate)),
},
{
"push_front",
GroupedFunctionAnnotation().Function(
Annotation(AnnotationType::kInvalidate)),
},
{
"emplace_front",
GroupedFunctionAnnotation().Function(
Annotation(AnnotationType::kInvalidate)),
},
},
},
};
llvm::raw_ostream& DebugStream() {
return llvm::nulls();
}
llvm::raw_ostream& InfoStream() {
return llvm::nulls();
}
clang::dataflow::Value* GetSyntheticFieldWithName(
llvm::StringRef name,
const clang::dataflow::Environment& env,
const clang::dataflow::RecordStorageLocation& loc) {
return env.getValue(loc.getSyntheticField(name));
}
clang::dataflow::BoolValue* GetIsValid(
const clang::dataflow::Environment& env,
const clang::dataflow::RecordStorageLocation& loc) {
return clang::cast_or_null<clang::dataflow::BoolValue>(
GetSyntheticFieldWithName("is_valid", env, loc));
}
clang::dataflow::BoolValue* GetIsEnd(
const clang::dataflow::Environment& env,
const clang::dataflow::RecordStorageLocation& loc) {
return clang::cast_or_null<clang::dataflow::BoolValue>(
GetSyntheticFieldWithName("is_end", env, loc));
}
void SetSyntheticFieldWithName(
llvm::StringRef name,
clang::dataflow::Environment& env,
const clang::dataflow::RecordStorageLocation& loc,
clang::dataflow::Value& res) {
env.setValue(loc.getSyntheticField(name), res);
}
void SwapSyntheticFieldWithName(
llvm::StringRef name,
clang::dataflow::Environment& env,
const clang::dataflow::RecordStorageLocation& loc_a,
const clang::dataflow::RecordStorageLocation& loc_b) {
auto* prev_value = env.getValue(loc_a.getSyntheticField(name));
env.setValue(loc_a.getSyntheticField(name),
*env.getValue(loc_b.getSyntheticField(name)));
env.setValue(loc_b.getSyntheticField(name), *prev_value);
}
void SetIsValid(clang::dataflow::Environment& env,
const clang::dataflow::RecordStorageLocation& loc,
clang::dataflow::BoolValue& res) {
SetSyntheticFieldWithName("is_valid", env, loc, res);
}
void SwapIsValid(clang::dataflow::Environment& env,
const clang::dataflow::RecordStorageLocation& loc_a,
const clang::dataflow::RecordStorageLocation& loc_b) {
SwapSyntheticFieldWithName("is_valid", env, loc_a, loc_b);
}
void SetIsEnd(clang::dataflow::Environment& env,
const clang::dataflow::RecordStorageLocation& loc,
clang::dataflow::BoolValue& res) {
SetSyntheticFieldWithName("is_end", env, loc, res);
}
void SwapIsEnd(clang::dataflow::Environment& env,
const clang::dataflow::RecordStorageLocation& loc_a,
const clang::dataflow::RecordStorageLocation& loc_b) {
SwapSyntheticFieldWithName("is_end", env, loc_a, loc_b);
}
const clang::dataflow::Formula& ForceBoolValue(
clang::dataflow::Environment& env,
const clang::Expr& expr) {
auto* value = env.get<clang::dataflow::BoolValue>(expr);
if (value != nullptr) {
return value->formula();
}
value = &env.makeAtomicBoolValue();
env.setValue(expr, *value);
return value->formula();
}
class InvalidIteratorAnalysis
: public clang::dataflow::DataflowAnalysis<InvalidIteratorAnalysis,
clang::dataflow::NoopLattice> {
public:
InvalidIteratorAnalysis(const clang::FunctionDecl* func,
clang::DiagnosticsEngine& diagnostic)
: DataflowAnalysis(func->getASTContext()), diagnostic_(diagnostic) {}
clang::dataflow::NoopLattice initialElement() const {
return clang::dataflow::NoopLattice();
}
void transfer(const clang::CFGElement& elt,
clang::dataflow::NoopLattice& state,
clang::dataflow::Environment& env) {
check_model_.transfer(elt, env);
if (auto cfg_stmt = elt.getAs<clang::CFGStmt>()) {
Transfer(*cfg_stmt->getStmt(), env);
}
}
llvm::StringMap<clang::QualType> GetSyntheticFields(clang::QualType Type) {
return llvm::StringMap<clang::QualType>{
{"is_valid", getASTContext().BoolTy},
{"is_end", getASTContext().BoolTy},
{"container", getASTContext().BoolTy},
};
}
private:
void Transfer(const clang::Stmt& stmt, clang::dataflow::Environment& env) {
if (auto* decl_stmt = clang::dyn_cast<clang::DeclStmt>(&stmt)) {
Transfer(*decl_stmt, env);
return;
}
if (auto* value_stmt = clang::dyn_cast<clang::ValueStmt>(&stmt)) {
Transfer(*value_stmt, env);
return;
}
}
void Transfer(const clang::DeclStmt& declaration_statement,
clang::dataflow::Environment& env) {
for (auto* decl : declaration_statement.decls()) {
if (auto* var_decl = clang::dyn_cast<clang::VarDecl>(decl)) {
Transfer(*var_decl, env);
}
}
}
void Transfer(const clang::VarDecl& var_decl,
clang::dataflow::Environment& env) {}
void Transfer(const clang::ValueStmt& value_stmt,
clang::dataflow::Environment& env) {
if (auto* expr = clang::dyn_cast<clang::Expr>(&value_stmt)) {
Transfer(*expr, env);
}
}
void Transfer(const clang::Expr& expr, clang::dataflow::Environment& env) {
if (auto* call_expr = clang::dyn_cast<clang::CallExpr>(&expr)) {
Transfer(*call_expr, env);
return;
}
if (auto* ctor = clang::dyn_cast<clang::CXXConstructExpr>(&expr)) {
Transfer(*ctor, env);
return;
}
if (auto* cast_expr = clang::dyn_cast<clang::CastExpr>(&expr)) {
Transfer(*cast_expr, env);
return;
}
}
void Transfer(const clang::CXXConstructExpr& expr,
clang::dataflow::Environment& env) {
if (!IsIterator(expr.getType().getCanonicalType())) {
return;
}
const clang::CXXConstructorDecl* ctor = expr.getConstructor();
assert(ctor != nullptr);
if (ctor->isCopyOrMoveConstructor() ||
ctor->isConvertingConstructor(false)) {
auto* it = UnwrapAsIterator(expr.getArg(0), env);
assert(it);
CloneIterator(&expr, *it, env);
}
}
void Transfer(const clang::CallExpr& callexpr,
clang::dataflow::Environment& env) {
TransferCallExprCommon(callexpr, env);
if (auto* expr = clang::dyn_cast<clang::CXXOperatorCallExpr>(&callexpr)) {
Transfer(*expr, env);
return;
}
}
void TransferCallExprCommon(const clang::CallExpr& expr,
clang::dataflow::Environment& env) {
std::optional<GroupedFunctionAnnotation> grouped_annotation =
GetFunctionAnnotation(expr);
if (!grouped_annotation) {
return;
}
ProcessAnnotationInvalidate(expr, grouped_annotation.value(), env);
ProcessAnnotationReturnIterator(expr, grouped_annotation.value(), env);
ProcessAnnotationSwap(expr, grouped_annotation.value(), env);
ProcessAnnotationRequireSameContainer(expr, grouped_annotation.value(),
env);
}
void ProcessAnnotationInvalidate(
const clang::CallExpr& expr,
const GroupedFunctionAnnotation& grouped_annotation,
clang::dataflow::Environment& env) {
ProcessAnnotationInvalidateArgs(expr, grouped_annotation, env);
ProcessAnnotationInvalidateContainer(expr, grouped_annotation, env);
}
void ProcessAnnotationInvalidateArgs(
const clang::CallExpr& expr,
const GroupedFunctionAnnotation& grouped_annotation,
clang::dataflow::Environment& env) {
for (size_t i = 0; i < grouped_annotation.args_annotations.size(); i++) {
Annotations args_annotation = grouped_annotation.args_annotations[i];
auto invalidate_arg_annotation =
FindAnnotation(args_annotation, AnnotationType::kInvalidate);
if (invalidate_arg_annotation == args_annotation.end()) {
continue;
}
clang::dataflow::RecordStorageLocation* iterator =
UnwrapAsIterator(expr.getArg(i), env);
if (iterator) {
InfoStream() << "INVALIDATING ONE: " << DebugString(env, *iterator)
<< '\n';
InvalidateIterator(env, *iterator);
} else {
clang::dataflow::Value* container =
GetContainerFromArg(env, *expr.getArg(i));
if (container) {
InfoStream() << "INVALIDATING MANY: Container: " << container << '\n';
InvalidateContainer(env, *container);
}
}
}
}
void ProcessAnnotationInvalidateContainer(
const clang::CallExpr& expr,
const GroupedFunctionAnnotation& grouped_annotation,
clang::dataflow::Environment& env) {
auto invalidate_function_annotation = FindAnnotation(
grouped_annotation.function_annotations, AnnotationType::kInvalidate);
if (invalidate_function_annotation ==
grouped_annotation.function_annotations.end()) {
return;
}
clang::dataflow::Value* container = GetContainerFromImplicitArg(env, expr);
if (container) {
InfoStream() << "INVALIDATING MANY: Container: " << container << '\n';
InvalidateContainer(env, *container);
}
}
void ProcessAnnotationReturnIterator(
const clang::CallExpr& expr,
const GroupedFunctionAnnotation& grouped_annotation,
clang::dataflow::Environment& env) {
auto container_return_annotation = FindAnnotation(
grouped_annotation.return_annotations,
AnnotationType::kContainer | AnnotationType::kEndContainer);
if (container_return_annotation ==
grouped_annotation.return_annotations.end()) {
return;
}
clang::dataflow::Value* container = nullptr;
for (size_t i = 0; i < grouped_annotation.args_annotations.size(); i++) {
Annotations args_annotation = grouped_annotation.args_annotations[i];
auto container_arg_annotation =
FindAnnotation(args_annotation, AnnotationType::kContainer,
container_return_annotation->identifier);
if (container_arg_annotation != args_annotation.end()) {
container = GetContainerFromArg(env, *expr.getArg(i));
break;
}
}
if (!container) {
if (clang::isa<clang::CXXMemberCallExpr>(expr)) {
container = GetContainerFromImplicitArg(env, expr);
} else {
container = GetContainerFromArg(env, *expr.getArg(0));
}
}
if (container) {
bool is_end =
container_return_annotation->type == AnnotationType::kEndContainer;
TransferCallReturningIterator(
&expr, *container,
is_end ? env.getBoolLiteralValue(false) : env.makeAtomicBoolValue(),
is_end ? env.getBoolLiteralValue(true) : env.makeAtomicBoolValue(),
env);
}
}
void ProcessAnnotationSwap(
const clang::CallExpr& expr,
const GroupedFunctionAnnotation& grouped_annotation,
clang::dataflow::Environment& env) {
llvm::DenseMap<llvm::StringRef,
std::vector<clang::dataflow::RecordStorageLocation*>>
id_to_locations;
auto swap_function_annotation = FindAnnotation(
grouped_annotation.function_annotations, AnnotationType::kSwap);
if (swap_function_annotation !=
grouped_annotation.function_annotations.end()) {
assert(clang::isa<clang::CXXMemberCallExpr>(&expr));
auto* member_call = clang::cast<clang::CXXMemberCallExpr>(&expr);
id_to_locations[swap_function_annotation->identifier].push_back(
clang::dyn_cast_or_null<clang::dataflow::RecordStorageLocation>(
env.getStorageLocation(
*member_call->getImplicitObjectArgument())));
}
for (size_t i = 0; i < grouped_annotation.args_annotations.size(); i++) {
Annotations args_annotation = grouped_annotation.args_annotations[i];
auto swap_arg_annotation =
FindAnnotation(args_annotation, AnnotationType::kSwap);
if (swap_arg_annotation != args_annotation.end()) {
id_to_locations[swap_arg_annotation->identifier].push_back(
clang::dyn_cast_or_null<clang::dataflow::RecordStorageLocation>(
env.getStorageLocation(*expr.getArg(i))));
}
}
for (const auto& [id, locations] : id_to_locations) {
assert(locations.size() == 2);
if (!locations[0] || !locations[1]) {
continue;
}
if (IsIterator(locations[0]->getType().getCanonicalType()) &&
IsIterator(locations[1]->getType().getCanonicalType())) {
SwapIterators(env, locations[0], locations[1]);
} else {
SwapContainers(env, GetContainerValue(env, *locations[0]),
GetContainerValue(env, *locations[1]));
}
}
}
void ProcessAnnotationRequireSameContainer(
const clang::CallExpr& expr,
const GroupedFunctionAnnotation& grouped_annotation,
clang::dataflow::Environment& env) {
llvm::DenseMap<
llvm::StringRef,
std::vector<std::pair<const clang::Expr*, clang::dataflow::Value*>>>
id_to_containers;
auto container_annotation = FindAnnotation(
grouped_annotation.function_annotations, AnnotationType::kContainer);
if (container_annotation != grouped_annotation.function_annotations.end()) {
id_to_containers[container_annotation->identifier].emplace_back(
&expr, GetContainerFromImplicitArg(env, expr));
}
for (size_t i = 0; i < grouped_annotation.args_annotations.size(); i++) {
Annotations args_annotation = grouped_annotation.args_annotations[i];
auto container_arg_annotation =
FindAnnotation(args_annotation, AnnotationType::kContainer);
if (container_arg_annotation != args_annotation.end()) {
id_to_containers[container_arg_annotation->identifier].emplace_back(
expr.getArg(i), GetContainerFromArg(env, *expr.getArg(i)));
}
}
for (const auto& [id, values] : id_to_containers) {
if (id == "") {
continue;
}
const clang::dataflow::Value* baseline = values[0].second;
for (size_t i = 1; i < values.size(); i++) {
if (values[i].second != baseline) {
Report(kIteratorMismatch, *values[i].first);
}
}
}
}
clang::dataflow::Value* GetContainerFromImplicitArg(
const clang::dataflow::Environment& env,
const clang::CallExpr& expr) {
const clang::CXXMemberCallExpr* member_call_expression =
clang::cast<clang::CXXMemberCallExpr>(&expr);
clang::Expr* callee = member_call_expression->getImplicitObjectArgument();
if (callee->getType()->isRecordType()) {
auto* callee_location =
env.get<clang::dataflow::RecordStorageLocation>(*callee);
return callee_location ? GetContainerValue(env, *callee_location)
: nullptr;
}
clang::dataflow::Value* container = env.getValue(*callee);
if (auto* pointer_value =
clang::dyn_cast_or_null<clang::dataflow::PointerValue>(container)) {
if (auto* pointee_location =
clang::dyn_cast<clang::dataflow::RecordStorageLocation>(
&pointer_value->getPointeeLoc())) {
return GetContainerValue(env, *pointee_location);
}
}
return container;
}
clang::dataflow::Value* GetContainerFromArg(
const clang::dataflow::Environment& env,
const clang::Expr& arg) {
clang::dataflow::RecordStorageLocation* iterator =
UnwrapAsIterator(&arg, env);
clang::dataflow::Value* container = nullptr;
if (iterator) {
container = GetContainerValue(env, *iterator);
} else {
auto* loc =
clang::dyn_cast_or_null<clang::dataflow::RecordStorageLocation>(
env.getStorageLocation(arg));
if (loc) {
container = GetContainerValue(env, *loc);
}
}
return container;
}
std::optional<GroupedFunctionAnnotation> GetFunctionAnnotation(
const clang::CallExpr& expr) {
auto* callee = expr.getDirectCallee();
if (!callee) {
return std::nullopt;
}
GroupedFunctionAnnotation annotated_grouped_annotation =
GetAnnotatedFunctionAnnotation(*callee);
GroupedFunctionAnnotation hardcoded_grouped_annotation =
GetHardcodedFunctionAnnotation(
*callee, clang::isa<clang::CXXMemberCallExpr>(expr));
auto merged_grouped_annotation = MergeGroupedFunctionAnnotations(
annotated_grouped_annotation, hardcoded_grouped_annotation);
ApplyIdentifiersFromTemplate(merged_grouped_annotation, callee);
return merged_grouped_annotation;
}
GroupedFunctionAnnotation GetHardcodedFunctionAnnotation(
const clang::FunctionDecl& callee,
const bool is_member_function) {
GroupedFunctionAnnotation grouped_annotation;
if (is_member_function) {
const std::string callee_type = clang::cast<clang::CXXMethodDecl>(callee)
.getParent()
->getQualifiedNameAsString();
auto container_annotations =
g_member_function_annotations.find(callee_type);
if (container_annotations != g_member_function_annotations.end()) {
const std::string callee_name = callee.getNameAsString();
auto it = container_annotations->second.find(callee_name);
if (it != container_annotations->second.end()) {
grouped_annotation = it->second;
}
}
} else {
std::string callee_name = callee.getQualifiedNameAsString();
auto it = g_functions_annotations.find(callee_name);
if (it != g_functions_annotations.end()) {
grouped_annotation = it->second;
}
}
auto* decl = clang::dyn_cast_or_null<clang::TypeDecl>(
callee.getReturnType()->getAsRecordDecl());
if (decl) {
auto it = g_types_annotations.find(decl->getNameAsString());
if (it != g_types_annotations.end()) {
grouped_annotation.return_annotations.insert(
grouped_annotation.return_annotations.end(), it->second.begin(),
it->second.end());
}
}
for (size_t i = 0; i < callee.getNumParams(); i++) {
auto* decl = clang::dyn_cast_or_null<clang::TypeDecl>(
callee.getParamDecl(i)->getType()->getAsRecordDecl());
if (!decl) {
continue;
}
auto it = g_types_annotations.find(decl->getNameAsString());
if (it != g_types_annotations.end()) {
if (i < grouped_annotation.args_annotations.size()) {
grouped_annotation.args_annotations[i].insert(
grouped_annotation.args_annotations[i].end(), it->second.begin(),
it->second.end());
} else {
grouped_annotation.args_annotations.push_back(it->second);
}
}
}
return grouped_annotation;
}
GroupedFunctionAnnotation GetAnnotatedFunctionAnnotation(
const clang::FunctionDecl& callee) {
Annotations function_annotations = ExtractAnnotationsFromDecl(callee);
llvm::DenseMap<llvm::StringRef, Annotations> context_types_annotations;
GetAnnotationsFromContext(context_types_annotations, callee.getParent());
Annotations return_annotations;
if (auto function_type_loc = callee.getFunctionTypeLoc()) {
return_annotations = ExtractAnnotationsFromTypeLoc(
function_type_loc.getReturnLoc(), context_types_annotations);
}
std::vector<Annotations> arguments_annotations;
for (size_t i = 0; i < callee.getNumParams(); i++) {
auto* param = callee.getParamDecl(i);
Annotations arg_annotations;
if (auto type_source = param->getTypeSourceInfo()) {
arg_annotations = ExtractAnnotationsFromTypeLoc(
type_source->getTypeLoc(), context_types_annotations);
}
arguments_annotations.emplace_back(arg_annotations);
}
return GroupedFunctionAnnotation{function_annotations, return_annotations,
arguments_annotations};
}
void ApplyIdentifiersFromTemplate(
GroupedFunctionAnnotation& grouped_annotation,
const clang::FunctionDecl* callee) {
clang::FunctionTemplateDecl* templ = callee->getPrimaryTemplate();
if (!templ) {
return;
}
const clang::FunctionDecl* callee_decl = templ->getTemplatedDecl();
for (size_t i = 0; i < callee_decl->getNumParams(); i++) {
auto* param = callee_decl->getParamDecl(i);
if (!clang::isa<clang::TemplateTypeParmType>(param->getType())) {
continue;
}
if (grouped_annotation.args_annotations.size() <= i) {
break;
}
std::string identifier = param->getType().getAsString();
for (auto& annotation : grouped_annotation.args_annotations[i]) {
if (annotation.type != AnnotationType::kContainer ||
annotation.identifier != "") {
continue;
}
annotation.identifier = identifier;
}
}
}
void GetAnnotationsFromContext(
llvm::DenseMap<llvm::StringRef, Annotations>& context_types_annotations,
const clang::DeclContext* context) {
for (auto decl : context->decls()) {
if (auto* type_decl = clang::dyn_cast<clang::TypeDecl>(decl)) {
Annotations annotations;
for (auto* attr : decl->attrs()) {
if (auto* annotate_attr =
clang::dyn_cast<clang::AnnotateAttr>(attr)) {
auto it = g_annotations.find(annotate_attr->getAnnotation());
if (it == g_annotations.end()) {
continue;
}
llvm::StringRef identifier =
GetIdentifierFromAnnotation(annotate_attr);
annotations.emplace_back(it->second, identifier);
}
}
if (!annotations.empty()) {
context_types_annotations[type_decl->getName()] = annotations;
}
}
}
}
llvm::StringRef GetIdentifierFromAnnotation(
const clang::AnnotateAttr* annotate_attr) {
llvm::StringRef identifier;
if (annotate_attr->args_size() > 0) {
const auto* string_literal =
GetStringLiteral(annotate_attr->args_begin()[0]);
assert(string_literal);
identifier = string_literal->getString();
}
return identifier;
}
llvm::StringRef GetIdentifierFromAnnotation(
const clang::AnnotateTypeAttr* annotate_type_attr) {
llvm::StringRef identifier;
if (annotate_type_attr->args_size() > 0) {
const auto* string_literal =
GetStringLiteral(annotate_type_attr->args_begin()[0]);
assert(string_literal);
identifier = string_literal->getString();
}
return identifier;
}
const clang::StringLiteral* GetStringLiteral(const clang::Expr* expr) {
using clang::ast_matchers::constantExpr;
using clang::ast_matchers::hasDescendant;
using clang::ast_matchers::match;
using clang::ast_matchers::selectFirst;
using clang::ast_matchers::stringLiteral;
return selectFirst<clang::StringLiteral>(
"str", match(constantExpr(hasDescendant(stringLiteral().bind("str"))),
*expr, getASTContext()));
}
Annotations ExtractAnnotationsFromDecl(const clang::Decl& decl) {
Annotations annotations;
if (decl.hasAttrs()) {
for (auto attr : decl.attrs()) {
llvm::StringRef annotation;
llvm::StringRef identifier;
if (auto* annotate_attr = clang::dyn_cast<clang::AnnotateAttr>(attr)) {
annotation = annotate_attr->getAnnotation();
identifier = GetIdentifierFromAnnotation(annotate_attr);
} else if (auto* annotate_type_attr =
clang::dyn_cast<clang::AnnotateTypeAttr>(attr)) {
annotation = annotate_type_attr->getAnnotation();
identifier = GetIdentifierFromAnnotation(annotate_type_attr);
}
auto it = g_annotations.find(annotation);
if (it != g_annotations.end()) {
annotations.emplace_back(it->second, identifier);
}
}
}
return annotations;
}
Annotations ExtractAnnotationsFromTypeLoc(
clang::TypeLoc type_loc,
const llvm::DenseMap<llvm::StringRef, Annotations>&
context_types_annotations) {
Annotations attrs;
std::string type_name = type_loc.getType()
.getNonReferenceType()
.getUnqualifiedType()
.getDesugaredType(getASTContext())
.getAsString();
auto it = context_types_annotations.find(type_name);
if (it != context_types_annotations.end()) {
attrs.insert(attrs.end(), it->second.begin(), it->second.end());
}
while (true) {
auto attributed_loc = type_loc.getAs<clang::AttributedTypeLoc>();
if (attributed_loc.isNull()) {
break;
}
auto* attr = attributed_loc.getAttrAs<clang::AnnotateTypeAttr>();
if (attr) {
auto annotation = attr->getAnnotation();
auto it = g_annotations.find(annotation);
if (it != g_annotations.end()) {
llvm::StringRef identifier = GetIdentifierFromAnnotation(attr);
attrs.emplace_back(it->second, identifier);
}
}
type_loc = type_loc.getNextTypeLoc();
}
return attrs;
}
void TransferCallReturningIterator(const clang::Expr* expr,
clang::dataflow::Value& container,
clang::dataflow::BoolValue& is_valid,
clang::dataflow::BoolValue& is_end,
clang::dataflow::Environment& env) {
clang::dataflow::RecordStorageLocation* loc = nullptr;
if (expr->isPRValue() && expr->getType()->isRecordType()) {
loc = &env.getResultObjectLocation(*expr);
} else {
loc = env.get<clang::dataflow::RecordStorageLocation>(*expr);
if (loc == nullptr) {
loc = &clang::cast<clang::dataflow::RecordStorageLocation>(
env.createStorageLocation(*expr));
env.setStorageLocation(*expr, *loc);
}
}
const clang::VarDecl* var_decl = nullptr;
auto parents = getASTContext().getParents(*expr);
while (!parents.empty() && !var_decl) {
if (auto* decl = parents[0].get<clang::VarDecl>()) {
var_decl = decl;
}
parents = getASTContext().getParents(parents[0]);
}
if (var_decl) {
iterator_types_mapping_.insert(var_decl->getType().getCanonicalType());
}
assert(loc);
PopulateIteratorValue(loc, container, is_valid, is_end, env);
}
void SwapContainers(clang::dataflow::Environment& env,
clang::dataflow::Value* container_a,
clang::dataflow::Value* container_b) {
llvm::DenseMap<clang::dataflow::RecordStorageLocation*,
clang::dataflow::Value*>
map = iterator_to_container_;
for (auto& [iterator_location, container] : map) {
if (container == container_a) {
SetContainerValue(env, *iterator_location, *container_b);
}
if (container == container_b) {
SetContainerValue(env, *iterator_location, *container_a);
}
}
}
void SwapIterators(clang::dataflow::Environment& env,
clang::dataflow::RecordStorageLocation* iterator_a,
clang::dataflow::RecordStorageLocation* iterator_b) {
SwapContainerValue(env, *iterator_a, *iterator_b);
SwapIsEnd(env, *iterator_a, *iterator_b);
SwapIsValid(env, *iterator_a, *iterator_b);
}
void Transfer(const clang::CXXOperatorCallExpr& expr,
clang::dataflow::Environment& env) {
if (expr.getOperator() == clang::OverloadedOperatorKind::OO_Star ||
expr.getOperator() == clang::OverloadedOperatorKind::OO_Arrow) {
assert(expr.getNumArgs() >= 1);
TransferExpressionAccessForDeref(expr.getArg(0), env);
return;
}
if (expr.getOperator() == clang::OverloadedOperatorKind::OO_PlusEqual ||
expr.getOperator() == clang::OverloadedOperatorKind::OO_MinusEqual) {
assert(expr.getNumArgs() == 2);
TransferExpressionAccessForCheck(expr.getArg(0), env);
if (auto* iterator = UnwrapAsIterator(expr.getArg(0), env)) {
SetIsValid(env, *iterator, env.makeAtomicBoolValue());
SetIsEnd(env, *iterator, env.makeAtomicBoolValue());
CloneIterator(&expr, *iterator, env);
}
return;
}
if (expr.getOperator() == clang::OverloadedOperatorKind::OO_Plus ||
expr.getOperator() == clang::OverloadedOperatorKind::OO_Minus) {
if (expr.getNumArgs() < 2) {
return;
}
TransferExpressionAccessForCheck(expr.getArg(0), env);
TransferExpressionAccessForCheck(expr.getArg(1), env);
auto deduce_return_value = [&](const clang::Expr* a,
const clang::Expr* b) {
clang::dataflow::RecordStorageLocation* iterator =
UnwrapAsIterator(a, env);
if (!iterator || !b->getType()->isIntegerType()) {
return;
}
CloneIterator(&expr, *iterator, env);
};
deduce_return_value(expr.getArg(0), expr.getArg(1));
deduce_return_value(expr.getArg(1), expr.getArg(0));
return;
}
if (expr.getOperator() == clang::OverloadedOperatorKind::OO_Equal) {
auto* lhs = UnwrapAsIterator(&expr, env);
auto* rhs = UnwrapAsIterator(expr.getArg(1), env);
if (lhs) {
assert(rhs);
SetContainerValue(env, *lhs, *GetContainerValue(env, *rhs));
}
return;
}
if (expr.getOperator() == clang::OverloadedOperatorKind::OO_EqualEqual ||
expr.getOperator() == clang::OverloadedOperatorKind::OO_ExclaimEqual) {
assert(expr.getNumArgs() >= 2);
TransferExpressionAccessForCheck(expr.getArg(0), env);
TransferExpressionAccessForCheck(expr.getArg(1), env);
clang::dataflow::RecordStorageLocation* lhs_it =
UnwrapAsIterator(expr.getArg(0), env);
clang::dataflow::RecordStorageLocation* rhs_it =
UnwrapAsIterator(expr.getArg(1), env);
if (!lhs_it || !rhs_it) {
return;
}
DebugStream() << DebugString(env, *lhs_it) << '\n';
DebugStream() << DebugString(env, *rhs_it) << '\n';
if (GetContainerValue(env, *lhs_it) != GetContainerValue(env, *rhs_it)) {
Report(kInvalidIteratorComparison, expr);
}
const auto& formula = ForceBoolValue(env, expr);
auto& arena = env.arena();
if (expr.getOperator() == clang::OverloadedOperatorKind::OO_EqualEqual) {
TransferIteratorsEquality(env, formula, lhs_it, rhs_it);
TransferIteratorsInequality(env, arena.makeNot(formula), lhs_it,
rhs_it);
} else {
TransferIteratorsInequality(env, formula, lhs_it, rhs_it);
TransferIteratorsEquality(env, arena.makeNot(formula), lhs_it, rhs_it);
}
return;
}
if (expr.getOperator() == clang::OverloadedOperatorKind::OO_PlusPlus ||
expr.getOperator() == clang::OverloadedOperatorKind::OO_MinusMinus) {
assert(expr.getNumArgs());
TransferExpressionAccessForDeref(expr.getArg(0), env);
if (auto* iterator = UnwrapAsIterator(expr.getArg(0), env)) {
SetIsValid(env, *iterator, env.makeAtomicBoolValue());
SetIsEnd(env, *iterator, env.makeAtomicBoolValue());
CloneIterator(&expr, *iterator, env);
}
return;
}
}
void Transfer(const clang::CastExpr& value_stmt,
clang::dataflow::Environment& env) {
if (auto* expr = clang::dyn_cast<clang::ImplicitCastExpr>(&value_stmt)) {
Transfer(*expr, env);
}
}
void Transfer(const clang::ImplicitCastExpr& expr,
clang::dataflow::Environment& env) {
if (expr.getCastKind() == clang::CastKind::CK_LValueToRValue) {
TransferExpressionAccessForDeref(expr.getSubExpr(), env);
}
}
void TransferIteratorsEquality(clang::dataflow::Environment& env,
const clang::dataflow::Formula& formula,
clang::dataflow::RecordStorageLocation* lhs,
clang::dataflow::RecordStorageLocation* rhs) {
auto& arena = env.arena();
env.assume(arena.makeImplies(
formula, arena.makeEquals(GetIsValid(env, *lhs)->formula(),
GetIsValid(env, *rhs)->formula())));
env.assume(arena.makeImplies(
formula, arena.makeEquals(GetIsEnd(env, *lhs)->formula(),
GetIsEnd(env, *rhs)->formula())));
}
void TransferIteratorsInequality(
clang::dataflow::Environment& env,
const clang::dataflow::Formula& formula,
clang::dataflow::RecordStorageLocation* lhs,
clang::dataflow::RecordStorageLocation* rhs) {
auto& arena = env.arena();
env.assume(arena.makeImplies(
arena.makeAnd(formula, GetIsEnd(env, *lhs)->formula()),
GetIsValid(env, *rhs)->formula()));
env.assume(arena.makeImplies(
arena.makeAnd(formula, GetIsEnd(env, *rhs)->formula()),
GetIsValid(env, *lhs)->formula()));
}
void TransferExpressionAccessForCheck(const clang::Expr* expr,
clang::dataflow::Environment& env) {
clang::dataflow::RecordStorageLocation* iterator =
UnwrapAsIterator(expr, env);
if (!iterator) {
return;
}
if (env.allows(GetIsValid(env, *iterator)->formula())) {
return;
}
if (env.proves(GetIsEnd(env, *iterator)->formula())) {
return;
}
TransferExpressionAccessForDeref(expr, env);
}
void TransferExpressionAccessForDeref(const clang::Expr* expr,
clang::dataflow::Environment& env) {
clang::dataflow::RecordStorageLocation* iterator =
UnwrapAsIterator(expr, env);
if (!iterator) {
return;
}
bool is_valid = env.proves(GetIsValid(env, *iterator)->formula());
DebugStream() << "[ACCESS] " << DebugString(env, *iterator) << '\n';
if (is_valid) {
return;
}
Report(kInvalidIteratorUsage, *expr);
}
void InvalidateContainer(clang::dataflow::Environment& env,
clang::dataflow::Value& container) {
for (auto& p : iterator_to_container_) {
if (p.second != &container) {
continue;
}
auto* value = GetContainerValue(env, *p.first);
if (!value) {
continue;
}
DebugStream() << DebugString(env, *p.first) << '\n';
SetIsValid(env, *p.first, env.getBoolLiteralValue(false));
}
}
void InvalidateIterator(clang::dataflow::Environment& env,
clang::dataflow::RecordStorageLocation& iterator) {
SetIsValid(env, iterator, env.getBoolLiteralValue(false));
}
void PopulateIteratorValue(clang::dataflow::RecordStorageLocation* iterator,
clang::dataflow::Value& container,
clang::dataflow::BoolValue& is_valid,
clang::dataflow::BoolValue& is_end,
clang::dataflow::Environment& env) {
iterator_types_mapping_.insert(iterator->getType().getCanonicalType());
SetContainerValue(env, *iterator, container);
SetIsValid(env, *iterator, is_valid);
SetIsEnd(env, *iterator, is_end);
}
void CloneIterator(const clang::Expr* expr,
clang::dataflow::RecordStorageLocation& iterator,
clang::dataflow::Environment& env) {
auto* container = GetContainerValue(env, iterator);
TransferCallReturningIterator(expr, *container, env.makeAtomicBoolValue(),
env.makeAtomicBoolValue(), env);
}
const clang::Expr* Unwrap(const clang::Expr* E) {
if (auto* implicitcast = clang::dyn_cast<clang::ImplicitCastExpr>(E)) {
return implicitcast->getSubExpr();
}
if (auto* construct = clang::dyn_cast<clang::CXXConstructExpr>(E)) {
if (construct->getNumArgs()) {
return construct->getArg(0);
}
}
return nullptr;
}
clang::dataflow::RecordStorageLocation* UnwrapAsIterator(
const clang::Expr* expr,
const clang::dataflow::Environment& env) {
while (expr) {
clang::dataflow::RecordStorageLocation* loc = nullptr;
if (expr->isGLValue()) {
loc = clang::dyn_cast_or_null<clang::dataflow::RecordStorageLocation>(
env.getStorageLocation(*expr));
} else if (expr->isPRValue() && expr->getType()->isRecordType()) {
loc = &env.getResultObjectLocation(*expr);
}
if (loc) {
if (IsIterator(loc->getType().getCanonicalType())) {
return loc;
}
}
expr = Unwrap(expr);
}
return nullptr;
}
clang::dataflow::Value* GetContainerValue(
const clang::dataflow::Environment& env,
const clang::dataflow::RecordStorageLocation& loc) {
return GetSyntheticFieldWithName("container", env, loc);
}
void SetContainerValue(clang::dataflow::Environment& env,
clang::dataflow::RecordStorageLocation& loc,
clang::dataflow::Value& res) {
iterator_to_container_[&loc] = &res;
SetSyntheticFieldWithName("container", env, loc, res);
}
void SwapContainerValue(clang::dataflow::Environment& env,
clang::dataflow::RecordStorageLocation& loc_a,
clang::dataflow::RecordStorageLocation& loc_b) {
iterator_to_container_[&loc_a] = GetContainerValue(env, loc_b);
iterator_to_container_[&loc_b] = GetContainerValue(env, loc_a);
SwapSyntheticFieldWithName("container", env, loc_a, loc_b);
}
bool IsIterator(clang::QualType type) {
return iterator_types_mapping_.count(type.getCanonicalType()) != 0;
}
std::string DebugString(
const clang::dataflow::Environment& env,
const clang::dataflow::RecordStorageLocation& iterator) {
auto* container = GetContainerValue(env, iterator);
std::string res;
const auto& formula = GetIsValid(env, iterator)->formula();
const bool is_valid = env.proves(formula);
const bool is_invalid = env.proves(env.arena().makeNot(formula));
llvm::StringRef status = is_valid ? "VALID"
: is_invalid ? "INVALID"
: "MAYBE_INVALID";
llvm::raw_string_ostream(res) << &iterator << " (container: " << container
<< " status: " << status << ")";
return res;
}
template <size_t N>
void Report(const char (&error_message)[N], const clang::Expr& expr) {
clang::SourceLocation location = expr.getSourceRange().getBegin();
if (reported_source_locations_.count({location, error_message})) {
return;
}
reported_source_locations_.insert({location, error_message});
diagnostic_.Report(
location, diagnostic_.getCustomDiagID(
clang::DiagnosticsEngine::Level::Error, error_message));
}
clang::dataflow::ChromiumCheckModel check_model_;
clang::DiagnosticsEngine& diagnostic_;
llvm::DenseSet<clang::QualType> iterator_types_mapping_;
llvm::DenseMap<clang::dataflow::RecordStorageLocation*,
clang::dataflow::Value*>
iterator_to_container_;
llvm::DenseSet<std::pair<clang::SourceLocation, clang::StringRef>>
reported_source_locations_;
};
class IteratorInvalidationCheck
: public clang::ast_matchers::MatchFinder::MatchCallback {
public:
void Register(clang::ast_matchers::MatchFinder& finder) {
using namespace clang::ast_matchers;
finder.addMatcher(
functionDecl(isExpansionInMainFile(), isDefinition(), hasBody(stmt()))
.bind("fun"),
this);
}
void run(const clang::ast_matchers::MatchFinder::MatchResult& result) final {
if (result.SourceManager->getDiagnostics().hasUncompilableErrorOccurred()) {
return;
}
const auto* func = result.Nodes.getNodeAs<clang::FunctionDecl>("fun");
assert(func);
if (!Supported(*func)) {
return;
}
InfoStream() << "[FUNCTION] " << func->getQualifiedNameAsString() << '\n';
auto control_flow_context = clang::dataflow::AdornedCFG::build(
*func, *func->getBody(), *result.Context);
if (!control_flow_context) {
llvm::report_fatal_error(control_flow_context.takeError());
return;
}
auto solver = std::make_unique<clang::dataflow::WatchedLiteralsSolver>();
clang::dataflow::DataflowAnalysisContext analysis_context(
std::move(solver));
clang::dataflow::Environment environment(analysis_context, *func);
InvalidIteratorAnalysis analysis(func,
result.SourceManager->getDiagnostics());
analysis_context.setSyntheticFieldCallback(
std::bind(&InvalidIteratorAnalysis::GetSyntheticFields, &analysis,
std::placeholders::_1));
auto analysis_result =
runDataflowAnalysis(*control_flow_context, analysis, environment);
if (!analysis_result) {
handleAllErrors(analysis_result.takeError(),
[](const llvm::StringError& E) {});
}
}
bool Supported(const clang::FunctionDecl& func) {
if (func.isTemplated()) {
return false;
}
if (auto* method = clang::dyn_cast<clang::CXXMethodDecl>(&func)) {
return Supported(*method);
}
return true;
}
bool Supported(const clang::CXXMethodDecl& method) {
const clang::CXXRecordDecl* record_declaration = method.getParent();
if (record_declaration && record_declaration->isLambda()) {
return false;
}
if (method.isStatic()) {
return true;
}
if (method.getThisType()->isDependentType()) {
return false;
}
if (method.getParent()->isTemplateDecl()) {
return false;
}
if (method.getThisType()->isUnionType()) {
return false;
}
std::vector<clang::QualType> type_stack;
type_stack.push_back(method.getThisType());
while (!type_stack.empty()) {
clang::QualType type = type_stack.back();
type_stack.pop_back();
if (type->isUnionType()) {
return false;
}
if (clang::CXXRecordDecl* cpp_record = type->getAsCXXRecordDecl()) {
for (auto f : cpp_record->fields()) {
type_stack.push_back(f->getType());
}
}
}
return true;
}
};
class IteratorInvalidationConsumer : public clang::ASTConsumer {
public:
IteratorInvalidationConsumer(clang::CompilerInstance& instance) {}
void HandleTranslationUnit(clang::ASTContext& context) final {
llvm::TimeTraceScope TimeScope(
"IteratorInvalidationConsumer::HandleTranslationUnit");
IteratorInvalidationCheck checker;
clang::ast_matchers::MatchFinder match_finder;
checker.Register(match_finder);
match_finder.matchAST(context);
}
};
class IteratorInvalidationPluginAction : public clang::PluginASTAction {
public:
IteratorInvalidationPluginAction() = default;
private:
std::unique_ptr<clang::ASTConsumer> CreateASTConsumer(
clang::CompilerInstance& instance,
llvm::StringRef ref) final {
llvm::EnablePrettyStackTrace();
return std::make_unique<IteratorInvalidationConsumer>(instance);
}
PluginASTAction::ActionType getActionType() final {
return CmdlineBeforeMainAction;
}
bool ParseArgs(const clang::CompilerInstance&,
const std::vector<std::string>& args) final {
return true;
}
};
static clang::FrontendPluginRegistry::Add<IteratorInvalidationPluginAction> X(
"iterator-checker",
"Check c++ iterator misuse");
}