ptrace(2) System Calls Manual ptrace(2) NOM ptrace - Suivre un processus BIBLIOTHEQUE Bibliotheque C standard (libc, -lc) SYNOPSIS #include long ptrace(enum __ptrace_request request, pid_t pid, void *addr, void *data); DESCRIPTION L'appel systeme ptrace() fournit a un processus (l'<< observateur >>) un moyen d'observer et de controler l'execution d'un autre processus (l'<< observe >>), et d'examiner et editer la memoire et les registres de l'observe. L'utilisation principale de cette fonction est l'implementation de points d'arret pour le debogage, et pour suivre les appels systeme. Un observe doit d'abord etre attache a l'observateur. L'attachement et les commandes suivantes sont par thread : dans un processus multithreade, chaque thread peut etre attache individuellement a un observateur (eventuellement different), ou etre laisse detache et donc non debogue. Par consequent, l'<< observe >> signifie toujours << (un) thread >>, jamais << un processus (eventuellement multithreade) >>. Les commandes ptrace sont toujours envoyees a un observe specifique en utilisant un appel de la forme ptrace(PTRACE_truc, pid, ...) ou pid est l'identifiant de thread du thread Linux correspondant. (Remarquez que dans cette page, un << processus multithreade >> signifie un groupe de threads constitue de threads crees en utilisant l'attribut CLONE_THREAD de clone(2).) Un processus peut demarrer un suivi en appelant fork(2) et faire en sorte que l'enfant cree fasse un PTRACE_TRACEME, suivi (en general) par un execve(2). Autrement, un processus peut commencer a suivre un autre processus en utilisant PTRACE_ATTACH ou PTRACE_SEIZE. L'observe s'arretera a chaque fois qu'un signal lui sera distribue, meme si le signal est ignore (a l'exception de SIGKILL qui a les effets habituels). L'observateur sera prevenu a son prochain appel de waitpid(2) (ou un des appels systeme lies a << wait >>) ; cet appel renverra une valeur status contenant les renseignements indiquant la raison de l'arret de l'observe. Lorsque l'observe est arrete, l'observateur peut utiliser plusieurs requetes ptrace pour inspecter et modifier l'observe. L'observateur peut egalement laisser continuer l'execution de l'observe, en ignorant eventuellement le signal ayant declenche l'arret, ou meme en envoyant un autre signal. Si l'option PTRACE_O_TRACEEXEC n'est pas effective, tous les appels reussis d'execve(2) par le processus suivi declencheront l'envoi d'un signal SIGTRAP, ce qui permet au parent de reprendre le controle avant que le nouveau programme commence son execution. Quand l'observateur a fini le suivi, il peut forcer l'observe a continuer normalement, en mode non suivi, avec PTRACE_DETACH. La valeur de l'argument request indique precisement l'action a entreprendre. PTRACE_TRACEME Le processus en cours va etre suivi par son parent. Un processus ne devrait sans doute pas envoyer cette requete si son parent n'est pas pret a le suivre. Dans cette requete pid, addr, et data sont ignores. La requete PTRACE_TRACEME ne sert qu'a l'observe. Les requetes restantes ne servent qu'a l'observateur. Par la suite, pid precise l'identifiant de thread de l'observe sur lequel agir. Pour d'autres requetes que PTRACE_ATTACH, PTRACE_SEIZE, PTRACE_INTERRUPT et PTRACE_KILL, l'observe doit etre arrete. PTRACE_PEEKTEXT PTRACE_PEEKDATA Lire un mot a l'adresse addr dans l'espace memoire de l'observe et le renvoyer en resultat de l'appel ptrace(). Linux ne separe pas les espaces d'adressage de code et de donnees, donc ces deux requetes sont equivalentes (data est ignore, consultez la section NOTES). PTRACE_PEEKUSER Lire un mot a la position addr dans l'espace USER de l'observe, qui contient les registres et divers renseignements sur le processus (voir ). Le mot est renvoyee en resultat de ptrace(). En principe, l'adresse doit etre alignee sur une frontiere de mots, bien que cela varie selon les architectures. Consultez la section NOTES. (data est ignore, consultez la section NOTES). PTRACE_POKETEXT PTRACE_POKEDATA Copier le mot data vers l'adresse addr de la memoire de l'observe. Comme pour PTRACE_PEEKTEXT et PTRACE_PEEKDATA, ces deux requetes sont equivalentes. PTRACE_POKEUSER Copier le mot data vers l'adresse addr dans l'espace USER de l'observe. Comme pour PTRACE_PEEKUSER, les emplacements doivent etre alignes sur une frontiere de mot. Pour maintenir l'integrite du noyau, certaines modifications de la zone USER sont interdites. PTRACE_GETREGS PTRACE_GETFPREGS Copier les registres generaux ou du processeur en virgule flottante de l'observe, vers l'adresse data de l'observateur. Consultez pour les details sur le format de ces donnees (addr est ignore). Remarquez que les systemes SPARC ont la signification de data et addr inversee, c'est-a-dire que data est ignore et les registres sont copies vers l'adresse addr. PTRACE_GETREGS et PTRACE_GETFPREGS ne sont pas presents sur toutes les architectures. PTRACE_GETREGSET (depuis Linux 2.6.34) Lire les registres de l'observe. addr indique, de maniere dependante de l'architecture, le type de registres a lire. NT_PRSTATUS (avec une valeur numerique de 1) a pour consequence habituelle la lecture de registres generaux. Si le processeur a, par exemple, des registres en virgule flottante ou en vecteur, ils peuvent etre recupere en configurant addr a la constante NT_foo correspondante. data pointe vers une struct iovec, qui decrit l'emplacement et la taille du tampon de destination. Le noyau modifie iov.len au retour pour indiquer le veritable nombre d'octets renvoyes. PTRACE_SETRGS PTRACE_SETFPREGS Modifier les registres generaux ou du processeur en virgule flottante de l'observe, depuis l'adresse data de l'observateur. Comme pour PTRACE_POKEUSER, certaines modifications de registres generaux pourraient etre interdites (addr est ignore). Remarquez que les systemes SPARC ont la signification de data et addr inversee, c'est-a-dire que data est ignore et les registres sont copies depuis l'adresse addr. PTRACE_SETREGS et PTRACE_SETFPREGS ne sont pas presents sur toutes les architectures. PTRACE_SETREGSET (depuis Linux 2.6.34) Modifier les registres de l'observe. La signification de addr et data est analogue a PTRACE_GETREGSET. PTRACE_GETSIGINFO (depuis Linux 2.3.99-pre6) Recuperer des renseignements sur le signal qui a provoque l'arret. Pour ce faire, copier une structure siginfo_t (consultez sigaction(2)) de l'observe a l'adresse data de l'observateur (addr est ignore). PTRACE_SETSIGINFO (depuis Linux 2.3.99-pre6) Definir les renseignements de signaux : copier une structure siginfo_t de l'adresse data de l'observateur vers l'observe. Cela n'affecte que les signaux qui auraient du etre distribues a l'observe et ont ete interceptes a cause de ptrace(). Differencier ces signaux normaux des signaux crees par ptrace() lui-meme peut etre delicat (addr est ignore). PTRACE_PEEKSIGINFO (depuis Linux 3.10) Recuperer les structures siginfo_t sans supprimer les signaux d'une file d'attente. addr pointe vers une structure ptrace_peeksiginfo_args qui indique la position ordinale a partir de laquelle la copie des signaux devrait commencer et le nombre de signaux a copier. Les structures siginfo_t sont copiees dans le tampon pointe par data. La valeur de retour contient le nombre de signaux copies (zero indique qu'il n'y a pas de signal correspondant a la position ordinale indiquee). Dans les structures siginfo renvoyees, le champ si_code contient des renseignements (__SI_CHLD, __SI_FAULT, etc.) qui sinon ne sont pas exposes a l'espace utilisateur. struct ptrace_peeksiginfo_args { u64 off; /* Position ordinale dans la file d'attente ou commencer la copie de signaux */ u32 flags; /* PTRACE_PEEKSIGINFO_SHARED ou 0 */ s32 nr; /* Nombre de signaux a copier */ }; Actuellement, seul l'attribut PTRACE_PEEKSIGINFO_SHARED permet de vider les signaux de la file de signaux par processus. Si cet attribut n'est pas defini, les signaux sont lus depuis la file par thread du thread indique. PTRACE_GETSIGMASK (depuis Linux 3.11) Placer une copie du masque des signaux bloques (consultez sigprocmask(2)) dans le tampon pointe par data qui devrait etre un pointeur vers un tampon de type sigset_t. L'argument addr contient la taille du tampon pointe par data (c'est-a-dire sizeof(sigset_t)). PTRACE_SETSIGMASK (depuis Linux 3.11) Modifier le masque des signaux bloques (consultez sigprocmask(2)) a la valeur indiquee dans le tampon pointe par data qui devrait etre un pointeur vers un tampon de type sigset_t. L'argument addr contient la taille du tampon pointe par data (c'est-a-dire sizeof(sigset_t)). PTRACE_SETOPTIONS (depuis Linux 2.4.6, consultez les remarques de BOGUES) Definir les options de ptrace a partir de l'adresse data (addr est ignore). data est interprete comme un masque d'options, qui est construit a partir des attributs suivants. PTRACE_O_EXITKILL (depuis Linux 3.8) Envoyer un signal SIGKILL a l'observe si l'observateur existe. Cet option est utile pour les gardiens ptrace qui veulent s'assurer que les observes ne peuvent jamais echapper au controle de l'observateur. PTRACE_O_TRACECLONE (depuis Linux 2.5.46) Arreter l'observe au prochain clone(2) et commencer automatiquement a suivre le nouveau processus clone, qui demarrera avec un signal SIGSTOP, ou PTRACE_EVENT_STOP si PTRACE_SEIZE est utilise. Un waitpid(2) par l'observateur renverra une valeur status comme status>>8 == (SIGTRAP | (PTRACE_EVENT_CLONE<<8)) Le PID du nouveau processus peut etre recupere avec PTRACE_GETEVENTMSG. Cette option peut ne pas intercepter tous les appels clone(2). Si l'observe appelle clone(2) avec l'attribut CLONE_VFORK, PTRACE_EVENT_VFORK sera envoye si PTRACE_O_TRACEVFORK est utilise. Sinon, si l'observe appelle clone(2) avec SIGCHLD comme signal de terminaison, PTRACE_EVENT_FORK sera envoye si PTRACE_O_TRACEFORK est utilise. PTRACE_O_TRACEEXEC (depuis Linux 2.5.46) Arreter l'observe au prochain execve(2). Un waitpid(2) par l'observateur renverra une valeur status comme status>>8 == (SIGTRAP | (PTRACE_EVENT_EXEC<<8)) Si le thread en cours d'execution n'est pas un leader de groupe de threads, l'identifiant de thread est reinitialise a l'identifiant du leader de groupe de threads avant cet arret. Depuis Linux 3.0, le premier identifiant de thread peut etre recupere avec PTRACE_GETEVENTMSG. PTRACE_O_TRACEEXIT (depuis Linux 2.5.60) Arreter l'observe a la terminaison. Un waitpid(2) par l'observateur renverra une valeur status comme status>>8 == (SIGTRAP | (PTRACE_EVENT_EXIT<<8)) L'etat de fin de l'observe peut etre recupere avec PTRACE_GETEVENTMSG. L'observe est arrete tot dans la terminaison du processus, alors que les registres sont toujours disponibles, ce qui permet au processus utilisant ptrace() de voir ou la terminaison s'est produite, alors que la notification de terminaison normale a lieu a la fin de cette terminaison. Meme si le contexte est disponible, l'observateur ne peut pas empecher la terminaison a ce moment la. PTRACE_O_TRACEFORK (depuis Linux 2.5.46) Arreter l'observe au prochain fork(2) et commencer automatiquement a suivre le nouveau processus cree, qui demarrera avec un signal SIGSTOP, ou PTRACE_EVENT_STOP si PTRACE_SEIZE est utilise. Un waitpid(2) par l'observateur renverra une valeur status comme status>>8 == (SIGTRAP | (PTRACE_EVENT_FORK<<8)) Le PID du nouveau processus peut etre recupere avec PTRACE_GETEVENTMSG. PTRACE_O_TRACESYSGOOD (depuis Linux 2.4.6) Lors des interceptions d'appel systeme, positionner le bit 7 sur le numero de signal (envoyer SIGTRAP|0x80). Cela facilite pour l'observateur la distinction entre les interceptions normales et celles provoquees par un appel systeme. PTRACE_O_TRACEVFORK (depuis Linux 2.5.46) Arreter l'observe au prochain vfork(2) et commencer automatiquement a suivre le nouveau processus cree, qui demarrera avec un signal SIGSTOP, ou PTRACE_EVENT_STOP si PTRACE_SEIZE est utilise. Un waitpid(2) par l'observateur renverra une valeur status comme status>>8 == (SIGTRAP | (PTRACE_EVENT_VFORK<<8)) Le PID du nouveau processus peut etre recupere avec PTRACE_GETEVENTMSG. PTRACE_O_TRACEVFORKDONE (depuis Linux 2.5.60) Arreter l'observe a la fin du prochain vfork(2). Un waitpid(2) par l'observateur renverra une valeur status comme status>>8 == (SIGTRAP | (PTRACE_EVENT_VFORK_DONE<<8)) Le PID du nouveau processus peut (depuis Linux 2.6.18) etre recupere avec PTRACE_GETEVENTMSG. PTRACE_O_TRACESECCOMP (depuis Linux 3.5) Arreter l'observe quand une regle SECCOMP_RET_TRACE de seccomp(2) est declenchee. Un waitpid(2) par l'observateur renverra une valeur status comme status>>8 == (SIGTRAP | (PTRACE_EVENT_SECCOMP<<8)) Si cela entraine un arret PTRACE_EVENT, c'est equivalent a un arret-entree-appel-systeme. Pour des details, voir la remarque sur PTRACE_EVENT_SECCOMP ci-dessous. Les donnees du message de l'evenement seccomp (issues de la partie SECCOMP_RET_DATA de la regle du filtre seccomp) peuvent etre recuperees avec PTRACE_GETEVENTMSG. PTRACE_O_SUSPEND_SECCOMP (depuis Linux 4.3) Suspendre les protections seccomp de l'observe. Cela s'applique quel que soit le mode et peut etre utilise lorsque l'observe n'a pas encore installe de filtres seccomp. Cela veut dire qu'un cas d'utilisation valable consiste a suspendre les protections seccomp d'un observe avant qu'elles ne soient installees par l'observe, laisser l'observe installer les filtres et vider cet attribut quand les filtres doivent etre reactives. La definition de cette option implique que l'observateur ait la capacite CAP_SYS_ADMIN, n'ait pas de protection seccomp installee et n'ait pas de PTRACE_O_SUSPEND_SECCOMP positionne sur lui-meme. PTRACE_GETEVENTMSG (depuis Linux 2.5.46) Recuperer un message (dans un unsigned long) concernant l'evenement ptrace qui vient d'arriver, en le placant a l'adresse data de l'observateur. Pour PTRACE_EVENT_EXIT, il s'agit du code de retour de l'observe. Pour PTRACE_EVENT_FORK, PTRACE_EVENT_VFORK, PTRACE_EVENT_VFORK_DONE et PTRACE_EVENT_CLONE, il s'agit du PID du nouveau processus. Pour PTRACE_EVENT_SECCOMP, il s'agit des SECCOMP_RET_DATA du filtre seccomp(2) associees a la regle declenchee (addr est ignoree). PTRACE_CONT Redemarrer l'observe arrete. Si data est non nul, il est interprete comme un numero de signal a distribuer a l'observe ; sinon aucun signal n'est distribue. L'observateur peut ainsi controler si un signal envoye a l'observe doit lui etre distribue ou non (addr est ignore). PTRACE_SYSCALL PTRACE_SINGLESTEP Redemarrer l'observe arrete comme pour PTRACE_CONT, mais en s'arrangeant pour qu'il soit arrete a la prochaine entree ou sortie d'un appel systeme, ou apres la prochaine instruction, respectivement (l'observe sera aussi arrete par l'arrivee d'un signal). Du point de vue de l'observateur, l'observe semblera etre arrete par SIGTRAP. Ainsi, pour PTRACE_SYSCALL l'idee est d'inspecter les arguments de l'appel systeme au premier arret puis de faire un autre PTRACE_SYSCALL et d'inspecter la valeur de retour au second arret. Le parametre data est interprete comme pour PTRACE_CONT (addr est ignore). PTRACE_SET_SYSCALL (depuis Linux 2.6.16) Lorsqu'il est en arret-entree-appel-systeme, passer le numero de l'appel systeme qui va etre execute a celui indique dans le parametre data. Le parametre addr est ignore. Cette requete n'est actuellement prise en charge que sur arm (et arm64, quoique uniquement a des fins de retrocompatibilite), mais la plupart des autres architectures ont d'autres moyens de faire cela (en general en modifiant le registre qui a passe l'appel systeme au code au niveau de l'utilisateur). PTRACE_SYSEMU PTRACE_SYSEMU_SINGLESTEP (depuis Linux 2.6.14) Pour PTRACE_SYSEMU, continuer puis s'arreter lors du prochain appel systeme, qui ne sera pas execute. Voir la documentation des syscall-stops ci-dessous. Pour PTRACE_SYSEMU_SINGLESTEP, faire la meme chose, mais executer pas a pas s'il ne s'agit pas d'un appel systeme. Cette fonction est utilisee par des programmes comme User Mode Linux, qui veulent emuler tous les appels systeme de l'observe. Le parametre data est interprete comme pour PTRACE_CONT. L'argument addr est ignore. Ces requetes ne sont pour l'instant disponibles que sur x86. PTRACE_LISTEN (depuis Linux 3.4) Redemarrer l'observe arrete, mais en l'empechant de s'executer. L'etat resultant de l'observe est similaire a celui d'un processus qui a ete arrete par un SIGSTOP (ou autre signal d'arret). Consultez la sous-section Arret-groupe pour des renseignements supplementaires. PTRACE_LISTEN ne fonctionne que sur les observes attaches par PTRACE_SEIZE. PTRACE_KILL Envoyer a l'observe un signal SIGKILL pour le terminer (addr et data sont ignores). Cette operation est obsolete, ne l'utilisez pas. A la place, envoyez un SIGKILL directement en utilisant kill(2) ou tgkill(2). Le probleme avec PTRACE_KILL est qu'il necessite que l'observe soit en arret-distribution-signal, sinon cela risque de ne pas fonctionner (c'est-a-dire risque de se terminer avec succes sans tuer l'observe). En revanche, envoyer SIGKILL directement n'est pas concerne par cette limite. PTRACE_INTERRUPT (depuis Linux 3.4) Arreter un observe. Si l'observe est en cours d'execution ou en sommeil dans l'espace utilisateur et que PTRACE_SYSCALL est effectif, l'appel systeme est interrompu et l'arret-sortie-appel-systeme est signale (l'appel systeme interrompu est redemarre quand l'observe est redemarre). Si l'observe avait deja ete arrete par un signal et que PTRACE_LISTEN lui avait ete envoye, l'observe s'arrete avec PTRACE_EVENT_STOP et WSTOPSIG(status) renvoie le signal d'arret. Si n'importe quel autre arret-ptrace est cree en meme temps (par exemple, si un signal est envoye a l'observe), cet arret-ptrace arrive. Si rien de ce qui precede ne s'applique (par exemple si l'observe est en cours d'execution en espace utilisateur), il s'arrete avec PTRACE_EVENT_STOP avec WSTOPSIG(status) == SIGTRAP. PTRACE_INTERRUPT ne fonctionne que sur les observes attaches par PTRACE_SEIZE. PTRACE_ATTACH Attacher le processus numero pid, pour le suivre. L'observe va recevoir un SIGSTOP, mais il ne sera peut-etre pas arrete tout de suite, utilisez waitid(2) pour attendre son arret. Consultez la sous-section Attachement et detachement pour obtenir de plus amples renseignements (addr et data sont ignores). Le droit d'effectuer un PTRACE_ATTACH est gere par la verification PTRACE_MODE_ATTACH_REALCREDS du mode d'acces de ptrace ; voir ci-dessous. PTRACE_SEIZE (depuis Linux 3.4) Attacher au processus indique dans pid, en faire un observe du processus appelant. Contrairement a PTRACE_ATTACH, PTRACE_SEIZE n'arrete pas le processus. Les arrets-groupe sont signales en tant que PTRACE_EVENT_STOP et WSTOPSIG(status) renvoie le signal d'arret. Les enfants automatiquement attaches avec PTRACE_EVENT_STOP et WSTOPSIG(status) renvoient SIGTRAP au lieu de recevoir un signal SIGSTOP. execve(2) n'envoie pas d'autres SIGTRAP. Seul un processus PTRACE_SEIZEe peut accepter des commandes PTRACE_INTERRUPT et PTRACE_LISTEN. Le comportement << seized >> qui vient d'etre decrit est recupere par les enfants automatiquement attaches en utilisant PTRACE_O_TRACEFORK, PTRACE_O_TRACEVFORK et PTRACE_O_TRACECLONE. addr doit etre de zero. data contient un masque de bit des options ptrace a activer immediatement. Le droit d'effectuer un PTRACE_SEIZE est gere par une verification PTRACE_MODE_ATTACH_REALCREDS du mode d'acces ptrace ; voir ci-dessous. PTRACE_SECCOMP_GET_FILTER (depuis Linux 4.4) Cette operation autorise l'observateur a vider les filtres BPF classiques de l'observe. addr est un entier indiquant l'index du filtre a vider. Le filtre le plus recemment installe a le numero d'index 0. Si addr est superieur aux numeros des filtres installes, l'operation echoue avec l'erreur ENOENT. data est soit un pointeur vers un tableau struct sock_filter assez grand pour stocker le programme BPF, soit NULL si le programme ne va pas etre stocke. En cas de succes, le code de retour est le nombre d'instructions du programme BPF. Si data etait NULL, ce code de retour peut etre utilise pour dimensionner correctement le tableau struct sock_filter passe dans un appel ulterieur. Cette operation echoue avec l'erreur EACCES si l'appelant n'a pas la capacite CAP_SYS_ADMIN ou s'il est en mode seccomp filtre ou restreint. Si le filtre auquel renvoie addr n'est pas un filtre BPF classique, l'operation echoue avec l'erreur EMEDIUMTYPE. Cette operation n'est disponible que si le noyau a ete configure avec les options CONFIG_SECCOMP_FILTER et CONFIG_CHECKPOINT_RESTORE. PTRACE_DETACH Relancer l'observe arrete comme avec PTRACE_CONT, mais en commencant par s'en detacher. Sous Linux un observe peut etre detache ainsi quelque soit la methode employee pour demarrer le suivi (addr est ignore). PTRACE_GET_THREAD_AREA (depuis Linux 2.6.0) Cette operation effectue une tache identique a get_thread_area(2). Elle lit l'entree TLS dans le GDT dont l'index est donne dans addr, mettant une copie de l'entree dans la struct user_desc vers laquelle pointe data (contrairement a get_thread_area(2), entry_number de la struct user_desc est ignoree). PTRACE_SET_THREAD_AREA (depuis Linux 2.6.0) Cette operation effectue la meme tache que set_thread_area(2). Elle positionne l'entree TLS dans le GDT dont l'index est donne dans addr, en lui affectant les donnees fournies dans la struct user_desc vers laquelle pointe data (contrairement a set_thread_area(2), entry_number de la struct user_desc est ignoree ; autrement dit, cette operation de ptrace ne peut pas etre utilisee pour affecter une entree TLS libre). PTRACE_GET_SYSCALL_INFO (depuis Linux 5.3) Recuperer des informations sur l'appel systeme qui a provoque l'arret. Les informations sont placees dans le tampon vers lequel pointe le parametre data, lequel doit etre un pointeur vers un tampon de type struct ptrace_syscall_info. Le parametre addr contient la taille du tampon vers lequel pointe le parametre data (a savoir sizeof(struct ptrace_syscall_info)). Le code de retour contient le nombre d'octets que le noyau peut ecrire. Si la taille des donnees que le noyau doit ecrire depasse celle indiquee par le parametre addr, les donnees de sortie sont tronquees. La structure ptrace_syscall_info contient les champs suivants : struct ptrace_syscall_info { __u8 op; /* Type d'appel systeme d'arret */ __u32 arch; /* Valeur AUDIT_ARCH_* ; voir seccomp(2) */ __u64 instruction_pointer; /* Pointeur vers l'instruction du processeur */ __u64 stack_pointer; /* Pointeur vers la pile du processeur */ union { struct { /* op == PTRACE_SYSCALL_INFO_ENTRY */ __u64 nr; /* Numero de l'appel systeme */ __u64 args[6]; /* Parametres de l'appel systeme */ } entry; struct { /* op == PTRACE_SYSCALL_INFO_EXIT */ __s64 rval; /* Code de retour de l'appel systeme */ __u8 is_error; /* Attribut d'erreur de l'appel systeme ; Booleen : rval contient-il un code d'erreur (-ERRCODE) ou un code de retour de non erreur ? */ } exit; struct { /* op == PTRACE_SYSCALL_INFO_SECCOMP */ __u64 nr; /* Numero de l'appel systeme */ __u64 args[6]; /* Parametres de l'appel systeme */ __u32 ret_data; /* Partie SECCOMP_RET_DATA de la valeur de retour de SECCOMP_RET_TRACE */ } seccomp; }; }; Les champs op, arch, instruction_pointer et stack_pointer sont definis pour tous les types d'arrets de l'appel systeme ptrace. Le reste de la structure est une union ; on ne doit lire que les champs significatifs pour le type d'arret de l'appel systeme indique par le champ op. Le champ op prend une des valeurs suivantes (definies dans ), indiquant le type d'arret qui s'est produit et la partie remplie de l'union : PTRACE_SYSCALL_INFO_ENTRY Le composant entry de l'union contient des informations liees a un arret d'entree appel systeme. PTRACE_SYSCALL_INFO_EXIT Le composant exit de l'union contient des informations sur un arret de sortie d'appel systeme. PTRACE_SYSCALL_INFO_SECCOMP Le composant seccomp de l'union contient des informations concernant un arret PTRACE_EVENT_SECCOMP. PTRACE_SYSCALL_INFO_NONE Aucun composant de l'union ne contient d'informations pertinentes. In case of system call entry or exit stops, the data returned by PTRACE_GET_SYSCALL_INFO is limited to type PTRACE_SYSCALL_INFO_NONE unless PTRACE_O_TRACESYSGOOD option is set before the corresponding system call stop has occurred. Mort sous ptrace Quand un processus (eventuellement multithreade) recoit un signal pour le tuer (un dont la disposition est configuree a SIG_DFL et dont l'action par defaut est de tuer le processus), tous les threads se terminent. Chaque observe signale sa mort a son ou ses observateurs. La notification de cet evenement est distribuee par waitpid(2). Remarquez que le signal tueur provoquera d'abord un arret-distribution-signal (sur un seul observe) et, seulement apres etre injecte par l'observateur (ou apres etre envoye a un thread qui n'est pas suivi), la mort du signal arrivera sur tous les observes d'un processus multithreade (le terme << arret-distribution-signal >> est explique plus bas). SIGKILL ne genere pas d'arret-distribution-signal et l'observateur ne peut par consequent pas le supprimer. SIGKILL tue meme a l'interieur des appels systemes (arret-sortie-appel-systeme n'est pas cree avant la mort par SIGKILL). L'effet direct est que SIGKILL tue toujours le processus (tout ses threads), meme si certains threads du processus sont suivis avec ptrace. Quand l'observe appelle _exit(2), il signale sa mort a son observateur. Les autres threads ne sont pas concernes. Quand n'importe quel thread execute exit_group(2), tous les observes de son groupe de threads signalent leur mort a leur observateur. Si l'option PTRACE_O_TRACEEXIT est active, PTRACE_EVENT_EXIT arrivera avant la mort reelle. Cela s'applique aux terminaisons avec exit(2), exit_group(2) et aux morts de signal (sauf SIGKILL, selon la version du noyau ; voir les BOGUES ci-dessous), et lorsque les threads sont detruits par execve(2) dans un processus multithreade. L'observateur ne peut pas assumer que l'observe arrete-ptrace existe. L'observe risque de mourir avant d'etre arrete dans plusieurs cas (comme avec SIGKILL). Par consequent, le trace doit etre prepare pour traiter une erreur ESRCH sur n'importe quelle operation ptrace. Malheureusement, la meme erreur est renvoyee si l'observe existe mais n'est pas arrete-ptrace (pour les commandes qui necessitent un observe arrete), ou s'il n'est pas suivi par le processus qui a envoye l'appel ptrace. L'observateur doit garder une trace de l'etat arrete ou en fonctionnement de l'observe, et interpreter ESRCH comme << l'observe s'est acheve de maniere inattendue >> seulement s'il sait que l'observe est effectivement entre en arret-ptrace. Remarquez qu'il n'est pas garanti que waitpid(WNOHANG) signale de facon fiable l'etat de mort de l'observe si une operation ptrace renvoie ESRCH. waitpid(WNOHANG) pourrait plutot renvoyer 0. Autrement dit, l'observe pourrait << ne pas etre encore mort >>, mais deja refuser des requetes ptrace. L'observateur ne peut pas assumer que l'observe finit toujours sa vie en signalant WIFEXITED(status) ou WIFSIGNALED(status) ; dans certains cas ca n'arrive pas. Par exemple si un thread different du leader de groupe de threads fait un execve(2), il disparait ; son PID ne sera plus jamais vu, tous les arrets suivants de ptrace seront signales sous le PID du leader de groupe de threads. Etats arretes Deux etats existent pour un observe : en cours d'execution ou a l'arret. Du point de vue de ptrace, un observe qui est bloque dans un appel systeme (comme read(2), pause(2), etc.) est neanmoins considere en cours d'execution, meme si l'observe est bloque depuis longtemps. L'etat de l'observe apres PTRACE_LISTEN est en quelque sorte une zone d'ombre : il n'est dans aucun arret-ptrace (les commandes ptrace n'auront aucun effet sur lui et il distribuera des notifications waitpid(2)), mais il pourrait aussi etre considere << arrete >> parce qu'il n'est pas en train d'executer des instructions (pas de programmation) et, s'il etait en arret-groupe avant PTRACE_LISTEN, il ne repondra pas aux signaux avant de recevoir SIGCONT. De nombreuses sortes d'etats sont possibles quand l'observe est arrete, et les discussions dans ptrace sont souvent confondues. Par consequent, l'utilisation de termes precis est importante. Dans cette page de manuel, tous les etats d'arret dans lesquels l'observe est pret a accepter des commandes ptrace de l'observateur sont appeles arret-ptrace. Les arrets-ptrace peuvent ensuite etre sous-divises en arret-distribution-signal, arret-groupe, arret-appel-systeme, arret-PTRACE_EVENT, etc. Ces etats d'arret sont decrits en detail ci-dessous. Lorsque l'observe en cours d'execution entre en arret-ptrace, il avise son observateur en utilisant waitpid(2) (ou un des autres appels systeme << wait >>). La plupart de cette page de manuel suppose que l'observateur attend avec : pid = waitpid(pid_ou_moins_1, &status, __WALL); Les observes arretes-ptrace sont signales comme renvoyes avec un pid strictement positif et WIFSTOPPED(status) vrai. L'attribut __WALL ne contient pas les attributs WSTOPPED et WEXITED, mais implique leur fonctionnalite. La configuration de l'attribut WCONTINUED en appelant waitpid(2) n'est pas conseillee : l'etat << execute >> est relatif au processus et l'utiliser peut embrouiller le vrai parent de l'observe. Utiliser l'attribut WNOHANG pourrait forcer waitpid(2) a renvoyer 0 (<< aucun resultat d'attente encore disponible >>) meme si l'observateur sait qu'il devrait y avoir une notification. Exemple : errno = 0; ptrace(PTRACE_CONT, pid, 0L, 0L); if (errno == ESRCH) { /* l'observe est mort */ r = waitpid(tracee, &status, __WALL | WNOHANG); /* r peut encore valoir 0 ici ! */ } Les sortes d'arrets-ptrace suivants existent : arrets-distribution-signal, arrets-groupe, arrets PTRACE_EVENT et arrets-appel-systeme. Ils sont signales par waitpid(2) avec WIFSTOPPED(status) vrai. Ils peuvent etre distingues en examinant la valeur status>>8, et en cas d'ambiguite dans cette valeur, en faisant une requete PTRACE_GETSIGINFO (remarque : la macro WSTOPSIG(status) ne peut pas etre utilisee pour realiser cet examen, car elle renvoie la valeur (status>>8) & 0xff.) Arret-distribution-signal Quand un processus (eventuellement multithreade) recoit n'importe quel signal sauf SIGKILL, le noyau choisi un thread arbitraire pour traiter le signal (si le signal est cree avec tgill(2), le thread cible peut etre explicitement choisi par l'appelant). Si le thread choisi est observe, il entre en arret-distribution-signal. A ce moment la, le signal n'est pas encore distribue au processus, et peut etre supprime par l'observateur. Si l'observateur ne supprime pas le signal, il passe le signal a l'observe lors de la requete suivante de redemarrage de ptrace. Cette deuxieme etape de distribution de signal est appelee injection de signal dans cette page de manuel. Remarquez que si le signal est bloque, l'arret-distribution-signal n'arrive pas avant que le signal soit debloque, a l'exception habituelle que SIGSTOP ne peut pas etre bloque. L'arret-distribution-signal est respecte par l'observateur tant que waitpid(2) retourne avec WIFSTOPPED(status) vrai, avec le signal renvoye par WSTOPSIG(status). Si le signal est SIGTRAP, cela pourrait etre un arret-ptrace de nature differente ; consultez les sections Arrets-appel-systeme et execve(2) sous ptrace plus bas pour obtenir de plus amples precisions. Si WSTOPSIG(status) renvoie un signal d'arret, cela pourrait etre un arret-groupe ; voir ci-dessous. Injection et suppression de signal Apres un arret-distribution-signal respecte par l'observateur, l'observateur devrait redemarrer l'observe avec l'appel ptrace(PTRACE_restart, pid, 0, sig) ou PTRACE_restart est une des requetes ptrace de redemarrage. Si sig est 0, alors aucun signal n'est distribue. Sinon, le signal sig est distribue. Cette operation est appelee injection de signal dans cette page de manuel, pour la distinguer de l'arret-distribution-signal. La valeur de sig peut etre differente de celle de WSTOPSIG(status) : l'observateur peut provoquer l'injection d'un autre signal. Remarquez qu'un signal supprime provoque toujours un retour premature des appels systeme. Dans ce cas, les appels systeme seront redemarres : l'observateur forcera l'observe a reexecuter l'appel systeme interrompu (ou l'appel systeme restart_syscall(2) pour les quelques appels systeme qui utilisent un autre mecanisme de redemarrage) si l'observateur utilise PTRACE_SYSCALL. Meme les appels systeme (comme poll(2)) qui ne sont pas redemarrables apres le signal sont redemarres apres la suppression du signal ; cependant, des bogues du noyau existent et certains appels systeme echouent avec EINTR meme si aucun signal observable n'est injecte dans l'observe. Lors du redemarrage des commandes ptrace emises dans d'autres arrets-ptrace qu'arret-distribution-signal, l'injection de signal n'est pas garantie, meme si sig est non nul. Aucune erreur n'est signalee ; un sig non nul risque simplement d'etre ignore. Les utilisateurs de ptrace ne devraient pas essayer de << creer un nouveau signal >> de cette facon : utilisez plutot tgkill(2). Le fait que des requetes d'injection de signal puissent etre ignorees lors du redemarrage de l'observe apres des arrets ptrace qui ne sont pas des arrets-distribution-signal est une source de confusion pour les utilisateurs de ptrace. Un scenario typique est que l'observateur remarque un arret-groupe, le confonde avec un arret-distribution-signal, et redemarre l'observe avec ptrace(PTRACE_restart, pid, 0, stopsig) dans le but d'injecter stopsig, mais stopsig sera ignore et l'observe continuera de fonctionner. Le signal SIGCONT a pour effet de bord de reveiller (tous les threads d')un processus arrete-groupe. Cet effet de bord arrive avant un arret-distribution-signal. L'observateur ne peut pas supprimer cet effet de bord (il ne peut que supprimer l'injection de signal, qui force seulement le gestionnaire de SIGCONT a ne pas etre execute dans l'observe, si un gestionnaire de ce type est installe). En fait, le reveil depuis un arret-groupe pourrait etre suivi par un arret-distribution-signal pour le ou les signaux differents de SIGCONT, s'ils etaient en attente quand SIGCONT a ete distribue. Autrement dit, SIGCONT pourrait ne pas etre le premier signal remarque par l'observe apres avoir ete envoye. L'arret de signaux force (tous les threads d')un processus a entrer en arret-groupe. Cet effet de bord arrive apres une injection de signal, et peut par consequent etre supprime par l'observateur. Sous Linux 2.4 et les versions precedentes, le signal SIGSTOP ne pouvait pas etre injecte. PTRACE_GETSIGINFO peut etre utilise pour recuperer une structure siginfo_t qui correspond au signal distribue. PTRACE_SETSIGINFO pourrait etre utilise pour le modifier. Si PTRACE_SETSIGINFO a ete utilise pour modifier siginfo_t, le champ si_signo et le parametre sig de la commande de redemarrage doivent correspondre, sinon le resultat est indefini. Arret-groupe Quand un processus (eventuellement multithreade) recoit un signal d'arret, tous les threads s'arretent. Si certains threads sont suivis, ils entrent en arret-groupe. Remarquez que le signal d'arret provoquera d'abord un arret-distribution-signal (sur un seul observe) et, seulement apres avoir ete injecte par l'observateur (ou apres avoir ete envoye a un thread qui n'est pas suivi), l'arret-groupe sera initie sur tous les observes d'un processus multithreade. Comme d'habitude, tous les observes signalent leur arret-groupe separement a l'observateur correspondant. L'arret-groupe est respecte par l'observateur tant que waitpid(2) retourne avec WIFSTOPPED(status) vrai, avec le signal d'arret disponible par l'intermediaire de WSTOPSIG(status). Le meme resultat est renvoye par d'autres classes d'arrets-ptrace, par consequent la methode conseillee est de realiser l'appel ptrace(PTRACE_GETSIGINFO, pid, 0, &siginfo) L'appel peut etre evite si le signal n'est pas SIGSTOP, SIGTSTP, SIGTTIN ou SIGTTOU ; seuls ces quatre signaux sont des signaux d'arret. Si l'observateur voit autre chose, ce ne peut pas etre un arret-groupe. Sinon, l'observateur doit appeler PTRACE_GETSIGINFO. Si PTRACE_GETSIGINFO echoue avec EINVAL, alors c'est definitivement un arret-groupe (d'autres codes d'echec sont possibles, comme ESRCH (<< pas de processus de ce type >>) si un SIGKILL a tue l'observe). Si l'observe etait attache en utilisant PTRACE_SEIZE, un arret-groupe est indique par PTRACE_EVENT_STOP : status>>16 == PTRACE_EVENT_STOP. Cela permet la detection d'arrets-groupe sans necessiter d'appel PTRACE_GETSIGINFO supplementaire. Depuis Linux 2.6.38, apres que l'observateur a vu l'arret-ptrace de l'observe et jusqu'a ce qu'il le redemarre ou le tue, l'observe ne fonctionnera pas, et n'enverra pas de notification (sauf mort par SIGKILL) a l'observateur, meme si l'observateur entre dans un autre appel waitpid(2). Le comportement du noyau decrit dans le paragraphe precedent pose un probleme avec la gestion transparente de signaux d'arret. Si l'observateur redemarre l'observe apres un arret-groupe, le signal d'arret est effectivement ignore -- l'observe ne reste pas arrete, il fonctionne. Si l'observateur ne redemarre pas l'observe avant d'entrer dans le prochain waitpid(2), les signaux SIGCONT suivants ne seront pas signales a l'observateur ; cela pourrait forcer des signaux SIGCONT a etre sans effet sur l'observe. Depuis Linux 3.4, une methode permet d'eviter ce probleme : a la place de PTRACE_CONT, une commande PTRACE_LISTEN peut etre utilisee pour redemarrer un observe de facon a ce qu'il ne s'execute pas, mais attende un nouvel evenement qu'il peut signaler a l'aide de waitpid(2) (comme s'il etait redemarre par un SIGCONT). Arrets PTRACE_EVENT Si l'observateur configure des options PTRACE_O_TRACE_*, l'observe entrera en arrets-ptrace appeles arrets PTRACE_EVENT. Les arrets PTRACE_EVENT sont respectes par l'observateur pendant que waitpid(2) renvoie WIFSTOPPED(status) et que WSTOPSIG(status) renvoie SIGTRAP (ou pour PTRACE_EVENT_STOP, renvoie le signal d'arret si l'observe est dans un arret de groupe). Un bit supplementaire est configure dans l'octet le plus haut du mot d'etat : la valeur status>>8 sera ((PTRACE_EVENT_foo<<8) | SIGTRAP). Les evenements suivants existent. PTRACE_EVENT_VFORK Arret avant de revenir de vfork(2) ou clone(2) avec l'attribut CLONE_VFORK. Quand l'observe est continue apres cet arret, il attendra une sortie ou execution de l'enfant avant de continuer son execution (autrement dit, le comportement normal avec vfork(2)). PTRACE_EVENT_FORK Arret avant de revenir de fork(2) ou clone(2) avec le signal de sortie configure a SIGCHLD. PTRACE_EVENT_CLONE Arret avant de revenir de clone(2). PTRACE_EVENT_VFORK_DONE Arret avant de revenir de vfork(2) ou clone(2) avec l'attribut CLONE_VFORK, mais apres que l'enfant a debloque son observe par sortie ou execution. Pour les quatre arrets decrits ci-dessus, l'arret arrive dans le parent (c'est-a-dire l'observe), pas dans le nouveau thread cree. PTRACE_GETEVENTMSG permet de recuperer l'identifiant du nouveau thread. PTRACE_EVENT_EXEC Arret avant le retour d'execve(2). Depuis Linux 3.0, PTRACE_GETEVENTMSG renvoie le premier identifiant de thread. PTRACE_EVENT_EXIT Arret avant la sortie (y compris la mort depuis exit_group(2)), la mort du signal ou la sortie provoquee par execve(2) dans un processus multithreade. PTRACE_GETEVENTMSG renvoie l'etat de sortie. Les registres peuvent etre examines (contrairement a quand une << vraie >> sortie arrive). L'observe est toujours actif ; il a besoin de PTRACE_CONT ou PTRACE_DETACH pour terminer sa sortie. PTRACE_EVENT_STOP Arret cause par la commande PTRACE_INTERRUPT, ou arret-groupe, ou arret-ptrace initial quand un nouvel enfant est attache (seulement s'il est attache en utilisant PTRACE_SEIZE). PTRACE_EVENT_SECCOMP Arret differe par une regle seccomp(2) sur l'entree appel systeme de l'observe quand PTRACE_O_TRACESECCOMP a ete positionne par l'observateur. Les donnees du message de l'evenement seccomp (issues de la portion SECCOMP_RET_DATA de la regle de filtrage seccomp) peuvent etre recuperees avec PTRACE_GETEVENTMSG. La semantique de cet arret est decrit en details dans une section distincte ci-dessous. PTRACE_GETSIGINFO sur les arrets PTRACE_EVENT renvoie SIGTRAP dans si_signo, avec si_code configure a (event<<8) | SIGTRAP. Arrets-appel-systeme Si l'observe etait redemarre par PTRACE_SYSCALL ou PTRACE_SYSEMU, l'observe entre en arret-entree-appel-systeme juste avant d'entrer dans n'importe quel appel systeme (qui ne sera pas execute si le redemarrage utilisait PTRACE_SYSEMU, quels que soient les changements apportes aux registres a ce point ou a la maniere dont l'observe redemarre apres cet arret). Peu importe la methode qui a conduit en arret-entree-appel-systeme si l'observateur redemarre l'observe avec PTRACE_SYSCALL, l'observe entre en arret-sortie-appel-systeme quand l'appel systeme est termine ou s'il est interrompu par un signal (c'est-a-dire qu'un arret-distribution-signal n'arrive jamais entre un arret-entree-appel-systeme et un arret-sortie-appel-systeme ; il arrive apres l'arret-sortie-appel-systeme). Si l'observe est poursuivi en utilisant une autre methode (notamment PTRACE_SYSEMU), aucun arret-sortie-appel-systeme ne se produit. Remarquez que toutes les mentions PTRACE_SYSEMU s'appliquent egalement a PTRACE_SYSEMU_SINGLESTEP. Toutefois, meme si l'observe a ete poursuivi en utilisant PTRACE_SYSCALL, il n'est pas garanti que le prochain arret sera un arret-sortie-appel-systeme. D'autres possibilites sont que l'observe pourrait s'arreter dans un arret PTRACE_EVENT, sortir (s'il est entre en _exit(2) ou exit_group(2)), etre tue par SIGKILL ou mourir silencieusement (s'il s'agit d'un leader de groupe de threads, que l'execve(2) est arrive dans un autre thread et que ce thread n'est pas suivi par le meme observateur ; cette situation sera abordee plus tard). Les arret-entree-appel-systeme et arret-sortie-appel-systeme sont respectes par l'observateur tant que waitpid(2) retourne avec WIFSTOPPED(status) vrai, et que WSTOPSIG(status) donne SIGTRAP. Si l'option PTRACE_O_TRACESYSGOOD etait configuree par l'observateur, alors WSTOPSIG(status) donnera la valeur (SIGTRAP | 0x80). Les arrets-appel-systeme peuvent etre distingues d'un arret-distribution-signal avec SIGTRAP en demandant PTRACE_GETSIGINFO pour les cas suivants. si_code <= 0 SIGTRAP a ete distribue comme resultat d'une action en espace utilisateur, par exemple, un appel systeme (tgkill(2), kill(2), sigqueue(3), etc.), l'expiration d'un minuteur POSIX, la modification d'etat sur une file de messages POSIX ou la fin d'une requete d'E/S asynchrone. si_code == SI_KERNEL (0x80) SIGTRAP a ete envoye par le noyau. si_code == SIGTRAP ou si_code == (SIGTRAP|0x80) C'est un arret-appel-systeme. Cependant, les arrets-appel-systeme arrivent tres souvent (deux fois par appel systeme) et realiser PTRACE_GETSIGINFO pour chaque arret-appel-systeme pourrait etre assez couteux. Certaines architectures permettent de distinguer ces cas en examinant les registres. Par exemple, sur x86, rax == -ENOSYS en arret-entree-appel-systeme. Puisque SIGTRAP (comme tout autre signal) arrive toujours apres l'arret-sortie-appel-systeme et que rax ne contient a ce moment presque jamais -ENOSYS, le SIGTRAP ressemble a un << arret-appel-systeme qui n'est pas un arret-entree-appel-systeme >> ; autrement dit, il ressemble a un << arret-sortie-appel-systeme perdu >> et peut etre detecte de cette facon. Une telle detection est neanmoins fragile, elle est donc a eviter. L'utilisation de l'option PTRACE_O_TRACESYSGOOD est la methode conseillee pour distinguer les arrets-appel-systeme des autres sortes d'arrets-ptrace, puisqu'il est fiable et n'induit pas de perte de performances. Les arret-entree-appel-systeme et arret-sortie-appel-systeme ne sont pas differentiables l'un de l'autre. L'observateur doit garder une trace de la suite d'arrets-ptrace afin de ne pas mal interpreter un arret-entree-appel-systeme comme un arret-sortie-appel-systeme ou vice versa. Generalement, l'arret-entree-appel-systeme est toujours suivi par un arret-sortie-appel-systeme, un arret PTRACE_EVENT ou la mort de l'observe ; aucune autre sorte d'arret-ptrace ne peut arriver entre-deux. Toutefois, remarquez que les arrets seccomp (voir ci-dessous) peuvent provoquer des arrets-sortie-appel-systeme sans arret-entree-appel-systeme prealable. Si seccomp, il faut faire attention a ne pas mal interpreter de tels arrets en arrets-entree-appel-systeme. Si suite a un arret-entree-appel-systeme, l'observateur utilise une commande de redemarrage differente de PTRACE_SYSCALL, l'arret-sortie-appel-systeme n'est pas cree. PTRACE_GETSIGINFO sur les arrets-appel-systeme renvoie SIGTRAP dans si_signo, avec si_code configure a SIGTRAP ou (SIGTRAP | 0x80). Arrets PTRACE_EVENT_SECCOMP (Linux 3.5 a Linux 4.7) Le comportement des arrets PTRACE_EVENT_SECCOMP et leur interaction avec les autres types d'arret ptrace a change entre les versions du noyau. Nous documentons ici le comportement lors de leur introduction dans Linux 4.7 (inclus). Le comportement dans les versions posterieures du noyau est documente dans la section suivante. Un arret PTRACE_EVENT_SECCOMP se produit a chaque fois qu'une regle SECCOMP_RET_TRACE est declenchee. Cela est independant de la methode utilisee pour redemarrer l'appel systeme. En particulier, seccomp s'execute toujours meme si l'observe a ete redemarre en utilisant PTRACE_SYSEMU et cet appel systeme est saute sans condition. Les redemarrages a partir de cet arret se comporteront comme si l'arret s'etait produit juste avant l'appel systeme en question. En particulier, tant PTRACE_SYSCALL que PTRACE_SYSEMU provoqueront normalement un arret-entree-appel-systeme ulterieur. Cependant, si apres le PTRACE_EVENT_SECCOMP le numero de l'appel systeme est negatif, l'arret-entree-appel-systeme et l'appel lui-meme seront tous deux sautes. Cela veut dire que si le numero d'appel systeme est negatif apres un PTRACE_EVENT_SECCOMP et si l'observe est redemarre en utilisant PTRACE_SYSCALL, le prochain arret observe sera un arret-sortie-appel-systeme et non un arret-entree-appel-systeme qui comme on aurait pu s'y attendre. Arrets PTRACE_EVENT_SECCOMP (depuis Linux 4.8) A partir de Linux 4.8, l'arret PTRACE_EVENT_SECCOMP a ete reamenage pour intervenir entre l'arret-entree-appel-systeme et l'arret-sortie-appel-systeme. Remarquez que seccomp ne s'execute plus (et aucun PTRACE_EVENT_SECCOMP ne sera renvoye) si l'appel systeme est saute du fait d'un PTRACE_SYSEMU. Pratiquement, un arret PTRACE_EVENT_SECCOMP fonctionne comme un arret-entree-appel-systeme (a savoir que les reprises utilisant PTRACE_SYSCALL provoqueront des arrets-sortie-appel-systeme, le numero de l'appel systeme peut etre modifie et tous les registres modifies sont visibles egalement a l'appel systeme a executer). Remarquez qu'il peut y avoir, sans obligation qu'il y ait deja eu, un arret-entree-appel-systeme precedent. Apres un arret PTRACE_EVENT_SECCOMP, seccomp sera reexecute, avec une regle SECCOMP_RET_TRACE qui fonctionne desormais de la meme maniere que SECCOMP_RET_ALLOW. En particulier, cela veut dire que si les registres ne sont pas modifies lors d'un arret PTRACE_EVENT_SECCOMP, l'appel systeme aura alors l'autorisation. Arrets PTRACE_SINGLESTEP [Les precisions sur ces types d'arrets sont encore a documenter.] Commandes ptrace d'information et de redemarrage La plupart des commandes ptrace (toutes sauf PTRACE_ATTACH, PTRACE_SEIZE, PTRACE_TRACEME, PTRACE_INTERRUPT et PTRACE_KILL) necessitent que l'observe soit en arret-ptrace, sinon il echoue avec ESRCH. Quand l'observe est en arret-ptrace, l'observateur peut lire et ecrire les donnes sur l'observe en utilisant les commandes d'information. Ces commandes laissent l'observe en etat arrete-ptrace : ptrace(PTRACE_PEEKTEXT/PEEKDATA/PEEKUSER, pid, addr, 0); ptrace(PTRACE_POKETEXT/POKEDATA/POKEUSER, pid, addr, 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); Remarquez que certaines erreurs ne sont pas signalees. Par exemple, la configuration d'informations de signal (siginfo) pourrait etre sans effet pour certains arrets-ptrace, alors que l'appel pourrait-etre reussi (en renvoyant 0 et sans definir errno) ; la demande de PTRACE_GETEVENTMSG pourrait reussir et renvoyer une quelconque valeur aleatoire si l'arret-ptrace actuel n'est pas documente comme renvoyant un message d'evenement significatif. L'appel ptrace(PTRACE_SETOPTIONS, pid, 0, PTRACE_O_flags); ne concerne qu'un observe. Les attributs actuels de l'observe sont remplaces. Les attributs sont herites par les nouveaux observes crees et << attaches automatiquement >> a l'aide d'options PTRACE_O_TRACEFORK, PTRACE_O_TRACEVFORK ou PTRACE_O_TRACECLONE actives. Un autre groupe de commandes peut redemarrer l'observe arrete-ptrace. Ils sont de la forme : ptrace(cmd, pid, 0, sig); ou cmd est PTRACE_CONT, PTRACE_LISTEN, PTRACE_DETACH, PTRACE_SYSCALL, PTRACE_SINGLESTEP, PTRACE_SYSEMU ou PTRACE_SYSEMU_SINGLESTEP. Si l'observe est en arret-distribution-signal, sig est le signal a injecter (s'il est non nul). Sinon, sig pourrait etre ignore (lors du redemarrage d'un observe depuis un arret-ptrace different d'un arret-distribution-signal, il est conseille de toujours passer 0 a sig). Attachement et detachement Un thread peut etre attache a l'observateur en utilisant l'appel ptrace(PTRACE_ATTACH, pid, 0, 0); ou ptrace(PTRACE_SEIZE, pid, 0, PTRACE_O_flags); PTRACE_ATTACH envoie aussi SIGSTOP a ce thread. Si l'observateur veut que SIGSTOP soit sans effet, il doit le supprimer. Remarquez que si d'autres signaux sont envoyes en meme temps a ce thread pendant l'attachement, l'observateur pourrait voir l'observe entrer en arret-distribution-signal avec d'autres signaux d'abord ! Ces signaux sont d'habitude reinjectes jusqu'a ce que SIGSTOP soit vu, puis l'injection SIGSTOP est supprimee. Le bogue de conception ici est qu'un attachement ptrace et un SIGSTOP distribues en meme temps peuvent entrer en competition et le SIGSTOP risque d'etre perdu. Puisque l'attachement envoie SIGSTOP et que l'observateur le supprime normalement, cela risque de forcer le retour d'un EINTR perdu de l'appel systeme en cours d'execution dans l'observe, tel que c'est decrit dans la section Injection et suppression de signal. Depuis Linux 3.4, PTRACE_SEIZE peut etre utilise a la place de PTRACE_ATTACH. PTRACE_SEIZE n'arrete pas le processus attache. Si vous devez l'arreter apres attachement (ou a n'importe quel autre moment) sans lui envoyer de signal du tout, utilisez la commande PTRACE_INTERRUPT. La requete ptrace(PTRACE_TRACEME, 0, 0, 0); transforme le thread appelant en observe. L'appel continue d'etre execute (n'entre pas en arret-ptrace). PTRACE_TRACEME est habituellement suivi avec raise(SIGSTOP); et permet au parent (qui est maintenant l'observateur) de respecter l'arret-distribution-signal. Si les options PTRACE_O_TRACEFORK, PTRACE_O_TRACEVFORK ou PTRACE_O_TRACECLONE font effet, alors les enfants respectivement crees par vfork(2) ou clone(2) avec l'attribut CLONE_VFORK, fork(2) ou clone(2) avec le signal de sortie configure a SIGCHLD, et d'autres sortes de clone(2), sont automatiquement attaches au meme observateur qui a suivi leur parent. SIGSTOP est distribue aux enfants, les forcant a entrer en arret-distribution-signal apres etre sortis de l'appel systeme qu'ils ont cree. Le detachement de l'observe est realise par : ptrace(PTRACE_DETACH, pid, 0, sig); PTRACE_DETACH est une operation de redemarrage ; par consequent elle necessite que l'observe soit en arret-ptrace. Si l'observe est en arret-distribution-signal, un signal peut etre injecte. Sinon, le parametre sig pourrait etre silencieusement ignore. Si l'observe est en cours d'execution quand l'observateur veut le detacher, la solution habituelle est d'envoyer SIGSTOP (en utilisant tgkill(2), pour s'assurer qu'il va au bon thread), d'attendre que l'observe s'arrete en arret-distribution-signal pour SIGSTOP et ensuite de le detacher (en supprimant l'injection SIGSTOP). Un bogue de conception est que l'observe pourrait entrer dans d'autres arrets-ptrace et avoir besoin d'etre redemarre et attendre encore, jusqu'a ce que SIGSTOP soit vu. Encore une autre complication est de s'assurer que l'observe n'est pas deja arrete-ptrace, parce qu'aucune distribution de signal n'arrive tant qu'il l'est -- pas meme SIGSTOP. Si l'observateur meurt, tous les observes sont automatiquement detaches et redemarres, sauf s'il etaient en arret-groupe. Le gestion de redemarrage depuis un arret-groupe est en ce moment dysfonctionnelle, mais le comportement << prevu >> est de laisser les observes arretes et d'attendre un SIGCONT. Si l'observe est redemarre depuis un arret-distribution-signal, le signal en attente est injecte. execve(2) sous ptrace Quand un thread de processus multithreade appelle execve(2), le noyau detruit tous les autres threads du processus, et reinitialise l'identifiant de thread du thread execute a l'identifiant de groupe de threads (PID) (ou, pour le presenter autrement, quand un processus multithreade fait un execve(2), a la fin de l'appel, il apparait comme si l'execve(2) s'etait applique au leader de groupe de threads, quelque soit le thread qui a fait execve(2)). Cette reinitialisation de l'identifiant de thread semble est tres deroutante pour les observateurs. - Tous les autres threads s'arretent en arret PTRACE_EVENT_EXIT, si l'option PTRACE_O_TRACEEXIT etait activee. Alors tous les autres threads sauf le leader de groupe de threads signalent leur mort comme s'il s'etaient termines par l'intermediaire de _exit(2) avec un code de retour 0. - L'observe en cours d'execution modifie son identifiant de thread pendant qu'il est dans l'execve(2) (rappelez-vous que, sous ptrace, le << pid >> renvoye par waitpid(2) ou fourni dans les appels ptrace, est l'identifiant de thread de l'observe). Ainsi, l'identifiant de thread de l'observe est reinitialise pour etre le meme que son identifiant de processus (PID), qui est le meme que l'identifiant de thread du leader de groupe de threads. - Ensuite un arret PTRACE_EVENT_EXEC arrive, si l'option PTRACE_O_TRACEEXEC etait activee. - Si le leader de groupe de threads a signale son arret PTRACE_EVENT_EXIT pendant ce temps, le leader de thread mort a l'air de << revenir de nulle part >> du point de vue de l'observateur (remarque : le leader de groupe de threads ne signale pas sa mort a l'aide de WIFEXITED(status) tant qu'au moins un autre thread est en vie. Cela enleve la possibilite a l'observateur de le voir mourir puis reapparaitre). Si le leader de groupe de threads etait encore en vie, cela pourrait etre vu par l'observateur comme si le leader de groupe revenait d'un autre appel systeme que celui dans lequel il etait entre, ou meme << revenait d'un appel systeme meme s'il n'y avait pas d'appel systeme >>. Si le leader de groupe de threads n'etait pas suivi (ou etait suivi par un autre observateur), alors pendant execve(2) il apparaitra comme s'il etait devenu un observe de l'observateur de l'observe en cours d'execution. Tous les effets precedents sont des artifices de la modification d'identifiant de thread de l'observe. L'option PTRACE_O_TRACEEXEC est l'outil conseille pour s'occuper de cette situation. D'abord, elle active l'arret PTRACE_EVENT_EXEC, qui arrive avant le retour d'execve(2). Dans cet arret, l'observateur peut utiliser PTRACE_GETEVENTMSG pour recuperer l'ancien identifiant de thread de l'observe (cette fonctionnalite a ete introduite avec Linux 3.0). Ensuite, l'option PTRACE_O_TRACEEXEC desactive la creation obsolete de SIGTRAP dans execve(2). Quand l'observe recoit une notification d'arret PTRACE_EVENT_EXEC, il est garanti qu'a part cet observe et le leader de groupe de threads, aucun autre thread du processus n'est en vie. Lors de la reception d'une notification d'arret PTRACE_EVENT_EXEC, l'observateur devrait nettoyer toutes ses structures de donnees internes decrivant les threads de ce processus et ne garder qu'une seule structure de donnees -- celle qui decrit l'unique observe en cours d'execution, avec identifiant de thread == identifiant de groupe de threads == identifiant de processus. Par exemple, soient deux threads qui appellent execve(2) en meme temps : *** arret-entree-appel-systeme obtenu dans le thread 1 : ** PID1 execve("/bin/truc", "truc" > *** PTRACE_SYSCALL emis pour le thread 1 ** *** arret-entree-appel-systeme obtenu dans le thread 2 : ** PID2 execve("/bin/bidule", "bidule" > *** PTRACE_SYSCALL emis pour le thread 2 ** *** PTRACE_EVENT_EXEC obtenu pour PID0, PTRACE_SYSCALL emis ** *** arret-sortie-appel-systeme obtenu pour PID0 : ** PID0 < retour d'execve> ) = 0 Si l'option PTRACE_O_TRACEEXEC n'est pas effective pour l'observe en cours d'execution et si l'observe a ete PTRACE_ATTACHe et non PTRACE_SEIZEe, le noyau distribue un SIGTRAP supplementaire a l'observe apres le retour d'execve(2). C'est un signal normal (similaire a celui qui peut etre cree par kill -TRAP), pas une sorte speciale d'arret-ptrace. L'utilisation de PTRACE_GETSIGINFO pour ce signal renvoie si_code configure a 0 (SI_USER). Ce signal pourrait etre bloque par un masque de signal et pourrait ainsi etre distribue (bien) plus tard. Normalement, l'observateur (par exemple strace(1)) ne voudrait pas montrer ce signal SIGTRAP supplementaire posterieur a execve a l'utilisateur, et voudrait supprimer sa distribution a l'observe (si SIGTRAP est configure a SIG_DFL, c'est un signal tueur). Cependant, determiner quel est le SIGTRAP a supprimer n'est pas simple. La configuration de l'option PTRACE_O_TRACEEXEC ou l'utilisation de PTRACE_SEIZE et par consequent la suppression du SIGTRAP supplementaire est l'approche conseillee. Vrai parent L'interface de programmation de ptrace utilise (parfois mal) la norme UNIX de signalement de parent ou enfant par l'intermediaire de waitpid(2). Cela a regulierement force le vrai parent du processus a arreter de recevoir plusieurs sortes de notifications de waitpid(2) quand le processus enfant est suivi par un autre processus. De nombreux bogues de ce type ont ete corriges, mais il en reste encore beaucoup dans Linux 2.6.38. Consultez la section BOGUES ci dessous. Depuis Linux 2.6.38, ce qui suit est cense fonctionner correctement : - l'execution ou la mort par signal sont d'abord signalees a l'observateur, puis, quand l'observateur consomme le resultat de waitpid(2), au vrai parent (au vrai parent seulement quand l'integralite du processus multithreade se termine). Si l'observateur et le vrai parent sont le meme processus, le signalement n'est envoye qu'une fois. VALEUR RENVOYEE En cas de succes, la requete PTRACE_PEEK* renvoie les donnees demandees (mais consultez les NOTES), la requete PTRACE_SECCOMP_GET_FILTER renvoie le nombre d'instructions du programme BPF, la requete PTRACE_GET_SYSCALL_INFO renvoie le nombre d'octets disponibles disponible pour etre ecrits par le noyau, alors que les autres requetes renvoient zero. En cas d'erreur, toutes les requetes renvoient -1 et errno est defini pour indiquer l'erreur. Comme la valeur renvoyee par une requete PTRACE_PEEK* peut legitimement etre -1, l'appelant doit effacer errno avant l'appel, et ensuite le verifier pour savoir si une erreur s'est produite. ERREURS EBUSY (i386 seulement) Une erreur est survenue lors de l'allocation ou de la liberation d'un registre de debogage. EFAULT Tentative de lire ou ecrire dans une zone memoire non valable de l'observateur ou de l'observe, probablement parce que la zone n'etait pas projetee ou accessible. Malheureusement sous Linux, certaines variantes de cette erreur declencheront EIO ou EFAULT plus ou moins arbitrairement. EINVAL Tentative d'utiliser une option non valable. EIO La requete request n'est pas valable ou une tentative de lecture ou d'ecriture dans une zone non valable de memoire de l'observateur ou de l'observe a eu lieu. Un probleme d'alignement a aussi pu survenir sur une frontiere de mot, ou une tentative de redemarrage en envoyant un signal non valable. EPERM Le processus indique ne peut pas etre suivi. Cela peut etre du a un manque de privilege de l'observateur (la capacite necessaire est CAP_SYS_PTRACE). Les processus non privilegies ne peuvent pas suivre les processus auxquels ils ne peuvent envoyer de signal, ou ceux qui s'executent Set-UID/Set-GID, pour des raisons evidentes. En outre, le processus vise peut etre deja suivi, ou (avant Linux 2.6.26) etre init(8) (le processus numero 1). ESRCH Le processus indique n'existe pas, ou n'est pas suivi par l'appelant, ou n'est pas arrete (pour les requetes qui ont besoin d'un observe arrete). STANDARDS Aucun. HISTORIQUE SVr4, 4.3BSD. Avant Linux 2.6.26, init(8), le processus numero 1, ne peut pas etre suivi. NOTES Bien que les arguments de ptrace() soient interpretes comme dans le prototype donne, la bibliotheque glibc declare ptrace comme une fonction variadique ou seul l'argument request est corrige. Il vaut mieux toujours fournir quatre arguments, meme si l'operation demandee ne les utilise pas, en configurant les arguments non utilises ou ignores a 0L ou (void *) 0. Le parent d'observes reste observateur meme s'il appelle execve(2). La disposition du contenu de la memoire et de la zone USER dependent du systeme d'exploitation et de l'architecture. Le decalage fourni et les donnees renvoyees peuvent ne pas correspondre entierement avec la definition d'une structure struct user. La taille d'un mot (<< word >>) est determinee par la version du systeme d'exploitation (par exemple 32 bits pour Linux 32 bits). Cette page documente le fonctionnement actuel de ptrace() sous Linux. Celui-ci peut varier significativement d'autres types d'UNIX. De toute facon, l'utilisation de ptrace() depend fortement de l'architecture et du systeme d'exploitation. Verification du mode d'acces ptrace Divers endroits de l'API de l'espace utilisateur du noyau (pas seulement les operations ptrace()) exigent ce qu'on appelle des << verifications de mode d'acces ptrace >>, dont le resultat determine si une operation est autorisee (ou, dans certains cas, fait renvoyer a l'operation << read >> des donnees nettoyees). Ces verifications sont effectuees dans les cas ou un processus peut acceder a des informations sensibles concernant un autre processus ou modifier son etat. Les verifications s'operent sur la base de facteurs tels que les droits et les capacites des deux processus, le fait que le processus << cible >> puisse generer un fichier core, et des resultats des verifications effectuees par un module de securite Linux active (LSM) (par exemple SELinux, Yama ou Smack) et par le LSM commoncap (qui est toujours appele). Avant Linux 2.6.27, toutes les verifications d'acces etaient d'un seul type. Depuis Linux 2.6.27, on distingue deux niveaux de modes d'acces : PTRACE_MODE_READ Pour les operations << read >> ou d'autres moins dangereuses telles que : get_robust_list(2) ; kcmp(2) ; la lecture de /proc/pid/auxv, /proc/pid/environ ou de /proc/pid/stat ; ou le readlink(2) d'un fichier /proc/pid/ns/*. PTRACE_MODE_ATTACH Pour les operations << write >> ou d'autres plus dangereuses telles que : le rattachement de ptrace (PTRACE_ATTACH) a un autre processus ou un appel process_vm_writev(2) (PTRACE_MODE_ATTACH etait celui par defaut avant Linux 2.6.27). Depuis Linux 4.5, les verifications de mode d'acces ci-dessus sont combinees (operation OU) avec un ou plusieurs des modificateurs suivants : PTRACE_MODE_FSCREDS Utiliser l'identifiant de groupe ou d'utilisateur du systeme de fichiers de l'appelant (voir credentials(7)) ou les capacites effectives pour les verifications LSM. PTRACE_MODE_REALCREDS Utiliser l'identifiant de groupe ou d'utilisateur reel de l'appelant ou les capacites autorisees pour les verifications LSM. C'etait l'action par defaut avant Linux 4.5. La combinaison d'un des modificateurs de droits avec un des modes d'acces ci-dessus etant classique, certaines macros sont definies dans les sources du noyau pour les combinaisons : PTRACE_MODE_READ_FSCREDS Definie en tant que PTRACE_MODE_READ | PTRACE_MODE_FSCREDS. PTRACE_MODE_READ_REALCREDS Definie en tant que PTRACE_MODE_READ | PTRACE_MODE_REALCREDS. PTRACE_MODE_ATTACH_FSCREDS Definie en tant que PTRACE_MODE_ATTACH | PTRACE_MODE_FSCREDS. PTRACE_MODE_ATTACH_REALCREDS Definie en tant que PTRACE_MODE_ATTACH | PTRACE_MODE_REALCREDS. Un modificateur supplementaire peut etre lie (operation OU) au mode d'acces : PTRACE_MODE_NOAUDIT (depuis Linux 3.3) Ne pas effectuer la verification de ce mode d'acces. Ce modificateur est utilise pour les verifications de mode d'acces ptrace (telles que celles faites lors de la lecture de /proc/pid/stat) qui filtrent ou nettoient la sortie au lieu de renvoyer une erreur a l'appelant. Dans ces cas, l'acces au fichier n'est pas une violation de securite et il n'y aucune raison de generer un enregistrement de l'evaluation de la securite. Ce modificateur supprime la generation d'un tel enregistrement pour cette verification d'acces particuliere. Remarquez que toutes les constantes PTRACE_MODE_* decrites dans cette sous-section sont internes au noyau et invisibles a l'espace utilisateur. Le nom des constantes est mentionne ici pour identifier les differents types de verification des modes d'acces ptrace effectuees pour divers appels systeme et divers acces a des pseudofichiers (comme dans /proc). Ces noms sont utilises dans d'autres pages de manuel pour fournir un raccourci simple d'identification des verifications du noyau. L'algorithme utilise pour la verification des modes d'acces ptrace determine si le processus appelant est autorise a effectuer l'action correspondante sur le processus cible (pour l'ouverture des fichiers /proc/pid, le << processus appelant >> est celui qui ouvre le fichier et le processus dont le PID correspond est le << processus cible >>). L'algorithme est comme suit : (1) Si le thread appelant et cible sont dans le meme groupe de threads, l'acces est toujours autorise. (2) Si le mode d'acces indique PTRACE_MODE_FSCREDS, lors de la verification de la prochaine etape, utiliser l'identifiant d'utilisateur et de groupe du systeme de fichiers appelant (comme indique dans credentials(7), les identifiants d'utilisateur et de groupe du systeme de fichiers ont presque toujours la meme valeur que les identifiants effectifs correspondant). Sinon le mode d'acces indique PTRACE_MODE_REALCREDS, donc utiliser l'identifiant d'utilisateur et de groupe reels de l'appelant lors des verifications de la prochaine etape (la plupart des API qui verifient les identifiants d'utilisateur et de groupe utilisent les identifiants effectifs. Pour des raisons historiques, la verification PTRACE_MODE_REALCREDS utilise plutot ceux reels). (3) Interdire l'acces si rien de ce qui suit n'est vrai : - Les identifiants utilisateur reel, effectif et defini de la cible correspondent a l'identifiant utilisateur de l'appelant et les identifiants reels, effectifs et definis de groupe de la cible correspondent a ceux de groupe de l'appelant. - L'appelant a la capacite CAP_SYS_PTRACE dans l'espace de noms utilisateur de la cible. (4) Interdire l'acces si l'attribut << dumpable >> du processus cible a une autre valeur que 1 (SUID_DUMP_USER ; voir le point sur PR_SET_DUMPABLE dans prctl(2)) et l'appelant n'a pas la capacite CAP_SYS_PTRACE dans l'espace de noms de l'utilisateur du processus cible. (5) L'interface security_ptrace_access_check() du LSM du noyau est appelee pour voir si l'acces ptrace est autorise. Le resultat depend des LSM. L'implementation de cette interface dans le LSM commoncap suit les etapes suivantes : (5.1) Si le mode d'acces comprend PTRACE_MODE_FSCREDS, utiliser l'ensemble des capacites effectives de l'appelant dans la prochaine verification ; sinon (si le mode d'acces indique PTRACE_MODE_REALCREDS), utiliser l'ensemble des capacites autorisees de l'appelant. (5.2) Interdire l'acces si rien de ce qui suit n'est vrai : - Les processus appelant et cible sont dans le meme espace de noms utilisateur et les capacites de l'appelant sont un surensemble des capacites autorisees du processus cible. - L'appelant a la capacite CAP_SYS_PTRACE dans l'espace de noms utilisateur du processus cible. Remarquez que le LSM commoncap ne fait pas de difference entre PTRACE_MODE_READ et PTRACE_MODE_ATTACH. (6) Si l'acces n'a pas ete interdit par une etape precedente, il est autorise. /proc/sys/kernel/yama/ptrace_scope Sur des systemes ayant un Yama Linux Security Module (LSM) installe (donc si le noyau a ete configure avec CONFIG_SECURITY_YAMA), le fichier /proc/sys/kernel/yama/ptrace_scope (disponible depuis Linux 3.4) peut etre utilise pour restreindre la possibilite d'observer un processus avec ptrace() (et ainsi, celle d'utiliser des outils tels que strace(1) et gdb(1)). Le but de telles restrictions est d'empecher des attaques en cascade par lesquelles un processus infecte peut s'attacher avec un ptrace a d'autres processus sensibles (comme un agent GPG ou une session SSH) appartenant a l'utilisateur, afin d'obtenir d'autres droits qui pourraient exister en memoire et ainsi, elargir l'objectif de l'attaque. Plus precisement, le Yama LSM limite deux types d'operations : - Toute operation qui effectue une verification PTRACE_MODE_ATTACH de mode d'acces (par exemple, PTRACE_ATTACH de ptrace(), voir le point sur les << verifications de mode d'acces ptrace >> ci-dessus). - PTRACE_TRACEME ptrace(). Un processus ayant la capacite CAP_SYS_PTRACE peut mettre a jour le fichier /proc/sys/kernel/yama/ptrace_scope avec une ou plusieurs des valeurs suivantes : 0 (droits ptrace << classiques >>) Aucune restriction supplementaire sur les operations qui effectuent des verifications PTRACE_MODE_ATTACH (au-dela de celles imposees par le LSM commoncap et les autres). L'utilisation de PTRACE_TRACEME ne change pas. 1 (<< ptrace restreint >> (valeur par defaut) Lors d'une operation qui exige une verification PTRACE_MODE_ATTACH, le processus appelant doit avoir soit la capacite CAP_SYS_PTRACE dans l'espace de noms utilisateur du processus cible, soit une relation predefinie avec le processus cible. Par defaut, la relation predefinie est que le processus cible doit etre un descendant de l'appelant. Un processus cible peut utiliser l'operation PR_SET_PTRACER de prctl(2) pour declarer un PID supplementaire autorise a effectuer des operations PTRACE_MODE_ATTACH sur la cible. Voir le fichier Documentation/admin-guide/LSM/Yama.rst des sources du noyau (ou Documentation/security/Yama.txt avant Linux 4.13) pour plus de details. L'utilisation de PTRACE_TRACEME ne change pas. 2 (attachement << admin-only >>) Seuls les processus ayant la capacite CAP_SYS_PTRACE dans l'espace de noms de l'utilisateur du processus cible peuvent effectuer des operations PTRACE_MODE_ATTACH ou observer les enfants qui utilisent PTRACE_TRACEME. 3 (<< pas d'attachement >>) Aucun processus ne peut effectuer d'operation PTRACE_MODE_ATTACH ou observer les enfants qui utilisent PTRACE_TRACEME. Une fois que cette valeur a ete ecrite dans le fichier, elle ne peut pas etre modifiee. Par rapport aux valeurs 1 et 2, remarquez que la creation d'un nouvel espace de noms utilisateur supprime de fait la protection apportee par Yama. Cela parce qu'un processus dans l'espace de noms de l'utilisateur parent dont l'identifiant utilisateur effectif correspond a celui de l'espace de noms enfant a toutes les capacites (y compris CAP_SYS_PTRACE) lorsqu'il effectue des operations dans l'espace de noms utilisateur de l'enfant (et les descendants supprimes plus tard de cet espace de noms). Par consequent, quand un processus essaie d'utiliser les espaces de noms utilisateur pour s'isoler lui-meme, il affaiblit a son insu les protections apportees par le Yama LSM. Differences entre bibliotheque C et noyau L'interface de programmation de l'appel systeme est differente pour les requetes PTRACE_PEEKTEXT, PTRACE_PEEKDATA et PTRACE_PEEKUSER : elles stockent le resultat a l'adresse indiquee par le parametre data, et la valeur de retour est l'attribut d'erreur. La fonction glibc encapsulant cet appel fournit une interface detaillee dans la section DESCRIPTION ci-dessus, et le resultat qu'elle renvoie est le resultat de l'appel systeme. BOGUES Sur les machines ayant des en-tetes du noyau Linux 2.6, PTRACE_SETOPTIONS est declare avec une valeur differente de celle de Linux 2.4. De ce fait, les applications compilees avec des en-tetes du noyau Linux 2.6 ne peuvent pas s'executer sous Linux 2.4. Il est possible de contourner cette difficulte en redefinissant PTRACE_SETOPTIONS a PTRACE_OLDSETOPTIONS, si cette derniere constante est definie. Les notifications d'arret-groupe sont envoyees a l'observateur, mais pas au vrai parent. C'etait encore vrai sur 2.6.38.6. Si un leader de groupe de threads est suivi et existe en appelant _exit(2), un arret PTRACE_EVENT_EXIT lui arrivera (si reclame), mais la notification WIFEXITED suivante ne sera pas distribuee avant la fin de tous les autres threads. Comme explique precedemment, si un des autres threads appelle execve(2), la mort du leader de groupe de threads ne sera jamais signalee. Si le thread execute n'est pas suivi par cet observateur, l'observe ne saura jamais qu'execve(2) est arrive. Un contournement possible est de PTRACE_DETACHer le leader de groupe de threads au lieu de le redemarrer dans ce cas. C'etait encore vrai sur 2.6.38.6. Un signal SIGKILL pourrait encore provoquer un arret PTRACE_EVENT_EXIT avant une veritable mort du signal. Cela pourrait evoluer a l'avenir. SIGKILL est suppose tuer immediatement les taches meme sous ptrace. C'etait encore vrai sur Linux 3.13. Certains appels systeme renvoient EINTR si un signal a ete envoye a l'observe, mais que la distribution a ete supprimee par l'observateur (c'est une operation tout a fait caracteristique : elle est normalement realisee par les debogueurs sur tous les attachements, afin de ne pas introduire de SIGSTOP defectueux). Depuis Linux 3.2.9, les appels systeme suivants sont concernes (cette liste est sans doute incomplete) : epoll_wait(2) et read(2) depuis un descripteur de fichier inotify(7). Le symptome classique de ce bogue est qu'en attachant a un processus quiescent avec la commande strace -p alors, au lieu de la ligne de sortie habituelle attendue comme restart_syscall(<... reprise de l'appel interrompu ...>_ ou select(6, [5], NULL, [5], NULL_ (<< _ >> indique la position du curseur), plusieurs lignes sont affichees. Par exemple : clock_gettime(CLOCK_MONOTONIC, {15370, 690928118}) = 0 epoll_wait(4,_ Ce qui n'est pas visible ici est que le processus a ete bloque dans epoll_wait(2) avant que strace(1) ne s'y soit attache. L'attachement a force epoll_wait(2) a revenir dans l'espace utilisateur avec l'erreur EINTR. Dans ce cas particulier, le programme a reagit a EINTR en verifiant l'heure actuelle et en executant encore epoll_wait(2) (les programmes qui ne s'attendent pas a de telles erreurs EINTR << perdue >> risquent de se comporter de facon inattendue sur une attache strace(1)). Contrairement aux regles normales, l'enveloppe de la glibc pour ptrace() peut positionner errno sur zero. VOIR AUSSI 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) TRADUCTION La traduction francaise de cette page de manuel a ete creee par Christophe Blaess , Stephan Rafin , Thierry Vignaud , Francois Micaux, Alain Portal , Jean-Philippe Guerard , Jean-Luc Coulon (f5ibh) , Julien Cristau , Thomas Huriaux , Nicolas Francois , Florentin Duneau , Simon Paillard , Denis Barbier , David Prevot et Jean-Philippe MENGUAL Cette traduction est une documentation libre ; veuillez vous reporter a la GNU General Public License version 3 concernant les conditions de copie et de distribution. Il n'y a aucune RESPONSABILITE LEGALE. Si vous decouvrez un bogue dans la traduction de cette page de manuel, veuillez envoyer un message a . Pages du manuel de Linux 6.06 31 octobre 2023 ptrace(2)