[clang] [LifetimeSafety] Add support for `new`/`delete` (PR #192504)
Utkarsh Saxena via cfe-commits
cfe-commits at lists.llvm.org
Sun Apr 19 11:28:16 PDT 2026
================
@@ -2663,6 +2663,213 @@ int *noreturn_dead_nested(bool cond, bool cond2, int *num) {
} // namespace conditional_operator_control_flow
+namespace heap_allocation {
+
+//===----------------------------------------------------------------------===//
+// new
+//===----------------------------------------------------------------------===//
+
+void new_view_from_dead_scope() {
+ View *p;
+ {
+ MyObj obj;
+ p = new View(obj); // expected-warning {{object whose reference is captured does not live long enough}}
+ } // expected-note {{destroyed here}}
+ p->use(); // expected-note {{later used here}}
+}
+
+void new_int_basic() {
+ int *p = new int; // expected-warning {{allocated object does not live long enough}}
+ delete p; // expected-note {{freed here}}
+ (void)*p; // expected-note {{later used here}}
+}
+
+void new_int_parens() {
+ int *p = new int(); // expected-warning {{allocated object does not live long enough}}
+ delete p; // expected-note {{freed here}}
+ (void)*p; // expected-note {{later used here}}
+}
+
+void new_int_braces() {
+ int *p = new int{}; // expected-warning {{allocated object does not live long enough}}
+ delete p; // expected-note {{freed here}}
+ (void)*p; // expected-note {{later used here}}
+}
+
+void conditional_delete(bool cond) {
+ int *p1 = new int; // expected-warning {{allocated object does not live long enough}}
+ int *p2 = new int;
+ delete (cond ? p1 : p2); // expected-note {{freed here}}
+ (void)*p1; // expected-note {{later used here}}
+}
+
+int* foo(int* x [[clang::lifetimebound]], int* y [[clang::lifetimebound]]);
+
+void delete_returned_from_call() {
+ int* x = new int(1); // expected-warning {{allocated object does not live long enough}}
+ int* y = new int(2); // expected-warning {{allocated object does not live long enough}}
+ delete foo(x, y); // expected-note 2 {{freed here}}
+ (void)x; // expected-note {{later used here}}
+ (void)y; // expected-note {{later used here}}
+}
+
+void new_pointer_from_pointer() {
+ MyObj **p;
+ {
+ MyObj obj;
+ MyObj *q = &obj; // expected-warning {{object whose reference is captured does not live long enough}}
+ p = new MyObj *(q);
+ } // expected-note {{destroyed here}}
+ (void)**p; // expected-note {{later used here}}
+}
+
+void new_pointer_from_dead_object() {
+ MyObj **p;
+ {
+ MyObj obj;
+ p = new MyObj *(&obj); // expected-warning {{object whose reference is captured does not live long enough}}
+ } // expected-note {{destroyed here}}
+ (void)**p; // expected-note {{later used here}}
+}
+
+struct MultiView {
+ MultiView(MyObj& a [[clang::lifetimebound]], MyObj& b [[clang::lifetimebound]]);
+};
+
+void new_multiview_from_mixed_scope() {
+ MyObj obj1;
+ MultiView *p;
+ {
+ MyObj obj2;
+ p = new MultiView(obj1, obj2); // expected-warning {{object whose reference is captured does not live long enough}}
+ } // expected-note {{destroyed here}}
+ (void)p; // expected-note {{later used here}}
+}
+
+void new_array_basic() {
+ int *p = new int[2]; // expected-warning {{allocated object does not live long enough}}
+ delete[] p; // expected-note {{freed here}}
+ (void)p[0]; // expected-note {{later used here}}
+}
+
+void new_array_parens() {
+ int *p = new int[2](); // expected-warning {{allocated object does not live long enough}}
+ delete[] p; // expected-note {{freed here}}
+ (void)p[0]; // expected-note {{later used here}}
+}
+
+void new_array_braces() {
+ int *p = new int[2]{}; // expected-warning {{allocated object does not live long enough}}
+ delete[] p; // expected-note {{freed here}}
+ (void)p[0]; // expected-note {{later used here}}
+}
+
+// FIXME: https://github.com/llvm/llvm-project/issues/187471
+void new_pointer_array_from_dead_objects() {
+ MyObj **arr;
+ {
+ MyObj a, b;
+ arr = new MyObj *[2]{&a, &b};
+ }
+ (void)arr[0]->id;
+ (void)arr[1]->id;
+}
+
+struct PointerArrayFieldHolder {
+ MyObj **Ptrs;
+};
+
+// FIXME: https://github.com/llvm/llvm-project/issues/187471
+void pointer_array_field_sensitivity() {
+ PointerArrayFieldHolder h;
+ {
+ MyObj a, b;
+ h.Ptrs = new MyObj *[2]{&a, &b};
+ }
+ (void)h.Ptrs[0]->id;
+}
+
+//===----------------------------------------------------------------------===//
+// delete
+//===----------------------------------------------------------------------===//
+
+void delete_direct_use_after_free() {
+ MyObj *p = new MyObj; // expected-warning {{allocated object does not live long enough}}
+ delete p; // expected-note {{freed here}}
+ (void)p->id; // expected-note {{later used here}}
+}
+
+void delete_alias_use_after_free() {
+ MyObj *p = new MyObj; // expected-warning {{allocated object does not live long enough}}
+ MyObj *q = p;
+ delete p; // expected-note {{freed here}}
+ (void)q->id; // expected-note {{later used here}}
+}
+
+void delete_pointer_propagation_use_after_free() {
+ MyObj *p = new MyObj; // expected-warning {{allocated object does not live long enough}}
+ MyObj **pp = &p;
+ delete p; // expected-note {{freed here}}
+ (void)(*pp)->id; // expected-note {{later used here}}
+}
+
+void delete_array_use_after_free() {
+ int *p = new int[2]; // expected-warning {{allocated object does not live long enough}}
+ delete[] p; // expected-note {{freed here}}
+ (void)p[1]; // expected-note {{later used here}}
+}
+
+void delete_nullptr_no_warning() {
+ int *p = nullptr;
+ delete p;
+}
+
+void delete_array_nullptr_no_warning() {
+ int *p = nullptr;
+ delete[] p;
+}
+
+struct ClassSpecificDelete {
+ int X;
+ static void operator delete(void *);
+};
+
+void class_specific_operator_delete_use_after_free() {
+ ClassSpecificDelete *p = new ClassSpecificDelete; // expected-warning {{allocated object does not live long enough}}
+ delete p; // expected-note {{freed here}}
+ (void)p->X; // expected-note {{later used here}}
+}
+
+struct PointerFieldHolder {
+ MyObj *Ptr;
+};
+
+// FIXME: https://github.com/llvm/llvm-project/issues/184344
+void placement_new_pointer_field_use_after_scope() {
+ PointerFieldHolder h;
+ PointerFieldHolder *p = &h;
+ {
+ MyObj obj;
+ p = new (&h) PointerFieldHolder{&obj};
+ }
+ (void)p->Ptr->id;
+}
+
+// FIXME: https://github.com/llvm/llvm-project/issues/184344
+void delete_through_pointer_field() {
+ PointerFieldHolder h{new MyObj};
+ delete h.Ptr;
+ (void)h.Ptr->id;
+}
+
+void delete_stack_object() {
+ MyObj obj;
+ delete &obj;
+ (void)obj.id;
+}
----------------
usx95 wrote:
It seems important to fix these in this PR. The solution seems to be doing exactly what we do for invalidations: Do not look just at the directly destroyed loans, but to all the live origins holding the same loans with the same path.
https://github.com/llvm/llvm-project/pull/192504
More information about the cfe-commits
mailing list