[clang] [clang][dataflow] Fix crash when analyzing a coroutine (PR #85957)

Eric Li via cfe-commits cfe-commits at lists.llvm.org
Wed Mar 20 09:33:45 PDT 2024


https://github.com/tJener updated https://github.com/llvm/llvm-project/pull/85957

>From 386e7dea15739df65ad9ecb3dd7f7dcd23b6acae Mon Sep 17 00:00:00 2001
From: Eric Li <li.zhe.hua at gmail.com>
Date: Wed, 20 Mar 2024 12:15:45 -0400
Subject: [PATCH 1/2] [clang][dataflow] Fix crash when analyzing a coroutine

A coroutine function body (`CoroutineBodyStmt`) may have null
children, which causes `isa` to segfault.
---
 .../lib/Analysis/FlowSensitive/AdornedCFG.cpp |  2 +-
 .../Analysis/FlowSensitive/TransferTest.cpp   | 53 ++++++++++++++++++-
 2 files changed, 52 insertions(+), 3 deletions(-)

diff --git a/clang/lib/Analysis/FlowSensitive/AdornedCFG.cpp b/clang/lib/Analysis/FlowSensitive/AdornedCFG.cpp
index 3813b8c3ee8a23..daa73bed1bd9f5 100644
--- a/clang/lib/Analysis/FlowSensitive/AdornedCFG.cpp
+++ b/clang/lib/Analysis/FlowSensitive/AdornedCFG.cpp
@@ -103,7 +103,7 @@ buildContainsExprConsumedInDifferentBlock(
   auto CheckChildExprs = [&Result, &StmtToBlock](const Stmt *S,
                                                  const CFGBlock *Block) {
     for (const Stmt *Child : S->children()) {
-      if (!isa<Expr>(Child))
+      if (!isa_and_nonnull<Expr>(Child))
         continue;
       const CFGBlock *ChildBlock = StmtToBlock.lookup(Child);
       if (ChildBlock != Block)
diff --git a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
index a243535d387257..2dc01a655264f3 100644
--- a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
+++ b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
@@ -37,6 +37,40 @@ using ::testing::Ne;
 using ::testing::NotNull;
 using ::testing::UnorderedElementsAre;
 
+// Declares a minimal coroutine library.
+constexpr llvm::StringRef CoroutineLibrary = R"cc(
+struct promise;
+struct task;
+
+namespace std {
+template <class, class...>
+struct coroutine_traits {};
+template <>
+struct coroutine_traits<task> {
+    using promise_type = promise;
+};
+
+template <class Promise = void>
+struct coroutine_handle {
+    static constexpr coroutine_handle from_address(void *addr) { return {}; }
+};
+}  // namespace std
+
+struct awaitable {
+    bool await_ready() const noexcept;
+    void await_suspend(std::coroutine_handle<promise>) const noexcept;
+    void await_resume() const noexcept;
+};
+struct task {};
+struct promise {
+    task get_return_object();
+    awaitable initial_suspend();
+    awaitable final_suspend() noexcept;
+    void unhandled_exception();
+    void return_void();
+};
+)cc";
+
 void runDataflow(
     llvm::StringRef Code,
     std::function<
@@ -4607,7 +4641,7 @@ TEST(TransferTest, LoopCanProveInvariantForBoolean) {
 }
 
 TEST(TransferTest, DoesNotCrashOnUnionThisExpr) {
-  std::string Code = R"(
+  std::string Code = R"cc(
     union Union {
       int A;
       float B;
@@ -4618,7 +4652,7 @@ TEST(TransferTest, DoesNotCrashOnUnionThisExpr) {
       Union B;
       A = B;
     }
-  )";
+  )cc";
   // This is a crash regression test when calling the transfer function on a
   // `CXXThisExpr` that refers to a union.
   runDataflow(
@@ -4628,6 +4662,21 @@ TEST(TransferTest, DoesNotCrashOnUnionThisExpr) {
       LangStandard::lang_cxx17, /*ApplyBuiltinTransfer=*/true, "operator=");
 }
 
+TEST(TransferTest, DoesNotCrashOnNullChildren) {
+  std::string Code = (CoroutineLibrary + R"cc(
+    task foo() noexcept {
+      co_return;
+    }
+  )cc").str();
+  // This is a crash regression test when calling `AdornedCFG::build` on a
+  // statement (in this case, the `CoroutineBodyStmt`) with null children.
+  runDataflow(
+      Code,
+      [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &,
+         ASTContext &) {},
+      LangStandard::lang_cxx20, /*ApplyBuiltinTransfer=*/true, "foo");
+}
+
 TEST(TransferTest, StructuredBindingAssignFromStructIntMembersToRefs) {
   std::string Code = R"(
     struct A {

>From 71a0ade85a22597d74dbd9507115ee74f6d3a2d4 Mon Sep 17 00:00:00 2001
From: Eric Li <li.zhe.hua at gmail.com>
Date: Wed, 20 Mar 2024 12:32:42 -0400
Subject: [PATCH 2/2] fixup! [clang][dataflow] Fix crash when analyzing a
 coroutine

---
 clang/unittests/Analysis/FlowSensitive/TransferTest.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
index 2dc01a655264f3..b8f8f1ada2fb98 100644
--- a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
+++ b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
@@ -4667,7 +4667,8 @@ TEST(TransferTest, DoesNotCrashOnNullChildren) {
     task foo() noexcept {
       co_return;
     }
-  )cc").str();
+  )cc")
+                         .str();
   // This is a crash regression test when calling `AdornedCFG::build` on a
   // statement (in this case, the `CoroutineBodyStmt`) with null children.
   runDataflow(



More information about the cfe-commits mailing list