-
Notifications
You must be signed in to change notification settings - Fork 2.4k
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
threadsafety issue with curve_keypair, libsodium, randombytes_close #4241
Comments
Adding another background thread that just issues new keys and/or opens and closes contexts makes the race much more likely (closer to every 1-20 client sockets): import itertools
from threading import Thread
import zmq
server_public, server_private = zmq.curve_keypair()
url = 'tcp://127.0.0.1:5555'
def server():
with zmq.Context() as ctx:
with ctx.socket(zmq.PULL) as s:
s.curve_server = True
s.curve_publickey = server_public
s.curve_secretkey = server_private
s.bind(url)
count = 0
while True:
try:
print(count, s.recv().decode("ascii"))
except KeyboardInterrupt:
print("interrupted...")
else:
count += 1
def spin_randombytes_close():
while True:
with zmq.Context(): # Context.term calls randombytes_close()
pass
# zmq.curve_keypair() # calls randombytes_close()
def client(i):
client_public, client_private = zmq.curve_keypair()
with zmq.Context() as ctx:
with ctx.socket(zmq.PUSH) as s:
s.curve_serverkey = server_public
s.curve_publickey = client_public
s.curve_secretkey = client_private
s.connect(url)
s.send(str(i).encode('ascii'))
def main():
Thread(target=spin_randombytes_close, daemon=True).start()
Thread(target=server, daemon=True).start()
for i in itertools.count():
client(i)
if __name__ == "__main__":
main() |
minrk
changed the title
threadafety issue with curve_keypair, libsodium, randombytes_close
threadsafety issue with curve_keypair, libsodium, randombytes_close
Aug 16, 2021
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Please use this template for reporting suspected bugs or requests for help.
Issue description
#3001 reverted the refcounting of libsodium init/close, meaning that
randombytes_close()
can be called many times while libsodium is still in use, e.g. ifzmq_curve_keypair()
is called afterzmq_context()
.When running with libsodium, calling curve_keypair triggers
zmq::random_close()
which in turn unconditionally triggersrandombytes_close()
. Calls to e.g.crypto_box_keypair()
for the internal keys incurve_server
andcurve_client
do not callzmq::random_open()
which means the following sequence occurs:sodium_init()
sodium_init(); randombytes_close()
crypto_box_keypair()
with randombytes closed!The same threadsafety issue can occur if one context is closed while another is in use.
This is not always a problem, as
randombytes_close()
is a no-op whengetrandom
is available, but this isn't always the case (e.g. macOS). Thiscrypto_box_keypair()
call can crash due to thread unsafety reinitializing the random data source.From the libsodium docs:
It seems like the 'right' fix is to just remove the call to
randombytes_close()
when using libsodium. Doing so could leave at most one FD open on/dev/urandom
, but not more than one. Otherwise, we need to restore the refcounting in #2636 for libsodium, which was removed in #3001, and/or callrandom::open/close
on every curve call, not just context construct/destruct.Environment
Minimal test code / Steps to reproduce the issue
Create a CURVE server and keep connecting clients until it crashes. Make sure to call
zmq_curve_keypair()
after starting a context to trigger the inappropraite use-after-close. In Python:What's the actual result? (include assertion message & call stack if applicable)
After a number of sockets (varies from 4 to 1000), crash with:
What's the expected result?
No crash.
The text was updated successfully, but these errors were encountered: