-
Notifications
You must be signed in to change notification settings - Fork 1.8k
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
Fixes potential cgo.Handle
panic
#13479
Conversation
…tricky bug in the process (#13407)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like you've definitely tracked down the source of the issue, but I'd like to clean up the solution a bit.
// rdpc.Wait() typically takes care of calling rdpc.Cleanup(). | ||
// However, if something fails between the call to New() and | ||
// rdpc.Wait(), the caller MUST call rdpc.Cleanup() to ensure | ||
// all memory is released. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a confusing API and I would like to avoid it.
How is someone supposed to know if "something fails between New() and Wait()" anyway?
Is there some way we could perhaps:
- unexport
cleanup()
- make it a required convention to always call
Wait()
after new, to ensure cleanup happens - make
Wait()
just perform cleanup and return immediately if something failed withNew
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree its a confusing API. What I was trying to express was what we have going on here:
teleport/lib/srv/desktop/windows_server.go
Line 878 in ad841e0
rdpc.Cleanup() |
where we create a New()
and then use it for things that might fail before calling Wait()
.
I think I found a way to refactor this so that the API makes more sense though, see f61a89f
…leasing of the rust client to ensure the rust client can't try to use that handle after its deleted
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks much better. A few nits but otherwise LGTM.
// UpdateClientActivity before starting monitor to | ||
// be doubly sure that the client isn't disconnected | ||
// due to an idle timeout before its had the chance to | ||
// call StartAndWait() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// call StartAndWait() | |
// call Run() |
@ibeckermayer See the table below for backport results.
|
@ibeckermayer don't forget to open backports :-) |
v9 backport fixed: #13590 |
The
rdpclient.Client
in Go has acgo.Handle
field, which is a special type cgo provides to deal with on of the idiosyncrasies of working between Go and C:The underlying object of a
cgo.Handle
can be retrieved by callinghandle.Value()
, like we do inhandle_bitmap
:teleport/lib/srv/desktop/rdp/rdpclient/client.go
Lines 393 to 396 in ad841e0
and its memory must be explicitly released after use by calling
handle.Delete()
. Previously, we were callinghandle.Delete()
inClient.Close()
teleport/lib/srv/desktop/rdp/rdpclient/client.go
Lines 445 to 456 in 8ce4e6b
which was deferred in both the video output streaming goroutine (which calls
handle_bitmap
repeatedly)teleport/lib/srv/desktop/rdp/rdpclient/client.go
Lines 239 to 245 in 8ce4e6b
and in the user input streaming goroutine:
teleport/lib/srv/desktop/rdp/rdpclient/client.go
Lines 259 to 264 in 8ce4e6b
When a user closes a tab like in #13407, the input streaming goroutine returns
teleport/lib/srv/desktop/rdp/rdpclient/client.go
Lines 268 to 270 in 8ce4e6b
meaning the
defer c.Close()
gets called and callsc.handle.Delete()
. Therefore, its possible that the video output streaming goroutine continues running for long enough to callhandle_bitmap
again, which attempts to callc.handle.Value()
on an invalid handle (the one that's beenDelete()
ed), which causes the panic. The precise mechanics of how this happens, and why it happens more often when smartcard negotiation is taking place but not in other circumstances, is not entirely clear to me (though I have the beginnings of a hypothesis). However with these changes, such a scenario is now impossible, and so the panic should never occur.Closes #13407