Skip to content

[LifetimeSafety] Implement a basic use-after-free diagnostic #149731

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 49 additions & 3 deletions clang/include/clang/Analysis/Analyses/LifetimeSafety.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,35 @@
#define LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_H
#include "clang/Analysis/AnalysisDeclContext.h"
#include "clang/Analysis/CFG.h"
#include "clang/Basic/SourceLocation.h"
#include "llvm/ADT/DenseMapInfo.h"
#include "llvm/ADT/ImmutableMap.h"
#include "llvm/ADT/ImmutableSet.h"
#include "llvm/ADT/StringMap.h"
#include <memory>

namespace clang::lifetimes {

/// Enum to track the confidence level of a potential error.
enum class Confidence {
None,
Maybe, // Reported as a potential error (-Wlifetime-safety-strict)
Definite // Reported as a definite error (-Wlifetime-safety-permissive)
};

class LifetimeSafetyReporter {
public:
LifetimeSafetyReporter() = default;
virtual ~LifetimeSafetyReporter() = default;

virtual void reportUseAfterFree(const Expr *IssueExpr, const Expr *UseExpr,
SourceLocation FreeLoc,
Confidence Confidence) {}
};

/// The main entry point for the analysis.
void runLifetimeSafetyAnalysis(AnalysisDeclContext &AC);
void runLifetimeSafetyAnalysis(AnalysisDeclContext &AC,
LifetimeSafetyReporter *Reporter);

namespace internal {
// Forward declarations of internal types.
Expand All @@ -53,6 +74,7 @@ template <typename Tag> struct ID {
IDBuilder.AddInteger(Value);
}
};

template <typename Tag>
inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, ID<Tag> ID) {
return OS << ID.Value;
Expand All @@ -66,6 +88,7 @@ using OriginID = ID<struct OriginTag>;
// TODO(opt): Consider using a bitset to represent the set of loans.
using LoanSet = llvm::ImmutableSet<LoanID>;
using OriginSet = llvm::ImmutableSet<OriginID>;
using ExpiredLoanMap = llvm::ImmutableMap<LoanID, const Fact *>;

/// A `ProgramPoint` identifies a location in the CFG by pointing to a specific
/// `Fact`. identified by a lifetime-related event (`Fact`).
Expand All @@ -78,7 +101,8 @@ using ProgramPoint = const Fact *;
/// encapsulates the various dataflow analyses.
class LifetimeSafetyAnalysis {
public:
LifetimeSafetyAnalysis(AnalysisDeclContext &AC);
LifetimeSafetyAnalysis(AnalysisDeclContext &AC,
LifetimeSafetyReporter *Reporter);
~LifetimeSafetyAnalysis();

void run();
Expand All @@ -87,7 +111,7 @@ class LifetimeSafetyAnalysis {
LoanSet getLoansAtPoint(OriginID OID, ProgramPoint PP) const;

/// Returns the set of loans that have expired at a specific program point.
LoanSet getExpiredLoansAtPoint(ProgramPoint PP) const;
ExpiredLoanMap getExpiredLoansAtPoint(ProgramPoint PP) const;

/// Finds the OriginID for a given declaration.
/// Returns a null optional if not found.
Expand All @@ -110,6 +134,7 @@ class LifetimeSafetyAnalysis {

private:
AnalysisDeclContext &AC;
LifetimeSafetyReporter *Reporter;
std::unique_ptr<LifetimeFactory> Factory;
std::unique_ptr<FactManager> FactMgr;
std::unique_ptr<LoanPropagationAnalysis> LoanPropagation;
Expand All @@ -118,4 +143,25 @@ class LifetimeSafetyAnalysis {
} // namespace internal
} // namespace clang::lifetimes

namespace llvm {
template <typename Tag>
struct DenseMapInfo<clang::lifetimes::internal::ID<Tag>> {
using ID = clang::lifetimes::internal::ID<Tag>;

static inline ID getEmptyKey() {
return {DenseMapInfo<uint32_t>::getEmptyKey()};
}

static inline ID getTombstoneKey() {
return {DenseMapInfo<uint32_t>::getTombstoneKey()};
}

static unsigned getHashValue(const ID &Val) {
return DenseMapInfo<uint32_t>::getHashValue(Val.Value);
}

static bool isEqual(const ID &LHS, const ID &RHS) { return LHS == RHS; }
};
} // namespace llvm

#endif // LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_H
5 changes: 4 additions & 1 deletion clang/include/clang/Basic/DiagnosticGroups.td
Original file line number Diff line number Diff line change
Expand Up @@ -533,7 +533,10 @@ def Dangling : DiagGroup<"dangling", [DanglingAssignment,
DanglingGsl,
ReturnStackAddress]>;

def LifetimeSafety : DiagGroup<"experimental-lifetime-safety">;
def LifetimeSafetyPermissive : DiagGroup<"experimental-lifetime-safety-permissive">;
def LifetimeSafetyStrict : DiagGroup<"experimental-lifetime-safety-strict">;
def LifetimeSafety : DiagGroup<"experimental-lifetime-safety", [LifetimeSafetyPermissive,
LifetimeSafetyStrict]>;

def DistributedObjectModifiers : DiagGroup<"distributed-object-modifiers">;
def DllexportExplicitInstantiationDecl : DiagGroup<"dllexport-explicit-instantiation-decl">;
Expand Down
11 changes: 8 additions & 3 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -10641,9 +10641,14 @@ def warn_dangling_reference_captured_by_unknown : Warning<
"object whose reference is captured will be destroyed at the end of "
"the full-expression">, InGroup<DanglingCapture>;

def warn_experimental_lifetime_safety_dummy_warning : Warning<
"todo: remove this warning after we have atleast one warning based on the lifetime analysis">,
InGroup<LifetimeSafety>, DefaultIgnore;
def warn_lifetime_safety_use_after_free_permissive : Warning<
"object whose reference is captured does not live long enough">,
InGroup<LifetimeSafetyPermissive>, DefaultIgnore;
def warn_lifetime_safety_use_after_free_strict : Warning<
"object whose reference is captured may not live long enough">,
InGroup<LifetimeSafetyStrict>, DefaultIgnore;
def note_lifetime_safety_used_here : Note<"later used here">;
def note_lifetime_safety_destroyed_here : Note<"destroyed here">;

// For non-floating point, expressions of the form x == x or x != x
// should result in a warning, since these always evaluate to a constant.
Expand Down
Loading
Loading