PEP 433 – Einfachere Unterdrückung der Vererbung von Dateideskriptoren
- Autor:
- Victor Stinner <vstinner at python.org>
- Status:
- Abgelöst
- Typ:
- Standards Track
- Erstellt:
- 10-Jan-2013
- Python-Version:
- 3.4
- Ersetzt-Durch:
- 446
Inhaltsverzeichnis
- Zusammenfassung
- Begründung
- Vorschlag
- Alternativen
- Vererbung standardmäßig aktiviert, Standard kann nur auf True gesetzt werden
- Vererbung standardmäßig aktiviert, Standard kann nur auf True gesetzt werden
- Vererbung standardmäßig deaktivieren
- Schließe Dateideskriptoren nach fork
- open(): füge das "e"-Flag zum Modus hinzu
- Bikeshedding am Namen des neuen Parameters
- Anwendungen, die die Vererbung von Dateideskriptoren nutzen
- Leistung
- Implementierung
- Abwärtskompatibilität
- Anhang: Betriebssystemunterstützung
- Links
- Fußnoten
- Urheberrecht
Zusammenfassung
Füge einen neuen optionalen Parameter cloexec zu Funktionen hinzu, die Dateideskriptoren erstellen, füge verschiedene Möglichkeiten hinzu, Standardwerte dieses Parameters zu ändern, und füge vier neue Funktionen hinzu
os.get_cloexec(fd)os.set_cloexec(fd, cloexec=True)sys.getdefaultcloexec()sys.setdefaultcloexec(cloexec)
Begründung
Ein Dateideskriptor hat ein Close-on-Exec-Flag, das angibt, ob der Dateideskriptor vererbt wird oder nicht.
Unter UNIX wird der Dateideskriptor nicht vererbt, wenn das Close-on-Exec-Flag gesetzt ist: Er wird bei der Ausführung von Kindprozessen geschlossen; andernfalls wird der Dateideskriptor von Kindprozessen geerbt.
Unter Windows wird der Dateideskriptor nicht vererbt, wenn das Close-on-Exec-Flag gesetzt ist; der Dateideskriptor wird von Kindprozessen geerbt, wenn das Close-on-Exec-Flag gelöscht ist und wenn CreateProcess() mit dem Parameter bInheritHandles auf TRUE gesetzt aufgerufen wird (wenn subprocess.Popen beispielsweise mit close_fds=False erstellt wird). Windows hat kein "Close-on-Exec"-Flag, sondern ein Vererbungsflag, das einfach den umgekehrten Wert hat. Das Setzen des Close-on-Exec-Flags bedeutet beispielsweise das Löschen des HANDLE_FLAG_INHERIT-Flags eines Handles.
Status in Python 3.3
Unter UNIX schließt das `subprocess`-Modul seit Python 3.2 standardmäßig Dateideskriptoren größer als 2 [1]. Alle vom Elternprozess erstellten Dateideskriptoren werden im Kindprozess automatisch geschlossen.
xmlrpc.server.SimpleXMLRPCServer setzt das Close-on-Exec-Flag des lauschenden Sockets, die Elternklasse socketserver.TCPServer setzt dieses Flag nicht.
Es gibt andere Fälle, bei denen beim Erstellen eines Subprozesses oder beim Ausführen eines neuen Programms Dateideskriptoren nicht geschlossen werden: Funktionen der Familien os.spawn*() und os.exec*() und Drittanbieter-Module, die exec() oder fork() + exec() aufrufen. In diesem Fall werden Dateideskriptoren zwischen dem Eltern- und dem Kindprozess geteilt, was normalerweise unerwartet ist und verschiedene Probleme verursacht.
Dieses PEP schlägt vor, die mit der Änderung in `subprocess` in Python 3.2 begonnene Arbeit fortzusetzen, um das Problem in jeglichem Code zu beheben und nicht nur in Code, der `subprocess` verwendet.
Probleme mit vererbten Dateideskriptoren
Das Schließen des Dateideskriptors im Elternprozess schließt die zugehörige Ressource (Datei, Socket usw.) nicht, da sie im Kindprozess noch geöffnet ist.
Der lauschende Socket von `TCPServer` wird bei exec() nicht geschlossen: Der Kindprozess kann Verbindungen von neuen Clients empfangen; wenn der Elternprozess den lauschenden Socket schließt und einen neuen lauschenden Socket an derselben Adresse erstellt, würde er eine Fehlermeldung "Adresse wird bereits verwendet" erhalten.
Das Nichtschließen von Dateideskriptoren kann zu Ressourcenerschöpfung führen: Selbst wenn der Elternprozess alle Dateien schließt, kann die Erstellung eines neuen Dateideskriptors mit "zu viele Dateien" fehlschlagen, da Dateien im Kindprozess noch geöffnet sind.
Siehe auch folgende Probleme
- Problem #2320: Race Condition in subprocess mit stdin (2008)
- Problem #3006: subprocess.Popen lässt Socket nach dem Schließen offen (2008)
- Problem #7213: subprocess leckt offene Dateideskriptoren zwischen Popen-Instanzen, was zu Hängern führt (2009)
- Problem #12786: subprocess wait() hängt, wenn stdin geschlossen wird (2011)
Sicherheit
Das Leck von Dateideskriptoren ist eine schwerwiegende Sicherheitslücke. Ein nicht vertrauenswürdiger Kindprozess kann sensible Daten wie Passwörter lesen und über geleckte Dateideskriptoren die Kontrolle über den Elternprozess übernehmen. Es ist beispielsweise eine bekannte Schwachstelle, um aus einem chroot zu entkommen.
Siehe auch die CERT-Empfehlung: FIO42-C. Stellen Sie sicher, dass Dateien ordnungsgemäß geschlossen werden, wenn sie nicht mehr benötigt werden.
Beispiele für Schwachstellen
- OpenSSH Security Advisory: portable-keysign-rand-helper.adv (April 2011)
- CWE-403: Offenlegung des Dateideskriptors an ungewollte Kontrollsphäre (2008)
- Hijacking Apache https by mod_php (Dez. 2003)
- Apache: Apr sollte FD_CLOEXEC setzen, wenn APR_FOPEN_NOCLEANUP nicht gesetzt ist (behoben in 2009)
- PHP: system() (und ähnliche) bereinigen keine geöffneten Handles von Apache (nicht behoben im Januar 2013)
Atomarität
Die Verwendung von fcntl() zum Setzen des Close-on-Exec-Flags ist in einer Multithread-Anwendung nicht sicher. Wenn ein Thread fork() und exec() zwischen der Erstellung des Dateideskriptors und dem Aufruf von fcntl(fd, F_SETFD, new_flags) aufruft: Der Dateideskriptor wird vom Kindprozess geerbt. Moderne Betriebssysteme bieten Funktionen, um das Flag während der Erstellung des Dateideskriptors zu setzen, was die Race Condition vermeidet.
Portabilität
Python 3.2 fügte das Flag socket.SOCK_CLOEXEC hinzu, Python 3.3 fügte das Flag os.O_CLOEXEC und die Funktion os.pipe2() hinzu. Es ist bereits möglich, das Close-on-Exec-Flag atomar in Python 3.3 zu setzen, wenn eine Datei geöffnet und eine Pipe oder ein Socket erstellt wird.
Das Problem ist, dass diese Flags und Funktionen nicht portabel sind: nur neuere Versionen von Betriebssystemen unterstützen sie. O_CLOEXEC und SOCK_CLOEXEC Flags werden von alten Linux-Versionen ignoriert und daher muss das FD_CLOEXEC Flag mit fcntl(fd, F_GETFD) überprüft werden. Wenn der Kernel das O_CLOEXEC oder SOCK_CLOEXEC Flag ignoriert, ist ein Aufruf von fcntl(fd, F_SETFD, flags) erforderlich, um das Close-on-Exec-Flag zu setzen.
Hinweis
OpenBSD älter als 5.2 schließt den Dateideskriptor mit gesetztem Close-on-Exec-Flag nicht, wenn fork() vor exec() verwendet wird, aber es funktioniert korrekt, wenn exec() ohne fork() aufgerufen wird. Versuchen Sie openbsd_bug.py.
Umfang
Anwendungen müssen immer noch explizit Dateideskriptoren nach einem fork() schließen. Das Close-on-Exec-Flag schließt Dateideskriptoren nur nach exec(), und somit nach fork() + exec().
Dieses PEP ändert nur das Close-on-Exec-Flag von Dateideskriptoren, die von der Python-Standardbibliothek oder von Modulen, die die Standardbibliothek verwenden, erstellt werden. Drittanbieter-Module, die die Standardbibliothek nicht verwenden, sollten angepasst werden, um diesem PEP zu entsprechen. Die neue Funktion os.set_cloexec() kann beispielsweise verwendet werden.
Hinweis
Siehe Schließen von Dateideskriptoren nach fork für eine mögliche Lösung für fork() ohne exec().
Vorschlag
Füge einen neuen optionalen Parameter cloexec zu Funktionen hinzu, die Dateideskriptoren erstellen, und verschiedene Möglichkeiten, den Standardwert dieses Parameters zu ändern.
Füge neue Funktionen hinzu
os.get_cloexec(fd:int) -> bool: Holt das Close-on-Exec-Flag eines Dateideskriptors. Nicht auf allen Plattformen verfügbar.os.set_cloexec(fd:int, cloexec:bool=True): Setzt oder löscht das Close-on-Exec-Flag eines Dateideskriptors. Nicht auf allen Plattformen verfügbar.sys.getdefaultcloexec() -> bool: Holt den aktuellen Standardwert des cloexec-Parameterssys.setdefaultcloexec(cloexec: bool): Setzt den Standardwert des cloexec-Parameters
Füge einen neuen optionalen Parameter cloexec zu
asyncore.dispatcher.create_socket()io.FileIOio.open()open()os.dup()os.dup2()os.fdopen()os.open()os.openpty()os.pipe()select.devpoll()select.epoll()select.kqueue()socket.socket()socket.socket.accept()socket.socket.dup()socket.socket.fromfdsocket.socketpair()
Der Standardwert des cloexec-Parameters ist sys.getdefaultcloexec().
Füge eine neue Kommandozeilenoption -e und eine Umgebungsvariable PYTHONCLOEXEC hinzu, um das Close-on-Exec-Flag standardmäßig zu setzen.
subprocess löscht das Close-on-Exec-Flag von Dateideskriptoren des Parameters pass_fds.
Alle Funktionen, die Dateideskriptoren in der Standardbibliothek erstellen, müssen den Standardwert des cloexec-Parameters respektieren: sys.getdefaultcloexec().
Dateideskriptoren 0 (stdin), 1 (stdout) und 2 (stderr) werden voraussichtlich vererbt, aber Python behandelt sie nicht unterschiedlich. Wenn os.dup2() verwendet wird, um Standardströme zu ersetzen, muss cloexec=False explizit angegeben werden.
Nachteile des Vorschlags
- Es ist nicht mehr möglich zu wissen, ob das Close-on-Exec-Flag für einen neu erstellten Dateideskriptor gesetzt wird oder nicht, nur durch Lesen des Quellcodes.
- Wenn die Vererbung eines Dateideskriptors wichtig ist, muss der cloexec-Parameter nun explizit angegeben werden, oder die Bibliothek oder die Anwendung wird je nach Standardwert des cloexec-Parameters nicht funktionieren.
Alternativen
Vererbung standardmäßig aktiviert, Standard kann nur auf True gesetzt werden
Füge einen neuen optionalen Parameter cloexec zu Funktionen hinzu, die Dateideskriptoren erstellen. Der Standardwert des cloexec-Parameters ist False, und dieser Standard kann nicht geändert werden. Die standardmäßige Aktivierung der Dateideskriptor-Vererbung ist auch der Standard unter POSIX und Windows. Diese Alternative ist die konservativste Option.
Diese Option löst die im Abschnitt Begründung aufgeführten Probleme nicht, sie bietet lediglich eine Hilfe zur Behebung. Alle Funktionen, die Dateideskriptoren erstellen, müssen geändert werden, um cloexec=True in jedem von einer Anwendung verwendeten Modul zu setzen, um all diese Probleme zu beheben.
Vererbung standardmäßig aktiviert, Standard kann nur auf True gesetzt werden
Diese Alternative basiert auf dem Vorschlag: Der einzige Unterschied ist, dass sys.setdefaultcloexec() kein Argument nimmt, es kann nur verwendet werden, um den Standardwert des cloexec-Parameters auf True zu setzen.
Vererbung standardmäßig deaktivieren
Diese Alternative basiert auf dem Vorschlag: Der einzige Unterschied ist, dass der Standardwert des cloexec-Parameters True ist (statt False).
Wenn eine Datei von Kindprozessen geerbt werden soll, kann der Parameter cloexec=False verwendet werden.
Vorteile des standardmäßigen Setzens des Close-on-Exec-Flags
- Es gibt weitaus mehr Programme, die von der FD-Vererbung bei exec betroffen sind (siehe Probleme mit vererbten Dateideskriptoren und Sicherheit) als Programme, die davon abhängen (siehe Anwendungen, die die Vererbung von Dateideskriptoren nutzen).
Nachteile des standardmäßigen Setzens des Close-on-Exec-Flags
- Es verstößt gegen das Prinzip der geringsten Überraschung. Entwickler, die das `os`-Modul verwenden, erwarten möglicherweise, dass Python dem POSIX-Standard entspricht und dass das Close-on-Exec-Flag nicht standardmäßig gesetzt ist.
- Das `os`-Modul ist als dünne Hülle für Systemaufrufe (für Funktionen der C-Standardbibliothek) geschrieben. Wenn atomare Flags zum Setzen des Close-on-Exec-Flags nicht unterstützt werden (siehe Anhang: Betriebssystemunterstützung), kann ein einzelner Python-Funktionsaufruf 2 oder 3 Systemaufrufe erfordern (siehe Abschnitt Leistung).
- Zusätzliche Systemaufrufe, falls vorhanden, können Python verlangsamen: siehe Leistung.
Abwärtskompatibilität: Nur wenige Programme sind auf die Vererbung von Dateideskriptoren angewiesen, und sie übergeben nur wenige Dateideskriptoren, normalerweise nur einen. Diese Programme schlagen sofort mit einem EBADF-Fehler fehl, und es wird einfach sein, sie zu beheben: Füge den Parameter cloexec=False hinzu oder verwende os.set_cloexec(fd, False).
Das `subprocess`-Modul wird ohnehin geändert, um das Close-on-Exec-Flag für Dateideskriptoren zu löschen, die im Parameter pass_fds des Popen-Konstruktors aufgeführt sind. Daher ist es möglich, dass diese Programme keine Korrektur benötigen, wenn sie das `subprocess`-Modul verwenden.
Schließe Dateideskriptoren nach fork
Dieses PEP behebt keine Probleme mit Anwendungen, die fork() ohne exec() verwenden. Python benötigt einen generischen Prozess zur Registrierung von Rückruffunktionen, die nach einem fork aufgerufen werden, siehe #16500: Hinzufügen eines atfork-Moduls. Eine solche Registrierung könnte verwendet werden, um Dateideskriptoren direkt nach einem fork() zu schließen.
Nachteile
- Es löst das Problem unter Windows nicht:
fork()existiert unter Windows nicht - Diese Alternative löst das Problem nicht für Programme, die
exec()ohnefork()verwenden. - Ein Drittanbieter-Modul kann direkt die C-Funktion
fork()aufrufen, die keine "atfork"-Rückruffunktionen aufruft. - Alle Funktionen, die Dateideskriptoren erstellen, müssen geändert werden, um einen Rückruf zu registrieren und dann ihren Rückruf zu deregistrieren, wenn die Datei geschlossen wird. Oder es muss eine Liste aller offenen Dateideskriptoren geführt werden.
- Das Betriebssystem ist besser geeignet als Python, um Dateideskriptoren automatisch zu schließen. Es ist beispielsweise nicht einfach, eine Race Condition zwischen dem Schließen der Datei und dem Deregistrieren des Rückrufs, der die Datei schließt, zu vermeiden.
open(): füge das "e"-Flag zum Modus hinzu
Ein neuer Modus "e" würde das Close-on-Exec-Flag setzen (bestmögliche Bemühung).
Diese Alternative löst das Problem nur für open(). `socket.socket()` und `os.pipe()` haben beispielsweise keinen mode-Parameter.
Seit Version 2.7 unterstützt die GNU libc das Flag "e" für fopen(). Es verwendet O_CLOEXEC, falls verfügbar, oder verwendet fcntl(fd, F_SETFD, FD_CLOEXEC). Mit Visual Studio akzeptiert `fopen()` ein "N"-Flag, das O_NOINHERIT verwendet.
Bikeshedding am Namen des neuen Parameters
inherit,inherited: näher an der Windows-Definitionsensibelsterile: „produziert keine Nachkommen.“
Anwendungen, die die Vererbung von Dateideskriptoren nutzen
Die meisten Entwickler wissen nicht, dass Dateideskriptoren standardmäßig vererbt werden. Die meisten Programme sind nicht auf die Vererbung von Dateideskriptoren angewiesen. Beispielsweise wurde subprocess.Popen in Python 3.2 geändert, um standardmäßig alle Dateideskriptoren größer als 2 im Kindprozess zu schließen. Bisher hat sich kein Benutzer über diese Verhaltensänderung beschwert.
Netzwerkserver, die fork verwenden, möchten möglicherweise den Client-Socket an den Kindprozess übergeben. Zum Beispiel übergibt ein CGI-Server unter UNIX den Client-Socket über die Dateideskriptoren 0 (stdin) und 1 (stdout) mittels dup2().
Um auf eine eingeschränkte Ressource zuzugreifen, wie z. B. das Erstellen eines Sockets, der auf einem TCP-Port unter 1024 lauscht, oder das Lesen einer Datei mit sensiblen Daten wie Passwörtern, ist eine gängige Praxis: Als Root-Benutzer starten, einen Dateideskriptor erstellen, einen Kindprozess erstellen, die Berechtigungen fallen lassen (z. B. den aktuellen Benutzer ändern), den Dateideskriptor an den Kindprozess übergeben und den Elternprozess beenden.
Sicherheit ist in einem solchen Anwendungsfall sehr wichtig: Das Leck eines anderen Dateideskriptors wäre eine kritische Sicherheitslücke (siehe Sicherheit). Der Root-Prozess beendet sich möglicherweise nicht, sondern überwacht den Kindprozess und startet einen neuen Kindprozess neu und übergibt denselben Dateideskriptor, wenn der vorherige Kindprozess abgestürzt ist.
Beispiele für Programme, die Dateideskriptoren über eine Kommandozeilenoption vom Elternprozess übernehmen
- gpg:
--status-fd <fd>,--logger-fd <fd>, usw. - openssl:
-pass fd:<fd> - qemu:
-add-fd <fd> - valgrind:
--log-fd=<fd>,--input-fd=<fd>, usw. - xterm:
-S <fd>
Unter Linux ist es möglich, den Dateinamen "/dev/fd/<fd>" zu verwenden, um einen Dateideskriptor an ein Programm zu übergeben, das einen Dateinamen erwartet.
Leistung
Das Setzen des Close-on-Exec-Flags kann zusätzliche Systemaufrufe für jede Erstellung neuer Dateideskriptoren erfordern. Die Anzahl der zusätzlichen Systemaufrufe hängt von der Methode ab, die zum Setzen des Flags verwendet wird
O_NOINHERIT: kein zusätzlicher SystemaufrufO_CLOEXEC: ein zusätzlicher Systemaufruf, aber nur bei der Erstellung des ersten Dateideskriptors, um zu prüfen, ob das Flag unterstützt wird. Wenn das Flag nicht unterstützt wird, muss Python auf die nächste Methode zurückfallen.ioctl(fd, FIOCLEX): ein zusätzlicher Systemaufruf pro Dateideskriptorfcntl(fd, F_SETFD, flags): zwei zusätzliche Systemaufrufe pro Dateideskriptor, einer zum Abrufen der alten Flags und einer zum Setzen der neuen Flags
Unter Linux hat das Setzen des Close-on-Flag geringe Auswirkungen auf die Leistung. Ergebnisse von bench_cloexec.py auf Linux 3.6
- Close-on-Flag nicht gesetzt: 7,8 us
O_CLOEXEC: 1 % langsamer (7,9 us)ioctl(): 3 % langsamer (8,0 us)fcntl(): 3 % langsamer (8,0 us)
Implementierung
os.get_cloexec(fd)
Holt das Close-on-Exec-Flag eines Dateideskriptors.
Pseudocode
if os.name == 'nt':
def get_cloexec(fd):
handle = _winapi._get_osfhandle(fd);
flags = _winapi.GetHandleInformation(handle)
return not(flags & _winapi.HANDLE_FLAG_INHERIT)
else:
try:
import fcntl
except ImportError:
pass
else:
def get_cloexec(fd):
flags = fcntl.fcntl(fd, fcntl.F_GETFD)
return bool(flags & fcntl.FD_CLOEXEC)
os.set_cloexec(fd, cloexec=True)
Setzt oder löscht das Close-on-Exec-Flag eines Dateideskriptors. Das Flag wird nach der Erstellung des Dateideskriptors gesetzt und ist daher nicht atomar.
Pseudocode
if os.name == 'nt':
def set_cloexec(fd, cloexec=True):
handle = _winapi._get_osfhandle(fd);
mask = _winapi.HANDLE_FLAG_INHERIT
if cloexec:
flags = 0
else:
flags = mask
_winapi.SetHandleInformation(handle, mask, flags)
else:
fnctl = None
ioctl = None
try:
import ioctl
except ImportError:
try:
import fcntl
except ImportError:
pass
if ioctl is not None and hasattr('FIOCLEX', ioctl):
def set_cloexec(fd, cloexec=True):
if cloexec:
ioctl.ioctl(fd, ioctl.FIOCLEX)
else:
ioctl.ioctl(fd, ioctl.FIONCLEX)
elif fnctl is not None:
def set_cloexec(fd, cloexec=True):
flags = fcntl.fcntl(fd, fcntl.F_GETFD)
if cloexec:
flags |= FD_CLOEXEC
else:
flags &= ~FD_CLOEXEC
fcntl.fcntl(fd, fcntl.F_SETFD, flags)
ioctl wird `fcntl` vorgezogen, da es nur einen Systemaufruf erfordert, im Gegensatz zu zwei Systemaufrufen für `fcntl`.
Hinweis
fcntl(fd, F_SETFD, flags) unterstützt nur ein Flag (FD_CLOEXEC), sodass fcntl(fd, F_GETFD) vermieden werden könnte. Es kann jedoch in Zukunft andere Flags fallen lassen, daher ist es sicherer, die beiden Funktionsaufrufe beizubehalten.
Hinweis
fopen()-Funktion der GNU libc ignoriert den Fehler, wenn fcntl(fd, F_SETFD, flags) fehlschlägt.
open()
- Windows:
open()mitO_NOINHERITFlag [atomar] open()mitO_CLOEXEC Flag[atomar]open()+os.set_cloexec(fd, True)[bestmögliche Bemühung]
os.dup()
- Windows:
DuplicateHandle()[atomar] fcntl(fd, F_DUPFD_CLOEXEC)[atomar]dup()+os.set_cloexec(fd, True)[bestmögliche Bemühung]
os.dup2()
fcntl(fd, F_DUP2FD_CLOEXEC, fd2)[atomar]dup3()mitO_CLOEXECFlag [atomar]dup2()+os.set_cloexec(fd, True)[bestmögliche Bemühung]
os.pipe()
- Windows:
CreatePipe()mitSECURITY_ATTRIBUTES.bInheritHandle=TRUEoder_pipe()mitO_NOINHERITFlag [atomar] pipe2()mitO_CLOEXECFlag [atomar]pipe()+os.set_cloexec(fd, True)[bestmögliche Bemühung]
socket.socket()
- Windows:
WSASocket()mitWSA_FLAG_NO_HANDLE_INHERITFlag [atomar] socket()mitSOCK_CLOEXECFlag [atomar]socket()+os.set_cloexec(fd, True)[bestmögliche Bemühung]
socket.socketpair()
socketpair()mitSOCK_CLOEXECFlag [atomar]socketpair()+os.set_cloexec(fd, True)[bestmögliche Bemühung]
socket.socket.accept()
accept4()mitSOCK_CLOEXECFlag [atomar]accept()+os.set_cloexec(fd, True)[bestmögliche Bemühung]
Abwärtskompatibilität
Es gibt keine abwärtskompatiblen Änderungen. Das Standardverhalten bleibt unverändert: Das Close-on-Exec-Flag ist standardmäßig nicht gesetzt.
Anhang: Betriebssystemunterstützung
Windows
Windows hat ein O_NOINHERIT-Flag: „Nicht in Kindprozessen vererben“.
Es wird beispielsweise von open() und _pipe() unterstützt.
Das Flag kann mit SetHandleInformation(fd, HANDLE_FLAG_INHERIT, 0) gelöscht werden.
CreateProcess() hat einen Parameter bInheritHandles: Wenn er FALSE ist, werden die Handles nicht vererbt. Wenn er TRUE ist, werden Handles mit gesetztem HANDLE_FLAG_INHERIT-Flag vererbt. subprocess.Popen verwendet die Option close_fds, um bInheritHandles zu definieren.
ioctl
Funktionen
ioctl(fd, FIOCLEX, 0): Setzt das Close-on-Exec-Flagioctl(fd, FIONCLEX, 0): Löscht das Close-on-Exec-Flag
Verfügbarkeit: Linux, Mac OS X, QNX, NetBSD, OpenBSD, FreeBSD.
fcntl
Funktionen
flags = fcntl(fd, F_GETFD); fcntl(fd, F_SETFD, flags | FD_CLOEXEC): Setzt das Close-on-Exec-Flagflags = fcntl(fd, F_GETFD); fcntl(fd, F_SETFD, flags & ~FD_CLOEXEC): Löscht das Close-on-Exec-Flag
Verfügbarkeit: AIX, Digital UNIX, FreeBSD, HP-UX, IRIX, Linux, Mac OS X, OpenBSD, Solaris, SunOS, Unicos.
Atomare Flags
Neue Flags
O_CLOEXEC: verfügbar auf Linux (2.6.23), FreeBSD (8.3), OpenBSD 5.0, Solaris 11, QNX, BeOS, nächste NetBSD-Veröffentlichung (6.1?). Dieses Flag ist Teil von POSIX.1-2008.SOCK_CLOEXECFlag fürsocket()undsocketpair(), verfügbar auf Linux 2.6.27, OpenBSD 5.2, NetBSD 6.0.WSA_FLAG_NO_HANDLE_INHERITFlag fürWSASocket(): unterstützt unter Windows 7 mit SP1, Windows Server 2008 R2 mit SP1 und neuerfcntl():F_DUPFD_CLOEXECFlag, verfügbar auf Linux 2.6.24, OpenBSD 5.0, FreeBSD 9.1, NetBSD 6.0, Solaris 11. Dieses Flag ist Teil von POSIX.1-2008.fcntl():F_DUP2FD_CLOEXECFlag, verfügbar auf FreeBSD 9.1 und Solaris 11.recvmsg():MSG_CMSG_CLOEXEC, verfügbar auf Linux 2.6.23, NetBSD 6.0.
Auf Linux älter als 2.6.23 wird das Flag O_CLOEXEC einfach ignoriert. Daher müssen wir prüfen, ob das Flag unterstützt wird, indem wir fcntl() aufrufen. Wenn es nicht funktioniert, müssen wir das Flag mithilfe von ioctl() oder fcntl() setzen.
Auf Linux älter als 2.6.27 schlägt bei Setzen des Flags SOCK_CLOEXEC im Socket-Typ socket() oder socketpair() fehl und errno wird auf EINVAL gesetzt.
Unter Windows XPS3 gibt WSASocket() mit WSAEPROTOTYPE zurück, wenn das Flag WSA_FLAG_NO_HANDLE_INHERIT verwendet wird.
Neue Funktionen
dup3(): verfügbar auf Linux 2.6.27 (und glibc 2.9)pipe2(): verfügbar auf Linux 2.6.27 (und glibc 2.9)accept4(): verfügbar auf Linux 2.6.28 (und glibc 2.10)
Wenn accept4() auf Linux älter als 2.6.28 aufgerufen wird, gibt accept4() -1 (Fehler) zurück und errno wird auf ENOSYS gesetzt.
Links
Links
- Secure File Descriptor Handling (Ulrich Drepper, 2008)
- win32_support.py des Tornado-Projekts: emuliert fcntl(fd, F_SETFD, FD_CLOEXEC) mittels
SetHandleInformation(fd, HANDLE_FLAG_INHERIT, 1) - LKML: [PATCH] nextfd(2)
Python-Probleme
- #10115: Unterstützung von accept4() für atomares Setzen von Flags bei Socket-Erstellung
- #12105: open() kann keine Flags setzen, wie z.B. O_CLOEXEC
- #12107: TCP-lauschende Sockets werden ohne FD_CLOEXEC-Flag erstellt
- #16500: Hinzufügen eines atfork-Moduls
- #16850: Füge den "e"-Modus zu open() hinzu: close-and-exec (O_CLOEXEC) / O_NOINHERIT
- #16860: Verwende O_CLOEXEC im tempfile-Modul
- #17036: Implementierung von PEP 433
- #16946: subprocess: _close_open_fd_range_safe() setzt das close-on-exec-Flag unter Linux < 2.6.23 nicht, wenn O_CLOEXEC definiert ist
- #17070: PEP 433: Verwende das neue cloexec, um die Sicherheit zu verbessern und Fehler zu vermeiden
Andere Sprachen
- Perl setzt das close-on-exec-Flag auf neu erstellte Dateideskriptoren, wenn ihre Nummer größer ist als
$SYSTEM_FD_MAX($^F). Siehe $SYSTEM_FD_MAX Dokumentation. Perl tut dies seit der Erstellung von Perl (es war bereits in Perl 1 vorhanden). - Ruby: Setze FD_CLOEXEC für alle FDs (außer 0, 1, 2)
- Ruby: O_CLOEXEC Flag fehlt für Kernel::open: der Commit wurde später rückgängig gemacht
- OCaml: PR#5256: Prozesse, die mit Unix.open_process* geöffnet wurden, erben alle geöffneten Dateideskriptoren (einschließlich Sockets). OCaml hat eine Funktion
Unix.set_close_on_exec.
Fußnoten
Urheberrecht
Dieses Dokument wurde gemeinfrei erklärt.
Quelle: https://github.com/python/peps/blob/main/peps/pep-0433.rst
Zuletzt geändert: 2025-02-01 08:59:27 GMT