Skip to content

Use pushFullExprCleanup for deferred destroy #88670

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Apr 15, 2024
Merged

Conversation

usx95
Copy link
Contributor

@usx95 usx95 commented Apr 14, 2024

Instead of directly pushing the Destroy, we should use pushFullExprCleanup which handles conditional branches.
This fixes a backend crash due to 89ba7e1.

Added minimized crash reproducer.

@usx95 usx95 requested a review from efriedma-quic April 14, 2024 21:26
@usx95 usx95 marked this pull request as ready for review April 14, 2024 21:27
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:codegen IR generation bugs: mangling, exceptions, etc. labels Apr 14, 2024
@llvmbot
Copy link
Member

llvmbot commented Apr 14, 2024

@llvm/pr-subscribers-clang-codegen

@llvm/pr-subscribers-clang

Author: Utkarsh Saxena (usx95)

Changes

Instead of directly pushing the Destroy, we should use pushFullExprCleanup which handles conditional branches.
This fixes a backend crash due to 89ba7e1.

Tested:

CLANG_DIR=/usr/local/google/home/usx/build

$CLANG_DIR/bin/clang++ \
  -Itools/clang/tools/extra/clangd \
  -I/usr/local/google/home/usx/src/llvm/llvm-project/clang-tools-extra/clangd \
  -I/usr/local/google/home/usx/src/llvm/llvm-project/clang-tools-extra/clangd/../include-cleaner/include \
  -Itools/clang/tools/extra/clangd/../clang-tidy \
  -I/usr/local/google/home/usx/src/llvm/llvm-project/clang/include \
  -Itools/clang/include \
  -Iinclude \
  -I/usr/local/google/home/usx/src/llvm/llvm-project/llvm/include \
  -I/usr/local/google/home/usx/src/llvm/llvm-project/clang-tools-extra/pseudo/lib/../include \
  -isystem$CLANG_DIR/lib/clang/19/include \
  -o  /usr/local/google/home/usx/build/bootstrap/tools/clang/tools/extra/clangd/CMakeFiles/obj.clangDaemon.dir/ClangdLSPServer.cpp.o \
  -c  /usr/local/google/home/usx/src/llvm/llvm-project/clang-tools-extra/clangd/ClangdLSPServer.cpp

Full diff: https://github.com/llvm/llvm-project/pull/88670.diff

1 Files Affected:

  • (modified) clang/lib/CodeGen/CGDecl.cpp (+5-2)
diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp
index 8bdafa7c569b08..3f05ebb561da57 100644
--- a/clang/lib/CodeGen/CGDecl.cpp
+++ b/clang/lib/CodeGen/CGDecl.cpp
@@ -2216,8 +2216,11 @@ void CodeGenFunction::pushDestroyAndDeferDeactivation(
 void CodeGenFunction::pushDestroyAndDeferDeactivation(
     CleanupKind cleanupKind, Address addr, QualType type, Destroyer *destroyer,
     bool useEHCleanupForArray) {
-  pushCleanupAndDeferDeactivation<DestroyObject>(
-      cleanupKind, addr, type, destroyer, useEHCleanupForArray);
+  llvm::Instruction *DominatingIP =
+      Builder.CreateFlagLoad(llvm::Constant::getNullValue(Int8PtrTy));
+  pushDestroy(cleanupKind, addr, type, destroyer, useEHCleanupForArray);
+  DeferredDeactivationCleanupStack.push_back(
+      {EHStack.stable_begin(), DominatingIP});
 }
 
 void CodeGenFunction::pushStackRestore(CleanupKind Kind, Address SPMem) {

@usx95
Copy link
Contributor Author

usx95 commented Apr 15, 2024

This fix is for a wrong refactoring change in 89ba7e1 and
I would be inclined towards landing this in a few hours to unblock the build breakage (if there are no review comments).
CC: @efriedma-quic

@cor3ntin
Copy link
Contributor

@usx95 Could you maybe reduce a test from the crash? Thanks!

@usx95
Copy link
Contributor Author

usx95 commented Apr 15, 2024

@cor3ntin Added tests for crash.


class Value {
public:
Value(std::initializer_list<Value> Elements);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The use of initializer_list here doesn't seem necessary; you could remove it to reduce the size of the testcase.

Can we integrate this into an existing test file?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you try this out ? I cannot reproduce the original crash without std::initializer_list.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

class Object {
public:
  struct KV;
  Object(KV);
};

struct A { A(); ~A(); };
class Value {
public:
  Value(A);
  Value(const char *V);
  ~Value();
};

class ObjectKey {
public:
  ObjectKey(const char *S);
  ~ObjectKey();
};

struct Object::KV {
  ObjectKey K;
  Value V;
};
bool foo();
void DestroyInConditionalCleanup() {
  foo() ? Object{{"key1", A()}} : Object{{"key2", "val2"}};
}

Not really a big deal either way, though; just a minor cleanup.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great. Thanks. I was able to reduce it further.

Copy link
Collaborator

@efriedma-quic efriedma-quic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@usx95 usx95 merged commit 43b4e5b into llvm:main Apr 15, 2024
aniplcc pushed a commit to aniplcc/llvm-project that referenced this pull request Apr 15, 2024
Instead of directly pushing the `Destroy`, we should use
`pushFullExprCleanup` which handles conditional branches.
This fixes a backend crash due to
89ba7e1.

Added minimized crash reproducer.
usx95 added a commit to usx95/llvm-project that referenced this pull request Apr 16, 2024
usx95 added a commit that referenced this pull request Apr 29, 2024
)

Latest diff:
https://github.com/llvm/llvm-project/pull/89154/files/f1ab4c2677394bbfc985d9680d5eecd7b2e6a882..adf9bc902baddb156c83ce0f8ec03c142e806d45

We address two additional bugs here: 

### Problem 1: Deactivated normal cleanup still runs, leading to
double-free
Consider the following:
```cpp

struct A { };

struct B { B(const A&); };

struct S {
  A a;
  B b;
};

int AcceptS(S s);

void Accept2(int x, int y);

void Test() {
  Accept2(AcceptS({.a = A{}, .b = A{}}), ({ return; 0; }));
}
```
We add cleanups as follows:
1. push dtor for field `S::a`
2. push dtor for temp `A{}` (used by ` B(const A&)` in `.b = A{}`)
3. push dtor for field `S::b`
4. Deactivate 3 `S::b`-> This pops the cleanup.
5. Deactivate 1 `S::a` -> Does not pop the cleanup as *2* is top. Should
create _active flag_!!
6. push dtor for `~S()`.
7. ...

It is important to deactivate **5** using active flags. Without the
active flags, the `return` will fallthrough it and would run both `~S()`
and dtor `S::a` leading to **double free** of `~A()`.
In this patch, we unconditionally emit active flags while deactivating
normal cleanups. These flags are deleted later by the `AllocaTracker` if
the cleanup is not emitted.

### Problem 2: Missing cleanup for conditional lifetime extension
We push 2 cleanups for lifetime-extended cleanup. The first cleanup is
useful if we exit from the middle of the expression (stmt-expr/coro
suspensions). This is deactivated after full-expr, and a new cleanup is
pushed, extending the lifetime of the temporaries (to the scope of the
reference being initialized).
If this lifetime extension happens to be conditional, then we use active
flags to remember whether the branch was taken and if the object was
initialized.
Previously, we used a **single** active flag, which was used by both
cleanups. This is wrong because the first cleanup will be forced to
deactivate after the full-expr and therefore this **active** flag will
always be **inactive**. The dtor for the lifetime extended entity would
not run as it always sees an **inactive** flag.

In this patch, we solve this using two separate active flags for both
cleanups. Both of them are activated if the conditional branch is taken,
but only one of them is deactivated after the full-expr.

---

Fixes #63818
Fixes #88478

---

Previous PR logs:
1. #85398
2. #88670
3. #88751
4. #88884
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:codegen IR generation bugs: mangling, exceptions, etc. clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants