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

panic when closing tor while an onion service is being published #57

Open
hkparker opened this issue Oct 14, 2021 · 3 comments
Open

panic when closing tor while an onion service is being published #57

hkparker opened this issue Oct 14, 2021 · 3 comments

Comments

@hkparker
Copy link

The context here is an application that publishes an onion service, but the user closes the app while everything is still being initialized. I want to close things down as gracefully as possible in that situation.

If tor.Start has already returned and we've got a reference to a *Tor we can Close() it, but if t.Listen has not returned yet, we don't have an *OnionService we can Close() first, even though we're in the process of publishing one. So we try to t.Close() while the onion service is still being published and occasionally we see:

panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x7ae17e]

goroutine 8 [running]:
github.com/cretz/bine/control.(*Conn).debugEnabled(...)
        /home/hkparker/Development/Go/pkg/mod/github.com/cretz/bine@v0.2.0/control/conn.go:91
github.com/cretz/bine/control.(*Conn).SendRequest(0x0, {0x1018d1c, 0xc00038b6c0}, {0xc00038b718, 0xc00038b728, 0x1})
        /home/hkparker/Development/Go/pkg/mod/github.com/cretz/bine@v0.2.0/control/conn.go:56 +0x5e
github.com/cretz/bine/control.(*Conn).sendRequestIgnoreResponse(...)
        /home/hkparker/Development/Go/pkg/mod/github.com/cretz/bine@v0.2.0/control/conn.go:47
github.com/cretz/bine/control.(*Conn).DelOnion(...)
        /home/hkparker/Development/Go/pkg/mod/github.com/cretz/bine@v0.2.0/control/cmd_onion.go:200
github.com/cretz/bine/tor.(*OnionService).Close(0xc00019e310)
        /home/hkparker/Development/Go/pkg/mod/github.com/cretz/bine@v0.2.0/tor/listen.go:325 +0x13b
github.com/cretz/bine/tor.(*Tor).Listen(0xc0003c6000, {0x114e930, 0xc00013c000}, 0xc00038bb78)
        /home/hkparker/Development/Go/pkg/mod/github.com/cretz/bine@v0.2.0/tor/listen.go:291 +0x141e
...

I tried using a cancelable context passed into t.Listen, and calling the cancel function before closing tor, but that hung for reasons unknown.

I think what's going on here is that (*Tor).Close assigns the control socket to nil, then somewhere in (*Tor).Listen there's an error, which at the end, results in an attempt to close the onion service. But by this point tor is closed and the control port is nil, so the onion service's closer is probably hitting the nil pointer dereference here. I thought about just PRing a nil check there, however I noticed there were other calls in the (*Tor).Listen function that might have issues.

So maybe the actual solution here is just to not assign the control port to nil, and just let all the other calls check and early return on "use of closed connection" errors. That's probably the most concurrency safe option, but I wanted to discuss it a little before submitting a PR. Alternatively I suppose the cancel function should really be what's used, and perhaps that's what I need to fix (potentially user error, I didn't spend to long on it) and a panic when t.Listen isn't canceled properly is expected.

Let me know what you make of this and if there's anything you'd like me to submit.

@cretz
Copy link
Owner

cretz commented Oct 15, 2021

I haven't worked on this library much lately. As you've guessed, it appears the "cleanup on error" logic in Listen cannot safely handle closing its receiver before it has completed (specifically when you Tor.Close, it removes Tor.Control which OnionService.Close needs which is called on cleanup inside of Listen).

The safest route here is to not call Tor.Close while any methods are still running on the Tor object. If you need to bail out of a listen, cancel the context passed as the parameter and make sure the Listen call completes before closing.

@hkparker
Copy link
Author

I tried using a cancelable context passed into t.Listen, and calling the cancel function before closing tor, but that hung for reasons unknown.

Maybe I just didn't wait long enough? Looked around the code a bit more and I'm not seeing why this would be the case. Either way, what do you think about not assigning Tor.Control to nil? Seems like the safer bet, errors trying to use the closed socket would be handled cleanly. If no I'll just keep deferring recover, not a huge deal, just doesn't feel good.

@cretz
Copy link
Owner

cretz commented Oct 22, 2021

Maybe I just didn't wait long enough?

Did you make sure Listen returned before attempting to close? I think that is the key.

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

No branches or pull requests

2 participants