New submission from Mark Williams:

Registering a file descriptor with EpollSelector.register that epoll(7) refuses 
results in the selector retaining a SelectorKey for that file descriptor, even 
though it's not monitored.

The following program attempts to register a file on the filesystem with an 
EpollSelector.  epoll_ctl(2) returns EPERM when given a such a file descriptor, 
so it shouldn't be registered with the selector, but it is registered.


import selectors
import tempfile
import traceback

sel = selectors.EpollSelector()

with tempfile.TemporaryFile() as f:
    try:
        sel.register(f, selectors.EVENT_READ)
    except PermissionError:
        traceback.print_exc()
    print("This should have raised a KeyError:", sel.get_key(f))


It produces this output on Pythons 3.4-3.6:

Traceback (most recent call last):
  File "/tmp/sel.py", line 9, in <module>
    sel.register(f, selectors.EVENT_READ)
  File "/usr/lib/python3.4/selectors.py", line 402, in register
    self._epoll.register(key.fd, epoll_events)
PermissionError: [Errno 1] Operation not permitted
This should have raised a KeyError: SelectorKey(fileobj=<_io.BufferedRandom 
name=8>, fd=8, events=1, data=None)

A similar problem exists with KqueueSelector.  Consider the following program:


import selectors
import tempfile
import traceback

sel = selectors.KqueueSelector()

f = tempfile.TemporaryFile()
filedescriptor = f.fileno()
f.close()

try:
    sel.register(filedescriptor, selectors.EVENT_READ)
except OSError:
    traceback.print_exc()

print("This should have raised a KeyError:", sel.get_key(filedescriptor))


In this case selectors._fileobj_to_fd doesn't detect that the integer file 
descriptor is closed.  

Note that _fileobj_to_fd should not be changed! Attempting to use, say, 
fcntl(fd, fcntl.GET_FD) to detect a closed file descriptor introduces a TOCTTOU 
bug.  Another thread (or another process, if the file descriptor refers to a 
socket shared between two or more processes and one calls shutdown(2) on it) 
can change the state of the file descriptor between the time it's checked and 
the time it's registered.  A new file might even be opened and given the 
previous file descriptor.

The attached patch catches any exception raised by EpollSelector.register or 
KqueueSelector.register, removes the SelectorKey from BaseSelector's table, and 
then re-raises the exception.

Note that I've included asyncio as an affected component, because asyncio wraps 
the selectors module.

----------
components: asyncio
files: selectors.patch
keywords: patch
messages: 272626
nosy: Mark.Williams, gvanrossum, haypo, yselivanov
priority: normal
severity: normal
status: open
title: selectors incorrectly retain invalid file descriptors
versions: Python 3.4, Python 3.5, Python 3.6
Added file: http://bugs.python.org/file44100/selectors.patch

_______________________________________
Python tracker <rep...@bugs.python.org>
<http://bugs.python.org/issue27759>
_______________________________________
_______________________________________________
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com

Reply via email to