execve(2) System Calls Manual execve(2) NOM execve - Executer un programme BIBLIOTHEQUE Bibliotheque C standard (libc, -lc) SYNOPSIS #include int execve(const char *pathname, char *const _Nullable argv[], char *const _Nullable envp[]); DESCRIPTION execve() execute le programme auquel renvoie pathname. Il s'ensuit que le programme en cours d'execution par le processus appelant sera remplace par un nouveau programme, avec une pile et un tas nouvellement initialises, et des segments de donnees (initialises et non initialises) pathname doit etre soit un executable binaire, soit un script qui commence par une ligne sous la forme : #!interpreteur [argument-optionnel] Pour des details sur ce dernier cas, consultez << Scripts d'interpreteur >> ci-dessous. argv est un tableau de pointeurs vers des chaines passees au nouveau programme en tant qu'arguments de la ligne de commande. Par convention, la premiere de ces chaines (a savoir argv[0]) devrait contenir le nom de fichier associe au fichier etant execute. Le tableau argv doit se terminer par un pointeur NULL (ainsi, dans le nouveau programme, argv[argc] sera un pointeur NULL). envp est un tableau de pointeurs vers des chaines, ayant par convention la forme cle=valeur, qui sont passes au nouveau programme en tant qu'environnement. Le tableau envp dois se terminer par un pointeur NULL. Cette page de manuel decrit l'appel systeme Linux en detail ; pour un apercu de la nomenclature et des nombreuses variantes, souvent preferables et standardisees de cette fonction, fournies par la libc, dont celles qui recherchent la variable d'environnement PATH, voir exec(3). Le vecteur d'argument et l'environnement sont accessibles a la fonction principale du nouveau programme quand elle est definie ainsi : int main(int argc, char *argv[], char *envp[]) Notez, cependant, que l'utilisation d'un troisieme argument dans la fonction principale n'est pas specifiee dans POSIX.1, selon laquelle l'environnement doit etre accessible par la variable externe environ(7). En cas de reussite, execve() ne renvoie rien et les segments de texte, les donnees initialisees et non initialisees (<< bss >>), ainsi que la pile du processus appelant sont remplaces par ceux du programme charge. Si l'on effectuait un ptrace(2) sur le programme actuel, un signal SIGTRAP lui est envoye apres la reussite de execve(). Si le bit set-user-ID est positionne sur le fichier du programme auquel renvoie pathname, l'ID de l'utilisateur effectif du processus appelant passe a celui du proprietaire du programme. De meme, lorsque le bit set-group-ID est positionne sur le fichier du programme, l'ID du groupe effectif du processus appelant est modifie pour correspondre a celui du groupe du fichier. Les transformations precitees des ID effectifs ne sont pas effectuees (c'est-a-dire que les bits set-user-ID et set-group-ID sont ignores) si un des elements suivants est vrai : - l'attribut no_new_privs est defini pour le thread appelant (voir prctl(2)) ; - le systeme de fichiers sous-jacent est monte en nosuid (le drapeau MS_NOSUID de mount(2)) ; - ou un ptrace va etre applique au processus appelant. Les capacites du fichier du programme (voir capabilities(7)) sont egalement ignorees si un des elements ci-dessus est vrai. L'UID effectif du processus est copie dans le set-user-ID sauvegarde ; de la meme maniere, le GID effectif est copie dans le set-group-ID sauvegarde. Ces copies ont lieu apres toute modification d'ID effectif a cause des bits de permission set-user-ID et set-group-ID. L'UID et le GID reel du processus ainsi que ses ID de groupe complementaires ne sont pas modifies par un appel a execve(). Si l'executable est un fichier binaire a.out lie dynamiquement, et contenant des appels aux bibliotheques partagees, l'editeur de liens dynamiques de Linux ld.so(8) est appele au debut de l'execution, afin de charger les bibliotheques partagees necessaires en memoire et d'effectuer l'edition des liens de l'executable avec eux. Si l'executable est au format ELF lie dynamiquement, l'interpreteur indique dans le segment PT_INTERP sera invoque pour charger les bibliotheques partagees. Cet interpreteur est generalement /lib/ld-linux.so.2 pour les fichiers binaires lies avec la glibc (voir ld-linux.so(8)). Effets sur les attributs de processus Tous les attributs de processus sont preserves lors d'un execve(), a l'exception des suivants : - Les signaux pour lesquels le processus avait place un gestionnaire sont reinitialises a leur valeur par defaut (consultez signal(7)). - L'eventuelle pile specifique pour les gestionnaires de signal n'est pas conservee (sigaltstack(2)). - Les projections en memoire ne sont pas conservees (mmap(2)). - Les segments de memoire partagee System V sont detaches (shmat(2)). - Les objets de memoire partagee POSIX sont supprimes (shm_open(3)). - Les descripteurs de files de messages POSIX ouverts sont fermes (mq_overview(7)). - Les semaphores nommes POSIX ouverts sont fermes (sem_overview(7)). - Les temporisations POSIX ne sont pas conservees (timer_create(2)). - Les flux de repertoires ouverts sont fermes (opendir(3)). - Les verrouillages de memoire ne sont pas preserves (mlock(2), mlockall(2)). - Les gestionnaires de terminaison ne sont pas preserves (atexit(3), on_exit(3)). - L'environnement de travail en virgule flottante est reinitialise a celui par defaut (consultez fenv(3)). Les attributs de processus listes ci-dessus sont specifies dans POSIX.1. Les attributs de processus specifiques a Linux suivants sont egalement reinitialises lors d'un execve() : - L'attribut << dumpable >> du processus est positionne sur la valeur 1, sauf si un programme set-user-ID, set-group-ID ou en ayant les capacites est execute, auquel cas l'attribut dumpable peut, au contraire, etre reinitialise a la valeur dans /proc/sys/fs/suid_dumpable, dans les circonstances decrites sous PR_SET_DUMPABLE dans prctl(2). Remarquez que les modifications d'un attribut << dumpable >> peuvent entrainer le passage du proprietaire des fichiers du repertoire /proc/pid du processus a root:root, comme decrit dans proc(5). - L'attribut PR_SET_KEEPCAPS de prctl(2) est efface. - (Depuis Linux 2.4.36 ou 2.6.23) Si un programme set-user-ID ou set-group-ID est execute, alors le signal de mort de son parent defini par l'attribut PR_SET_PDEATHSIG de prctl(2) est efface. - Le nom du processus, positionne par prctl(2) PR_SET_NAME (et affiche avec ps -o comm), est reinitialise avec le nom du nouvel executable. - L'attribut securebits de SECBIT_KEEP_CAPS est efface. Consultez capabilities(7). - Le signal de terminaison est reinitialise a SIGCHLD (consultez clone(2)). - La table des descripteurs de fichier n'est pas partagee, ce qui annule les effets de l'attribut CLONE_FILES de clone(2). Notez egalement les points suivants : - Tous les threads autres que l'appelant sont detruits lors d'un execve(). Les mutex, les variables de condition, et les autres objets de pthreads sont detruits. - L'equivalent de setlocale(LC_ALL, "C") est execute au demarrage du programme. - POSIX.1 indique que les actions pour les signaux ignores ou places a la valeur par defaut ne sont pas modifiees. Une exception est neanmoins specifiee dans POSIX.1 : si SIGCHLD est ignore, l'implementation peut laisser l'action inchangee ou la replacer a la valeur par defaut ; Linux ne modifie pas l'action. - Toutes les operations d'E/S asynchrones en cours sont annulees (aio_read(3), aio_write(3)). - Pour le traitement des capacites lors d'un execve(), consultez capabilities(7). - Par defaut, les descripteurs de fichier restent ouverts au travers d'un execve(). Les descripteurs marques close-on-exec sont fermes ; consultez la description de FD_CLOEXEC dans fcntl(2). (Si un descripteur de fichier est ferme, cela cause la liberation de tous les verrous d'enregistrement obtenus sur le fichier correspondant par ce processus. Consultez fcntl(2) pour les details.) POSIX.1 indique que si les descripteurs de fichiers 0, 1 et 2 devaient etre fermes apres un execve() reussi, et que le processus devient privilegie en raison d'un bit set-user-ID ou set-group-ID sur le fichier execute, le systeme peut ouvrir un fichier non indique pour chacun de ces descripteurs. En general, aucun programme portable, privilegie ou pas, ne peut considerer que ces trois descripteurs resteront fermes apres un execve(). Scripts d'interpreteur Un script d'interpreteur est un fichier textuel dont le bit d'execution est active et dont la premiere ligne est de la forme : #!interpreteur [argument-optionnel] L'interpreteur doit etre le chemin valable d'un fichier executable. Si l'argument pathname de execve() indique un script interprete, l'interpreteur sera appele avec les arguments suivants : interpreteur [argument-optionnel] pathname arg... ou pathname est le chemin du fichier indique en premier argument de execve(), et arg... est la serie de mots vers lesquels pointe l'argument argv de execve(), a partir de argv[1]. Remarquez qu'il n'y a aucune maniere d'obtenir argv[0] passe a l'appel execve(). Pour etre portable, argument-optionnel doit soit etre absent, soit etre un seul mot (c'est-a-dire ne pas contenir d'espace) ; consultez les NOTES ci-dessous. Depuis Linux 2.6.28, le noyau autorise l'interpreteur de script a etre lui-meme un script. Cette autorisation est recursive jusqu'a quatre niveaux, pour qu'un interpreteur puisse etre interprete par un script qui est interprete par un script, et ainsi de suite. Limites sur la taille des parametres et d'environnement La plupart des implementations UNIX imposent des limites sur la taille totale des chaines des parametres des lignes de commande (argv) et de l'environnement (envp) qui peuvent etre passees a un nouveau programme. POSIX.1 permet a une implementation d'annoncer cette limite en utilisant la constante ARG_MAX (soit definie dans , soit disponible a l'execution en utilisant l'appel sysconf(_SC_ARG_MAX)). Avant Linux 2.6.23, la memoire utilisee pour stocker les chaines d'environnements et d'arguments etait limitee a 32 pages (definie par la constante noyau MAX_ARG_PAGES). Sur les architectures dont la taille de page est 4 Ko, cela donne un maximum de 128 Ko. Sur Linux 2.6.23 et ulterieurs, la plupart des architectures ont une limite de taille derivee de la limite de ressources souple RLIMIT_STACK (consultez getrlimit(2)) qui est en vigueur au moment de l'appel a execve() (ce n'est pas le cas pour les architectures sans unite de gestion memoire : elles conservent la limite des noyaux anterieurs a Linux 2.6.23). Ce changement permet aux programmes d'avoir une liste de parametres ou un environnement beaucoup plus grand. Pour ces architectures, la taille totale est limitee a 1/4 de la taille de pile permise (imposer une limite de 1/4 permet d'assurer que le nouveau programme garde de l'espace pour la pile). De plus, la taille totale est limitee a 3/4 de la valeur de la constante _STK_LIM du noyau (8 Mio). Depuis Linux 2.6.25, le noyau place une limite inferieure de 32 pages a cette limite de taille, de telle sorte que meme si RLIMIT_STACK est tres faible, il est garanti aux applications qu'elles auront au moins autant de place pour les parametres et leur environnement que ce qui etait fourni par Linux 2.6.23 et les precedents (cette garantie n'etait pas presente dans Linux 2.6.23 et 2.6.24). De plus, la limite par chaine est de 32 pages (la constante noyau MAX_ARG_STRLEN), et le nombre maximal de chaines est de 0x7FFFFFFF. VALEUR RENVOYEE En cas de reussite, execve() ne renvoie rien, en cas d'echec il renvoie -1 et errno est positionne pour indiquer l'erreur. ERREURS E2BIG Le nombre total d'octets dans l'environnement (envp) et la liste d'arguments sont trop longs, une chaine d'argument ou d'environnement est trop longue ou le pathname complet de l'executable est trop long. L'octet NULL terminal est compte comme faisant partie de la longueur de la chaine. EACCES Le droit de parcours est refuse pour un des composants du prefixe du chemin pathname ou du nom d'un interpreteur de script (consultez aussi path_resolution(7)). EACCES Le fichier ou l'interpreteur de script n'est pas un fichier regulier. EACCES L'autorisation d'execution est refusee pour le fichier, ou un interpreteur de script, ou un interpreteur ELF. EACCES Le systeme de fichiers est monte avec l'option noexec. EAGAIN (depuis Linux 3.1) Ayant modifie son UID reel avec un des appels set*uid(), l'appelant etait - et est toujours -- au-dela de sa limite de ressources RLIMIT_NPROC (consultez setrlimit(2)). Pour une explication plus precise de cette erreur, consultez NOTES. EFAULT pathname ou l'un des pointeurs du vecteur argv ou envp pointe en dehors de l'espace d'adressage accessible. EINVAL Un executable ELF a plusieurs segments PT_INTERP (indique plusieurs interpreteurs). EIO Une erreur d'entree-sortie s'est produite. EISDIR L'interpreteur ELF cite est un repertoire. ELIBBAD L'interpreteur ELF mentionne n'est pas dans un format connu. ELOOP Trop de liens symboliques rencontres dans la resolution de pathname ou du nom de l'interpreteur de script ou ELF. ELOOP La limite maximale de niveaux a ete atteinte pendant l'interpretation recursive du script (voir << scripts interpreteurs >> ci-dessus). Avant Linux 3.8 l'erreur generee dans ce cas etait ENOEXEC. EMFILE La limite du nombre de descripteurs de fichiers par processus a ete atteinte. ENAMETOOLONG nom_chemin est trop long. ENFILE La limite du nombre total de fichiers ouverts pour le systeme entier a ete atteinte. ENOENT Aucun fichier pathname ou interpreteur de script ou ELF n'existe. ENOEXEC Le fichier executable n'est pas dans le bon format, ou est destine a une autre architecture. ENOMEM La memoire disponible du noyau n'etait pas suffisante. ENOTDIR Un composant du prefixe du chemin de pathname ou de l'interpreteur de script ou ELF n'est pas un repertoire. EPERM Le systeme de fichiers est monte avec l'attribut nosuid, l'utilisateur n'est pas le superutilisateur et le fichier a un bit set-user-ID ou set-group-ID positionne. EPERM Le processus est suivi avec ptrace(2), l'utilisateur n'est pas le superutilisateur, et le fichier a un bit set-user-ID ou set-group-ID positionne. EPERM Une application << capability-dumb >> n'obtiendrait pas toutes les capacites rendues possibles par le fichier executable. Voir capabilities(7). ETXTBSY L'executable specifie etait ouvert en ecriture par un ou plusieurs processus. VERSIONS POSIX ne documente pas le comportement de << #! >> mais celui-ci existe (avec quelques variations) sur d'autres systemes UNIX. Sur Linux, argv et envp peuvent etre indiques comme NULL. Dans les deux cas, cela a le meme effet que de specifier un argument comme un pointeur sur une liste contenant un seul pointeur NULL. N'en profitez pas pour faire des choses non standard et non portables !. Sur de nombreux autres systemes UNIX, specifier argv comme NULL donnera une erreur (EFAULT). D'autres systemes UNIX traitent le cas envp==NULL comme Linux. POSIX.1 indique que les valeurs renvoyees par sysconf(3) ne doivent pas changer pendant la vie d'un processus. Cependant, depuis Linux 2.6.23, si la limite de ressources RLIMIT_STACK change, alors la valeur renvoyee par _SC_ARG_MAX changera egalement, pour refleter le fait que la limite de l'espace qui recoit les parametres de la ligne de commande et les variables d'environnement a change. Scripts d'interpreteur Le noyau impose une longueur maximale de texte apres les caracteres << #! >> au debut du script ; les caracteres au-dela de cette limite sont ignores. Avant Linux 5.1, la limite etait de 127 caracteres. Depuis Linux 5.1, elle est de 255 caracteres. La semantique de l'argument-optionnel d'un script differe selon les implementations. Sous Linux, la chaine qui suit le nom de l'interpreteur est passee a l'interpreteur comme un seul mot, et cette chaine peut contenir des espaces. Cependant, le comportement est different sur d'autres systemes. Certains utilisent la premiere espace comme fin de l'argument-optionnel. Sur certains systemes, un script peut avoir plusieurs arguments, delimites par des espaces dans argument-optionnel. Linux (comme la plupart des autres systemes UNIX modernes) ignore les bits set-user-ID et set-group-ID sur les scripts. STANDARDS POSIX.1-2008. HISTORIQUE POSIX.1-2001, SVr4, 4.3BSD. Avec UNIX V6, la liste des arguments d'un appel exec() se terminait par 0, alors que la liste des arguments de main se terminait par -1. Aussi, cette liste d'arguments n'etait pas utilisable directement dans un appel exec() supplementaire. Depuis UNIX V7, les deux terminateurs sont NULL. NOTES On pourrait parfois voir execve() (et les fonctions associees decrites dans exec(3)) decrit comme un << executeur de nouveau processus >> (ou equivalent). C'est une description tres trompeuse : il n'y a pas de nouveau processus ; beaucoup d'attributs du processus appelant demeurent inchanges (en particulier son PID). Tout ce que fait execve() est de s'organiser pour qu'un processus existant (le processus appelant) execute un nouveau programme. Les processus set-user-ID et set-group-ID ne peuvent pas etre suivis par ptrace(2). Le resultat d'un montage de systeme de fichiers avec l'attribut nosuid peut varier suivant les versions du noyau Linux : certaines refuseront l'execution des fichiers set-user-ID et set-group-ID lorsque cela donnerait a l'appelant des privileges qu'il n'a pas (et renverront l'erreur EPERM), d'autres ignoreront simplement les bits set-user-ID et set-group-ID mais accepteront d'effectuer l'appel exec(). Dans la plupart des cas ou execve() echoue, le controle renvoie vers l'image executable d'origine et l'appelant de execve() peut alors traiter l'erreur. Cependant, dans de (rares) cas (typiquement provoques par un epuisement de ressources), l'echec pourrait se produire apres le point de non-retour : l'image executable d'origine a ete supprimee, mais la nouvelle image n'a pas pu etre construite completement. Dans ces cas-la, le noyau tue le processus avec un signal SIGSEGV (SIGKILL jusqu'a Linux 3.17). execve() et EAGAIN Une explication plus detaillee de l'erreur EAGAIN qui peut se produire (depuis Linux 3.1) en appelant execve() est comme suit. L'erreur EAGAIN peut se produire quand un appel precedent de setuid(2), setreuid(2) ou setresuid(2) a cause la modification de l'identifiant d'utilisateur reel du processus et que cette modification a force le processus a depasser sa limite de ressources RLIMIT_NPROC (c'est-a-dire que le nombre de processus appartenant au nouvel UID reel depasse la limite de ressources). De Linux 2.6.0 a Linux 3.0, cela provoquait un echec de l'appel set*uid() (avant Linux 2.6, la limite de ressources n'etait pas imposee sur les processus qui modifiaient leurs identifiants d'utilisateur). Depuis Linux 3.1, le scenario precedemment decrit ne provoque plus un echec de l'appel set*uid(), parce que cela avait trop souvent pour consequence des trous de securite quand les applications boguees ne verifiaient pas l'etat de retour et assumait que -- si l'appelant avait les droits du superutilisateur -- l'appel reussirait toujours. A la place, les appels set*uid() modifient vraiment l'UID reel, mais le noyau definit un attribut interne, appele PF_NPROC_EXCEEDED, pour noter que la limite de ressources RLIMIT_NPROC a ete depassee. Si l'attribut PF_NPROC_EXCEEDED est defini et que la limite de ressources est toujours depassee au moment d'un appel execve() ulterieur, cet appel echoue avec l'erreur EAGAIN. Cette logique du noyau assure que la limite de ressources RLIMIT_NPROC est toujours respectee pour le mode de fonctionnement habituel du demon avec droits -- c'est-a-dire fork(2) + set*uid() + execve(). Si la limite de ressources n'etait pas encore depassee au moment de l'appel execve() (parce que d'autres processus appartenant a cet UID reel se sont termines entre l'appel set*uid() et l'appel execve()), alors l'appel execve() reussit et le noyau efface l'attribut de processus PF_NPROC_EXCEEDED. L'attribut est aussi efface si un appel ulterieur de fork(2) par ce processus reussit. EXEMPLES Le programme suivant est concu pour etre execute par le second programme ci-dessous. Il se contente d'afficher les parametres de sa ligne de commande, un par ligne. /* myecho.c */ #include #include int main(int argc, char *argv[]) { for (size_t j = 0; j < argc; j++) printf("argv[%zu]: %s\n", j, argv[j]); exit(EXIT_SUCCESS); } Ce programme peut etre utilise pour executer le programme donne comme argument de ligne de commande : /* execve.c */ #include #include #include int main(int argc, char *argv[]) { static char *newargv[] = { NULL, "hello", "world", NULL }; static char *newenviron[] = { NULL }; if (argc != 2) { fprintf(stderr, "Usage: %s \n", argv[0]); exit(EXIT_FAILURE); } newargv[0] = argv[1]; execve(argv[1], newargv, newenviron); perror("execve"); /* execve() ne s'interrompt qu'en cas d'erreur */ exit(EXIT_FAILURE); } On peut utiliser le second programme pour executer le premier de la facon suivante : $ cc myecho.c -o myecho $ cc execve.c -o execve $ ./execve ./myecho argv[0]: ./myecho argv[1]: hello argv[2]: world On peut aussi utiliser ces programmes pour montrer l'utilisation d'un interpreteur de scripts. Pour ce faire, on cree un script dont l'<< interpreteur >> est notre programme myecho : $ cat > script #!./myecho script-arg ^D $ chmod +x script On peut alors utiliser notre programme pour executer le script : $ ./execve ./script argv[0]: ./myecho argv[1]: script-arg argv[2]: ./script argv[3]: hello argv[4]: world VOIR AUSSI chmod(2), execveat(2), fork(2), get_robust_list(2), ptrace(2), exec(3), fexecve(3), getauxval(3), getopt(3), system(3), capabilities(7), credentials(7), environ(7), path_resolution(7), ld.so(8) 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 1 novembre 2023 execve(2)