This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

Author Justin.Cappos
Recipients Justin.Cappos, bbangert, giampaolo.rodola, loewis, nicdumz, ronaldoussoren
Date 2010-02-22.22:09:40
SpamBayes Score 2.22045e-16
Marked as misclassified No
Message-id <1266876583.45.0.97810567794.issue7995@psf.upfronthosting.co.za>
In-reply-to
Content
Suppose there is a program that has a listening socket that calls accept to obtain new sockets for client connections.   socketmodule.c assumes that these client sockets have timeouts / blocking in the default state for new sockets (which on most systems means the sockets will block).   However, socketmodule.c does not verify the state of the socket object that is returned by the system call accept.

From http://linux.die.net/man/2/accept :
On Linux, the new socket returned by accept() does not inherit file status flags such as O_NONBLOCK and O_ASYNC from the listening socket. This behaviour differs from the canonical BSD sockets implementation. Portable programs should not rely on inheritance or non-inheritance of file status flags and always explicitly set all required flags on the socket returned from accept(). 


socketmodule.c does not explicitly set or check these flags for sockets returned by accept.   The attached program will print the following on Linux regardless of whether the settimeout line for s exists or not:

a has timeout: None
O_NONBLOCK is set: False
received: hi



On Mac / BSD, the program will produce the following output when the timeout is set on the listening socket:

a has timeout: None
O_NONBLOCK is set: True
Traceback (most recent call last):
  File "python-nonportable.py", line 39, in <module>
    message = a.recv(1024)
socket.error: (35, 'Resource temporarily unavailable')


When the timeout is removed, the behavior is the same as linux:

a has timeout: None
O_NONBLOCK is set: False
received: hi


Note that the file descriptor problem crops up in odd ways on Mac systems.   It's possible that issue 5154 may be due to this bug.   I am aware of other problems with the socketmodule on Mac and will report them in other tickets.


I believe that this problem can be easily mitigated by always calling fcntl to unset the O_NONBLOCK flag after accept (O_ASYNC should be unset too, for correctness).   I would recommend adding the below code snippet at line 1653 in socketmodule.c (r78335).   The resulting code would look something like this (with '+' in front of the added lines):


'''
#ifdef MS_WINDOWS
        if (newfd == INVALID_SOCKET)
#else
        if (newfd < 0)
#endif
                return s->errorhandler();

+#if defined(__APPLE__) || defined(__OpenBSD__) || defined(__FreeBSD__)
+        int starting_flag;
+        // Unset O_NONBLOCK an O_ASYNC if they are inherited.
+        starting_flag = fcntl(newfd, F_GETFL, 0);
+        starting_flag &= ~(O_NONBLOCK | O_ASYNC);
+        fcntl(newfd, F_SETFL, starting_flag);
+#endif

        /* Create the new object with unspecified family,
           to avoid calls to bind() etc. on it. */
        sock = (PyObject *) new_sockobject(newfd,
                                           s->sock_family,
                                           s->sock_type,
                                           s->sock_proto);
'''

I've tested this patch on my Mac and Linux systems and it seems to work fine.   I haven't had a chance to test on BSD.   Also, I did not test for this problem in Python 3, but I assume it exists there as well and the same fix should be applied.
History
Date User Action Args
2010-02-22 22:09:44Justin.Cappossetrecipients: + Justin.Cappos, loewis, ronaldoussoren, giampaolo.rodola, nicdumz, bbangert
2010-02-22 22:09:43Justin.Cappossetmessageid: <1266876583.45.0.97810567794.issue7995@psf.upfronthosting.co.za>
2010-02-22 22:09:41Justin.Capposlinkissue7995 messages
2010-02-22 22:09:41Justin.Capposcreate