#include <unistd.h>
int execve(const char *pathname, char *const _Nullable argv[],
char *const _Nullable envp[]);
pathname doit être soit un exécutable binaire, soit un script qui commence par une ligne sous la forme :
#!interpréteur [argument-optionnel]
Pour des détails sur ce dernier cas, consultez « Scripts d’interpréteur » ci-dessous.
argv est un tableau de pointeurs vers des chaînes passées au nouveau programme en tant qu'arguments de la ligne de commande. Par convention, la première de ces chaînes (à savoir argv[0]) devrait contenir le nom de fichier associé au fichier étant exécuté. 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 chaînes, ayant par convention la forme clé=valeur, qui sont passés au nouveau programme en tant qu'environnement. Le tableau envp dois se terminer par un pointeur NULL.
Cette page de manuel décrit l'appel système Linux en détail ; pour un aperçu de la nomenclature et des nombreuses variantes, souvent préférables et standardisées 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 à la fonction principale du nouveau programme quand elle est définie ainsi :
int main(int argc, char *argv[], char *envp[])
Notez, cependant, que l'utilisation d'un troisième argument dans la fonction principale n'est pas spécifiée dans POSIX.1, selon laquelle l'environnement doit être accessible par la variable externe environ(7).
En cas de réussite, execve() ne renvoie rien et les segments de texte, les données initialisées et non initialisées (« bss »), ainsi que la pile du processus appelant sont remplacés par ceux du programme chargé.
Si l'on effectuait un ptrace(2) sur le programme actuel, un signal SIGTRAP lui est envoyé après la réussite de execve().
Si le bit set-user-ID est positionné sur le fichier du programme auquel renvoie pathname, l'ID de l'utilisateur effectif du processus appelant passe à celui du propriétaire du programme. De même, lorsque le bit set-group-ID est positionné sur le fichier du programme, l'ID du groupe effectif du processus appelant est modifié pour correspondre à celui du groupe du fichier.
Les transformations précitées des ID effectifs ne sont pas effectuées (c'est-à-dire que les bits set-user-ID et set-group-ID sont ignorés) si un des éléments suivants est vrai :
Les capacités du fichier du programme (voir capabilities(7)) sont également ignorées si un des éléments ci-dessus est vrai.
L'UID effectif du processus est copié dans le set-user-ID sauvegardé ; de la même manière, le GID effectif est copié dans le set-group-ID sauvegardé. Ces copies ont lieu après toute modification d'ID effectif à cause des bits de permission set-user-ID et set-group-ID.
L'UID et le GID réel du processus ainsi que ses ID de groupe complémentaires ne sont pas modifiés par un appel à execve().
Si l'exécutable est un fichier binaire a.out lié dynamiquement, et contenant des appels aux bibliothèques partagées, l'éditeur de liens dynamiques de Linux ld.so(8) est appelé au début de l'exécution, afin de charger les bibliothèques partagées nécessaires en mémoire et d'effectuer l'édition des liens de l'exécutable avec eux.
Si l'exécutable est au format ELF lié dynamiquement, l'interpréteur indiqué dans le segment PT_INTERP sera invoqué pour charger les bibliothèques partagées. Cet interpréteur est généralement /lib/ld-linux.so.2 pour les fichiers binaires liés avec la glibc (voir ld-linux.so(8)).
Les attributs de processus listés ci-dessus sont spécifiés dans POSIX.1. Les attributs de processus spécifiques à Linux suivants sont également réinitialisés lors d'un execve() :
Notez également les points suivants :
#!interpréteur [argument-optionnel]
L’interpréteur doit être le chemin valable d'un fichier exécutable.
Si l'argument pathname de execve() indique un script interprété, l'interpréteur sera appelé avec les arguments suivants :
interpréteur [argument-optionnel] pathname arg...
où pathname est le chemin du fichier indiqué en premier argument de execve(), et arg... est la série de mots vers lesquels pointe l'argument argv de execve(), à partir de argv[1]. Remarquez qu'il n'y a aucune manière d'obtenir argv[0] passé à l'appel execve().
Pour être portable, argument-optionnel doit soit être absent, soit être un seul mot (c'est-à-dire ne pas contenir d'espace) ; consultez les NOTES ci-dessous.
Depuis Linux 2.6.28, le noyau autorise l'interpréteur de script à être lui-même un script. Cette autorisation est récursive jusqu'à quatre niveaux, pour qu'un interpréteur puisse être interprété par un script qui est interprété par un script, et ainsi de suite.
Avant Linux 2.6.23, la mémoire utilisée pour stocker les chaînes d'environnements et d'arguments était limitée à 32 pages (définie 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 ultérieurs, la plupart des architectures ont une limite de taille dérivée de la limite de ressources souple RLIMIT_STACK (consultez getrlimit(2)) qui est en vigueur au moment de l'appel à execve() (ce n'est pas le cas pour les architectures sans unité de gestion mémoire : elles conservent la limite des noyaux antérieurs à Linux 2.6.23). Ce changement permet aux programmes d'avoir une liste de paramètres ou un environnement beaucoup plus grand. Pour ces architectures, la taille totale est limitée à 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 limitée à 3/4 de la valeur de la constante _STK_LIM du noyau (8 Mio). Depuis Linux 2.6.25, le noyau place une limite inférieure de 32 pages à cette limite de taille, de telle sorte que même si RLIMIT_STACK est très faible, il est garanti aux applications qu'elles auront au moins autant de place pour les paramètres et leur environnement que ce qui était fourni par Linux 2.6.23 et les précédents (cette garantie n'était pas présente dans Linux 2.6.23 et 2.6.24). De plus, la limite par chaîne est de 32 pages (la constante noyau MAX_ARG_STRLEN), et le nombre maximal de chaînes est de 0x7FFFFFFF.
Sur Linux, argv et envp peuvent être indiqués comme NULL. Dans les deux cas, cela a le même effet que de spécifier 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 systèmes UNIX, spécifier argv comme NULL donnera une erreur (EFAULT). D'autres systèmes UNIX traitent le cas envp==NULL comme Linux.
POSIX.1 indique que les valeurs renvoyées 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 renvoyée par _SC_ARG_MAX changera également, pour refléter le fait que la limite de l'espace qui reçoit les paramètres de la ligne de commande et les variables d'environnement a changé.
La sémantique de l'argument-optionnel d'un script diffère selon les implémentations. Sous Linux, la chaîne qui suit le nom de l'interpréteur est passée à l'interpréteur comme un seul mot, et cette chaîne peut contenir des espaces. Cependant, le comportement est différent sur d'autres systèmes. Certains utilisent la première espace comme fin de l'argument-optionnel. Sur certains systèmes, un script peut avoir plusieurs arguments, délimités par des espaces dans argument-optionnel.
Linux (comme la plupart des autres systèmes UNIX modernes) ignore les bits set-user-ID et set-group-ID sur les scripts.
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'était pas utilisable directement dans un appel exec() supplémentaire. Depuis UNIX V7, les deux terminateurs sont NULL.
Les processus set-user-ID et set-group-ID ne peuvent pas être suivis par ptrace(2).
Le résultat d'un montage de système de fichiers avec l'attribut nosuid peut varier suivant les versions du noyau Linux : certaines refuseront l'exécution des fichiers set-user-ID et set-group-ID lorsque cela donnerait à l'appelant des privilèges 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 où execve() échoue, le contrôle renvoie vers l’image exécutable d’origine et l’appelant de execve() peut alors traiter l’erreur. Cependant, dans de (rares) cas (typiquement provoqués par un épuisement de ressources), l’échec pourrait se produire après le point de non-retour : l’image exécutable d’origine a été supprimée, mais la nouvelle image n’a pas pu être construite complètement. Dans ces cas-là, le noyau tue le processus avec un signal SIGSEGV (SIGKILL jusqu'à Linux 3.17).
L’erreur EAGAIN peut se produire quand un appel précédent de setuid(2), setreuid(2) ou setresuid(2) a causé la modification de l’identifiant d’utilisateur réel du processus et que cette modification a forcé le processus à dépasser sa limite de ressources RLIMIT_NPROC (c’est-à-dire que le nombre de processus appartenant au nouvel UID réel dépasse la limite de ressources). De Linux 2.6.0 à Linux 3.0, cela provoquait un échec de l’appel set*uid() (avant Linux 2.6, la limite de ressources n’était pas imposée sur les processus qui modifiaient leurs identifiants d’utilisateur).
Depuis Linux 3.1, le scénario précédemment décrit ne provoque plus un échec de l’appel set*uid(), parce que cela avait trop souvent pour conséquence des trous de sécurité quand les applications boguées ne vérifiaient pas l’état de retour et assumait que — si l’appelant avait les droits du superutilisateur — l’appel réussirait toujours. À la place, les appels set*uid() modifient vraiment l’UID réel, mais le noyau définit un attribut interne, appelé PF_NPROC_EXCEEDED, pour noter que la limite de ressources RLIMIT_NPROC a été dépassée. Si l’attribut PF_NPROC_EXCEEDED est défini et que la limite de ressources est toujours dépassée au moment d’un appel execve() ultérieur, cet appel échoue avec l’erreur EAGAIN. Cette logique du noyau assure que la limite de ressources RLIMIT_NPROC est toujours respectée pour le mode de fonctionnement habituel du démon avec droits — c’est-à-dire fork(2) + set*uid() + execve().
Si la limite de ressources n’était pas encore dépassée au moment de l’appel execve() (parce que d’autres processus appartenant à cet UID réel se sont terminés entre l’appel set*uid() et l’appel execve()), alors l’appel execve() réussit et le noyau efface l’attribut de processus PF_NPROC_EXCEEDED. L’attribut est aussi effacé si un appel ultérieur de fork(2) par ce processus réussit.
/* myecho.c */
#include <stdio.h>
#include <stdlib.h>
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 être utilisé pour exécuter le programme donné comme argument de ligne de commande :
/* execve.c */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int
main(int argc, char *argv[])
{
static char *newargv[] = { NULL, "hello", "world", NULL };
static char *newenviron[] = { NULL };
if (argc != 2) {
fprintf(stderr, "Usage: %s <file-to-exec>\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 exécuter le premier de la façon 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 interpréteur de scripts. Pour ce faire, on crée un script dont l'« interpréteur » est notre programme myecho :
$ cat > script #!./myecho script-arg haD $ chmod +x script
On peut alors utiliser notre programme pour exécuter le script :
$ ./execve ./script argv[0]: ./myecho argv[1]: script-arg argv[2]: ./script argv[3]: hello argv[4]: world
Cette traduction est une documentation libre ; veuillez vous reporter à la GNU General Public License version 3 concernant les conditions de copie et de distribution. Il n'y a aucune RESPONSABILITÉ LÉGALE.
Si vous découvrez un bogue dans la traduction de cette page de manuel, veuillez envoyer un message à