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

runtime: missed deferred calls #43941

Open
mdempsky opened this issue Jan 27, 2021 · 14 comments
Open

runtime: missed deferred calls #43941

mdempsky opened this issue Jan 27, 2021 · 14 comments

Comments

@mdempsky
Copy link
Member

@mdempsky mdempsky commented Jan 27, 2021

This program should run successfully: https://play.golang.org/p/XIVsN45Oyzz

It does with gccgo and with cmd/compile using -gcflags=-N, but not when compiled normally. It also still fails at CL 286712, PS 3.

The problem is the "defer step(12)" and "defer step(13)" calls are being missed. Instead, execution jumps directly to the "step(14)" defer.

Sorry the reproducer is still so large. I'm having trouble minimizing it any further. It seems particularly sensitive to changes that I wouldn't expect to have any effect.

/cc @danscales @randall77

@mdempsky
Copy link
Member Author

@mdempsky mdempsky commented Jan 27, 2021

FWIW, since this is a valid program that's misbehaving silently (modulo the test program's own consistency checks), I think this is probably a higher priority issue than #43920 or #43942, which are also valid programs but failing loudly.

@mdempsky
Copy link
Member Author

@mdempsky mdempsky commented Jan 27, 2021

Somewhat more minimized test case: https://play.golang.org/p/60U6DFD6Ew- https://play.golang.org/p/Zpgkg3tZ8BO https://play.golang.org/p/jP3CpfpxSku https://play.golang.org/p/mX8ZnRSzZEh https://play.golang.org/p/AjhP4QHH1QU

After the panic(4), the ok = true assignment should execute. But instead execution jumps directly to the expect(4, recover())+log.Fatal defer.

It seems like it's particularly sensitive to stack frame layout. In particular, the issue was I needed to keep making matching changes to both of the main function literals within main. It seems like some of the state left on the stack by the first function is getting reused without proper reinitialization by the second function?

@mdempsky
Copy link
Member Author

@mdempsky mdempsky commented Jan 27, 2021

Having stared at these cases a bunch, I'm starting to think they're likely duplicates. In each of the reproducers, there's some code of the shape:

defer func() {
	defer func() {
		expect(3, recover())
	}()
	defer panic(3)
	panic(2)
}()
defer func() {
	expect(1, recover())
}()
panic(1)

The differences are all in the code around the above. I suspect the above code is just corrupting the defer stack, and then the surrounding code just influences how that corruption ultimately manifests. Most commonly it's simply "bad defer entry in panic" (#43920), but when the stars align (or stacks align?) it manifests instead as issues like #43941 and #43942.

@go101
Copy link

@go101 go101 commented Oct 9, 2021

A small variant:

package main

func main() {
	defer func() {
		println(recover().(int))
	}()
	func() {
		func() (_ [2]int) { type _ int; return }()
		func() {
			defer func() {
				defer func() {
					recover()
				}()
				defer panic(3)
				panic(2)
			}()
			defer func() {
				recover()
			}()
			panic(1)
		}()
		defer func() {}()
	}()
	
	var x = 123
	func() {
		defer print(x) // not executed!
		func() {
			defer func() {}()
			panic(4)
		}()
	}()
}

@batara666
Copy link

@batara666 batara666 commented Oct 9, 2021

this need to be fix

@ianlancetaylor ianlancetaylor added this to the Go1.18 milestone Oct 9, 2021
@ianlancetaylor
Copy link
Contributor

@ianlancetaylor ianlancetaylor commented Oct 9, 2021

@go101 I think that your example is a different problem. Please open a different issue for it. Thanks.

@ianlancetaylor
Copy link
Contributor

@ianlancetaylor ianlancetaylor commented Oct 9, 2021

@danscales The test case in the initial comment appears to work with Go 1.14, Go 1.17, and tip. It fails with Go 1.15 and Go 1.16. Go 1.15 is no longer supported. Do you think it would be feasible to backport the fix (whatever it is) to Go 1.16?

@ianlancetaylor ianlancetaylor removed this from the Go1.18 milestone Oct 9, 2021
@ianlancetaylor ianlancetaylor added this to the Go1.16.10 milestone Oct 9, 2021
@go101
Copy link

@go101 go101 commented Oct 10, 2021

@ianlancetaylor My test case is based on @mdempsky's one: #43941 (comment), I think they are same in principle.

@ianlancetaylor
Copy link
Contributor

@ianlancetaylor ianlancetaylor commented Oct 10, 2021

@go101 They may be the same in principle but they are clearly different in practice. The original test case in this issue works on Go 1.17 and on tip. Your test case, if I understand it correctly, fails on Go 1.17 and tip. Therefore the two test cases, though they may be similar, demonstrate different problems. Please open a new issue. Thanks.

@danscales
Copy link

@danscales danscales commented Oct 11, 2021

@ianlancetaylor OK, I did the bisection for the test case in the original comment. As you say, it worked in 1.14, which is when fast defers appeared. It started not working with this change:

https://go-review.googlesource.com/c/go/+/229601

[cmd/compile: move fixVariadicCall from walk to order]. That seems very unrelated, so maybe that change revealed an existing bug with defers.

Then, not unexpectedly, the test started working again with:

https://go-review.googlesource.com/c/go/+/310175

[internal/buildcfg: enable regabidefer by default], which enabled a major change in how defers are implemented by @aclements (in service of regabi, I believe).

So, I don't think there is an obvious simple fix for 1.16, but I will investigate further. If I don't see anything fairly obvious/easy to fix, I would probably lean toward not back-porting a fix for 1.16, since this case is so unusual.

@ianlancetaylor
Copy link
Contributor

@ianlancetaylor ianlancetaylor commented Oct 11, 2021

I agree that if there isn't a simple fix we should just move on, since it works in Go 1.17. Thanks for looking.

@aclements
Copy link
Member

@aclements aclements commented Oct 13, 2021

I'm really surprised that enabling regabidefer would have fixed a missed defer. It was a significant change to how defers are invoked, but not to our overall handling of the defer stack.

@aclements
Copy link
Member

@aclements aclements commented Oct 13, 2021

It might be that once we understand #48898 we'll understand why regabidefer fixed this test program.

@gopherbot
Copy link

@gopherbot gopherbot commented Oct 14, 2021

Change https://golang.org/cl/356011 mentions this issue: cmd/compile: fix some defer bugs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
7 participants