[clang] [LifetimeSafety] Implement a basic use-after-free diagnostic (PR #149731)
Yitzhak Mandelbaum via cfe-commits
cfe-commits at lists.llvm.org
Thu Jul 31 09:40:56 PDT 2025
================
@@ -0,0 +1,263 @@
+// RUN: %clang_cc1 -fsyntax-only -fexperimental-lifetime-safety -Wexperimental-lifetime-safety -verify %s
+
+struct MyObj {
+ int id;
+ ~MyObj() {} // Non-trivial destructor
+ MyObj operator+(MyObj);
+};
+
+//===----------------------------------------------------------------------===//
+// Basic Definite Use-After-Free (-W...permissive)
+// These are cases where the pointer is guaranteed to be dangling at the use site.
+//===----------------------------------------------------------------------===//
+
+void definite_simple_case() {
+ MyObj* p;
+ {
+ MyObj s;
+ p = &s; // expected-warning {{object whose reference is captured does not live long enough}}
+ } // expected-note {{destroyed here}}
+ (void)*p; // expected-note {{later used here}}
+}
+
+void no_use_no_error() {
+ MyObj* p;
+ {
+ MyObj s;
+ p = &s;
+ }
+}
+
+void definite_pointer_chain() {
+ MyObj* p;
+ MyObj* q;
+ {
+ MyObj s;
+ p = &s; // expected-warning {{does not live long enough}}
+ q = p;
+ } // expected-note {{destroyed here}}
+ (void)*q; // expected-note {{later used here}}
+}
+
+void definite_multiple_uses_one_warning() {
+ MyObj* p;
+ {
+ MyObj s;
+ p = &s; // expected-warning {{does not live long enough}}
+ } // expected-note {{destroyed here}}
+ (void)*p; // expected-note {{later used here}}
+ // No second warning for the same loan.
+ p->id = 1;
+ MyObj* q = p;
+ (void)*q;
+}
+
+void definite_multiple_pointers() {
+ MyObj *p, *q, *r;
+ {
+ MyObj s;
+ p = &s; // expected-warning {{does not live long enough}}
+ q = &s; // expected-warning {{does not live long enough}}
+ r = &s; // expected-warning {{does not live long enough}}
+ } // expected-note 3 {{destroyed here}}
+ (void)*p; // expected-note {{later used here}}
+ (void)*q; // expected-note {{later used here}}
+ (void)*r; // expected-note {{later used here}}
+}
+
+void definite_single_pointer_multiple_loans(bool cond) {
+ MyObj *p;
+ if (cond){
+ MyObj s;
+ p = &s; // expected-warning {{does not live long enough}}
+ } // expected-note {{destroyed here}}
+ else {
+ MyObj t;
+ p = &t; // expected-warning {{does not live long enough}}
+ } // expected-note {{destroyed here}}
+ (void)*p; // expected-note 2 {{later used here}}
+}
+
+
+//===----------------------------------------------------------------------===//
+// Potential (Maybe) Use-After-Free (-W...strict)
+// These are cases where the pointer *may* become dangling, depending on the path taken.
+//===----------------------------------------------------------------------===//
+
+void potential_if_branch(bool cond) {
+ MyObj safe;
+ MyObj* p = &safe;
+ if (cond) {
+ MyObj temp;
+ p = &temp; // expected-warning {{object whose reference is captured may not live long enough}}
+ } // expected-note {{destroyed here}}
+ (void)*p; // expected-note {{later used here}}
+}
+
+void potential_else_branch(bool cond) {
+ MyObj safe;
+ MyObj* p = &safe;
+ if (cond) {
+ MyObj temp;
+ p = &temp; // expected-warning {{may not live long enough}}
+ } // expected-note {{destroyed here}}
+ (void)*p; // expected-note {{later used here}}
+}
+
+// If all paths lead to a dangle, it becomes a definite error.
+void potential_becomes_definite(bool cond) {
+ MyObj* p;
+ if (cond) {
+ MyObj temp1;
+ p = &temp1; // expected-warning {{does not live long enough}}
+ } // expected-note {{destroyed here}}
+ else {
+ MyObj temp2;
+ p = &temp2; // expected-warning {{does not live long enough}}
+ } // expected-note {{destroyed here}}
+ (void)*p; // expected-note 2 {{later used here}}
+}
+
+void definite_potential_together(bool cond) {
+ MyObj safe;
+ MyObj* p_maybe = &safe;
+ MyObj* p_definite = nullptr;
+
+ {
+ MyObj s;
+ p_definite = &s; // expected-warning {{does not live long enough}}
+ if (cond) {
+ p_maybe = &s; // expected-warning {{may not live long enough}}
+ }
+ } // expected-note 2 {{destroyed here}}
+ (void)*p_definite; // expected-note {{later used here}}
+ (void)*p_maybe; // expected-note {{later used here}}
+}
+
+void definite_overrides_potential(bool cond) {
+ MyObj safe;
+ MyObj* p;
+ MyObj* q;
+ {
+ MyObj s;
+ q = &s; // expected-warning {{does not live long enough}}
+ p = q;
+ } // expected-note {{destroyed here}}
+
+ if (cond) {
+ // 'q' is conditionally "rescued". 'p' is not.
+ q = &safe;
+ }
+
+ // The use of 'p' is a definite error because it was never rescued.
+ (void)*q;
+ (void)*p; // expected-note {{later used here}}
+ (void)*q;
+}
+
+
+//===----------------------------------------------------------------------===//
+// Control Flow Tests
+//===----------------------------------------------------------------------===//
+
+void potential_for_loop_use_after_loop(MyObj safe) {
+ MyObj* p = &safe;
+ for (int i = 0; i < 1; ++i) {
+ MyObj s;
+ p = &s; // expected-warning {{may not live long enough}}
+ } // expected-note {{destroyed here}}
+ (void)*p; // expected-note {{later used here}}
+}
+
+void potential_for_loop_use_before_loop(MyObj safe) {
----------------
ymand wrote:
"before loop" is a little confusing. maybe "use before loop body"?
https://github.com/llvm/llvm-project/pull/149731
More information about the cfe-commits
mailing list