Skip to content

Commit cb9424f

Browse files
committed
Issue python#27759: Fix selectors incorrectly retain invalid file descriptors.
(Backported to 3.4 as this bug might be exploited to for DoS)
1 parent 26d998c commit cb9424f

File tree

3 files changed

+43
-9
lines changed

3 files changed

+43
-9
lines changed

Lib/selectors.py

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -399,7 +399,11 @@ def register(self, fileobj, events, data=None):
399399
epoll_events |= select.EPOLLIN
400400
if events & EVENT_WRITE:
401401
epoll_events |= select.EPOLLOUT
402-
self._epoll.register(key.fd, epoll_events)
402+
try:
403+
self._epoll.register(key.fd, epoll_events)
404+
except BaseException:
405+
super().unregister(fileobj)
406+
raise
403407
return key
404408

405409
def unregister(self, fileobj):
@@ -465,14 +469,18 @@ def fileno(self):
465469

466470
def register(self, fileobj, events, data=None):
467471
key = super().register(fileobj, events, data)
468-
if events & EVENT_READ:
469-
kev = select.kevent(key.fd, select.KQ_FILTER_READ,
470-
select.KQ_EV_ADD)
471-
self._kqueue.control([kev], 0, 0)
472-
if events & EVENT_WRITE:
473-
kev = select.kevent(key.fd, select.KQ_FILTER_WRITE,
474-
select.KQ_EV_ADD)
475-
self._kqueue.control([kev], 0, 0)
472+
try:
473+
if events & EVENT_READ:
474+
kev = select.kevent(key.fd, select.KQ_FILTER_READ,
475+
select.KQ_EV_ADD)
476+
self._kqueue.control([kev], 0, 0)
477+
if events & EVENT_WRITE:
478+
kev = select.kevent(key.fd, select.KQ_FILTER_WRITE,
479+
select.KQ_EV_ADD)
480+
self._kqueue.control([kev], 0, 0)
481+
except BaseException:
482+
super().unregister(fileobj)
483+
raise
476484
return key
477485

478486
def unregister(self, fileobj):

Lib/test/test_selectors.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import signal
66
import socket
77
import sys
8+
import tempfile
89
from test import support
910
from time import sleep
1011
import unittest
@@ -447,13 +448,35 @@ class EpollSelectorTestCase(BaseSelectorTestCase, ScalableSelectorMixIn):
447448

448449
SELECTOR = getattr(selectors, 'EpollSelector', None)
449450

451+
def test_register_file(self):
452+
# epoll(7) returns EPERM when given a file to watch
453+
s = self.SELECTOR()
454+
with tempfile.NamedTemporaryFile() as f:
455+
with self.assertRaises(IOError):
456+
s.register(f, selectors.EVENT_READ)
457+
# the SelectorKey has been removed
458+
with self.assertRaises(KeyError):
459+
s.get_key(f)
460+
450461

451462
@unittest.skipUnless(hasattr(selectors, 'KqueueSelector'),
452463
"Test needs selectors.KqueueSelector)")
453464
class KqueueSelectorTestCase(BaseSelectorTestCase, ScalableSelectorMixIn):
454465

455466
SELECTOR = getattr(selectors, 'KqueueSelector', None)
456467

468+
def test_register_bad_fd(self):
469+
# a file descriptor that's been closed should raise an OSError
470+
# with EBADF
471+
s = self.SELECTOR()
472+
bad_f = support.make_bad_fd()
473+
with self.assertRaises(OSError) as cm:
474+
s.register(bad_f, selectors.EVENT_READ)
475+
self.assertEqual(cm.exception.errno, errno.EBADF)
476+
# the SelectorKey has been removed
477+
with self.assertRaises(KeyError):
478+
s.get_key(bad_f)
479+
457480

458481
def test_main():
459482
tests = [DefaultSelectorTestCase, SelectSelectorTestCase,

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ Library
2929
HTTP_PROXY variable when REQUEST_METHOD environment is set, which indicates
3030
that the script is in CGI mode.
3131

32+
- Issue #27759: Fix selectors incorrectly retain invalid file descriptors.
33+
Patch by Mark Williams.
34+
3235
Tests
3336
-----
3437

0 commit comments

Comments
 (0)