Skip to content

Commit c7af374

Browse files
committed
gh-160561: Close the duplicated descriptor when fromfd() is rejected
1 parent 457ba33 commit c7af374

3 files changed

Lines changed: 29 additions & 1 deletion

File tree

‎Lib/socket.py‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -572,7 +572,13 @@ def fromfd(fd, family, type, proto=0):
572572
descriptor. The remaining arguments are the same as for socket().
573573
"""
574574
nfd = dup(fd)
575-
return socket(family, type, proto, nfd)
575+
try:
576+
return socket(family, type, proto, nfd)
577+
except BaseException:
578+
# socket() rejected the arguments; the dup()'d descriptor would
579+
# otherwise leak, since nothing holds a reference to it.
580+
close(nfd)
581+
raise
576582

577583
if hasattr(_socket.socket, "sendmsg"):
578584
def send_fds(sock, buffers, fds, flags=0, address=None):

‎Lib/test/test_socket.py‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3195,6 +3195,27 @@ def testFromFd(self):
31953195
def _testFromFd(self):
31963196
self.serv_conn.send(MSG)
31973197

3198+
def testFromFdInvalidArgsDoesNotLeak(self):
3199+
# fromfd() dup()s the descriptor before socket() validates its
3200+
# arguments; a rejected call must not leave the duplicate open.
3201+
fddir = '/proc/self/fd'
3202+
if not os.path.isdir(fddir):
3203+
self.skipTest('test needs /proc/self/fd')
3204+
fd = self.cli_conn.fileno()
3205+
base = dict(family=socket.AF_INET, type=socket.SOCK_STREAM, proto=0)
3206+
# family, type and proto are all converted to a C int, so each of them
3207+
# can fail after the dup() has already happened.
3208+
for name in base:
3209+
kwargs = dict(base, **{name: 1 << 100})
3210+
with self.subTest(name=name):
3211+
before = len(os.listdir(fddir))
3212+
with self.assertRaises(OverflowError):
3213+
socket.fromfd(fd, **kwargs)
3214+
self.assertEqual(len(os.listdir(fddir)), before)
3215+
3216+
def _testFromFdInvalidArgsDoesNotLeak(self):
3217+
pass
3218+
31983219
def testDup(self):
31993220
# Testing dup()
32003221
sock = self.cli_conn.dup()
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix a descriptor leak in :func:`socket.fromfd` when the socket constructor rejects its arguments. The duplicated descriptor was never closed.

0 commit comments

Comments
 (0)