python / cpython

The Python programming language
https://www.python.org
Other
63.52k stars 30.43k forks source link

socket.getservbyname(), socket.getservbyport(), socket.getprotobyname() are not threadsafe #74667

Open 37e87ed0-0fc8-46b1-8ca1-56f847d9cee8 opened 7 years ago

37e87ed0-0fc8-46b1-8ca1-56f847d9cee8 commented 7 years ago
BPO 30482
Nosy @pitrou, @vstinner, @njsmith, @dwfreed

Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

Show more details

GitHub fields: ```python assignee = None closed_at = None created_at = labels = ['extension-modules', 'type-bug', '3.7'] title = 'socket.getservbyname(), socket.getservbyport(), socket.getprotobyname() are not threadsafe' updated_at = user = 'https://github.com/dwfreed' ``` bugs.python.org fields: ```python activity = actor = 'njs' assignee = 'none' closed = False closed_date = None closer = None components = ['Extension Modules'] creation = creator = 'dwfreed' dependencies = [] files = [] hgrepos = [] issue_num = 30482 keywords = [] message_count = 3.0 messages = ['294540', '294545', '294564'] nosy_count = 4.0 nosy_names = ['pitrou', 'vstinner', 'njs', 'dwfreed'] pr_nums = [] priority = 'normal' resolution = None stage = 'needs patch' status = 'open' superseder = None type = 'behavior' url = 'https://bugs.python.org/issue30482' versions = ['Python 2.7', 'Python 3.5', 'Python 3.6', 'Python 3.7'] ```

37e87ed0-0fc8-46b1-8ca1-56f847d9cee8 commented 7 years ago

On at least Linux (and probably most other UNIXes, except OS X), the C functions getservbyname(), getservbyport(), and getprotobyname() are not threadsafe. CPython's wrappers around these functions in the socket module do nothing to cover up this fact. Simple reproduction script for getservbyname (others similar):

import threading
import socket

def getservbyname_loop(service, port):
        while True:
                result = socket.getservbyname(service)
                if result != port:
                        raise RuntimeError("thread-safety broken, got %d, expected %d" % (result, port))

thread1 = threading.Thread(target=getservbyname_loop, args=("ssh", 22))
thread2 = threading.Thread(target=getservbyname_loop, args=("smtp", 25))
thread1.start()
thread2.start()

One of the threads will throw the RuntimeError, saying it got the port number the other thread should have gotten.

Naive fix: a lock (eg, just use the netdb_lock already created in the module)

Proper fix: use the libc's reentrant variant if available, and fall back to locking if not (see gethostbyname_ex() implementation for example).

I'd be happy to work on this, but as I don't have access to anything other than Linux and OS X at the moment, it would be helpful if platform maintainers could chime in on what if any reentrant variants of these functions exist on their platforms so we can have a more proper fix.

pitrou commented 7 years ago

The configure.ac script should check for the availability of gethostbyname_r and friends, like it already does for other functions.

37e87ed0-0fc8-46b1-8ca1-56f847d9cee8 commented 7 years ago

It already checks for gethostbyname_r, but the comments in socketmodule.c mention that configure seems to get it wrong. Those comments are probably old, though, so perhaps that can be revisited as well.