-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
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
Fix LibGit2 securezero! issue #24731
Conversation
cc: @staticfloat |
Here's a minimal reproduction of the issue: julia> git_cred = LibGit2.GitCredential("https", "localhost", nothing, "bob")
Base.LibGit2.GitCredential(Nullable{String}("https"), Nullable{String}("localhost"), Nullable{String}(), Nullable{String}("bob"), Nullable{String}(), true)
julia> u = Base.get(git_cred.username, "") # Should be using `deepcopy` here
"bob"
julia> git_cred = nothing
julia> gc()
julia> u # Was intended to be used later
"\0\0\0" |
Could you add the reproducing example as a test? |
I should be able to write a test that verifies that garbage collection of the |
I’m not sure if this is evidence that securezero! is a good idea or a bad one. |
I think the ideal version of this would be to only wipe the string once all references to it have been lost. Wiping a string upon the first reference being dropped is the core issue. |
I thought that the consensus in #23232 was to not use a |
I'll be using be switching to |
AppVeyor and Travis CI Mac failures look unrelated. |
Any other comments? I'll merge this later today. |
test/libgit2.jl
Outdated
# Triggering a garbage collection will cause the internal GitCredential | ||
# used in `authenticate_userpass` to be securely wiped but this should | ||
# not cause the payload credential to be wiped (#24731). | ||
gc() |
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.
Wouldn't it be better to add a manual finalize()
call in authenticate_userpass
when it is done with the internal GitCredential
?
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 can add an explicit securezero!
call in authenticate_userpass
but this gc()
will still be required to ensure that the memory has been duplicated.
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.
What do you mean by gc
ensuring that memory is duplicated?
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.
What do you mean by gc ensuring that memory is duplicated?
The original issue was that in authenticate_userpass
sensitive information was being loaded into git_cred
and then shallow copied into a UserPasswordCredentials
credential. Since the data was being shallow copied once the git_cred
was garbage collected (and then finalized) the data stored within the cred
would also be securely wiped.
The gc()
in this test case ensures that the git_cred
is consistently garbage collected (this was issue was previously only cropping up on 32-bit systems) which will cause the git_cred
data to be wiped out. Since the git_cred
information is now deep copied to cred
the secure wiping of git_cred
should not impact the data within cred
.
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.
Adding a manual finalize()
call would cause the git_cred
memory to be wiped sooner (definitely good idea) and would also make sure that cred
and git_cred
data is independent. My concern with removing the gc()
call and only having a finalize()
call only is that the conditions to reproduce this issue exist within the source code and not within the tests.
Potentially a future developer could remove deepcopy
calls and the finalize
calls and the tests would still pass as long as garbage collection hasn't happened to run.
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.
With all of that said having a manual gc()
call in a test case is a strange thing to do. If we do consider it bad practise we can go with just using finalize()
and hope only me or stevengj modify this code in the future ;)
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.
Just having it in a test case is fine.
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.
@stevengj Good to know. I'll merge as is, without the gc
call, so we can get this resolved.
472f2be
to
b548c8a
Compare
I squashed the commits and added an explicit |
After writing my speech on leaving in the |
# Use `deepcopy` to ensure zeroing the `git_cred` doesn't also zero the `cred`s copy | ||
cred.user = deepcopy(Base.get(git_cred.username, "")) | ||
cred.pass = deepcopy(Base.get(git_cred.password, "")) | ||
securezero!(git_cred) | ||
revised = true |
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.
It seems like we should also call securezero!(cred)
at the end of the authenticate_userpass
function. The function returns the result of git_cred_userpass_plaintext_new
, but this function makes its own copy of the password.
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.
We can't run securezero!(cred)
at the end of authenticate_userpass
as the credential information is still needed to be used in the approve(::CredentialPayload)
and the reject(::CredentialPayload)
methods. We should be able to run securezero!
on the credentials at the end of the approve/reject methods but I'll need to add in some extra logic to skip zeroing the data when running the test cases.
I was planning to do this in a future PR.
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.
Oh, I see, cred
is actually still available via get(p.credential)
.
Another reminder of the dangers with
securezero!
. When running on 32-bit machines theGitCredential
finalizer was causing copies of Strings to get wiped out. Fixes this issue: