execve(2) System Calls Manual execve(2)

execve - voer programma uit

Standard C bibliotheek (libc, -lc)

#include <unistd.h>
int execve(const char *padnaam, char *const argv[],
           char *const _NULL_baarenvp[]);

execve() voert het programma uit waar padnaam naar wijst. Dit zorgt er voor dat het programma dan momenteel wordt uitgevoerd door het aanroepende proces wordt vervangen door een nieuw programma, met een niet-geïnitialiseerde stack, heap en (geïnitialiseerde en niet-geïnitialiseerde) data segmenten.

padnaam moet ofwel een binair uitvoerbaar bestand zijn, ofwel een script dat begint met een regel van de vorm:


#!interpreter[optioneel-arg]

Voor details over het laatste geval, zie "Interpreter scripts" hieronder.

argv is een lijst van wijzers naar tekenreeksen die worden doorgegeven naar een nieuw programma als zijn commando-regel argumenten. Per conventie zou de eerste van deze tekenreeksen (m.a.w. argv[0]) de bestandsnaam moeten zijn van het bestand dat uitgevoerd wordt. De argv lijst moet worden afgesloten met een NULL wijzer. (Daarom, zal in het nieuwe programma, argv[argc] NULL zijn.)

envp is een lijst van wijzers naar tekenreeksen, bij conventie van de vorm key=value, die worden doorgegeven als omgeving van een nieuw programma. De envp lijst moet worden afgesloten met een NULL wijzer.

Deze handleiding beschrijft de Linux systeem aanroep in detail; voor een overzicht van de nomenclatuur en de vele, vaak te prefereren, gestandaardiseerde varianten van de functie voorzien in libc, inclusief die de PATH omgevingsvariabele doorzoeken, zie exec(3).

De argumenten vector en omgeving kunnen worden benaderd door de main-functie van het nieuwe programma, mits deze is gedefinieerd als:


int main(int argc, char *argv[], char *envp[])

Let evenwel op dat het gebruik van het derde argument van de main-functie niet gespecificeerd is in POSIX.1; volgens POSIX.1, zou de omgeving moeten worden benaderd via de externe variabele environ(7).

Bij succes keert execve() niet terug, en de text, geïnitialiseerde data, niet-geinitialiseerde data (bss) en stack van het aanroepende proces worden overschreven volgens de inhoud van het nieuw geladen programma.

Als het huidige programma momenteel wordt ge-ptrace´ed, dan wordt er een SIGTRAP signaal naar verstuurd na een succesvolle execve() aanroep .

Als het set-user-ID bit is gezet op het programma bestand aangewezen door padnaam, dan wordt het effectieve gebruiker ID van het aanroepende proces veranderd naar dat van de eigenaar van het programma bestand. Vergelijkbaar, is het set-group-ID bit is gezet op het programma bestand, dan wordt het effectieve group ID van het aanroepende proces gezet op de groep van het programma bestand.

De hiervoor genoemde transformaties van de effectieve ID worden niet uitgevoerd (m.a.w. de set-user-ID en set-group-ID bits worden genegeerd) als een van de volgende waar is:

het no_new_privs attribuut wordt gezet voor de aanroepende thread (zie prctl(2));
het onderliggende bestandssysteem is gekoppeld nosuid (de MS_NOSUID vlag voor mount(2)); of
het aanroepende proces wordt ge-ptrace´d.

