Skip to content
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

Incorrect stack coloring for alloca in SEH cleanuppad #66984

Closed
nikic opened this issue Sep 21, 2023 · 5 comments · Fixed by #66988 or llvm/llvm-project-release-prs#706
Closed

Incorrect stack coloring for alloca in SEH cleanuppad #66984

nikic opened this issue Sep 21, 2023 · 5 comments · Fixed by #66988 or llvm/llvm-project-release-prs#706

Comments

@nikic
Copy link
Contributor

nikic commented Sep 21, 2023

Consider the following example (https://llvm.godbolt.org/z/h1WovoPMG):

@type_info = external global ptr

define void @test(ptr %arg) personality ptr @__CxxFrameHandler3 {
bb:
  %a1 = alloca ptr, align 4
  %a2 = alloca ptr, align 4
  call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %a2)
  invoke void @throw()
          to label %bb14 unwind label %bb8

bb8:                                              ; preds = %bb7
  %i9 = cleanuppad within none []
  call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %a1)
  store ptr %arg, ptr %a1, align 4
  call fastcc void @foo(ptr %a1) [ "funclet"(token %i9) ]
  call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %a1)
  cleanupret from %i9 unwind label %bb15

bb14:                                             ; preds = %bb7
  unreachable

bb15:                                             ; preds = %bb13, %bb5
  %cs = catchswitch within none [label %bb17] unwind to caller

bb17:                                             ; preds = %bb15
  %cp = catchpad within %cs [ptr @type_info, i32 8, ptr %a2]
  %p = load ptr, ptr %a2, align 4
  call fastcc void @cleanup(ptr %p) [ "funclet"(token %cp) ]
  catchret from %cp to label %exit

exit:
  call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %a2)
  ret void
}

declare i32 @__CxxFrameHandler3(...)
declare void @throw()
declare void @cleanup(ptr)
declare void @foo(ptr)
declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture)
declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture)

Here %a1 has a lifetime fully contained within the cleanuppad, while %a2 has a lifetime across the whole function. However, the first use of %a2 is inside the catchpad. %a2 here is the pointer into which the exception object for the catchpad gets written.

With stackcoloring-lifetime-start-on-first-use (the default), %a1 and %a2 are allocated to the same stack slot. On the surface, this looks fine (because %a2 is only used after the cleanuppad). However, there is a quirk of SEH exception handling that makes this incorrect: The exception will be copied into %a2 by the personality function before both the cleanuppad and catchpad are called. As such, the write to %a2 happens earlier than our IR modeling implies.

There has been a patch to fix this issue in https://reviews.llvm.org/D86673. Unfortunately, that patch does not handle this case, because it checks for NumLoadInCatchPad[slot] > 1 -- I think the intention might have been to write > 0. (Funnily, just completely undoing the changes from that patch doesn't make the patch test case fail...)

@nikic
Copy link
Contributor Author

nikic commented Sep 22, 2023

/cherry-pick 8b4e29b a47fa1ea35e5fe37c7e36b91ef172ad237f56a65 aa70f4d

@llvmbot
Copy link
Member

llvmbot commented Sep 22, 2023

Failed to cherry-pick: a47fa1ea35e5fe37c7e36b91ef172ad237f56a65

https://github.com/llvm/llvm-project/actions/runs/6273163713

Please manually backport the fix and push it to your github fork. Once this is done, please add a comment like this:

/branch <user>/<repo>/<branch>

@nikic
Copy link
Contributor Author

nikic commented Sep 22, 2023

/cherry-pick 8b4e29b b3cb4f0 aa70f4d

@llvmbot
Copy link
Member

llvmbot commented Sep 22, 2023

/branch llvm/llvm-project-release-prs/issue66984

llvmbot pushed a commit to llvm/llvm-project-release-prs that referenced this issue Sep 22, 2023
The write to the SEH catch object happens before cleanuppads are
executed, while the first reference to the object will typically
be in a catchpad.

If we make use of first-use analysis, we may end up allocating
an alloca used inside the cleanuppad and the catch object at the
same stack offset, which would be incorrect.

https://reviews.llvm.org/D86673 was a previous attempt to fix it.
It used the heuristic "a slot loaded in a WinEH pad and never
written" to detect catch objects. However, because it checks
for more than one load (while probably more than zero was
intended), the fix does not actually work.

The general approach also seems dubious to me, so this patch
reverts that change entirely, and instead marks all catch object
slots as conservative (i.e. excluded from first-use analysis)
based on the WinEHFuncInfo. As far as I can tell we don't need
any heuristics here, we know exactly which slots are affected.

Fixes llvm/llvm-project#66984.

(cherry picked from commit b3cb4f069c2cb99bdae68d6906156af20e76314f)
@llvmbot
Copy link
Member

llvmbot commented Sep 22, 2023

/pull-request llvm/llvm-project-release-prs#706

@nikic nikic moved this from Needs Triage to Needs Review in LLVM Release Status Sep 22, 2023
tru pushed a commit to llvm/llvm-project-release-prs that referenced this issue Sep 27, 2023
The write to the SEH catch object happens before cleanuppads are
executed, while the first reference to the object will typically
be in a catchpad.

If we make use of first-use analysis, we may end up allocating
an alloca used inside the cleanuppad and the catch object at the
same stack offset, which would be incorrect.

https://reviews.llvm.org/D86673 was a previous attempt to fix it.
It used the heuristic "a slot loaded in a WinEH pad and never
written" to detect catch objects. However, because it checks
for more than one load (while probably more than zero was
intended), the fix does not actually work.

The general approach also seems dubious to me, so this patch
reverts that change entirely, and instead marks all catch object
slots as conservative (i.e. excluded from first-use analysis)
based on the WinEHFuncInfo. As far as I can tell we don't need
any heuristics here, we know exactly which slots are affected.

Fixes llvm/llvm-project#66984.

(cherry picked from commit b3cb4f069c2cb99bdae68d6906156af20e76314f)
tru pushed a commit that referenced this issue Sep 27, 2023
(cherry picked from commit 8b4e29b)
@tru tru moved this from Needs Review to Done in LLVM Release Status Sep 27, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment