fix(remote): make Ctrl-C interrupt blocking getaddrinfo() (#2540)#2728
fix(remote): make Ctrl-C interrupt blocking getaddrinfo() (#2540)#2728ChrisJr404 wants to merge 1 commit into
Conversation
…#2540) socket.getaddrinfo() holds the GIL during the libc DNS round-trip and on glibc swallows SIGINT, so a hung resolver leaves remote() unkillable until the kernel timeout. Per @Arusekk's suggestion in Gallopsled#2540, swap SIGINT to SIG_DFL only for the duration of the resolution call, then restore the previous handler. Restricted to the main thread (where signal handlers are settable); a no-op everywhere else. Closes Gallopsled#2540
|
i can confirm it fixes the issue for me |
|
This global How could we test this? Configure some DNS server that doesn't exist and abort a query? |
|
I can confirm that this works for me, too. The simplest way to test it is Is there any official account of this problem? Any upstream bug report that we can link? |
|
|
This solution kills the python process without triggering the cleanup routines. from pwn import *
try:
io = remote('notexisting.lol', 2451)
io.interactive()
finally:
print('DONE!!')Replacing the target nameserver in resolv.conf with some random IP not hosting a dns server stalls the DNS resolution. Pressing Ctrl+C once immediately stops the python process but doesn't print The other solution in #2541 using asyncio in a new thread allows to abort the dns resolution somewhat while running the cleanup code. It requires three Ctrl+C presses before the process stops though, so it's not perfect. But I think allowing graceful shutdown should be desirable, so this solution feels too disruptive. @Arusekk What do you think? |
Closes #2540.
What
pwnlib.tubes.remote.remote(host, port)callssocket.getaddrinfo()to resolvehost. On glibc,getaddrinfoholds the GIL during the libc DNS round-trip and swallows SIGINT, so a hung resolver leaves the script unkillable from Ctrl-C until the kernel-level resolver timeout (often ~30s).This PR follows the workaround @Arusekk suggested in the issue thread: swap the SIGINT handler to
SIG_DFLfor the duration of the resolution call, then restore the original handler.remote._connectnow wraps thesocket.getaddrinfo(...)call in this context.Why
Quoted from the issue's main reporter (@tesuji):
And from @Arusekk:
That's exactly what this does, scoped narrowly to the resolver call so it doesn't perturb anything else.
Tests
remote()round-trip against a locallisten()socket still passes (resolution to a literal IP is instantaneous)._interruptible_blockswap+restore verified directly:signal.getsignal(SIGINT)isSIG_DFLinside, equal to the original handler outside.Notes
remote().pwnlib.tubes.remote.remote._connect; if a similar hang ever shows up in another tube's resolver call it can reuse_interruptible_block(or be lifted into a shared module).