diff --git a/.github/workflows/ci-macos.yml b/.github/workflows/ci-macos.yml index dc9ee56..22b878a 100644 --- a/.github/workflows/ci-macos.yml +++ b/.github/workflows/ci-macos.yml @@ -16,7 +16,7 @@ jobs: strategy: matrix: build_type: [ Release ] - os: [macos-13, macos-14, macos-15] + os: [macos-latest] compiler: - cc: cc cxx: c++ diff --git a/integration_test/legacy/attr-psets.cpp b/integration_test/legacy/attr-psets.cpp index 44e561b..6354a79 100644 --- a/integration_test/legacy/attr-psets.cpp +++ b/integration_test/legacy/attr-psets.cpp @@ -1102,10 +1102,11 @@ void kill_materialized_temporary() { void delete_pointee(int *p) { int *q = p; __lifetime_pset(q); // expected-warning {{((null), *p)}} + // expected-note@+1 {{deleted here}} delete q; // expected-warning {{naked new-deletes disables lifetime analysis}} __lifetime_pset(q); // expected-warning {{((invalid))}} __lifetime_pset(p); // expected-warning {{((invalid))}} -} +} // expected-warning {{returning a dangling pointer as output value '*p'}} int throw_local() { int i; @@ -1673,6 +1674,7 @@ struct Node { __lifetime_pset(next_temp); // expected-warning {{(*(*this).next)}} // expected-warning@-1 {{((global), *(*this).next)}} // expected-warning@-2 {{((global), *(*this).next)}} + // expected-note@+1 {{deleted here}} delete cur; // expected-warning {{naked new-deletes disables lifetime analysis}} __lifetime_pset(next_temp); // expected-warning {{(*(*this).next)}} // expected-warning@-1 {{((global), *(*this).next)}} @@ -1682,7 +1684,7 @@ struct Node { // expected-warning@-2 {{(invalid)}} cur = next_temp; } - } + } // expected-warning {{returning a dangling pointer as output value '*this'}} }; namespace boundedness { diff --git a/integration_test/options/warn_delete.cpp b/integration_test/options/warn_delete.cpp new file mode 100644 index 0000000..dbe9cdc --- /dev/null +++ b/integration_test/options/warn_delete.cpp @@ -0,0 +1,32 @@ +#include "../feature/common.h" + +void free(int* p) +{ + delete p; + __lifetime_pset(p); // expected-warning {{pset(p) = ((invalid))}} +} + +void free2(void* p) +{ + delete (int*)p; + __lifetime_pset(p); // expected-warning {{pset(p) = ((invalid))}} +} + +void free3(void* p) +{ + delete static_cast(p); + __lifetime_pset(p); // expected-warning {{pset(p) = ((invalid))}} +} + +void free4(void* p) +{ + auto* t = (int*)p; + delete t; + __lifetime_pset(p); // expected-warning {{pset(p) = ((invalid))}} +} + +CPPSAFE_POST("*p", ":invalid") +void free5(int* p) +{ + delete p; +} diff --git a/integration_test/options/warn_delete_strict.cpp b/integration_test/options/warn_delete_strict.cpp new file mode 100644 index 0000000..7978d43 --- /dev/null +++ b/integration_test/options/warn_delete_strict.cpp @@ -0,0 +1,41 @@ +// ARGS: --Wlifetime-disabled + +#include "../feature/common.h" + +void free(int* p) +{ + // expected-note@+1 {{deleted here}} + delete p; // expected-warning {{naked new-deletes disables lifetime analysis}} + __lifetime_pset(p); // expected-warning {{pset(p) = ((invalid))}} +} // expected-warning {{returning a dangling pointer as output value '*p'}} + +void free2(void* p) +{ + // expected-note@+1 {{deleted here}} + delete (int*)p; // expected-warning {{naked new-deletes disables lifetime analysis}} + // expected-warning@-1 {{unsafe cast disables lifetime analysis}} + __lifetime_pset(p); // expected-warning {{pset(p) = ((invalid))}} +} // expected-warning {{returning a dangling pointer as output value '*p'}} + +void free3(void* p) +{ + // expected-note@+1 {{deleted here}} + delete static_cast(p); // expected-warning {{naked new-deletes disables lifetime analysis}} + // expected-warning@-1 {{unsafe cast disables lifetime analysis}} + __lifetime_pset(p); // expected-warning {{pset(p) = ((invalid))}} +} // expected-warning {{returning a dangling pointer as output value '*p'}} + +void free4(void* p) +{ + auto* t = (int*)p; + // expected-warning@-1 {{unsafe cast disables lifetime analysis}} + // expected-note@+1 {{deleted here}} + delete t; // expected-warning {{naked new-deletes disables lifetime analysis}} + __lifetime_pset(p); // expected-warning {{pset(p) = ((invalid))}} +} // expected-warning {{returning a dangling pointer as output value '*p'}} + +CPPSAFE_POST("*p", ":invalid") +void free5(int* p) +{ + delete p; // expected-warning {{naked new-deletes disables lifetime analysis}} +} \ No newline at end of file diff --git a/integration_test/safety/contract_return_aggr.cpp b/integration_test/safety/contract_return_aggr.cpp index 8d76f98..36bb842 100644 --- a/integration_test/safety/contract_return_aggr.cpp +++ b/integration_test/safety/contract_return_aggr.cpp @@ -76,7 +76,6 @@ B createStaticB() { static int t = 0; static const B b { .x = &t }; - __lifetime_pmap(); return b; } diff --git a/integration_test/safety/new_delete.cpp b/integration_test/safety/new_delete.cpp new file mode 100644 index 0000000..aa3e2ee --- /dev/null +++ b/integration_test/safety/new_delete.cpp @@ -0,0 +1,10 @@ +#include "../feature/common.h" + +void foo() +{ + auto* p = new int{}; + auto* q = p; + delete q; + __lifetime_pset(p); // expected-warning {{((global))}} + __lifetime_pset(q); // expected-warning {{((invalid))}} +} \ No newline at end of file diff --git a/lib/lifetime/LifetimePsetBuilder.cpp b/lib/lifetime/LifetimePsetBuilder.cpp index a338081..ad49518 100644 --- a/lib/lifetime/LifetimePsetBuilder.cpp +++ b/lib/lifetime/LifetimePsetBuilder.cpp @@ -191,10 +191,23 @@ class PSetsBuilder final : public ConstStmtVisitor, public P for (const auto& Var : PS.vars()) { // TODO: diagnose if we are deleting the buffer of on owner? invalidateVar(Var, InvalidationReason::deleted(DE->getSourceRange(), CurrentBlock)); + + // void free(int* p) + // after the call, *p is invalid. but to avoid noisy, turn it off by default + if (Reporter.getOptions().LifetimeDisabled) { + setPSet(PSet::singleton(Var), + PSet::invalid(InvalidationReason::deleted(DE->getSourceRange(), CurrentBlock)), + DE->getSourceRange()); + } } - setPSet(getPSet(DE->getArgument()->IgnoreImplicit()), - PSet::invalid(InvalidationReason::deleted(DE->getSourceRange(), CurrentBlock)), DE->getSourceRange()); + const auto* E = DE->getArgument()->IgnoreCasts(); + // auto* p = new int{}; delete p; + if (E->isLValue()) { + setPSet(getPSet(E), PSet::invalid(InvalidationReason::deleted(DE->getSourceRange(), CurrentBlock)), + DE->getSourceRange()); + return; + } } } @@ -1866,7 +1879,9 @@ void PSetsBuilder::onFunctionFinish(const CFGBlock& B) } auto OutVarIt = PMap.find(OutVarInPostCond); - CPPSAFE_ASSERT(OutVarIt != PMap.end()); + if (OutVarIt == PMap.end()) { + continue; + } OutVarIt->second.transformVars([&](Variable V) { if (!V.isField()) { @@ -1910,6 +1925,11 @@ void PSetsBuilder::onFunctionFinish(const CFGBlock& B) // TODO: add this&& if (OutPSet.containsInvalid()) { + auto It = PostConditions.find(OutVar); + if (It != PostConditions.end() && It->second.containsInvalid()) { + continue; + } + Reporter.warnNullDangling( WarnType::Dangling, Range, ValueSource::OutputParam, OutVar.getName(), !OutPSet.isInvalid()); OutPSet.explainWhyInvalid(Reporter); diff --git a/lib/lifetime/LifetimeTypeCategory.cpp b/lib/lifetime/LifetimeTypeCategory.cpp index 8f09eb2..758172a 100644 --- a/lib/lifetime/LifetimeTypeCategory.cpp +++ b/lib/lifetime/LifetimeTypeCategory.cpp @@ -22,7 +22,6 @@ #include #include #include -#include #include #include @@ -521,9 +520,6 @@ static QualType getPointeeType(const CXXRecordDecl* R) } if (classifyTypeCategory(PointeeType) != TypeCategory::Pointer) { - // TODO: diag? - llvm::errs() << "begin() function does not return a Pointer!\n"; - FoundMD->dump(); return {}; } PointeeType = getPointeeType(PointeeType);