================
@@ -454,6 +462,17 @@ void FactsGenerator::handleLifetimeEnds(const
CFGLifetimeEnds &LifetimeEnds) {
BL->getID(), LifetimeEnds.getTriggerStmt()->getEndLoc()));
}
}
+ // In loops, the back-edge can make a dead origin appear live at its
+ // pointee's ExpireFact. Expiring the origin here prevents that.
+ if (OriginList *List = getOriginsList(*LifetimeEndsVD)) {
+ for (OriginList *L = List; L; L = L->peelOuterOrigin()) {
----------------
aeft wrote:
If we don't go through the whole origin list, it fails the below case:
```cpp
void multi_level_pointer_in_loop() {
for (int i = 0; i < 10; ++i) {
MyObj obj;
MyObj* p;
MyObj** pp;
if (i > 5) {
p = &obj;
pp = &p;
}
(void)**pp;
}
}
```
```
/tmp/test_multi.cpp:8:12: warning: object whose reference is captured may not
live long enough [-Wlifetime-safety-strict]
8 | p = &obj;
| ^~~
/tmp/test_multi.cpp:12:3: note: destroyed here
12 | }
| ^
/tmp/test_multi.cpp:11:13: note: later used here
11 | (void)**pp;
| ^~
```
Here is the debug output:
```
Block B4:
Issue (2 (Path: obj), ToOrigin: 2 (Expr: DeclRefExpr, Decl: obj))
OriginFlow:
Dest: 3 (Expr: UnaryOperator, Type : MyObj *)
Src: 2 (Expr: DeclRefExpr, Decl: obj)
Use (5 (Decl: p, Type : MyObj *), Write)
Issue (3 (Path: p), ToOrigin: 4 (Expr: DeclRefExpr, Decl: p))
OriginFlow:
Dest: 5 (Decl: p, Type : MyObj *)
Src: 3 (Expr: UnaryOperator, Type : MyObj *)
Use (5 (Decl: p, Type : MyObj *), Read)
Issue (4 (Path: p), ToOrigin: 6 (Expr: DeclRefExpr, Decl: p))
OriginFlow:
Dest: 7 (Expr: UnaryOperator, Type : MyObj **)
Src: 6 (Expr: DeclRefExpr, Decl: p)
OriginFlow:
Dest: 8 (Expr: UnaryOperator, Type : MyObj *)
Src: 5 (Decl: p, Type : MyObj *)
Use (10 (Decl: pp, Type : MyObj **), 11 (Decl: pp, Type : MyObj *), Write)
Issue (5 (Path: pp), ToOrigin: 9 (Expr: DeclRefExpr, Decl: pp))
OriginFlow:
Dest: 10 (Decl: pp, Type : MyObj **)
Src: 7 (Expr: UnaryOperator, Type : MyObj **)
OriginFlow:
Dest: 11 (Decl: pp, Type : MyObj *)
Src: 8 (Expr: UnaryOperator, Type : MyObj *)
End of Block
Block B3:
Use (10 (Decl: pp, Type : MyObj **), 11 (Decl: pp, Type : MyObj *), Read)
Issue (6 (Path: pp), ToOrigin: 12 (Expr: DeclRefExpr, Decl: pp))
OriginFlow:
Dest: 13 (Expr: ImplicitCastExpr, Type : MyObj **)
Src: 10 (Decl: pp, Type : MyObj **)
OriginFlow:
Dest: 14 (Expr: ImplicitCastExpr, Type : MyObj *)
Src: 11 (Decl: pp, Type : MyObj *)
OriginFlow:
Dest: 15 (Expr: UnaryOperator, Type : MyObj *&)
Src: 13 (Expr: ImplicitCastExpr, Type : MyObj **)
OriginFlow:
Dest: 16 (Expr: UnaryOperator, Type : MyObj *)
Src: 14 (Expr: ImplicitCastExpr, Type : MyObj *)
OriginFlow:
Dest: 17 (Expr: ImplicitCastExpr, Type : MyObj *)
Src: 16 (Expr: UnaryOperator, Type : MyObj *)
OriginFlow:
Dest: 18 (Expr: UnaryOperator, Type : MyObj &)
Src: 17 (Expr: ImplicitCastExpr, Type : MyObj *)
Expire (2 (Path: obj))
Expire (5 (Path: pp))
Expire (6 (Path: pp))
ExpireOrigin (10 (Decl: pp, Type : MyObj **))
Expire (3 (Path: p))
Expire (4 (Path: p))
ExpireOrigin (5 (Decl: p, Type : MyObj *))
End of Block
```
`pp` has two origins (one per indirection level), both with unique OriginIDs
tied to pp's `ValueDecl`:
```
10 (Decl: pp, Type: MyObj **) // outer, holds L_p
11 (Decl: pp, Type: MyObj *) // inner, holds L_obj (propagated via flow from
p)
```
These are pp's private tracking slots, not shared with p's origin (ID=5). When
pp dies, both become unreachable. Without clearing origin 11, the checker finds
it still live and holding L_obj at Expire(obj), causing a false positive.
https://github.com/llvm/llvm-project/pull/182368
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits