#90 File descriptors for os.devnull is not closed after os.dup2 in redirect_stream
Opened by jun66j5. Modified

The redirect_stream() opens os.devnull and duplicates the file descriptor to stdin, stdout and stderr. However, the file descriptor is not closed.

Demo script:

from daemon import DaemonContext
import os, subprocess, sys
kwargs = {
    'stdin': sys.stdin,
    'stderr': sys.stderr,
    'files_preserve': [sys.stdout],
}
with DaemonContext(**kwargs):
    subprocess.run(['lsof', '-p', str(os.getpid())], stdout=sys.stderr)

Result:

$ /tmp/venv/bin/python /tmp/python-daemon-fd-inherit.py
$ COMMAND     PID    USER   FD   TYPE DEVICE SIZE/OFF     NODE NAME
python  2752676 jun66j5  cwd    DIR    8,1     4096      2 /
python  2752676 jun66j5  rtd    DIR    8,1     4096      2 /
python  2752676 jun66j5  txt    REG    8,1  5801848 131113 /usr/bin/python3.10
python  2752676 jun66j5  mem    REG    8,1    51856 267351 /lib/x86_64-linux-gnu/libnss_files-2.31.so
python  2752676 jun66j5  mem    REG    8,1   105560 266163 /lib/x86_64-linux-gnu/libnsl-2.31.so
python  2752676 jun66j5  mem    REG    8,1    51856 267371 /lib/x86_64-linux-gnu/libnss_nis-2.31.so
python  2752676 jun66j5  mem    REG    8,1    43992 266272 /lib/x86_64-linux-gnu/libnss_compat-2.31.so
python  2752676 jun66j5  mem    REG    8,1  8429328 132286 /usr/lib/locale/locale-archive
python  2752676 jun66j5  mem    REG    8,1   108936 264604 /lib/x86_64-linux-gnu/libz.so.1.2.11
python  2752676 jun66j5  mem    REG    8,1   182560 263584 /lib/x86_64-linux-gnu/libexpat.so.1.6.11
python  2752676 jun66j5  mem    REG    8,1  1369384 266109 /lib/x86_64-linux-gnu/libm-2.31.so
python  2752676 jun66j5  mem    REG    8,1    14880 267378 /lib/x86_64-linux-gnu/libutil-2.31.so
python  2752676 jun66j5  mem    REG    8,1    18848 266108 /lib/x86_64-linux-gnu/libdl-2.31.so
python  2752676 jun66j5  mem    REG    8,1   157224 267374 /lib/x86_64-linux-gnu/libpthread-2.31.so
python  2752676 jun66j5  mem    REG    8,1  2029592 265907 /lib/x86_64-linux-gnu/libc-2.31.so
python  2752676 jun66j5  mem    REG    8,1    19424 176184 /usr/lib/python3.10/lib-dynload/resource.cpython-310-x86_64-linux-gnu.so
python  2752676 jun66j5  mem    REG    8,1    27002 173131 /usr/lib/x86_64-linux-gnu/gconv/gconv-modules.cache
python  2752676 jun66j5  mem    REG    8,1   191504 264490 /lib/x86_64-linux-gnu/ld-2.31.so
python  2752676 jun66j5    0u   CHR  136,3      0t0      6 /dev/pts/3
python  2752676 jun66j5    1u   CHR    1,3      0t0      6 /dev/null
python  2752676 jun66j5    2u   CHR  136,3      0t0      6 /dev/pts/3
python  2752676 jun66j5    3u   CHR    1,3      0t0      6 /dev/null  # <== should be closed
--- daemon/daemon.py.orig   2024-06-27 18:18:57.814226447 +0900
+++ daemon/daemon.py        2024-06-28 05:03:01.592264223 +0900
@@ -978,6 +978,8 @@
     else:
         target_fd = target_stream.fileno()
     os.dup2(target_fd, system_stream.fileno())
+    if target_stream is None and target_fd != system_stream.fileno():
+        os.close(target_fd)
 def make_default_signal_map():

Metadata