De capaciteiten van het programma bestand (zie capabilities(7) worden ook genegeerd als enig van de bovenstaande waar is.

Het effectieve gebruiker ID van het proces wordt gekopieerd naar het bewaarde set-user-ID; vergelijkbaar, het effectieve groep ID wordt gekopieerd naar het bewaarde set-group-ID. Het kopiëren vindt plaats na effectieve ID veranderingen die optreden vanwege de set-user-ID en set-group-ID modus bits.

De echte UID en echte GID van het proces, zowel als zijn supplementaire groep ID´s blijven onveranderd door de aanroep van execve().

Als het uitvoerbaar bestand een a.out dynamisch gelinked binair uitvoerbaar bestand is dat ook shared-library stompjes bevat, dan wordt de Linux dynamische linker ld.so(8) aangeroepen na de aanvang van uitvoering, om zo de benodigde gedeelde objecten in het geheugen te laden en het uitvoerbaar bestand hiermee te linken.

Als de executable een dynamisch gelinkte ELF executable is, dan wordt de interpreter genoemd in het PT_INTERP segment gebruikt om de benodigde gedeelde objecten te laden. Deze interpreter is typisch /lib/ld-linux.so.2 voor binaire bestanden gelinked met glibc (zie ld-linux.so(8)).

Alle proces attributen blijven behouden tijdens een execve(), behalve de volgende:

De disposities van elk signaal dat wordt gevangen wordt gereset naar het standaard (signal(7)).
Alle afwisselende signaal stacks blijven niet behouden (sigaltstack(2)).
Geheugen indelingen blijven niet behouden (mmap(2)).
Aangekoppelde System V gedeelde geheugen segmenten worden los gekoppeld (shmat(2)).
POSIX gedeelde geheugen regionen worden vrijgegeven (shm_open(3)).
Open POSIX berichtrij beschrijvingen worden gesloten (mq_overview(7)).
Alle open POSIX genaamde semaforen worden gesloten (sem_overview(7)).
POSIX timers blijven niet behouden (timer_create(2)).
Alle open map streams worden gesloten (opendir(3)).
Geheugen sloten blijven niet behouden mlock(2), mlockall(2)).
Beëindiging afhandelaren blijven niet behouden (atexit(3), on_exit(3)).
De drijvende-komma omgeving wordt gereset naar de standaard (zie fenv(3)).

De proces attributen in de voorafgaande lijst zijn alle gespecificeerd in POSIX.1. De volgende Linux-specifieke proces attributen blijven niet behouden tijdens een execve():

Het "dumpable" attribuut van het proces is gezet op de waarde 1, behalve als een set-user-ID programma, of een programma met capaciteiten wordt uitgevoerd, in welk geval de "dumpable" vlag mag worden gereset op de waarde in /proc/sys/fs/suid_dumpable, in de omstandigheden zoals beschreven in PR_SET_DUMPABLE in prctl(2). Merk op dat veranderingen in het ¨dumpable" attribuut ervoor kunnen zorgen dat het eigenaarschap van de bestanden in de /proc/pid map van het proces kunnen worden veranderd naar root:root, zoals beschreven in proc(5).
De prctl(2) PR_SET_KEEPCAPS vlag wordt gewist.
(Vanaf Linux 2.4.36 / 2.6.23) Als een set-user-ID of set-user-ID programma wordt uitgevoerd, dan wordt het dood signaal van de ouder, gezet door de prctl(2) PR_SET_PDEATHSIG vlag, gewist.
De proces naam, zoals gezet door prctl(2) PR_SET_NAME (en getoond door ps -o comm), wordt gereset naar de naam van het nieuwe uitvoerbare bestand.
De SECBIT_KEEP_CAPS securebits vlag wordt gewist. Zie capabilities(7).
Het beëindiging signaal wordt gereset naar SIGCHLD (zie clone(2)).
De bestandsindicator tabel is niet meer gedeeld, daarbij het effect van de CLONE_FILES vlag van clone(2) ongedaan makend.

Let op de volgende punten:

Alle thread anders dan de aanroepende thread worden vernietigd tijdens een execve(). Mutexes, conditie variabelen, en andere pthreads objecten blijven niet behouden.
Het equivalent van setlocale(LC_ALL, "C") wordt uitgevoerd bij de start van een programma.
POSIX.1 specificeert dat de disposities van enig signaal dat wordt genegeerd of gezet op de standaard onveranderd zullen blijven. POSIX.1 specificeer een uitzondering: als SIGCHLD wordt genegeerd, dan mag de implementatie deze dispositie onveranderd laten of deze op de standaard zetten; Linux doet het eerste.
Alle uitstaande asynchrone Invoer/Uitvoer operaties worden beëindigd (aio_read(3), aio_write(3)).
Voor de behandeling van capaciteiten tijdens execve(), zie capabilities(7).
Standaard blijven bestandsindicatoren open langs een execve(). Bestandsindicatoren die zijn gemarkeerd als close-on-exec worden gesloten; die de beschrijving van FD_CLOEXEC in fcntl(2). (Als een bestandsindicator wordt gesloten, dan zal dit de vrijgave van alle locks op het onderliggende bestand door dit proces veroorzaken. Zie fcntl(2) voor details.) POSIX.1 zegt dat als de bestandsindicatoren 0, 1 en 2 ook zouden worden gesloten na een succesvolle execve(), en het proces zou privileges verkrijgen omdat het set-user-ID of set-group-ID bit werd gezet om het uitgevoerde bestand, dan mag het systeem een niet gespecificeerd bestand voor elk van deze bestandsindicatoren openen. Als een algemeen principe kan een niet-overdraagbaar programma, al dan niet geprivilegieerd, aannemen dat deze drie bestandsindicatoren gesloten blijven langs een execve().

Interpreter scripts

Een interpreter script is een tekst bestand met de uitvoer permissie aan gezet en wiens eerste regel van de vorm is:


#!interpreter[optioneel-arg]

De interpreter moet een geldige padnaam voor een uitvoerbaar bestand zijn.

Als het padnaam argument van execve() een interpreter script specificeert dan zal de interpreter worden aangeroepen met de volgende argumenten:


interpreter  [optional-arg] padnaam arg...

waar padnaam de padnaam van het bestand dat als eerste argument werd gespecificeerd van execve(), en arg... is een serie woorden aangewezen door het argv argument van execve() beginnende bij argv[1]. Merk op dat er geen manier is om argv[0] te verkrijgen die doorgegeven werd naar de execve() aanroep.

Voor overdraagbaar gebruik, zou optional-arg afwezig moeten zijn of moeten worden gespecificeerd als een enkel woord (m.a.w. het zou geen witruimtes moeten bevatten); zie OPMERKINGEN hieronder.

Vanaf Linux 2.6.28 staat de kernel toe dat de interpreter van een script, zelf een script is. Dit recht is recursief, tot aan een limiet van vier recursies, zodat de interpreter een script mag zijn dat wordt geïnterpreteerd door een script, en zo voorts.

De meeste UNIX implementaties stellen een limiet aan de totale lengte van het argument op de commando regel (argv) en de omgevings (envp) tekenreeksen die mogen worden doorgegeven aan een nieuw programma. POSIX.1 staat een implementatie toe deze limiet te adverteren gebruikmakend van de ARG_MAX constante (ofwel gedefinieerd in <limits.h> ofwel beschikbaar tijdens de uitvoering door gebruik te maken van de aanroep sysconf(_SC_ARG_MAX)).

Voor Linux 2.6.23, was het geheugen dat gebruikt wordt om de omgeving en de argument tekenreeksen op te slaan beperkt tot 32 pagina´s (gedefinieerd in de kernel constante MAX_ARG_PAGES). Bij architecturen met een 4-kB pagina grootte, dit geeft een maximum grootte van 128 kB.

