Provided by: manpages-de-dev_2.5-1_all 

BEZEICHNUNG
ptrace - Prozessverfolgung
ÜBERSICHT
#include <sys/ptrace.h>
long ptrace(enum __ptrace_request Anfrage, pid_t PID,
void *Adresse, void *Daten);
BESCHREIBUNG
Der Systemaufruf ptrace() stellt ein Mittel bereit, wodurch ein Prozess (der »Verfolger«) die Ausführung
eines anderen Prozesses (des »verfolgten Prozesses«) beobachten und steuern kann und seinen Speicher
sowie die Register untersuchen und ändern kann. Er wird in erster Linie benutzt, um Fehlersuche mittels
Haltepunkten zu implementieren und Systemaufrufe zu verfolgen.
Ein verfolgter Prozess muss zuerst an den Verfolger angehängt werden. Anhängen und nachfolgende Befehle
bestehen pro Thread: In einem Prozess mit mehreren Threads kann jeder Thread individuell an einen
(möglicherweise unterschiedlichen) Verfolger angehängt werden oder nicht angehängt und folglich nicht auf
Fehler untersucht werden. Daher bedeutet »verfolgter Prozess« immer »(ein) Thread«, niemals »ein Prozess
(mit möglicherweise mehreren Threads)«. Ptrace-Befehle werden immer an einen bestimmten verfolgten
Prozess gesandt. Der Aufruf hat folgende Form:
ptrace(PTRACE_foo, PID, …)
wobei PID die Thread-Kennung des zugehörigen Linux-Threads ist.
(Beachten Sie, dass auf dieser Seite ein »Prozess aus mehreren Threads« eine Thread-Gruppe bezeichnet,
die aus Threads besteht, die mittels des clone(2)-Schalters CLONE_THREAD erzeugt wurde.)
Ein Prozess kann eine Verfolgung mittels fork(2) starten und als Ergebnis einen Kindprozess erhalten, der
PTRACE_TRACEME ausführt, was (üblicherweise) von einem execve(2) gefolgt wird. Alternativ kann ein
Prozess die Verfolgung eines anderen Prozesses mittels PTRACE_ATTACH oder PTRACE_SEIZE beginnen.
Während der Prozess verfolgt wird, wird er jedesmal stoppen, wenn ein Signal gesandt wird, sogar wenn das
Signal ignoriert wird. (Eine Ausnahme ist SIGKILL, das seine normale Wirkung erzielt.) Der Verfolger wird
bei seinem nächsten Aufruf von waitpid(2) (oder den zugehörigen »wait«-Systemaufrufen) benachrichtigt;
dies wird einen Statuswert zurückgeben, der Informationen enthält, die den Grund angeben, weshalb der
verfolgte Prozess stoppte. Während der verfolgte Prozess angehalten ist, kann der Verfolger verschiedene
Ptrace-Anfragen verwenden, um den verfolgten Prozess zu untersuchen und zu verändern. Dann veranlasst der
Verfolger den verfolgten Prozess fortzufahren und wahlweise das versandte Signal zu ignorieren (oder
stattdessen sogar ein anderes Signal zu senden).
Falls die Option PTRACE_O_TRACEEXEC nicht in Kraft ist, werden alle erfolgreichen Aufrufe von execve(2)
durch den verfolgten Prozess zum Senden eines SIGTRAP-Signals veranlassen, um dem Elternprozess die
Möglichkeit zu geben, die Kontrolle zu erlangen, bevor die Ausführung des neuen Programms beginnt.
Wenn der Verfolger die Verfolgung beendet hat, kann er veranlassen, das der verfolgte Prozess mit
PTRACE_DETACH in einem normal Modus ohne Verfolgung fortfährt.
Der Wert des Arguments Anfrage legt die Aktion des Systemaufrufs fest:
PTRACE_TRACEME
zeigt an, dass dieser Prozess durch seinen Elternprozess verfolgt wird. Ein Prozess sollte diese
Anfrage wahrscheinlich nicht stellen, falls sein Elternprozess nicht erwartet, ihn zu
verfolgen.(PID, Adresse und Daten werden ignoriert.)
Die PTRACE_TRACEME-Anfrage wird nur vom verfolgten Prozess benutzt; die verbleibenden Anfragen
werden nur vom verfolgenden Prozess benutzt. In den folgenden Anfragen gibt PID die Thread-Kennung
des verfolgten Prozesses an, der beeinflusst werden soll. Für andere Anfragen als PTRACE_ATTACH,
PTRACE_SEIZE, PTRACE_INTERRUPT und PTRACE_KILL muss der verfolgte Prozess gestoppt werden.
PTRACE_PEEKTEXT, PTRACE_PEEKDATA
liest ein »word« an der Stelle Adresse im Speicher des verfolgten Prozesses und gibt das »word«
als Ergebnis des ptrace()-Aufrufs zurück. Linux hat keine separaten Adressräume für Text und
Daten, daher sind diese beiden Anfragen derzeit gleichwertig. (Das Argument Daten wird ignoriert,
lesen Sie aber auch ANMERKUNGEN.)
PTRACE_PEEKUSER
Liest ein »word« bei Versatz Adresse im BENUTZERbereich des verfolgten Prozesses, der die Register
und andere Informationen über den Prozess enthält (siehe <sys/user.h>). Das »word« wird als
Ergebnis des ptrace()-Aufrufs zurückgegeben. Typischerweise muss der Versatz am »word«
ausgerichtet sein, obwohl dies je nach Architektur variieren kann. Lesen Sie die ANMERKUNGEN.
(Daten wird ignoriert, lesen Sie aber auch ANMERKUNGEN.)
PTRACE_POKETEXT, PTRACE_POKEDATA
kopiert das »word« Daten an die Stelle Adresse im Speicher des verfolgenden Prozesses. Wie bei
PTRACE_PEEKTEXT und PTRACE_PEEKDATA sind die beiden Anfragen derzeit gleichwertig.
PTRACE_POKEUSER
kopiert das »word« Daten an den Versatz Adresse im BENUTZERbereich des verfolgten Prozesses. Wie
für PTRACE_PEEKUSER muss der Versatz am »word« ausgerichtet sein. Um die Integrität des Kernels
aufrecht zu erhalten, sind einige Änderungen in BENUTZERbereich nicht erlaubt.
PTRACE_GETREGS, PTRACE_GETFPREGS
kopiert die Mehrzweck- beziehungsweise Fließpunktregister des verfolgten Prozesses an die Stelle
Daten im Verfolger. Lesen Sie <sys/user.h>, um Informationen über das Format dieser Daten zu
erhalten. (Adresse wird ignoriert.) Beachten Sie, dass auf SPARC-Systemen die Bedeutung von Daten
und Adresse umgekehrt ist; daher wird Daten ignoriert und die Register werden an die Adresse
Adresse kopiert. PTRACE_GETREGS und PTRACE_GETFPREGS sind nicht auf allen Architekturen verfügbar.
PTRACE_GETREGSET (seit Linux 2.6.34)
liest die Register des verfolgten Prozesses. Adresse gibt auf eine von der Architektur unabhängige
Weise den Typ des Registers an, das gelesen werden soll. NT_PRSTATUS (mit numerischem Wert 1)
führt normalerweise dazu, dass Allzweckregister gelesen werden. Falls die CPU zum Beispiel
Fließkomma- und/oder Vektorregister hat, können sie durch Setzen von Adresse auf die entsprechende
Konstante NT_foo ermittelt werden. Daten zeigt auf einen struct iovec, der den Speicherort und die
Länge des Zielpuffers beschreibt. Bei der Rückkehr ändert der Kernel iov.len, um die tatsächliche
Anzahl zurückgegebener Byte anzuzeigen.
PTRACE_SETREGS, PTRACE_SETFPREGS
verändert die Mehrzweck- beziehungsweise Fließpunktregister des verfolgten Prozesses von der
Adresse Daten im Verfolger. Wie für PTRACE_POKEUSER können einige Änderungen am Mehrzweckregister
verboten sein. (Adresse wird ignoriert.) Beachten Sie, dass auf SPARC-Systemen die Bedeutung von
Daten und Adresse umgekehrt ist; daher wird Daten ignoriert und die Register werden von der
Adresse Adresse kopiert. PTRACE_SETREGS und PTRACE_SETFPREGS sind nicht auf allen Architekturen
verfügbar.
PTRACE_SETREGSET (seit Linux 2.6.34)
verändert die Register des verfolgten Prozesses. Die Bedeutung von Adresse und Daten ist analog zu
PTRACE_GETREGSET.
PTRACE_GETSIGINFO (seit Linux 2.3.99-pre6)
ruft Informationen über das Signal ab, das den Stopp verursachte. Kopiert eine siginfo_t-Struktur
(siehe sigaction(2)) vom verfolgten Prozess an die Stelle Daten im Verfolger. (Adresse wird
ignoriert.)
PTRACE_SETSIGINFO (seit Linux 2.3.99-pre6)
setzt Signalinformationen: kopiert eine siginfo_t-Struktur von der Adresse Daten vom verfolgenden
zum verfolgten Prozess. Dies wird nur Signale betreffen, die normalerweise an den verfolgten
Prozess zugestellt würden und vom Verfolger abgefangen wurden. Es könnte schwierig werden, diese
normalen Signale von künstlichen Signalen zu unterscheiden, die von ptrace() selbst generiert
wurden. (Adresse wird ignoriert.)
PTRACE_PEEKSIGINFO (seit Linux 3.10)
fragt siginfo_t-Strukturen ab, ohne Signale aus einer Warteschlange zu entfernen. Adresse zeigt
auf eine ptrace_peeksiginfo_args-Struktur, die die Ordnungsposition angibt, von der das Kopieren
der Signale starten soll sowie die Anzahl zu kopierender Signale. siginfo_t-Strukturen werden in
den Puffer kopiert, auf den Daten verweist. Der Rückgabewert enthält die Anzahl der kopierten
Signale (null zeigt an, dass es an der angegebenen Ordnungsposition kein entsprechendes Signal
gibt). Innerhalb der zurückgegebenen siginfo-Strukturen enthält das Feld si_code Informationen
(__SI_CHLD, __SI_FAULT, etc.), die ansonsten nicht an den Anwendungsraum offengelegt werden.
struct ptrace_peeksiginfo_args {
u64 off; /* Ordnungsposition in der Warteschlange, an der
mit dem Kopieren der Signale begonnen wird */
u32 flags; /* PTRACE_PEEKSIGINFO_SHARED oder 0 */
s32 nr; /* Anzahl zu kopierender Signale */
};
Derzeit gibt es nur einen Schalter, PTRACE_PEEKSIGINFO_SHARED, um Signale aus der prozessweiten
Signalwarteschlange auszugeben. Falls dieser Schalter nicht gesetzt ist, werden Signale von der
Thread-eigenen Warteschlange des angegebenen Threads gelesen.
PTRACE_GETSIGMASK (seit Linux 3.11)
Platzieren Sie eine Kopie der Maske blockierter Signale (siehe sigprocmask(2)) in den Puffer, auf
den Daten zeigt. Dies sollte ein Zeiger auf einen Puffer des Typs sigset_t sein. Das Argument
Adresse enthält die Größe des Puffers, auf den Daten zeigt (d.h. sizeof(sigset_t)).
PTRACE_SETSIGMASK (seit Linux 3.11)
ändert die Maske blockierter Signale (siehe sigprocmask(2)) auf den Wert, der im Puffer angegeben
wird, auf den Daten zeigt. Dies sollte ein Zeiger auf einen Puffer des Typs sigset_t sein. Das
Argument Adresse enthält die Größe des Puffers, auf den Daten zeigt (d.h. sizeof(sigset_t)).
PTRACE_SETOPTIONS (seit Linux 2.4.6; siehe FEHLER für Vorsichtsmaßnahmen)
setzt Ptrace-Optionen von Daten. (Adresse wird ignoriert.) Daten wird als Bit in der Maske der
Optionen interpretiert, die durch die folgenden Schalter angegeben wird:
PTRACE_O_EXITKILL (seit Linux 3.8)
schickt an den verfolgten Prozess ein SIGKILL-Signal, falls der verfolgende Prozess beendet
wird. Diese Option ist für diejenigen nützlich, die Ptrace einsperren und sicherstellen
wollen, dass der verfolgte Prozess nie der Steuerung des verfolgenden Prozesses entkommt.
PTRACE_O_TRACECLONE (seit Linux 2.5.46)
stoppt den verfolgten Prozess beim nächsten Aufruf von clone(2) und startet automatisch die
Verfolgung des neu geklonten Prozesses, der mit einem SIGSTOP oder, falls PTRACE_SEIZE
benutzt wurde, mit PTRACE_EVENT_STOP starten wird. Ein waitpid(2) durch den Verfolger wird
einen Statuswert wie diesen zurückgeben:
status>>8 == (SIGTRAP | (PTRACE_EVENT_CLONE<<8))
Die PID des neuen Prozesses kann mit PTRACE_GETEVENTMSG abgefragt werden.
Diese Option kann nicht in allen Fällen clone(2)-Aufrufe abfangen. Falls der verfolgte
Prozess clone(2) mit dem Schalter CLONE_VFORK aufruft, wird stattdessen PTRACE_EVENT_VFORK
geschickt, wenn PTRACE_O_TRACEVFORK gesetzt ist; andernfalls wird PTRACE_EVENT_FORK
geschickt, wenn der verfolgte Prozess clone(2) mit dem auf SIGCHLD gesetzten
Beendigungssignal aufgerufen wird, falls PTRACE_O_TRACEFORK gesetzt ist.
PTRACE_O_TRACEEXEC (seit Linux 2.5.46)
stoppt den verfolgten Prozess beim nächsten execve(2). Ein waitpid(2) durch den Verfolger
wird einen Statuswert wie diesen zurückgeben:
status>>8 == (SIGTRAP | (PTRACE_EVENT_EXEC<<8))
Falls der ausführende Thread kein führender Thread der Gruppe ist, wird die Thread-Kennung
vor dem Stopp auf die Kennung des führenden Threads der Gruppe zurückgesetzt. Seit Linux
3.0 kann die vorherige Thread-Kennung mit PTRACE_GETEVENTMSG abgefragt werden.
PTRACE_O_TRACEEXIT (seit Linux 2.5.60)
stoppt den verfolgten Prozess beim Beenden. Ein waitpid(2) durch den Verfolger wird einen
Statuswert wie diesen zurückgeben:
status>>8 == (SIGTRAP | (PTRACE_EVENT_EXIT<<8))
Der Exit-Status des verfolgten Prozesses kann mit PTRACE_GETEVENTMSG abgefragt werden.
Der verfolgte Prozess wird frühzeitig während des Beendens gestoppt, wenn die Register noch
verfügbar sind, was es dem Verfolger ermöglicht, zu sehen, wo das Beenden veranlasst wurde,
wohingegen die normale Benachrichtigung über die Beendigung geschickt wird, wenn der
Prozess das Beenden abgeschlossen hat. Auch wenn der Kontext verfügbar ist, kann der
Verfolger das Beenden an diesem Punkt nicht mehr verhindern.
PTRACE_O_TRACEFORK (seit Linux 2.5.46)
stoppt den verfolgten Prozess beim nächsten Aufruf von fork(2) und startet die Verfolgung
des neuen Prozesszweiges, der mit einem SIGSTOP oder, falls PTRACE_SEIZE benutzt wurde, mit
PTRACE_EVENT_STOP starten wird. Ein waitpid(2) durch den Verfolger wird einen Statuswert
wie diesen zurückgeben:
status>>8 == (SIGTRAP | (PTRACE_EVENT_FORK<<8))
Die PID des neuen Prozesses kann mit PTRACE_GETEVENTMSG abgefragt werden.
PTRACE_O_TRACESYSGOOD (seit Linux 2.4.6)
Wenn Systemaufrufe abgefangen werden, wird Bit 7 in der Signalnummer gesetzt (d.h. SIGTRAP
| 0x80 geschickt). Dies erleichtert es dem Verfolger, den Unterschied zwischen normalen,
abgefangenen Signalen und denen, die durch einen Systemaufruf verursacht wurden,
mitzuteilen. (PTRACE_O_TRACESYSGOOD funktioniert möglicherweise nicht auf allen
Architekturen.)
PTRACE_O_TRACEVFORK (seit Linux 2.5.46)
stoppt den verfolgten Prozess beim nächsten Aufruf von vfork(2) und startet automatisch die
Verfolgung des neuen »vfork«-Prozesszweiges, der mit einem SIGSTOP oder, falls PTRACE_SEIZE
benutzt wurde, mit PTRACE_EVENT_STOP starten wird. Ein waitpid(2) durch den Verfolger wird
einen Statuswert wie diesen zurückgeben:
status>>8 == (SIGTRAP | (PTRACE_EVENT_VFORK<<8))
Die PID des neuen Prozesses kann mit PTRACE_GETEVENTMSG abgefragt werden.
PTRACE_O_TRACEVFORKDONE (seit Linux 2.5.60)
stoppt den verfolgten Prozess bei Vollendung des nächsten vfork(2). Ein waitpid(2) durch
den Verfolger wird einen Statuswert wie diesen zurückgeben:
status>>8 == (SIGTRAP | (PTRACE_EVENT_VFORK_DONE<<8))
Die PID des neuen Prozesses kann (seit Linux 2.6.18) abgefragt werden mit
PTRACE_O_TRACESECCOMP (seit Linux 3.5)
stoppt den verfolgten Prozess, wenn eine seccomp(2) SECCOMP_RET_TRACE-Regel ausgelöst wird.
Ein waitpid(2) durch den Verfolger wird einen Statuswert wie diesen zurückgeben:
status>>8 == (SIGTRAP | (PTRACE_EVENT_SECCOMP<<8))
Während dies ein PTRACE_EVENT-Stopp auslöst, ist es einem Systemaufrufeintrittsstopp
ähnlich. Für Details lesen Sie die Bemerkungen über PTRACE_EVENT_SECCOMP weiter unten. Die
Seccomp-Ereignisnachrichtendaten (aus dem Abschnitt SECCOMP_RET_DATA der
Seccomp-Filterregel) können über PTRACE_GETEVENTMSG ermittelt werden.
PTRACE_O_SUSPEND_SECCOMP (seit Linux 4.3)
setzt den Seccomp-Schutz des verfolgten Prozesses aus. Dies gilt unabhängig vom Modus und
kann verwandt werden, wenn der verfolgte Prozess noch keine Seccomp-Filter installiert hat.
Das bedeutet, dass ein gültiger Anwendungsfall darin besteht, den Seccomp-Schutz
auszusetzen, bevor er von dem verfolgten Prozess installiert wird, dann den verfolgten
Prozess die Filter installieren zu lassen und danach diesen Schalter wieder zurückzusetzen,
wenn die Filter wiederaufgenommen werden sollen. Um diese Option zu setzen, muss der
verfolgende Prozess über die Capability CAP_SYS_ADMIN verfügen, er darf selber keinen
Seccomp-Schutz installiert haben und darf nicht PTRACE_O_SUSPEND_SECCOMP auf sich selbst
setzen.
PTRACE_GETEVENTMSG (seit Linux 2.5.46)
fragt eine Nachricht (als unsigned long) über das Ptrace-Ereignis, das einfach so auftrat, ab und
platziert es an die Adresse Daten im Verfolger. Für PTRACE_EVENT_EXIT ist dies der Exit-Status des
verfolgten Prozesses. Für PTRACE_EVENT_FORK, PTRACE_EVENT_VFORK und PTRACE_EVENT_CLONE ist dies
die PID des neuen Prozesses. Für PTRACE_EVENT_SECCOMP ist das SECCOMP_RET_DATA von seccomp(2)s
Filter, das der ausgelösten Regel zugeordnet ist. (Adresse wird ignoriert.)
PTRACE_CONT
startet den gestoppten, verfolgten Prozess erneut. Falls Daten nicht null ist, wird es als Nummer
des Signals interpretiert, das an den verfolgten Prozess geschickt wird; andernfalls wird kein
Signal geschickt. Dadurch kann der Verfolger zum Beispiel steuern, ob ein Signal an den verfolgten
Prozess geschickt wird oder nicht. (Adresse wird ignoriert.)
PTRACE_SYSCALL, PTRACE_SINGLESTEP
startet den gestoppten, verfolgten Prozess wie für PTRACE_CONT, arrangiert aber, dass der
verfolgte Prozess beim nächsten Eintritt oder einem Systemaufruf beziehungsweise nach der
Ausführung einer einzelnen Anweisung gestoppt wird. (Der verfolgte Prozess wird auch, wie üblich,
über den Empfang des Signals gestoppt.) Aus der Sicht des Verfolgers scheint es, als ob der
verfolgte Prozess durch Empfang eines SIGTRAP gestoppt wurde. Daher gibt es zum Beispiel für
PTRACE_SYSCALL die Idee, beim ersten Stopp die Argumente des Systemaufrufs zu prüfen, dann einen
anderen PTRACE_SYSCALL zu schicken und den Rückgabewert des Systemaufrufs am zweiten Stopp zu
prüfen. Das Argument Daten wird wie für PTRACE_CONT behandelt. (Adresse wird ignoriert.)
PTRACE_SYSEMU, PTRACE_SYSEMU_SINGLESTEP (seit Linux 2.6.14)
für PTRACE_SYSEMU beim nächsten Eintritt für den Systemaufruf, der nicht ausgeführt wird,
fortfahren und stoppen. Siehe die Dokumentation zu Systemaufrufstopps weiter unten. Für
PTRACE_SYSEMU_SINGLESTEP das gleiche tun, aber in einem einzigen Schritt, wenn es sich nicht um
einen Systemaufruf handelt. Dieser Aufruf wird von Programmen wie »User Mode Linux« verwandt, die
die Systemaufrufe des verfolgten Prozesses emulieren wollen. Das Argument Daten wird wie für
PTRACE_CONT behandelt. Das Argument Adresse wird ignoriert. Diese Anfragen werden derzeit nur
unter x86 unterstützt.
PTRACE_LISTEN (seit Linux 3.4)
startet den gestoppten verfolgten Prozess erneut, verhindert jedoch seine Ausführung. Der
resultierende Status ist dem eines Prozesses ähnlich, der durch ein SIGSTOP (oder ein anderes
Stoppsignal) angehalten wurde. Zusätzliche Informationen finden Sie im Unterabschnitt
»Gruppenstopp«. PTRACE_LISTEN funktioniert nur bei verfolgten Prozessen, die durch PTRACE_SEIZE
angehängt wurden.
PTRACE_KILL
sendet dem verfolgten Prozess ein SIGKILL, um ihn zu beenden. (Adresse und Daten werden
ignoriert.)
Diese Aktion ist überholt; benutzen Sie sie nicht! Senden Sie stattdessen ein SIGKILL direkt
mittels kill(2) oder tgkill(2). Das Problem bei PTRACE_KILL ist, dass es verlangt, dass sich der
verfolgte Prozess in einem Signallieferstopp befindet, andernfalls funktioniert es möglicherweise
nicht (d.h. es könnte komplett erfolgreich sein, würde aber den verfolgten Prozess killen). Im
Gegensatz dazu hat das direkte Senden von einem SIGKILL keine derartige Beschränkung.
PTRACE_INTERRUPT (seit Linux 3.4)
stoppt einen verfolgten Prozess. Falls der verfolgte Prozess im Kernel-Space läuft oder schläft
und PTRACE_SYSCALL in Kraft ist, wird der Systemaufruf unterbrochen und
Systemaufrufbeendigungsstopp gemeldet. (Der unterbrochene Systemaufruf wird beim Neustart des
verfolgten Prozesses ebenfalls neu gestartet.) Falls der verfolgte Prozess bereits durch ein
Signal gestoppt und PTRACE_LISTEN an ihn gesendet wurde, stoppt der verfolgte Prozess mit
PTRACE_EVENT_STOP und WSTOPSIG(Status) gibt das Stoppsignal zurück. Falls zur selben Zeit
irgendein anderes »ptrace-stop« erzeugt wird (zum Beispiel, weil ein Signal an den verfolgten
Prozess gesendet wird), tritt dieses »ptrace-stop« auf. Falls nichts von obigem zutrifft (zum
Beispiel, weil der verfolgte Prozess im Anwendungsraum läuft), stoppt er mit PTRACE_EVENT_STOP mit
WSTOPSIG(status) == SIGTRAP. PTRACE_INTERRUPT funktioniert nur bei verfolgten Prozessen, die durch
PTRACE_SEIZE angehängt wurden.
PTRACE_ATTACH
hängt an den Prozess, der durch PID angegeben wird, an und lässt ihn zum verfolgten Prozess des
aufrufenden Prozesses werden. Dem verfolgten Prozess wird ein SIGSTOP gesandt, er wird aber nicht
notwendigerweise durch die Vollendung dieses Aufrufs gestoppt; benutzen Sie waitpid(2), um auf das
Stoppen des verfolgten Prozesses zu warten. Lesen Sie den Unterabschnitt »Anhängen und Loslösen«,
um zusätzliche Informationen zu erhalten. (Adresse und Daten werden ignoriert.)
Berechtigungen, ein PTRACE_ATTACH durchzuführen, werden durch eine
Ptrace-Zugriffsmodus-PTRACE_MODE_ATTACH_REALCREDS-Überprüfung geregelt; siehe unten.
PTRACE_SEIZE (seit Linux 3.4)
hängt an den zu dem in PID angegebenen Prozess an, wodurch er ein verfolgter Prozess des
aufrufenden Prozesses wird. Anders als PTRACE_ATTACH beendet PTRACE_SEIZE den Prozess nicht.
Gruppenstopps werden als PTRACE_EVENT_STOP berichtet und WSTOPSIG(status) liefert das Stopp-Signal
zurück. Automatisch angehängte Kinder stoppen mit PTRACE_EVENT_STOP und WSTOPSIG(status) liefert
SIGTRAP zurück, statt ein SIGSTOP Signal geliefert zu bekommen. execve(2) liefert kein
zusätzliches SIGTRAP aus. Nur ein Prozess, der PTRACE_SEIZE ist, kann die Befehle PTRACE_INTERRUPT
und PTRACE_LISTEN akzeptieren. Das gerade beschriebene »beschlagnahmte« (engl. »seized«) Verhalten
wird von Kindern geerbt, die automatisch mittels PTRACE_O_TRACEFORK, PTRACE_O_TRACEVFORK und
PTRACE_O_TRACECLONE angehängt sind. Adresse muss Null sein. Daten enthält eine Bitmaske, die die
sofort zu aktivierenden Optionen enthält.
Berechtigungen, ein PTRACE_SEIZE durchzuführen, werden durch eine
Ptrace-Zugriffsmodus-PTRACE_MODE_ATTACH_REALCREDS-Überprüfung geregelt; siehe unten.
PTRACE_SECCOMP_GET_FILTER (seit Linux 4.4)
Diese Aktion erlaubt es dem verfolgenden Prozess, die klassischen BPF-Filter des verfolgten
Prozesses auszugeben.
Adresse ist eine Ganzzahl, die den Index des Filters, der ausgegeben werden soll, festlegt. Der
neuste installierte Filter hat den Index 0. Falls Adresse größer als die Anzahl der installierten
Filter ist, schlägt die Aktion mit dem Fehler ENOENT fehl.
Daten ist entweder ein Zeiger auf ein Feld struct sock_filter, das groß genug ist, ein
BPF-Programm zu speichern, oder NULL, falls das Programm nicht gespeichert werden soll.
Im Erfolgsfall ist der Rückgabewert die Anzahl der Befehle in dem BPF-Programm. Falls Daten NULL
war, dann kann dieser Rückgabewert benutzt werden, um in einem folgenden Aufruf die korrekte Größe
des Feldes struct sock_filter zu übergeben.
Diese Aktion schlägt mit dem Fehler EACCESS fehl, falls der Aufrufende nicht die Capability
CAP_SYS_ADMIN hat oder falls der Aufrufende in einem strikten oder Filter-Seccomp-Modus ist. Falls
der durch Adresse referenzierte Filter kein klassischer BPF-Filter ist, schlägt die Aktion mit dem
Fehler EMEDIUMTYPE fehl.
Diese Aktion ist verfügbar, falls der Kernel mit den Optionen CONFIG_SECCOMP_FILTER und
CONFIG_CHECKPOINT_RESTORE konfiguriert wurde.
PTRACE_DETACH
startet den gestoppten, verfolgten Prozess wie für PTRACE_CONT, löst ihn aber zuerst vom Prozess
ab. Unter Linux kann ein verfolgter Prozess auf diese Art abgelöst werden, ohne Rücksicht darauf
zu nehmen, welche Methode zum Starten der Verfolgung benutzt wurde. (Adresse wird ignoriert.)
PTRACE_GET_THREAD_AREA (seit Linux 2.6.0)
Diese Aktion führt eine ähnliche Aufgabe wie get_thread_area(2) durch. Sie liest den TLS-Eintrag
in dem GDT, dessen Index in addr gegeben ist und legt eine Kopie des Eintrags in das durch Daten
verwiesende struct user_desc. (Im Gegensatz wird mit get_thread_area(2) die entry_number des
struct user_desc ignoriert.)
PTRACE_SET_THREAD_AREA (seit Linux 2.6.0)
Diese Aktion führt eine ähnliche Aufgabe wie set_thread_area(2) durch. Sie setzt den TLS-Eintrag
in dem GDT, dessen Index in Adresse gegeben ist, und weist ihm die in dem durch Daten verwiesenen
struct user_desc zu. (Im Gegensatz zu set_thread_area(2) wird die entry_number des struct
user_desc ignoriert; mit anderen Worten, diese Ptrace-Aktion kann nicht zur Bereitstellung eines
freien TLS-Eintrags verwandt werden.)
Tod unter Ptrace
Wenn ein Prozess (der möglicherweise aus mehreren Threads besteht) ein tötendes Signal erhält (eines,
dessen Zuordnung auf SIG_DFL gesetzt ist und dessen Standardaktion das Töten des Prozesses ist) werden
alle Threads beendet. Verfolgte Prozesse melden ihren Tod an ihre(n) Verfolger. Die Benachrichtigung über
dieses Ereignis wird über waitpid(2) zugestellt.
Beachten Sie, dass das killende Signal zuerst einen Signallieferstopp (auf nur einen verfolgten Prozess)
verursachen wird und nur nachdem es durch den Verfolger eingespeist wurde (oder nachdem es an einen nicht
verfolgten Thread versandt wurde), wird der Tod von dem Signal auf alle verfolgten Prozesse innerhalb
eines Prozesses mit mehreren Threads ausgehen. (Der Begriff »Signallieferstopp« wird nachfolgend
erklärt.)
SIGKILL erzeugt keinen »Signallieferstopp«. Daher kann der Verfolger es nicht unterdrücken. SIGKILL killt
sogar innerhalb von Systemaufrufen (der Systemaufrufbeendigungsstopp wird nicht vorrangig vor dem Tod
durch SIGKILL erzeugt). Der reine Effekt besteht darin, dass SIGKILL den Prozess (all seine Threads)
immer killt, sogar dann, wenn einige Threads des Prozesses verfolgt werden.
Wenn der verfolgte Prozess _exit(2) aufruft, meldet er seinem Verfolger seinen Tod. Andere Threads werden
nicht beeinflusst.
Wenn irgendein Thread exit_group(2) ausführt, meldet jeder verfolgte Prozess in dessen Gruppe seinen Tod
an den Verfolger.
Falls die Option PTRACE_O_TRACEEXIT aktiv ist, wird PTRACE_EVENT_EXIT vor dem tatsächlichen Tod
auftreten. Dies wird angewandt, um mittels exit(2), exit_group(2) und Todessignalen (ausgenommen SIGKILL,
abhängig von der Kernel-Version, siehe FEHLER unten) zu beenden und wenn Threads bei execve(2) in einem
Prozess mit mehreren Threads zerrissen werden.
Der Verfolger kann nicht abschätzen, ob der von Ptrace gestoppte Prozess existiert. Es gibt mehrere
Szenarien, in denen der verfolgte Prozess sterben kann, während er gestoppt ist (wie SIGKILL). Daher muss
der Verfolger vorbereitet werden, bei allen Ptrace-Aktionen einen ESRCH-Fehler zu handhaben. Leider wird
der gleiche Fehler zurückgegeben, falls der verfolgte Prozess existiert, aber nicht von Ptrace gestoppt
wurde (für Befehle, die einen gestoppten, verfolgten Prozess erfordern) oder falls er nicht durch den
Prozess verfolgt wird, der den Ptrace-Aufruf abschickte. Der Verfolger muss den Überblick über den
»laufend«-/»gestoppt«-Status des verfolgten Prozesses behalten und ESRCH nur dann als »verfolgter Prozess
starb unerwartet« interpretieren, falls er weiß, dass der verfolgte Prozess beobachtet wurde, um in einen
Ptrace-Stopp einzutreten. Beachten Sie, dass es keine Garantie gibt, dass waitpid(WNOHANG) zuverlässig
den Todesstatus des verfolgten Prozesses meldet, falls eine Ptrace-Aktion ESRCH zurückgibt.
waitpid(WNOHANG) könnte stattdessen 0 zurückgeben. In anderen Worten kann es sein, dass der verfolgte
Prozess »noch nicht vollständig tot« ist, aber bereits Ptrace-Anfragen ablehnt.
Der Verfolger kann nicht abschätzen, ob der verfolgte Prozess immer sein Leben durch Melden von
WIFEXITED(status) oder WIFSIGNALED(status) aushaucht. Es gibt Fälle, in denen dies nicht geschieht. Falls
ein Thread, der nicht führender Thread der Thread-Gruppe ist, zum Beispiel ein execve(2) ausführt,
verschwindet er; seine PID wird nie wieder gesehen und alle nachfolgenden Ptrace-Stopps werden unter der
PID des führenden Threads der Thread-Gruppe gemeldet.
Gestoppt-Status
Ein verfolgter Prozess kann zwei Status haben: laufend oder gestoppt. Für die Zwecke von Ptrace wird ein
durch einen Systemaufruf (wie read(2), pause(2), etc.) blockierter verfolgter Prozess dennoch als laufend
betrachtet, sogar, wenn der verfolgte Prozess für lange Zeit blockiert ist. Der Status des verfolgten
Prozesses nach PTRACE_LISTEN ist eine Grauzone: Er ist nicht in einem Ptrace-Stopp (Ptrace-Befehle
funktionieren nicht bei ihm und er wird waitpid(2)-Benachrichtigungen übermitteln), aber er kann auch als
»gestoppt« angesehen werden, da er keine Anweisungen ausführt (ist nicht eingeplant) und falls er vor
PTRACE_LISTEN in einem Gruppenstopp war, wird er nicht auf Signale antworten, bis er SIGCONT empfängt.
Es gibt viele Arten von Status, wenn ein verfolgter Prozess gestoppt wurde und in Ptrace-Diskussionen
werden sie oft durcheinandergebracht. Daher ist es wichtig, präzise Begriffe zu verwenden.
In dieser Handbuchseite wird jeder Gestoppt-Status, in dem der verfolgte Prozess bereit ist,
Ptrace-Befehle vom Verfolger zu akzeptieren, Ptrace-Stopp genannt. Ptrace-Stopps können weiter in
Signallieferstopp, Gruppenstopp, PTRACE_EVENT-Stopps und so fort unterteilt werden. Diese gestoppten
Status werden nachfolgend im Detail beschrieben.
Wenn der laufende, verfolgte Prozess in Ptrace-Stopp eintritt, benachrichtigt er seinen Verfolger mittels
waitpid(2) (oder einem anderen der »wait«-Systemaufrufe). Meistens geht diese Handbuchseite davon aus,
dass der Verfolger wartet mit:
PID = waitpid(PID_ODER_Minus1, &status, __WALL);
Mit Ptrace-Stopp angehaltene, verfolgte Prozesse werden als Rückgaben mit PID größer als 0 und
»WIFSTOPPED(status) true« gemeldet.
Der Schalter __WALL enthält nicht die Schalter WSTOPPED und WEXITED, impliziert aber ihre Funktionalität.
Es wird nicht empfohlen, den Schalter WCONTINUED zu setzen, wenn waitpid(2) aufgerufen wird: Der Status
»continued« gilt pro Prozess und ihn zu verbrauchen, kann den echten Elternprozess des verfolgten
Prozesses verwirren.
Die Benutzung des Schalters WNOHANG könnte waitpid(2) veranlassen, 0 zurückzugeben (»noch keine
Warteergebnisse verfügbar«), sogar dann, wenn der Verfolger weiß, dass dort eine Benachrichtigung sein
soll. Beispiel:
errno = 0;
ptrace(PTRACE_CONT, pid, 0L, 0L);
if (errno == ESRCH) {
/* verfolgter Prozess ist tot */
r = waitpid(tracee, &status, __WALL | WNOHANG);
/* r kann hier immer noch 0 sein! */
}
Die folgenden Arten von Ptrace-Stopps existieren: Signallieferstopps, Gruppenstopps, PTRACE_EVENT-Stopps
und Systemaufrufstopps. Sie alle werden von waitpid(2) mit »WIFSTOPPED(status) true« gemeldet. Sie
könnten durch Untersuchen des Wertes status>>8 unterschieden werden und, falls es eine Unklarheit im Wert
gibt, durch Abfragen von PTRACE_GETSIGINFO. (Hinweis: Das Makro WSTOPSIG(status) kann nicht für diese
Untersuchung verwandt werden, da es den Wert (status>>8) & 0xff zurückgibt.)
Signallieferstopp
Wenn ein Prozess (möglicherweise mit mehreren Threads) ein Signal außer SIGKILL empfängt, wählt der
Kernel einen beliebigen Thread aus, der das Signal handhabt. (Falls das Signal mit tgkill(2) erzeugt
wurde, kann der Ziel-Thread explizit durch den Aufrufenden ausgewählt werden.) Falls der ausgewählte
Thread verfolgt wird, tritt er in einen Signallieferstopp ein. An diesem Punkt wird das Signal noch nicht
an den Prozess zugestellt und kann durch den Verfolger unterdrückt werden. Falls der Verfolger das Signal
nicht unterdrückt, übergibt er das Signal bei der nächsten Ptrace-Neustartanfrage an den verfolgten
Prozess. Dieser zweite Schritt der Signalzustellung wird in dieser Handbuchseite Signaleinspeisung
genannt. Beachten Sie, dass, falls das Signal blockiert ist, der Signallieferstopp nicht auftritt, bis
die Blockade des Signals aufgehoben wurde, mit der üblichen Ausnahme, dass SIGSTOP nicht blockiert werden
kann.
Der Signallieferstopp wird vom Verfolger als waitpid(2) beobachtet und kehrt mit »WIFSTOPPED(status)
true« mit dem Signal zurück, das von WSTOPSIG(status) zurückgegeben wurde. Falls das Signal SIGTRAP ist,
könnte dies eine andere Art eines Ptrace-Stopps sein; Einzelheiten finden Sie in den Abschnitten
»Systemaufrufstopps« und »execve« unterhalb. Falls WSTOPSIG(status) ein stoppendes Signal zurückgibt,
könnte dies ein Gruppenstopp sein; siehe unten.
Signaleinspeisung und -unterdrückung
Nachdem der Signallieferstopp durch den Verfolger beobachtet wurde, sollte der Verfolger den verfolgten
Prozess mit dem Aufruf
ptrace(PTRACE_restart, PID, 0, Signal)
neu starten, wobei PTRACE_restart einer der neu startenden Ptrace-Anfragen ist. Falls Signal 0 ist, wird
das Signal nicht zugestellt. Andernfalls wird das Signal Signal zugestellt. Diese Aktion wird in dieser
Handbuchseite Signaleinspeisung genannt, um sie vom Signallieferstopp zu unterscheiden.
Der Signalwert kann sich vom Wert WSTOPSIG(status) unterschieden: Der Verfolger kann veranlassen, dass
ein anderes Signal eingespeist wird.
Beachten Sie, dass ein unterdrücktes Signal immer noch Systemaufrufe verursacht, um vorzeitig
zurückzukehren. In diesem Fall werden Systemaufrufe neu gestartet: Der Verfolger wird den verfolgten
Prozess beobachten, um den unterbrochenen Systemaufruf neu auszuführen (oder den Systemaufruf
restart_syscall(2) für wenige Systemaufrufe, die unterschiedliche Mechanismen zum erneuten Starten
verwenden), falls der Verfolger PTRACE_SYSCALL benutzt. Sogar Systemaufrufe (wie poll(2)), die nach einem
Signal nicht mehr neu startbar sind, werden nach dem Unterdrücken des Signals neu gestartet werden. Es
existieren jedoch einige Kernelfehler, die zum Fehlschlagen einiger Systemaufrufe mit EINTR führen,
sogar, wenn kein beobachtbares Signal in den verfolgten Prozess eingespeist wurde.
Es wird nicht garantiert, dass beim Neustarten von Ptrace-Befehlen, die in anderen Ptrace-Stopps als
Signallieferstopps angestoßen wurden,ein Signal eingespeist wird, nicht einmal, wenn Signal nicht Null
ist. Es wird kein Fehler gemeldet; ein Signal ungleich Null könnte einfach ignoriert werden.
Ptrace-Benutzer sollten nicht versuchen, auf diese Art »ein neues Signal zu erzeugen«: Benutzen Sie
stattdessen tgkill(2).
Die Tatsache, dass Signaleinspeisungsanfragen beim erneuten Starten des verfolgten Prozesses nach
Ptrace-Stopps, die keine Signallieferstopps sind, ignoriert werden können, ist ein Grund für Verwirrung
bei Ptrace-Benutzern. Ein typisches Szenario ist, dass der Verfolger Gruppenstopps beobachtet, sie
fälschlicherweise für Signallieferstopps hält und den verfolgen Prozess mit
ptrace(PTRACE_restart, PID, 0, Stoppsignal)
neu startet mit der Absicht Stoppsignal einzuspeisen, Stoppsignal aber ignoriert wird und der verfolgte
Prozess weiter läuft.
Das Signal SIGCONT hat einen Seiteneffekt, dass es einen Prozess im Gruppenstopp (alle Threads davon)
aufweckt. Dieser Seiteneffekt tritt vor dem Signallieferstopp auf. Der Verfolger kann diesen Seiteneffekt
nicht unterdrücken (er kann nur Signaleinspeisung unterdrücken, was nur dazu führt, dass die
SIGCONT-Handhabung nicht im verfolgten Prozess ausgeführt wird, falls eine solche Handhabung installiert
ist). Tatsächlich könnte dem Aufwecken aus den Gruppenstopp ein Signallieferstopp für andere Signale als
SIGCONT folgen, falls sie ausstehen, wenn SIGCONT gesandt wurde. In anderen Worten könnte es sein, dass
SIGCONT nicht das erste durch den Verfolger beobachtete Signal ist, nachdem es gesandt wurde.
Stoppen von Signalen führt dazu, das ein Prozess (alle Threads davon) in einen Gruppenstopp eintritt.
Dieser Seiteneffekt tritt nach der Signaleinspeisung auf und kann daher durch den Verfolger unterdrückt
werden.
In Linux 2.4 und älter kann das Signal SIGSTOP nicht eingespeist werden.
PTRACE_GETSIGINFO kann benutzt werden, um eine siginfo_t-Struktur zu erhalten, die dem gesandten Signal
entspricht. PTRACE_SETSIGINFO kann benutzt werden, um es zu verändern. Falls PTRACE_SETSIGINFO benutzt
wurde, um siginfo_t zu verändern, müssen das Feld si_signo und der Parameter Signal im Befehl zum
Neustart übereinstimmen, andernfalls ist das Ergebnis undefiniert.
Gruppenstopp
Wenn ein Prozess (der möglicherweise aus mehreren Threads besteht) ein Stoppsignal empfängt, werden alle
Threads gestoppt. Falls einige Threads verfolgt werden, treten sie in eine Thread-Gruppe ein. Beachten
Sie, dass das Stoppsignal zuerst einen Signallieferstopp verursachen wird (nur auf den verfolgten
Prozess) und nur, nachdem es durch den Verfolger eingespeist wurde (oder nachdem es an einen Thread
geschickt wurde, der nicht verfolgt wird), wird der Gruppenstopp auf alle verfolgten Prozesse innerhalb
eines Prozesses aus mehreren Threads eingeleitet. Wie üblich meldet jeder verfolgte Prozess seinen
Gruppenstopp separat an den entsprechenden Verfolger.
Der Gruppenstopp wird vom Verfolger als waitpid(2) beobachtet und kehrt mit »WIFSTOPPED(status) true« mit
dem Stoppsignal zurück, das über WSTOPSIG(status) verfügbar ist. Dasselbe Ergebnis wird von einigen
anderen Klassen von Ptrace-Stopps zurückgegeben, daher ist die empfohlene Vorgehensweise, folgenden
Aufruf zu tätigen:
ptrace(PTRACE_GETSIGINFO, PID, 0, &siginfo)
Der Aufruf kann vermieden werden, falls das Signal nicht SIGSTOP, SIGTSTP, SIGTTIN oder SIGTTOU ist. Nur
diese vier Signale sind Stoppsignale. Falls der Verfolger etwas anderes sieht, kann es kein Gruppenstopp
sein. Andernfalls benötigt der Verfolger den Aufruf PTRACE_GETSIGINFO. Falls PTRACE_GETSIGINFO mit EINVAL
fehlschlägt, ist es definitiv ein Gruppenstopp. (Andere Fehlerkodes wie ESRCH (»kein derartiger Prozess«)
sind möglich, falls ein SIGKILL den verfolgten Prozess gekillt hat.
Falls ein verfolgter Prozess mittels PTRACE_SEIZE angehängt wurde, wird ein Gruppenstopp durch
PTRACE_EVENT_STOP angezeigt: Status>>16 == PTRACE_EVENT_STOP. Dies ermöglicht, einen Gruppenstopp
festzustellen, ohne dass ein zusätzlicher PTRACE_GETSIGINFO-Aufruf erforderlich ist.
Ab Linux 2.6.38 wird der verfolgte Prozess, nachdem der Verfolger den Ptrace-Stopp des verfolgten
Prozesses sieht und bis er neu startet oder ihn killt, nicht laufen und keine Benachrichtigungen an den
Verfolger senden (außer dem Tod durch SIGKILL), nicht einmal, wenn der Verfolger in einen anderen
waitpid(2)-Aufruf gelangt.
Das im vorhergehenden Absatz beschriebene Verhalten verursacht ein Problem bei transparenter Behandlung
von Stoppsignalen. Falls der Verfolger den verfolgten Prozess nach einem Gruppenstopp neu startet, wird
das Stoppsignal effektiv ignoriert – der verfolgte Prozess bleibt nicht gestoppt, er läuft. Falls der
Verfolger den verfolgten Prozess neu startet, bevor er in das nächste waitpid(2) eintritt, werden
zukünftige SIGCONT-Signale nicht an den Verfolger gemeldet. Dies würde dazu führen, dass die
SIGCONT-Signale keine Auswirkungen auf den verfolgten Prozess haben.
Seit Linux 3.4 gibt es eine Methode, die dieses Problem bewältigt: Statt PTRACE_CONT kann ein
PTRACE_LISTEN-Befehl zum Neustart eines verfolgten Prozesses auf eine Weise benutzt werden, die ihn nicht
ausführt, aber auf ein neues Ereignis wartet, das er über waitpid(2) melden kann, wenn er zum Beispiel
mit SIGCONT neu gestartet wird.
PTRACE_EVENT-Stopps
Falls der Verfolger PTRACE_O_TRACE_*-Optionen setzt, wird der verfolgte Prozess in PTRACE_EVENT-Stopps
genannte Stopps gelangen.
PTRACE_EVENT-Stopps werden durch den Verfolger als waitpid(2) beobachtet, kehren mit »WIFSTOPPED(status)
true« zurück und WSTOPSIG(status) gibt SIGTRAP zurück. Es wird ein zusätzliches Bit in das höhere Bit des
Status (Datentyp Word) gesetzt: Der Wert status>>8 wird wie folgt sein:
(SIGTRAP | PTRACE_EVENT_foo << 8).
Es gibt die folgenden Ereignisse:
PTRACE_EVENT_VFORK
stoppt vor dem Zurückkehren von vfork(2) oder clone(2) mit dem Schalter CLONE_VFORK. Wenn der
verfolgte Prozess nach diesem Stopp fortgeführt wird, wird er auf das Beenden/Ausführen des
Kindprozesses warten, bevor er mit seiner Ausführung fortfährt (in anderen Worten, das übliche
Verhalten auf vfork(2)).
PTRACE_EVENT_FORK
stoppt vor dem Zurückkehren von fork(2) oder clone(2) mit dem auf SIGCHLD gesetzten
Beendigungssignal.
PTRACE_EVENT_CLONE
stoppt vor dem Zurückkehren von clone(2).
PTRACE_EVENT_VFORK_DONE
stoppt vor dem Zurückkehren von vfork(2) oder clone(2) mit dem Schalter CLONE_VFORK, aber nachdem
die Blockade dieses verfolgten Prozesses durch Beenden oder Ausführung aufgehoben wurde.
Für alle vier oben beschriebenen Stopps tritt der Stopp im Elternprozess auf (d.h. im verfolgenden
Prozess), nicht im neu erstellten Thread. PTRACE_GETEVENTMSG kann benutzt werden, um die Kennung des
neuen Threads zu erhalten.
PTRACE_EVENT_EXEC
stoppt vor dem Zurückkehren von execve(2). Ab Linux 3.0, gibt PTRACE_GETEVENTMSG die vorherige
Thread-Kennung zurück.
PTRACE_EVENT_EXIT
stoppt vor dem Beenden (einschließlich des Todes aus exit_group(2)), dem Signaltod oder endet,
verursacht durch execve(2), in einem Prozess aus mehreren Threads. PTRACE_GETEVENTMSG gibt den
Exit-Status zurück. Register können untersucht werden (solange nicht »wirklich« beendet wird). Der
verfolgte Prozess ist immer noch lebendig; er benötigt zum Fertigstellen des Beendens PTRACE_CONT
oder PTRACE_DETACH.
PTRACE_EVENT_STOP
Stopp, der durch einen PTRACE_INTERRUPT-Befehl, einen Gruppenstopp oder ein »ptrace-stop« zu
Beginn veranlasst wurde, wenn ein neuer Kindprozess angehängt wird (nur beim Anhängen mittels
PTRACE_SEIZE).
PTRACE_EVENT_SECCOMP
Stopp, der durch eine seccomp(2)-Regel durch den Eintritt des verfolgten Prozesses in einen
Systemaufruf ausgelöst wird, wenn PTRACE_O_TRACESECCOMP vom Verfolger gesetzt wird. Die
Seccomp-Ereignisdaten (von dem Anteil SECCOMP_RET_DATA der Filterregel) können durch
PTRACE_GETEVENTMSG ermittelt werden. Die Semantik dieses Stopps werden in einem separaten
Abschnitt weiter unten beschrieben.
PTRACE_GETSIGINFO auf PTRACE_EVENT-Stopps gibt SIGTRAP in si_signo zurück, wobei si_code auf
(event<<8) | SIGTRAP gesetzt ist.
Systemaufrufstopps
Falls der verfolgte Prozess durch PTRACE_SYSCALL oder PTRACE_SYSEMU neu gestartet wurde, gerät der
verfolgte Prozess in einen Systemaufrufeintrittsstopp kurz vor dem Eintritt in irgendeinen Systemaufruf
(der nicht ausgeführt wird, falls der Neustart PTRACE_SYSEMU verwandte, unabhängig von allen Änderungen,
die zu diesem Zeitpunkt an den Registern vorgenommen wurden oder wie der verfolgte Prozess nach diesem
Stopp neu gestartet wird). Unabhängig davon, welche Methode den Systemaufrufeintrittsstopp verursachte,
falls der Verfolger den verfolgten Prozess mit PTRACE_SYSCALL neu startet, gerät der verfolgte Prozess in
einen Systemaufrufbeendigungsstopp, wenn der Systemaufruf beendet ist oder falls er durch ein Signal
unterbrochen wurde. (Sprich, der Signallieferstopp tritt nie zwischen Systemaufrufeintrittsstopp und
Systemaufrufbeendigungsstopp auf; er findet nach dem Systemaufrufbeendigungsstopp statt.) Falls der
verfolgte Prozess mittels irgendeiner anderen Methode fortgesetzt wird (einschließlich PTRACE_SYSEMU)
erfolgt kein Systemaufrufbeendigungsstop. Beachten Sie, dass alle Erwähnungen von PTRACE_SYSEMU genauso
auf PTRACE_SYSEMU_SINGLESTEP zutreffen.
Selbst falls der verfolgte Prozess mittels PTRACE_SYSCALL fortgesetzt wird, wird nicht garantiert, dass
der nächste Stopp ein Systemaufrufbeendigungsstopp sein wird. Andere Möglichkeiten sind, dass der
verfolgte Prozess in einem PTRACE_EVENT-Stopp (einschließlich Seccomp-Stopp) stoppen könnte, endet (falls
er in _exit(2) oder exit_group(2) eintritt), durch SIGKILL gekillt wird oder leise stirbt (falls er die
Thread-Gruppe anführt, kommt das execve(2) in einem anderen Thread vor und der Thread wird nicht vom
selben Verfolger verfolgt; diese Situation wird später besprochen).
Systemaufrufeintrittsstopp und Systemaufrufbeendigungsstopp werden vom Verfolger als waitpid(2)
beobachtet, kehren mit »WIFSTOPPED(status) true« zurück und WSTOPSIG(status) gibt SIGTRAP zurück. Falls
die Option PTRACE_O_TRACESYSGOOD durch den Verfolger gesetzt wurde, wird WSTOPSIG(status) den Wert
(SIGTRAP | 0x80) zurückgeben.
Systemaufrufstopps können von Signallieferstopps mit SIGTRAP durch Abfrage von PTRACE_GETSIGINFO für die
folgenden Fälle unterschieden werden:
si_code <= 0
SIGTRAP wurde mit einem Ergebnis einer Anwendungsraumaktion, zum Beispiel einem Systemaufruf
((tgkill(2), kill(2), sigqueue(3), etc.), Ablauf eines POSIX-Timers, Statusänderung einer
POSIX-Nachrichtenwarteschlange oder Vervollständigung einer asynchronen E/A-Anfrage geliefert.
si_code == SI_KERNEL (0x80)
SIGTRAP wurde vom Kernel gesandt.
si_code == SIGTRAP or si_code == (SIGTRAP|0x80)
Dies ist ein Systemaufrufstopp.
Systemaufrufstopps kommen jedoch sehr oft vor (zweimal pro Systemaufruf) und das Ausführen von
PTRACE_GETSIGINFO für jeden Systemaufrufstopp könnte etwas aufwendig sein.
Einige Architekturen erlauben, die Fälle durch Untersuchen der Register zu unterscheiden. Zum Beispiel
auf x86, rax == -ENOSYS im Systemaufrufeintrittsstopp. Da SIGTRAP (wie jedes andere Signal) immer nach
dem Systemaufrufbeendigungsstopp auftritt und rax an diesem Punkt fast nie ENOSYS enthält, sieht das
SIGTRAP aus wie ein »Systemaufrufstopp, der kein Systemaufrufeintrittsstopp ist«; in anderen Worten, er
sieht aus wie ein »herrenloser Systemaufrufbeendigungsstopp« und kann auf diese Art erkannt werden. Aber
eine solche Erkennung ist fragil und wird am besten vermieden.
Die Benutzung der Option PTRACE_O_TRACESYSGOOD ist die empfohlene Methode, um Systemaufrufstopps von
anderen Arten der Ptrace-Stopps zu unterscheiden, da sie zuverlässig ist und sich keine Leistungseinbuße
zuzieht.
Systemaufrufeintrittsstopp und Systemaufrufbeendigungsstopp sind für den Verfolger nicht voneinander zu
unterscheiden. Der Verfolger muss den Überblick über die Abfolge der Ptrace-Stopps behalten, um nicht den
Systemaufrufeintrittsstopp fälschlicherweise als Systemaufrufbeendigungsstopp oder umgekehrt zu
interpretieren. Im Allgemeinen folgt diesem Systemaufrufeintrittsstopp immer ein
Systemaufrufbeendigungsstopp, PTRACE_EVENT-Stopp oder der Tod des verfolgten Prozesses; dazwischen können
keine anderen Arten von Ptrace-Stopps auftreten. Beachten Sie allerdings, dass Seccomp-Stopps (siehe
unten) Systemaufrufbeendigungsstopps ohne vorhergehende Systemaufrufeintrittsstopps hervorrufen können.
Falls Seccomp verwandt wird, muss Sorgfalt eingesetzt werden, um solche Stopps nicht als
Systemaufrufeintrittsstopps misszuinterpretieren.
Falls der Verfolger nach dem Systemaufrufeintrittsstopp einen anderen Befehl zum Neustarten als
PTRACE_SYSCALL verwendet, wird der Systemaufrufbeendigungsstopp nicht erzeugt.
PTRACE_GETSIGINFO auf Systemaufrufstopps gibt SIGTRAP in si_signo zurück, wobei si_code auf SIGTRAP oder
(SIGTRAP|0x80) gesetzt ist.
PTRACE_EVENT_SECCOMP-Stopps (Linux 3.5 bis 4.7)
Das Verhalten des PTRACE_EVENT_SECCOMP-Stopps und seiner Wechselwirkung mit anderen Arten von
Ptrace-Stopps hat sich zwischen Kernel-Versionen geändert. Hier wird das Verhalten von seiner Einführung
bis Linux 4.7 (einschließlich) beschrieben. Das Verhalten in neueren Kernelversionen wird im nächsten
Abschnitt beschrieben.
Ein PTRACE_EVENT_SECCOMP-Stopp erfolgt, wann immer eine SECCOMP_RET_TRACE-Regel ausgelöst wird. Dies ist
von der Methode, die zum Neustart des Systemaufrufes verwandt wurde, unabhängig. Insbesondere läuft
Seccomp immer noch, selbst falls der verfolgte Prozess mittels PTRACE_SYSEMU neu gestartet wurde und
dieser Systemaufruf wird bedingungslos übersprungen.
Neustarts aus diesem Stopp verhalten sich so, als ob der Stopp direkt vor dem in Frage stehenden
Systemaufruf stattgefunden hätte. Insbesondere werden sowohl PTRACE_SYSCALL als auch PTRACE_SYSEMU
normalerweise einen folgenden Systemaufrufeintrittsstopp auslösen. Falls allerdings nach dem
PTRACE_EVENT_SECCOMP die Systemaufrufnummer negativ ist, werden sowohl der Systemaufrufeintrittsstopp als
auch der Systemaufruf selbst übersprungen. Das bedeutet, dass falls die Systemaufrufnummer nach dem
PTRACE_EVENT_SECCOMP negativ ist und der verfolgte Prozess mittels PTRACE_SYSCALL neu gestartet wird, der
nächste beobachtete Stopp ein Systemaufrufbeendigungsstopp statt des vielleicht erwarteten
Systemaufrufeintrittsstopps sein wird.
PTRACE_EVENT_SECCOMP-Stopps (seit Linux 4.8)
Beginnend mit Linux 4.8 wurde der Stopp PTRACE_EVENT_SECCOMP neu geordnet, so dass er zwischen
Systemaufrufeintrittsstopp und Systemaufrufbeendigungsstopp auftritt. Beachten Sie, dass Seccomp nicht
länger ausgeführt wird (und kein PTRACE_EVENT_SECCOMP berichtet wird) falls der Systemaufruf aufgrund
PTRACE_SYSEMU übersprungen wird.
Funktional arbeitet ein PTRACE_EVENT_SECCOMP-Stopp vergleichbar mit einem Systemaufrufeintrittsstopp
(d.h. Fortsetzungen mittels PTRACE_SYSCALL werden einen Systemaufrufbeendigungsstopp auslösen, die
Systemaufrufnummer könnte sich ändern und alle anderen veränderten Register sind im gleich auszuführenden
Systemaufruf ebenfalls sichtbar). Beachten Sie, dass es einen vorhergehenden Systemaufrufeintrittsstopp
gegeben haben kann, aber nicht muss.
Nach einem Stopp PTRACE_EVENT_SECCOMP wird Seccomp mit einer Regel SECCOMP_RET_TRACE, die identisch zu
einer SECCOMP_RET_ALLOW funktioniert, erneut ausgeführt. Insbesondere bedeutet dies, dass falls Register
nicht während des Stopps PTRACE_EVENT_SECCOMP verändert wurden, der Systemaufruf dann erlaubt wird.
PTRACE_SINGLESTEP-Stopps
[Einzelheiten dieser Arten von Stopps sind noch nicht dokumentiert.]
Benachrichtigende und neustartende Ptrace-Befehle
Die meisten Ptrace-Befehle (alle außer PTRACE_ATTACH, PTRACE_SEIZE, PTRACE_TRACEME, PTRACE_INTERRUPT und
PTRACE_KILL) erfordern, dass der verfolgte Prozess in einem Ptrace-Stopp ist, andernfalls scheitern sie
mit ESRCH.
Wenn der verfolgte Prozess im Ptrace-Stopp ist, kann der Verfolger Daten des verfolgten Prozesses mittels
benachrichtigenden Befehlen lesen und schreiben. Diese Befehle belassen den verfolgten Prozess im Status
Ptrace-gestoppt:
ptrace(PTRACE_PEEKTEXT/PEEKDATA/PEEKUSER, PID, Adresse, 0);
ptrace(PTRACE_POKETEXT/POKEDATA/POKEUSER, PID, Adresse, long_val);
ptrace(PTRACE_GETREGS/GETFPREGS, PID, 0, &struct);
ptrace(PTRACE_SETREGS/SETFPREGS, PID, 0, &struct);
ptrace(PTRACE_GETREGSET, PID, NT_foo, &iov);
ptrace(PTRACE_SETREGSET, PID, NT_foo, &iov);
ptrace(PTRACE_GETSIGINFO, PID, 0, &siginfo);
ptrace(PTRACE_SETSIGINFO, PID, 0, &siginfo);
ptrace(PTRACE_GETEVENTMSG, PID, 0, &long_var);
ptrace(PTRACE_SETOPTIONS, PID, 0, PTRACE_O_flags);
Beachten Sie, dass einige Fehler nicht gemeldet wurden. Das Setzen des Informationssignals (siginfo) hat
zum Beispiel in einigen Ptrace-Stopps möglicherweise keine Auswirkungen, der Aufruf kann jedoch
erfolgreich sein (0 zurückgeben und errno nicht setzen); Abfragen von PTRACE_GETEVENTMSG könnte
erfolgreich sein und einen zufälligen Wert zurückgeben, falls der aktuelle Ptrace-Stopp nicht
dokumentiert ist, um eine aussagekräftige Ereignisnachricht zurückzugeben.
Der Aufruf
ptrace(PTRACE_SETOPTIONS, PID, 0, PTRACE_O_flags);
beeinflusst einen verfolgten Prozess. Die aktuellen Schalter des verfolgten Prozesses werden ersetzt.
Schalter werden geerbt durch neu erzeugte Prozesse und »automatisch angehängt« über aktive
PTRACE_O_TRACEFORK-, PTRACE_O_TRACEVFORK- oder PTRACE_O_TRACECLONE-Optionen.
Eine weitere Gruppe von Befehlen lässt die per Ptrace gestoppten, verfolgten Prozesse laufen. Sie haben
die Form:
ptrace(Befehl, PID, 0, Signal);
wobei Befehl PTRACE_CONT, PTRACE_LISTEN, PTRACE_DETACH, PTRACE_SYSCALL, PTRACE_SINGLESTEP, PTRACE_SYSEMU
oder PTRACE_SYSEMU_SINGLESTEP ist. Falls der verfolgte Prozess sich im Signallieferstopp befindet, ist
Signal das Signal, das eingespeist wird (falls es ungleich Null ist). Andernfalls kann Signal ignoriert
werden. (Wenn ein verfolgter Prozess von einem anderen Ptrace-Stopp als dem Signallieferstopp neu
gestartet wird, ist die empfohlene Vorgehensweise, 0 in Signal zu übergeben.)
Anhängen und Loslösen
Ein Thread kann an den Verfolger angehängt werden mit dem Aufruf
ptrace(PTRACE_ATTACH, PID, 0, 0);
oder
ptrace(PTRACE_SEIZE, PID, 0, PTRACE_O_flags);
PTRACE_ATTACH sendet außerdem SIGSTOP an diesen Thread. Falls der Verfolger möchte, dass dieser SIGSTOP
keine Auswirkungen hat, muss er ihn unterdrücken. Beachten Sie, dass der Verfolger, falls während des
Anhängens gleichzeitig weitere Signale an diesen Thread gesandt werden, den Eintritt in den
Signallieferstopp mit anderen Signalen zuerst sieht! Die übliche Vorgehensweise ist, diese Signale neu
einzuspeisen, bis SIGSTOP gesehen wird und dann die Einspeisung von SIGSTOP zu unterdrücken. Der
Entwurfsfehler ist hierbei, dass sich ein Ptrace-Anhängen und ein gleichzeitig gesandtes SIGSTOP einen
Wettlauf liefern und das gleichzeitige SIGSTOP verloren gegangen sein kann.
Da Anhängen SIGSTOP sendet und der Verfolger es üblicherweise unterdrückt, könnte dies zu einer
herrenlosen EINTR-Rückgabe vom aktuell ausgeführten Systemaufruf in diesem verfolgten Prozess führen, wie
er im Abschnitt »Signaleinspeisung und -unterdrückung« beschrieben wird.
Seit Linux 3.4 kann PTRACE_SEIZE anstelle von PTRACE_ATTACH benutzt werden. PTRACE_SEIZE stoppt nicht den
angehängten Prozess. Falls Sie ihn nach dem Anhängen (oder zu einem anderen Zeitpunkt) stoppen wollen
ohne irgendwelche Signale zu senden, verwenden Sie den Befehl PTRACE_INTERRUPT.
Die Anfrage
ptrace(PTRACE_TRACEME, 0, 0, 0);
verwandelt den aufrufenden Thread in einen verfolgten Prozess. Der Thread fährt mit der Ausführung fort
(gerät nicht in den Ptrace-Stopp). Eine übliche Vorgehensweise besteht darin, PTRACE_TRACEME mit
raise(SIGSTOP);
zu folgen und dem Elternprozess (der nun der Verfolger ist) zu ermöglichen, den Signallieferstopp zu
beobachten.
Falls die Optionen PTRACE_O_TRACEFORK, PTRACE_O_TRACEVFORK oder PTRACE_O_TRACECLONE in Kraft sind, werden
Kindprozesse mit vfork(2), beziehungsweise clone(2) mit dem Schalter CLONE_VFORK, fork(2) oder clone(2)
mit auf SIGCHLD gesetztem Beendigungssignal und anderen Arten von clone(2) automatisch an den gleichen
Verfolger angehängt, der ihren Elternprozess verfolgte. SIGSTOP wird an die Kindprozesse gesandt, was sie
veranlasst, in einen Signallieferstopp zu gelangen, nachdem sie den Systemaufruf beenden, der sie erzeugt
hat.
Loslösen des verfolgten Prozesses wird erreicht durch:
ptrace(PTRACE_DETACH, PID, 0, Signal);
PTRACE_DETACH ist eine Neustartaktion; daher erfordert sie, dass der verfolgte Prozess in einem
Ptrace-Stopp ist. Falls der verfolgte Prozess in einem Signallieferstopp ist, kann ein Signal eingespeist
werden. Andernfalls wird der Parameter Signal stillschweigend ignoriert.
Falls der verfolgte Prozess läuft, wenn der Verfolger ihn loslösen möchte, besteht die übliche Lösung
darin, SIGSTOP zu senden (mittels tgkill(2), um sicherzustellen, dass es an den korrekten Thread geht),
darauf zu warten, dass der verfolgte Prozess in einen Signallieferstopp für SIGSTOP stoppt und ihn dann
loszulösen (SIGSTOP-Einspeisung wird unterdrückt). Ein Entwurfsfehler besteht darin, dass sich dies mit
gleichzeitigen SIGSTOPs Ressourcenwettläufe liefern kann. Eine weitere Komplikation besteht darin, dass
der verfolgte Prozess in andere Ptrace-Stopps geraten kann und neu gestartet werden muss und nochmals
gewartet werden muss, bis SIGSTOP gesehen wird. Noch eine weitere Komplikation besteht darin, dass nicht
sicher ist, ob der verfolgte Prozess nicht bereits mit Ptrace gestoppt wurde, da keine Signallieferung
stattfindet, obwohl es noch nicht einmal SIGSTOP ist.
Falls der Verfolger stirbt, werden alle verfolgten Prozesse automatisch losgelöst und neu gestartet, es
sei denn, sie sind im Gruppenstopp. Die Handhabung des Neustarts aus dem Gruppenstopp ist derzeit
fehlerhaft, aber das »wie-geplant«-Verhalten ist, den verfolgten Prozess gestoppt zu lassen und auf
SIGCONT zu warten. Falls der verfolgte Prozess neu vom Signallieferstopp gestartet wurde, wird das
ausstehende Signal einspeist.
execve(2) unter Ptrace
Wenn ein Thread in einem Prozess mit mehreren Threads execve(2) aufruft, zerstört der Kernel alle anderen
Threads im Prozess und setzt die Thread-Kennung des ausführenden Threads auf die Gruppenkennung
(Prozesskennung) zurück. (Oder anders ausgedrückt, wenn ein Prozess mit mehreren Threads ein execve(2)
bei Vervollständigung des Aufrufs ausführt, scheint es durch das execve(2) im führenden Thread der
Prozessgruppe aufzutreten, unabhängig davon, welcher Thread das execve(2) aufrief.) Dieses Zurücksetzen
der Thread-Kennung sieht für Verfolger sehr verwirrend aus:
* Alle anderen Threads stoppen im PTRACE_EVENT_EXIT-Stopp, falls die Option PTRACE_O_TRACEEXIT
eingeschaltet wurde. Dann melden alle anderen Threads außer dem führenden Thread der Gruppe den Tod,
als ob sie über _exit(2) mit dem Exit-Code 0 beendet worden wären.
* Der ausführende, verfolgte Prozess ändert seine Thread-Kennung, während er in dem execve(2) ist.
(Denken Sie daran, unter Ptrace ist die von waitpid(2) zurückgegebene oder in Ptrace-Aufrufe gespeiste
»PID«, die Thread-Kennung des verfolgten Prozesses.) Sprich, die Thread-Kennung des verfolgten
Prozesses wird zurückgesetzt, so dass sie ihrer Prozesskennung entspricht, die dieselbe ist, wie die
Thread-Kennung des führenden Threads der Thread-Gruppe.
* Dann kommt es zu einem PTRACE_EVENT_EXEC-Stopp, falls die Option PTRACE_O_TRACEEXEC eingeschaltet
wurde.
* Falls der führende Thread der Gruppe seinen PTRACE_EVENT_EXEC-Stopp mittlerweile gemeldet hat, scheint
es für den Verfolger, als ob der tote führende Thread »aus dem Nichts wieder auftaucht«. (Hinweis: Der
führende Thread der Gruppe meldet den Tod nicht über WIFEXITED(status) bis es mindestens einen
lebenden anderen Thread gibt. Dies eliminiert die Möglichkeit, dass der Verfolger ihn sterben und dann
erneut erscheinen sieht.) Falls der führende Thread der Gruppe immer noch lebt, könnte dies für den
Verfolger so aussehen, als ob der führende Thread der Gruppe von einem anderen Systemaufruf als dem
beigetretenen zurückkehrt oder sogar »von einem Systemaufruf zurückkehrt, obwohl er in keinem
Systemaufruf war«. Falls der führende Thread der Gruppe nicht verfolgt wurde (oder von einem anderen
Verfolger verfolgt wurde), dann wird es während execve(2) so aussehen, als ob er ein verfolgter
Prozess des Verfolgers des ausführenden verfolgten Prozesses geworden wäre.
All die Auswirkungen oberhalb sind Artefakte des Thread-Kennungswechsels im verfolgten Prozess.
Die Option PTRACE_O_TRACEEXEC ist das empfohlene Werkzeug für den Umgang mit dieser Situation. Zuerst
aktiviert es PTRACE_EVENT_EXEC-Stopp, der vor der Rückkehr von execve(2) auftritt. In diesem Stopp kann
der Verfolger PTRACE_GETEVENTMSG verwenden, um die vorherige Thread-Kennung des verfolgten Prozesses zu
erhalten. (Diese Funktion wurde in Lunux 3.0 eingeführt.) Als zweites deaktiviert die Option
PTRACE_O_TRACEEXEC die alte SIGTRAP-Erzeugung auf execve(2).
Wenn der Verfolger die PTRACE_EVENT_EXEC-Stoppbenachrichtigung empfängt, ist garantiert, dass außer
diesem verfolgten Prozess und dem führenden Thread der Gruppe keine anderen Threads des Prozesses
lebendig sind.
Beim Empfang der PTRACE_EVENT_EXEC-Stoppbenachrichtigung sollte der Verfolger all seine internen
Datenstrukturen aufräumen, die Threads dieses Prozesses beschreiben und nur eine Datenstruktur behalten,
– eine, die den einzelnen, laufenden, verfolgten Prozess beschreibt mit
Thread-Kennung == Thread-Gruppenkennung == Prozesskennung.
Beispiel: Zwei Threads rufen zur gleichen Zeit execve(2) auf:
*** wir bekommen einen Systemaufrufeintrittsstopp in Thread 1: **
PID1 execve("/bin/foo", "foo" <nicht abgeschlossen …>
*** wir liefern PTRACE_SYSCALL für Thread 1 **
*** wir bekommen einen Systemaufrufeintrittsstopp in Thread 2: **
PID2 execve("/bin/bar", "bar" <nicht abgeschlossen …>
*** wir liefern PTRACE_SYSCALL für Thread 2 **
*** wir bekommen PTRACE_EVENT_EXEC für PID0, wir liefern PTRACE_SYSCALL **
*** wir bekommen Systemaufrufbeendigungsstopp für PID0: **
PID0 <… execve wieder aufgenommen> ) = 0
Falls die Option PTRACE_O_TRACEEXEC für den ausführenden, verfolgten Prozess nicht in Kraft ist und falls
der verfolgte Prozess PTRACE_ATTACHed statt PTRACE_SEIZEd war, sendet der Kernel ein zusätzliches SIGTRAP
an den verfolgten Prozess, nachdem execve(2) zurückgekehrt ist. Dies ist ein gewöhnliches Signal (ähnlich
einem, das durch kill -TRAP erzeugt werden kann), keine Spezialart eines Ptrace-Stopps. Unter Einsatz von
PTRACE_GETSIGINFO für dieses Signal gibt si_code auf 0 gesetzt (SI_USER) zurück. Dieses Signal kann durch
die Signalmaske blockiert sein und könnte daher (viel) später gesandt werden.
Üblicherweise würde der Verfolger dem Anwender dieses zusätzliche SIGTRAP-Signal nach Execve nicht zeigen
wollen und seinen Versand an den verfolgten Prozess unterdrücken (falls SIGTRAP auf SIGTRAP gesetzt ist,
killt es das Signal). Es ist jedoch nicht einfach zu bestimmen, welches SIGTRAP zu unterdrücken ist. Die
empfohlene Herangehensweise ist, die Option PTRACE_O_TRACEEXEC zu setzen oder PTRACE_SEIZE zu verwenden
und damit dieses zusätzliche SIGTRAP zu unterdrücken.
Echter Elternprozess
Die Ptrace-Programmierschnittstelle (miss)braucht die Standard-UNIX-Eltern-/Kindprozess-Signalgebung über
waitpid(2). Diese wird benutzt, um den echten Elternprozess zum Stopp des Empfangs mehrerer Arten von
waitpid(2)-Benachrichtigungen zu veranlassen, wenn der Kindprozess durch einen anderen Prozess verfolgt
wird.
Viele dieser Fehler wurden behoben, aber ab Linux 2.6.38 existieren etliche immer noch; siehe FEHLER
oberhalb.
Ab Linux 2.6.38 wird davon ausgegangen, dass folgendes korrekt funktioniert:
* Beenden/Sterben durch Signal wird zuerst an den Verfolger gemeldet, dann, wenn der Verfolger das
waitpid(2)-Ergebnis verbraucht, an den echten Elternprozess (an den echten Elternprozess nur, wenn der
ganze Prozess aus mehreren Threads existiert). Falls der Verfolger und der echte Elternprozess
derselbe Prozess sind, wird der Bericht nur einmal gesandt.
RÜCKGABEWERT
Bei Erfolg geben die PTRACE_PEEK*-Anfragen die angeforderten Daten zurück (aber siehe die ANMERKUNGEN),
während andere Anfragen null zurückgeben.
Bei einem Fehler geben alle Anfragen -1 zurück und errno wird entsprechend gesetzt. Da der Wert, der von
einer erfolgreichen PTRACE_PEEK*-Anfrage zurückgegeben wurde, -1 sein könnte, muss der Aufrufende vor dem
Aufruf errno leeren und es dann hinterher untersuchen, um festzustellen, ob ein Fehler aufgetreten ist
oder nicht.
FEHLER
EBUSY (nur i386) Es ist beim Reservieren oder der Freigabe eines Debug-Registers ein Fehler aufgetreten.
EFAULT Es gab einen Versuch in einem ungültigen Bereich im Speicher des Verfolgers oder des verfolgten
Prozesses zu lesen oder zu schreiben, wahrscheinlich, weil der Bereich nicht abgebildet war oder
kein Zugriff möglich war. Unglücklicherweise geben unter Linux mehrere Variationen dieser Störung
mehr oder weniger willkürlich EIO oder EFAULT zurück.
EINVAL Es wurde versucht, eine ungültige Option zu setzen.
EIO Anfrage ist ungültig, es wurde versucht, in einem ungültigen Bereich im Speicher des Verfolgers
oder des verfolgten Prozesses zu lesen oder zu schreiben, es gab eine Verletzung der Ausrichtung
an der »word«-Größe oder es wurde während des Neustarts der Abfrage ein ungültiges Signal
angegeben.
EPERM Der angegebene Prozess kann nicht verfolgt werden. Dies könnte daher rühren, dass der Verfolger
über unzureichende Privilegien verfügt (die Capability CAP_SYS_PTRACE wird benötigt);
unprivilegierte Prozesse können keine Prozesse verfolgen, denen sie keine Signale senden können
oder die SUID-/SGID-Programme ausführen, was naheliegend ist. Alternativ könnte der Prozess
bereits verfolgt werden oder (auf Kerneln vor 2.6.26) init(1) (PID 1) sein.
ESRCH Der angegebene Prozess existiert nicht, wird derzeit nicht vom Aufrufenden verfolgt oder ist nicht
gestoppt (bei Anfragen, die einen gestoppten verfolgten Prozess erfordern).
KONFORM ZU
SVr4, 4.3BSD.
ANMERKUNGEN
Obwohl Argumente für ptrace() gemäß dem angegebenen Prototypen interpretiert werden, deklariert Glibc
derzeit ptrace() als eine variable Funktion mit nur dem festen Anfrage-Argument. Es wird empfohlen, immer
vier Argumente anzugeben, sogar dann, wenn die angeforderte Aktion sie nicht verwendet. Setzen Sie
unbenutzte/ignorierte Argumente auf 0L oder (void *) 0.
In Linux-Kerneln vor 2.6.26 kann init(1) den Prozess mit der Prozessnummer 1 nicht verfolgen.
Der Elternprozess des verfolgten Prozesses wird weiterhin der Verfolger sein, selbst wenn der Verfolger
execve(2) aufruft.
Das Layout des Speicherinhalts und des BENUTZERbereichs sind ziemlich betriebsystem- und
architekturspezifisch. Der mitgelieferte Versatz und die zurückgegebenen Daten passen möglicherweise
nicht ganz zu der Definition von struct user.
Die Größe eines »word« wird durch die Betriebsystemvariante festgelegt (z.B. ist es für ein 32-Bit-Linux
32 Bit).
Diese Seite dokumentiert die Möglichkeit, wie der ptrace()-Aufruf derzeit in Linux arbeitet. Sein
Verhalten unterscheidet sich auf anderen unixoiden Betriebssystemen deutlich. Auf jeden Fall ist die
Benutzung von ptrace() in hohem Grad abhängig vom Betriebssystem und der Architektur.
Ptrace-Zugriffsmodusüberprüfung
Various parts of the kernel-user-space API (not just ptrace() operations), require so-called "ptrace
access mode" checks, whose outcome determines whether an operation is permitted (or, in a few cases,
causes a "read" operation to return sanitized data). These checks are performed in cases where one
process can inspect sensitive information about, or in some cases modify the state of, another process.
The checks are based on factors such as the credentials and capabilities of the two processes, whether or
not the "target" process is dumpable, and the results of checks performed by any enabled Linux Security
Module (LSM)—for example, SELinux, Yama, or Smack—and by the commoncap LSM (which is always invoked).
Vor Linux 2.6.27 waren alle Zugriffsprüfungen von einem einzigen Typ. Seit Linux 2.6.27 werden zwei
Zugriffsmodi unterschieden:
PTRACE_MODE_READ
Für »Lese«-Aktionen oder andere Aktionen, die weniger gefährlich sind, wie get_robust_list(2);
kcmp(2); Lesen aus /proc/[PID]/auxv, /proc/[PID]/environ oder /proc/[PID]/stat; oder readlink(2)
einer /proc/[PID]/ns/*-Datei.
PTRACE_MODE_ATTACH
Für »Schreibe«-Aktionen oder andere Aktionen, die gefährlicher sind, wie Ptrace-Anhängungen
(PTRACE_ATTACH) an einen anderen Prozess oder Aufrufe von process_vm_writev(2).
(PTRACE_MODE_ATTACH war tatsächlich die Vorgabe vor Linux 2.6.27.)
Seit Linux 4.5 sind die obigen Zugriffsmodusprüfungen mittels ODER mit einem der folgenden Modifikatoren
verknüpft:
PTRACE_MODE_FSCREDS
Die Dateisystem-UID und -GID (siehe credentials(7)) oder die effektiven Capabilitys für
LSM-Prüfungen des Aufrufenden verwenden.
PTRACE_MODE_REALCREDS
Die echte UID und GID oder die erlaubten Capabilitys für LSM-Prüfungen des Aufrufenden verwenden.
Dies war vor Linux 4.5 die Vorgabe.
Da die Kombination eines der Berechtigungsnachweise-Modifikatoren mit einem der vorgenannten Zugriffsmodi
typisch ist, sind ein paar Makros in den Kernelquellen für die Kombinationen definiert.
PTRACE_MODE_READ_FSCREDS
Definiert als PTRACE_MODE_READ | PTRACE_MODE_FSCREDS.
PTRACE_MODE_READ_REALCREDS
Definiert als PTRACE_MODE_READ | PTRACE_MODE_REALCREDS.
PTRACE_MODE_ATTACH_FSCREDS
Definiert als PTRACE_MODE_ATTACH | PTRACE_MODE_FSCREDS.
PTRACE_MODE_ATTACH_REALCREDS
Definiert als PTRACE_MODE_ATTACH | PTRACE_MODE_REALCREDS.
Ein weiterer Modifikator kann mit den Zugriffsmodus mittels ODER verknüpft werden:
PTRACE_MODE_NOAUDIT (seit Linux 3.3)
Diese Zugriffsmodusprüfung nicht auditieren. Dieser Modifikator wird für
Ptrace-Zugriffsmodusprüfungen eingesetzt (wie z.B. Prüfungen beim Lesen von /proc/[PID]/stat), die
lediglich die Ausgabe filtern oder bereinigen, statt dem Aufrufenden einen Fehler zurückzuliefern.
In diesen Fällen ist der Zugriff auf die Datei keine Sicherheitsverletzung und es gibt keinen
Grund, einen Sicherheitsauditdatensatz zu erstellen. Dieser Modifikator unterdrückt die Erstellung
eines solchen Auditdatensatzes für diese Zugriffsprüfung.
Beachten Sie, dass alle in diesem Unterabschnitt beschriebenen Konstanten PTRACE_MODE_* kernelintern und
nicht im Anwendungsraum sichtbar sind. Die Konstantennamen werden hier benannt, um den verschiedenen
Arten von Ptrace-Zugriffsmodusprüfungen, die für verschiedene Systemaufrufe und Zugriff auf verschiedene
Pseudodateien (z.B. unter /proc) durchgeführt werden, einen Namen zu geben. Diese Namen werden in anderen
Handbuchseiten benutzt, um eine einfache Abkürzung für die Benennung der verschiedenen Kernelprüfungen
bereitzustellen.
Der für Ptrace-Zugriffsmodusprüfungen eingesetzte Algorithmus bestimmt, ob dem aufrufenden Prozess
erlaubt wird, die entsprechende Aktion auf dem Zielprozess durchzuführen. (Im Falle des Öffnens von
/proc/[PID]-Dateien ist der »aufrufende Prozess« derjenige, der die Datei öffnet, und der Prozess mit der
entsprechenden PID der »Zielprozess«). Der Algorithmus geht wie folgt:
1. Falls der aufrufende Thread und der Ziel-Thread in der gleichen Thread-Gruppe sind, wird der Zugriff
immer erlaubt.
2. Falls der Zugriffsmodus PTRACE_MODE_FSCREDS festlegt, dann wird für die Prüfung im nächsten Schritt
die Dateisystem-UID und -GID des Aufrufenden verwandt. (Wie in credentials(7) vermerkt, haben die
Dateisystem-UID und -GID fast immer die gleichen Werte wie die entsprechenden effektiven IDs.)
Andernfalls legt der Zugriffsmodus PTRACE_MODE_REALCREDS fest, so dass die echte UID und GID für die
Prüfungen im nächsten Schritt verwandt werden. (Die meisten APIs, die die UIDs und GIDs des
Aufrufenden prüfen, verwenden effektive IDs. Aus historischen Gründen verwendet die Prüfung
PTRACE_MODE_REALCREDS stattdessen die echten IDs.)
3. Zugriff verweigern, falls keines der Folgenden wahr ist:
• The real, effective, and saved-set user IDs of the target match the caller's user ID, and the real,
effective, and saved-set group IDs of the target match the caller's group ID.
• Der Aufrufende verfügt über die Capability CAP_SYS_PTRACE in dem Benutzernamensraum des Ziels.
4. Deny access if the target process "dumpable" attribute has a value other than 1 (SUID_DUMP_USER; see
the discussion of PR_SET_DUMPABLE in prctl(2)), and the caller does not have the CAP_SYS_PTRACE
capability in the user namespace of the target process.
5. Die Schnittstelle security_ptrace_access_check() wird aufgerufen, um zu erkennen, ob Ptrace-Zugriff
erlaubt ist. Das Ergebnis hängt von dem/den LSM(en) ab. Die Implementierung dieser Schnittstelle im
LSM Commoncap führt die folgenden Schritte durch:
a) Falls der Zugriffsmodus PTRACE_MODE_FSCREDS enthält, dann wird die effektive Capability-Menge des
Aufrufenden in der nachfolgenden Prüfung verwandt, andernfalls (der Zugriffsmodus legt
PTRACE_MODE_REALCREDS fest) wird die erlaubte Capability-Menge des Aufrufenden verwandt.
b) Zugriff verweigern, falls keines der Folgenden wahr ist:
• Der aufrufende und der Zielprozess sind im gleichen Benutzernamensraum und die Capabilitys des
Aufrufenden sind eine korrekte Obermenge der erlaubten Capabilitys des Zielprozesses.
• Der Aufrufende verfügt über die Capability CAP_SYS_PTRACE in dem Benutzernamensraum des
Zielprozesses.
Beachten Sie, dass das LSM Commoncap nicht zwischen PTRACE_MODE_READ und PTRACE_MODE_ATTACH
unterscheidet.
6. Falls der Zugriff in den vorhergehenden Schritten nicht verweigert wurde, dann wird er erlaubt.
/proc/sys/kernel/yama/ptrace_scope
Auf Systemen, auf denen das Yama Linux Security Module (LSM) installiert (d.h. der Kernel mit
CONFIG_SECURITY_YAMA konfiguriert worden) ist, kann die Datei /proc/sys/kernel/yama/ptrace_scope
(verfügbar seit Linux 3.4) zum Einschränken der Nachverfolgung von Prozessen mit ptrace() verwandt werden
(und damit auch die Möglichkeit, Werkzeuge wie strace(1) und gdb(1) zu verwenden). Das Ziel einer solchen
Einschränkung besteht darin, Angriffseskalationen zu vermeiden, bei denen ein kompromittierter Prozess
sich mittels Ptrace-attach an andere sensitive Prozesse (z.B. einem GPG-Agenten oder einer SSH-Sitzung),
die dem Benutzer gehören, anhängen könnte, um zusätzliche Berechtigungsnachweise zu erlangen, die im
Speicher existieren, und damit den Umfang des Angriffs zu erhöhen.
Genauer gesagt begrenzt die Yama LSM zwei Arten von Aktionen:
* Jede Aktion, die eine Ptrace-Zugriffsbeschränkungsüberprüfung PTRACE_MODE_ATTACH durchführt –
beispielsweise ptrace() PTRACE_ATTACH. (Siehe die obige Diskussion »Ptrace-Zugriffsmodusüberprüfung«).
* ptrace() PTRACE_TRACEME.
Ein Prozess, der über die Capability CAP_SYS_PTRACE verfügt, kann die Datei
/proc/sys/kernel/yama/ptrace_scope mit einem der folgenden Werte aktualisieren:
0 (»klassische Ptrace-Berechtigungen«)
Keine zusätzlichen Beschränkungen bei Aktionen, die PTRACE_MODE_ATTACH-Überprüfungen durchführen
(die über die von Commoncap und anderen LSMs hinausgehen).
PTRACE_TRACEME wird unverändert verwandt.
1 (»eingeschränkter Ptrace«) [Vorgabewert]
Wenn eine Aktion durchgeführt wird, die eine PTRACE_MODE_ATTACH-Überprüfung benötigt, muss der
aufrufende Prozess entweder über die Capability CAP_SYS_PTRACE in dem Benutzernamensraum des
Zielprozesses verfügen oder er muss eine vorbestimmte Beziehung zum Zielprozess haben.
Standardmäßig ist die vorbestimmte Beziehung, dass der Zielprozess ein Nachkomme des Aufrufenden
sein muss.
Ein Zielprozess kann die prctl(2)-Aktion PR_SET_PTRACER einsetzen, um eine zusätzliche PID zu
erklären, der es erlaubt ist, PTRACE_MODE_ATTACH-Aktionen auf dem Ziel durchzuführen. Siehe die
Kernelquelldatei Documentation/admin-guide/LSM/Yama.rst (oder Documentation/security/Yama.txt vor
Linux 4.13) für weitere Details.
PTRACE_TRACEME wird unverändert verwandt.
2 (»nur Admin-Anhängung«)
Nur Prozesse mit der Capability CAP_SYS_PTRACE im Benutzernamensraum des Zielprozesses dürfen
PTRACE_MODE_ATTACH-Aktionen durchführen oder Kinder, die PTRACE_TRACEME einsetzen, verfolgen.
3 (»keine Anhängung«)
Kein Prozess darf PTRACE_MODE_ATTACH-Aktionen durchführen oder Kindprozesse verfolgen, die
PTRACE_TRACEME einsetzen.
Sobald dieser Wert in die Datei geschrieben wurde, kann er nicht mehr geändert werden.
Beachten Sie im Hinblick auf die Werte 1 und 2, dass die Erstellung eines neuen Benutzernamensraums
effektiv den durch Yama bereitgestellten Schutz entfernt. Dies rührt daher, dass der Prozess in dem
Elternbenutzerraum, dessen effektive UID auf die UID des Erstellers des Kindnamensraums passt, über alle
Capabilitys (einschließlich CAP_SYS_PTRACE) verfügt, wenn er Aktionen innerhalb des Kindnamensraums (und
weiter entfernter Nachkommen dieses Namensraums) durchführt. Wenn ein Prozess versucht, einen
Benutzernamensraum zu verwenden, um sich in eine Sandbox zu bringen, wird er konsequenterweise den durch
das Yama LSM bereitgestellten Schutz schwächen.
Unterschiede C-Bibliothek/Kernel
Auf der Systemaufrufebene haben die Anfragen PTRACE_PEEKTEXT, PTRACE_PEEKDATA und PTRACE_PEEKUSER eine
unterschiedliche Programmierschnittstelle: Sie speichern das Ergebnis an der durch den Parameter Daten
angegebenen Adresse und der Rückgabewert ist ein Fehlercode. Die Glibc-Wrapper-Funktion stellt die oben
in BESCHREIBUNG angegebene Programmierschnittstelle bereit. Ihr Ergebnis wird über den Rückgabewert der
Funktion zurückgegeben.
FEHLER
Auf Rechnern mit 2.6 Kernel-Headern ist PTRACE_SETOPTIONS mit einem anderen Wert deklariert, als auf
einem für 2.4. Dies führt dazu, dass Anwendungen, die mit 2.6-Kernel-Headern kompiliert wurden, bei der
Ausführung auf 2.4er Kerneln scheitern. Dies kann durch Neudefinieren von PTRACE_SETOPTIONS zu
PTRACE_OLDSETOPTIONS umgangen werden, wenn dies definiert ist.
Gruppenstoppbenachrichtigungen werden an der Verfolger gesandt, aber nicht an den echten Elternprozess.
Zuletzt auf 2.6.38.6 bestätigt.
Falls ein führender Thread einer Gruppe verfolgt und durch den Aufruf von _exit(2) beendet wird, wird es
für ihn zu einem PTRACE_EVENT_EXIT-Stopp kommen (falls angefordert), aber die nachfolgende
WIFEXITED-Benachrichtigung wird nicht gesandt, bis alle anderen Threads beendet sind. Wie oben erklärt,
wird der Tod des führenden Prozesses der Gruppe gemeldet, falls einer der anderen Threads execve(2)
aufruft. Falls der ausgeführte Thread nicht durch den Verfolger verfolgt wird, wird der Verfolger niemals
erfahren, dass execve(2) auftrat. Eine mögliche Notlösung ist ein PTRACE_DETACH für den führenden Thread
der Gruppe, anstatt ihn in diesem Fall neu zu starten. Zuletzt auf 2.6.38.6 bestätigt.
Ein SIGKILL-Signal kann immer noch einen PTRACE_EVENT_EXIT-Stopp vor dem tatsächlichen Signaltod
verursachen. Dies könnte in Zukunft geändert werden; SIGKILL ist dazu gedacht, Aufgaben immer sofort zu
killen, sogar unter Ptrace. Zuletzt auf Linux 3.13 bestätigt.
Einige Systemaufrufe kehren mit EINTR zurück, falls ein Signal an den verfolgten Prozess gesandt, die
Auslieferung aber durch den Verfolger unterdrückt wurde. (Dies ist eine ganz typische Aktion: Sie wird
normalerweise von Fehlersuchprogrammen bei jedem Anhängen durchgeführt, um kein fingiertes SIGSTOP
einzuleiten.) Ab Linux 3.2.9 werden die folgenden Systemaufrufe beeinflusst (diese Liste ist
wahrscheinlich nicht vollständig): epoll_wait(2) und read(2) von einem inotify(7)-Dateideskriptor. Das
übliche Anzeichen für diesen Fehler ist, falls Sie einen ruhenden Prozess mit dem Befehl
strace -p <Prozesskennung>
anhängen, dass Sie statt der erwarteten einzeiligen Ausgabe, wie
restart_syscall(<… resuming interrupted call …>_
oder
select(6, [5], NULL, [5], NULL_
('_' kennzeichnet die Cursor-Position) mehr als eine Zeile beobachten können, zum Beispiel:
clock_gettime(CLOCK_MONOTONIC, {15370, 690928118}) = 0
epoll_wait(4,_
Was hier nicht sichtbar ist, ist, dass der Prozess in epoll_wait(2) blockiert wurde, bevor strace(1) an
ihn angehängt hat. Das Anhängen verursachte ein epoll_wait(2), um zum Anwendungsraum mit dem Fehler EINTR
zurückzukehren. In diesem besonderen Fall reagiert das Programm auf EINTR, indem die aktuelle Zeit
geprüft und dann epoll_wait(2) erneut ausgeführt wird. (Programme, die keine derartigen »verirrten«
EINTR-Fehler erwarten, können sich bei einem strace(1)-Anhängen in unbeabsichtigter Weise verhalten.)
SIEHE AUCH
gdb(1), ltrace(1), strace(1), clone(2), execve(2), fork(2), gettid(2), prctl(2), seccomp(2),
sigaction(2), tgkill(2), vfork(2), waitpid(2), exec(3), capabilities(7), signal(7)
KOLOPHON
Diese Seite ist Teil der Veröffentlichung 4.15 des Projekts Linux-man-pages. Eine Beschreibung des
Projekts, Informationen, wie Fehler gemeldet werden können sowie die aktuelle Version dieser Seite finden
sich unter https://www.kernel.org/doc/man-pages/.
ÜBERSETZUNG
Die deutsche Übersetzung dieser Handbuchseite wurde von Patrick Rother <krd@gulu.net>, Chris Leick
<c.leick@vollbio.de>, Helge Kreutzmann <debian@helgefjell.de> und Mario Blättermann
<mario.blaettermann@gmail.com> erstellt.
Diese Übersetzung ist Freie Dokumentation; lesen Sie die GNU General Public License Version 3 oder neuer
bezüglich der Copyright-Bedingungen. Es wird KEINE HAFTUNG übernommen.
Wenn Sie Fehler in der Übersetzung dieser Handbuchseite finden, schicken Sie bitte eine E-Mail an
<debian-l10n-german@lists.debian.org>.
Linux 15. September 2017 PTRACE(2)