Skip to content

Commit 3085bda

Browse files
[analyzer][solver] Fix infeasible constraints (PR49642)
Additionally, this patch puts an assertion checking for feasible constraints in every place where constraints are assigned to states. Differential Revision: https://reviews.llvm.org/D98948
1 parent 68dafe4 commit 3085bda

File tree

2 files changed

+61
-22
lines changed

2 files changed

+61
-22
lines changed

clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp

Lines changed: 37 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -448,12 +448,12 @@ class EquivalenceClass : public llvm::FoldingSetNode {
448448
EquivalenceClass Other);
449449

450450
/// Return a set of class members for the given state.
451-
LLVM_NODISCARD inline SymbolSet getClassMembers(ProgramStateRef State);
451+
LLVM_NODISCARD inline SymbolSet getClassMembers(ProgramStateRef State) const;
452452
/// Return true if the current class is trivial in the given state.
453-
LLVM_NODISCARD inline bool isTrivial(ProgramStateRef State);
453+
LLVM_NODISCARD inline bool isTrivial(ProgramStateRef State) const;
454454
/// Return true if the current class is trivial and its only member is dead.
455455
LLVM_NODISCARD inline bool isTriviallyDead(ProgramStateRef State,
456-
SymbolReaper &Reaper);
456+
SymbolReaper &Reaper) const;
457457