Op Linux 2.6.23 en later, ondersteunen de meeste architecturen een grootte limiet afgeleid van de zachte RLIMIT_STACK hulpbron limiet (zie getrlimit(2)) die geldig is te tijde van de execve() aanroep. (Architecturen zonder geheugen management unit zijn uitgezonderd: deze behouden de limiet die geldig was voor Linux 2.6.23.) Deze verandering staat programma´s toe een veel grotere argument en/of omgevings lijst te hebben. Voor deze architecturen, is de totale grootte beperkt tot 1/4 van de toegestane stack grootte. (Het opleggen van de 1/4-limiet verzekert dat een nieuw programma altijd enige stack ruimte heeft.) Daarenboven is de totale grootte beperkt tot 3/4 van de waarde van de constante _STK_LIM (8 MiB). Vanaf Linux 2.6.25, zet de kernel een ondergrens van 32 pagina´s o deze grootte limiet, zodat zelfs, wanneer RLIMIT_STACK erg laag is gezet, applicaties worden gegarandeerd dat ze op zijn minst evenveel argument en omgevingsruimte hebben zoals voorzien door Linux 2.6.22 en eerder. (Deze garantie werd niet voorzien in Linux 2.6.23 en 2.6.24.) Aanvullend: de limiet per tekenreeks is 32 pagina´s (de kernel constante MAX_ARG_STRLEN), en het maximum aantal tekenreeksen is 0x7FFFFFFF.

Bij succes keert execve() niet terug, bij falen wordt -1 teruggegeven, en wordt errno overeenkomstig gezet.

Het totaal aantal bytes in de omgeving (envp en de lijst van argumenten (argv) is te groot, een argument of omgeving tekenreeks is te lang, of de volledige padnaam van het uitvoerbaar bestand is te lang. Het afsluitende null-byte wordt mee geteld als een onderdeel van de lengte van de tekenreeks.
Zoek recht wordt geweigerd op een component van het pad voorvoegsel van padnaam of de naam van een script interpreter. Zie ook path_resolution(7).)
Het bestand of de script interpreter is geen normaal bestand.
Uitvoer toestemming werd geweigerd voor het bestand of een script of ELF interpreter.
Het bestandsysteem is gemount met noexec.
Ondanks dat de echte UID van de aanroeper is verandert door een van de set*uid() aanroepen, was en — is nog steeds— de aanroeper nog steeds boven zijn RLIMIT_NPROC resource limiet (zie setrlimit(2)). Voor een gedetailleerde verklaring van deze fout, zie OPMERKINGEN.
padnaam of een van de wijzers in de vectoren argv of envp wijst buiten door de, voor u, toegankelijke adres ruimte.
Een ELF uitvoerbaar bestand heeft meer dan 1 PT_INTERP segment (probeerde meer dan 1 interpreter te benoemen).
Een Invoer/Uitvoer fout trad op.
Een ELF interpreter was een map.
Een ELF interpreter had een onbekende vorm.
Teveel symbolische koppelingen werden tegengekomen bij het "oplossen" van padnaam of de naam van een script of ELF interpreter.
De maximale recursie limiet werd bereikt door uitvoeren van de recursieve script interpreter (zie "Interpreter scripts", hierboven) Voor Linux 3.8 werd de fout ENOEXEC geproduceerd voor dit geval.
De per-proces limiet van het aantal open bestandsbeschrijvingen werd bereikt.
padnaam is te lang.
De grens aan het aantal open bestanden van het systeem is bereikt.
Het bestand padnaam of een script of een ELF interpreter bestaat niet.
Een uitvoerbaar bestand is niet in een bekende vorm: het is voor de verkeerde architectuur, of heeft een of andere vormfout waardoor het niet kan worden uitgevoerd.
Onvoldoende kernelgeheugen voorhanden.
Een deel van het pad-voorvoegsel van padnaam of van een script of ELF interpreter is geen map.
Het bestandssysteem was gekoppeld met nosuid, de gebruiker is niet de supergebruiker, en het bestand heeft het set-user-ID of set-group-ID bit aan staan.
Het proces wordt gevolgd, de gebruiker is niet de supergebruiker en het bestand heeft een set-user-ID of set-group-ID bit aan staan.
Een "domme-capaciteiten" toepassing zou niet de volledige verzameling van toegestane capaciteiten die door het uitvoerbaar bestand zijn toegekend kunnen verkrijgen. Zie capaciteiten(7).
Uitvoerbaar bestand is open voor schrijven bij een of meer processen.

POSIX documenteert het #! gedrag niet, maar het bestaat (met enige variaties) op andere UNIX systems.

Op Linux kunnen argv en envp worden opgegeven als NULL. In beide gevallen heeft dat hetzelfde effect als het opgeven van het argument als een wijzer naar een lijst die een enkele NULL wijzer bevat. Maak geen gebruik van dit niet-standaard en neit-overdraagbaar gedrag. Op veel andere Linux systemen zal het opgeven van argv als NULL resulteren in een fout (EFAULT). Sommige andere Linux systemen behandelen het envp==NULL geval hetzelfde als Linux.

POSIX.1 zegt dat waarden zoals teruggeven worden door sysconf(3) niet mogen veranderen gedurende de levensduur van een proces. Hoewel vanaf Linux 2.6.23 de door _SC_ARG_MAX gerapporteerde waarde zal veranderen zodra de RLIMIT_STACK resource limiet veranderd. Dit om het feit aan te geve dan de limiet op de ruimte die de argumenten en de omgevingsvariabelen op commando regel in beslag nemen werd veranderd.

Interpreter scripts

De kernel beperkt de maximale lengte van de tekst die volgt op de "#!" lettertekens aan het begin van een script; lettertekens voorbij deze limiet worden genegeerd. Voor Linux 5.1 bedroeg deze limiet 127 tekens. Vanaf Linux 5.1 bedraagt de limiet 255 tekens.

De semantiek van het optional-arg argument van een interpreter script varieert per implementatie. Op Linux wordt de volledige tekenreeks die volgt op de interpreter naam doorgegeven als één enkel argument aan de interpreter, en mag deze tekenreeks witte spaties bevatten. Hoewel dit gedrag verschilt op enkelen andere systemen. Sommige systemen gebruiken de eerste witte spatie om optional-arg te beëindigen. Op sommige systemen kan een interpeter-script meerdere argumenten hebben, en kunnen witte spaties in optional-arg worden gebruikt om argumenten te onderscheiden.

Linux (net als andere moderne UNIX systemen) negeert de set-user-ID en set-group-ID bits bij scripts.

POSIX.1-2008.

POSIX.1-2001, SVr4, 4.3BSD.

Met UNIX V6, eindigde de argumenten lijst van een exec() aanroep op 0, terwijl de argumenten lijst van main eindigde op -1. Daarom was deze argumenten lijst niet meteen bruikbaar in een volgende exec() aanroep. Vanaf UNIX V7, zijn beiden NULL.

Soms ziet men execve() (en de gerelateerde functies beschreven in in exec(3)) beschreven als "uitvoeren van een nieuw proces" (of vergelijkbaar). Dit is een hoogst misleidende beschrijving: er is geen nieuw proces; veel attributen van het aanroepende proces blijven onveranderd (in het bijzonder, zijn PID). Het enige dat execve() doet is het regelen dat een bestand proces (het aanroepende proces) een nieuw program kan uitvoeren.

Set-user-ID en set-group-ID processen kunnen niet ptrace(2)d zijn.

Het resultaat van het aankoppelen van een bestandssysteem nosuid varieert van Linux kernel versies: sommigen weigeren het uitvoeren van set-user-ID en set-group-ID uitvoerbare bestanden als dat zou betekenen dat de gebruiker rechten zou krijgen die hij eerder niet had (en EPERM retourneren), sommige zullen de set-user-ID en set-group-ID bits negeren en exec() succesvol uitvoeren,

In de meeste gevallen waar execve() faalt, wordt controle naar de originele uitvoerbare afbeelding teruggegeven, en kan de aanroeper van execve() de foutmelding afhandelen. Hoewel, in (zeldzame) gevallen (typisch veroorzaakt door uitputting van hulpbronnen) kan een fout optreden voorbij het punt waarbij terugkeer niet mogelijk is: de oorspronkelijk uitvoerbare afbeelding is al afgebroken, maar de nieuwe nog niet compleet gebouwd. In deze gevallen, stopt de kernel het proces met een SIGSEGV (SIGKILL tot Linux 3.17) signaal.

