lists.openwall.net | lists / announce owl-users owl-dev john-users john-dev passwdqc-users yescrypt popa3d-users / oss-security kernel-hardening musl sabotage tlsify passwords / crypt-dev xvendor / Bugtraq Full-Disclosure linux-kernel linux-netdev linux-ext4 linux-hardening PHC | |
Open Source and information security mailing list archives
| ||
|
Date: Sat, 30 Oct 2021 10:03:19 +0000 From: Sargun Dhillon <sargun@...gun.me> To: linux-fsdevel@...r.kernel.org Cc: linux-kernel@...r.kernel.org, viro@...iv.linux.org.uk, akpm@...ux-foundation.org, willy@...radead.org, arnd@...nel.org, Willem de Bruijn <willemb@...gle.com> Subject: epoll may leak events on dup I discovered an interesting behaviour in epoll today. If I register the same file twice, under two different file descriptor numbers, and then I close one of the two file descriptors, epoll "leaks" the first event. This is fine, because one would think I could just go ahead and remove the event, but alas, that isn't the case. Some example python code follows to show the issue at hand. I'm not sure if this is really considered a "bug" or just "interesting epoll behaviour", but in my opinion this is kind of a bug, especially because leaks may happen by accident -- especially if files are not immediately freed. I'm also not sure why epoll events are registered by file, and not just fd. Is the expectation that you can share a single epoll amongst multiple "users" and register different files that have the same file descriptor number (at least for purposes other than CRIU). Maybe someone can shed light on the behaviour. (FWIW: On closing of all references the file, the callback finally occurs clearing the event) #!/usr/bin/env python3 import select import socket import os n = 2 sockets = [] epoll = select.epoll() for i in range(n): sockets.append(socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)) sockets[-1].bind(('::1', 0)) for i in sockets: epoll.register(i) clones = [os.dup(i.fileno()) for i in sockets] epoll.poll() # Returns: [(4, 4), (5, 4)] for i in clones: epoll.register(i) epoll.poll() # Returns: [(4, 4), (5, 4), (6, 4), (7, 4)] socket0name = sockets[0].getsockname() socket0fileno = sockets[0].fileno() print(f'Closing {socket0fileno}') os.close(socket0fileno) epoll.poll() # Returns: [(4, 4), (5, 4), (6, 4), (7, 4)] badsocket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) badsocket.sendto(b'test', (socket0name[0], socket0name[1])) epoll.poll() # Returns [(4, 5), (5, 4), (6, 5), (7, 4)] # Indicating fd 4, and 6 have events. # Oh whoops, I forgot to unregister 4... epoll.unregister(4) # Returns: # Traceback (most recent call last): # File "<stdin>", line 1, in <module> # FileNotFoundError: [Errno 2] No such file or directory print(open(f'/proc/{os.getpid()}/fdinfo/{epoll.fileno()}').read()) # Returns: # pos: 0 # flags: 02000002 # mnt_id: 15 # ino: 10994 # tfd: 5 events: 1f data: 5 pos:0 ino:30ffc5 sdev:8 # tfd: 7 events: 1f data: 7 pos:0 ino:30ffc5 sdev:8 # tfd: 4 events: 1f data: 4 pos:0 ino:30ffc4 sdev:8 # tfd: 6 events: 1f data: 6 pos:0 ino:30ffc4 sdev:8
Powered by blists - more mailing lists