Skip to content

fix(remote): make Ctrl-C interrupt blocking getaddrinfo() (#2540)#2728

Open
ChrisJr404 wants to merge 1 commit intoGallopsled:devfrom
ChrisJr404:feature/remote-resolve-ctrl-c
Open

fix(remote): make Ctrl-C interrupt blocking getaddrinfo() (#2540)#2728
ChrisJr404 wants to merge 1 commit intoGallopsled:devfrom
ChrisJr404:feature/remote-resolve-ctrl-c

Conversation

@ChrisJr404
Copy link
Copy Markdown
Contributor

Closes #2540.

What

pwnlib.tubes.remote.remote(host, port) calls socket.getaddrinfo() to resolve host. On glibc, getaddrinfo holds 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_DFL for the duration of the resolution call, then restore the original handler.

@contextlib.contextmanager
def _interruptible_block():
    if threading.current_thread() is not threading.main_thread():
        yield
        return
    try:
        old = signal.signal(signal.SIGINT, signal.SIG_DFL)
    except ValueError:
        yield
        return
    try:
        yield
    finally:
        signal.signal(signal.SIGINT, old)

remote._connect now wraps the socket.getaddrinfo(...) call in this context.

Why

Quoted from the issue's main reporter (@tesuji):

I would like to use ctrl-C to quit immediately in these cases.

And from @Arusekk:

Maybe we can set SIGINT handler to SIG_DFL as a workaround?

That's exactly what this does, scoped narrowly to the resolver call so it doesn't perturb anything else.

Tests

  • Existing remote() round-trip against a local listen() socket still passes (resolution to a literal IP is instantaneous).
  • _interruptible_block swap+restore verified directly: signal.getsignal(SIGINT) is SIG_DFL inside, equal to the original handler outside.
  • On a non-main thread the context is a no-op (signal handlers can only be changed from the main thread, so we don't try).

Notes

  • Pure-additive: no existing call signature changes. Anyone who wants to keep their custom SIGINT handler around their own getaddrinfo can call it directly without going through remote().
  • The fix only covers 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).

…#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
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

Successfully merging this pull request may close these issues.

Script stuck (cannot ctrl-C to quit) when DNS resolution hang (using remote())

1 participant