Een meer gedetailleerde uitleg van de EAGAIN fout die kan optreden (vanaf Linux 3.1) bij het aanroepen van execve() is als volgt.

De EAGAIN fout kan optreden wanneer een voorafgaande aanroep van setuid(2), setreuid(2), of setresuid(2) ervoor zorgde dat het echte user ID van het proces veranderde, en dat deze verandering er voor zorgde dat het proces zijn RLIMIT_NPROC hulpbron limiet overschreed (m.a.w. het aantal processen behorende bij de nieuwe echte UID overschreed de hulpbron limiet). Van Linux 2.6.0 tot 3.0 zorgde dit voor het falen van de set* uid() aanroep. (Voor 2.6, werd deze resource limiet niet opgelegd op processen die hun user ID´s veranderden.)

Vanaf Linux 3.1 veroorzaakt het zojuist beschreven scenario het falen van de set*uid() aanroep niet langer, omdat het te vaak leidde tot veiligheids gaten waar foutieve applicaties niet de teruggeven status controleerden en aannamen dat—als de aanroeper systeembeheerder rechten had—de aanroep altijd zou slagen. In plaats daarvan verandert de set*uid() aanroep nu succesvol de echte UID maar zet de kernel een interne vlag, PF_NPROC_EXCEEDED genaamd, op aan te geven dat de limiet van de RLIMIT_NPROC hulpbron overschreden werd. Als de PF_NPROC_EXCEEDED vlag werd gezet en de hulpbron limiet is nog steeds overschreden op het tijdstip van een volgende execve() aanroep, dan zal deze aanroep falen met de fout EAGAIN. Deze kernel logica verzekert dat de RLIMIT_NPROC hulpbron limiet afgedwongen blijft voor de gewoonlijke geprivilegieerde workflow– namelijk fork(2) + set*uid() + execve().

Indien de hulpbron limiet nog niet overschreden was ten tijde van de execve() aanroep (omdat andere processen behorende bij dit echte UID werden beëindigd tussen de set*uid() aanroep en de execve() aanroep), dan is de execve() aanroep succesvol en wist de kernel de PF_NPROC_EXCEEDED proces vlag. Deze vlag wordt ook gewist als een volgende aanroep van fork(2) door dit proces succesvol is.

Het volgende programma is ontworpen om te worden uitgevoerd door het tweede programma daaronder. Het echo´d de commando-regel argumenten, een per regel.


/* 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);
}

Dit programma kan worden gebruikt om het op de commando regel genoemde programma uit te voeren:


/* 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() stopt alleen bij een fout */
    exit(EXIT_FAILURE);
}

We kunnen het tweede programma om het eerste, als volgt, uit te voeren


$ cc myecho.c -o myecho
$ cc execve.c -o execve
$ ./execve ./myecho
argv[0]: ./myecho
argv[1]: hello
argv[2]: world

We kunnen deze programma´s ook gebruiken om het gebruik van ee script interpreter te demonstreren. Om dit te doen creëren wij een script wiens "interpreter' on myecho programma is:


$ cat > script
#!./myecho script-arg
^D
$ chmod +x script

We kunnen nu ons programma gebruiken om het script uit te voeren:


$ ./execve ./script
argv[0]: ./myecho
argv[1]: script-arg
argv[2]: ./script
argv[3]: hello
argv[4]: world

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)

De Nederlandse vertaling van deze handleiding is geschreven door Jos Boersema <joshb@xs4all.nl>, Mario Blättermann <mario.blaettermann@gmail.com> en Luc Castermans <luc.castermans@gmail.com>

Deze vertaling is vrije documentatie; lees de GNU General Public License Version 3 of later over de Copyright-voorwaarden. Er is geen AANSPRAKELIJKHEID.

Indien U fouten in de vertaling van deze handleiding zou vinden, stuur een e-mail naar debian-l10n-dutch@lists.debian.org.

2 mei 2024 Linux man-pages 6.8