458458
LLVM_NODISCARD static inline ProgramStateRef
459459
markDisequal(BasicValueFactory &BV, RangeSet::Factory &F,
@@ -521,7 +521,7 @@ class EquivalenceClass : public llvm::FoldingSetNode {
521521
ProgramStateRef State, SymbolSet Members,
522522
EquivalenceClass Other,
523523
SymbolSet OtherMembers);
524-
static inline void
524+
static inline bool
525525
addToDisequalityInfo(DisequalityMapTy &Info, ConstraintRangeTy &Constraints,
526526
BasicValueFactory &BV, RangeSet::Factory &F,
527527
ProgramStateRef State, EquivalenceClass First,
@@ -535,6 +535,15 @@ class EquivalenceClass : public llvm::FoldingSetNode {
535535
// Constraint functions
536536
//===----------------------------------------------------------------------===//
537537

538+
LLVM_NODISCARD LLVM_ATTRIBUTE_UNUSED bool
539+
areFeasible(ConstraintRangeTy Constraints) {
540+
return llvm::none_of(
541+
Constraints,
542+
[](const std::pair<EquivalenceClass, RangeSet> &ClassConstraint) {
543+
return ClassConstraint.second.isEmpty();
544+
});
545+
}
546+
538547
LLVM_NODISCARD inline const RangeSet *getConstraint(ProgramStateRef State,
539548
EquivalenceClass Class) {
540549
return State->get<ConstraintRange>(Class);
@@ -1397,15 +1406,6 @@ class RangeConstraintManager : public RangedConstraintManager {
13971406
return EquivalenceClass::merge(getBasicVals(), F, State, LHS, RHS);
13981407
}
13991408

1400-
LLVM_NODISCARD LLVM_ATTRIBUTE_UNUSED static bool
1401-
areFeasible(ConstraintRangeTy Constraints) {
1402-
return llvm::none_of(
1403-
Constraints,
1404-
[](const std::pair<EquivalenceClass, RangeSet> &ClassConstraint) {
1405-
return ClassConstraint.second.isEmpty();
1406-
});
1407-
}
1408-
14091409
LLVM_NODISCARD ProgramStateRef setConstraint(ProgramStateRef State,
14101410
EquivalenceClass Class,
14111411
RangeSet Constraint) {
@@ -1428,7 +1428,7 @@ class RangeConstraintManager : public RangedConstraintManager {
14281428
getRange(State, DisequalClass).Delete(getBasicVals(), F, *Point);
14291429

14301430
// If we end up with at least one of the disequal classes to be
1431-
// constrainted with an empty range-set, the state is infeasible.
1431+
// constrained with an empty range-set, the state is infeasible.
14321432
if (UpdatedConstraint.isEmpty())
14331433
return nullptr;
14341434

@@ -1574,6 +1574,9 @@ EquivalenceClass::mergeImpl(BasicValueFactory &ValueFactory,
15741574
// Assign new constraints for this class.
15751575
Constraints = CRF.add(Constraints, *this, *NewClassConstraint);
15761576

1577+
assert(areFeasible(Constraints) && "Constraint manager shouldn't produce "
1578+
"a state with infeasible constraints");
1579+
15771580
State = State->set<ConstraintRange>(Constraints);
15781581
}
15791582

@@ -1644,7 +1647,7 @@ EquivalenceClass::getMembersFactory(ProgramStateRef State) {
16441647
return State->get_context<SymbolSet>();
16451648
}
16461649

1647-
SymbolSet EquivalenceClass::getClassMembers(ProgramStateRef State) {
1650+
SymbolSet EquivalenceClass::getClassMembers(ProgramStateRef State) const {
16481651
if (const SymbolSet *Members = State->get<ClassMembers>(*this))
16491652
return *Members;
16501653

@@ -1654,12 +1657,12 @@ SymbolSet EquivalenceClass::getClassMembers(ProgramStateRef State) {
16541657
return F.add(F.getEmptySet(), getRepresentativeSymbol());
16551658
}
16561659

1657-
bool EquivalenceClass::isTrivial(ProgramStateRef State) {
1660+
bool EquivalenceClass::isTrivial(ProgramStateRef State) const {
16581661
return State->get<ClassMembers>(*this) == nullptr;
16591662
}
16601663

16611664
bool EquivalenceClass::isTriviallyDead(ProgramStateRef State,
1662-
SymbolReaper &Reaper) {
1665+
SymbolReaper &Reaper) const {
16631666
return isTrivial(State) && Reaper.isDead(getRepresentativeSymbol());
16641667
}
16651668

@@ -1694,18 +1697,22 @@ EquivalenceClass::markDisequal(BasicValueFactory &VF, RangeSet::Factory &RF,
16941697

16951698
// Disequality is a symmetric relation, so if we mark A as disequal to B,
16961699
// we should also mark B as disequalt to A.
1697-
addToDisequalityInfo(DisequalityInfo, Constraints, VF, RF, State, *this,
1698-
Other);
1699-
addToDisequalityInfo(DisequalityInfo, Constraints, VF, RF, State, Other,
1700-
*this);
1700+
if (!addToDisequalityInfo(DisequalityInfo, Constraints, VF, RF, State, *this,
1701+
Other) ||
1702+
!addToDisequalityInfo(DisequalityInfo, Constraints, VF, RF, State, Other,
1703+
*this))
1704+
return nullptr;
1705+
1706+
assert(areFeasible(Constraints) && "Constraint manager shouldn't produce "
1707+
"a state with infeasible constraints");
17011708

17021709
State = State->set<DisequalityMap>(DisequalityInfo);
17031710
State = State->set<ConstraintRange>(Constraints);
17041711

17051712
return State;
17061713
}
17071714

1708-
inline void EquivalenceClass::addToDisequalityInfo(
1715+
inline bool EquivalenceClass::addToDisequalityInfo(
17091716
DisequalityMapTy &Info, ConstraintRangeTy &Constraints,
17101717
BasicValueFactory &VF, RangeSet::Factory &RF, ProgramStateRef State,
17111718
EquivalenceClass First, EquivalenceClass Second) {
@@ -1734,8 +1741,16 @@ inline void EquivalenceClass::addToDisequalityInfo(
17341741
VF, RF, State, First.getRepresentativeSymbol());
17351742

17361743
FirstConstraint = FirstConstraint.Delete(VF, RF, *Point);
1744+
1745+
// If the First class is about to be constrained with an empty
1746+
// range-set, the state is infeasible.
1747+
if (FirstConstraint.isEmpty())
1748+
return false;
1749+
17371750
Constraints = CRF.add(Constraints, First, FirstConstraint);
17381751
}
1752+
1753+
return true;
17391754
}
17401755

17411756
inline Optional<bool> EquivalenceClass::areEqual(ProgramStateRef State,

clang/test/Analysis/PR49642.c

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// RUN: %clang_analyze_cc1 -w -verify %s \
2+
// RUN: -analyzer-checker=core \
3+
// RUN: -analyzer-checker=apiModeling.StdCLibraryFunctions
4+
5+
// expected-no-diagnostics
6+
7+
typedef ssize_t;
8+
b;
9+
10+
unsigned c;
11+
int write(int, const void *, unsigned long);
12+
13+
a() {
14+
d();
15+
while (c > 0) {
16+
b = write(0, d, c);
17+
if (b)
18+
c -= b;
19+
b < 1;
20+
}
21+
if (c && c) {
22+
// ^ no-crash
23+
}
24+
}

0 commit comments

Comments
 (0)