mprotect(2) | System Calls Manual | mprotect(2) |
mprotect, pkey_mprotect - Définir la protection d'une partie de la mémoire
Bibliothèque C standard (libc, -lc)
#include <sys/mman.h>
int mprotect(void addr[.len], size_t len, int prot);
#define _GNU_SOURCE /* Consultez feature_test_macros(7) */ #include <sys/mman.h>
int pkey_mprotect(void addr[.len], size_t len, int prot, int pkey);
mprotect() change les protections d'accès pour la (les) page(s) de mémoire du processus appelant contenant tout ou une partie de l'intervalle [addr, addr+len-1]. addr doit être aligné sur une limite de page.
Si le processus appelant essaie d'accéder à la mémoire en violant la protection, le noyau génère un signal SIGSEGV pour ce processus.
prot est une combinaison des attributs d'accès suivants : PROT_NONE ou le résultat d’une opération OU bit à bit parmi les autres valeurs de la liste suivante :
En outre (depuis Linux 2.6.0), il est possible de positionner les attributs suivants sur prot :
Comme mprotect(), pkey_mprotect() modifie la protection des pages indiquées par addr et len. Le paramètre pkey indique la clé de protection (voir pkeys(7))) à assigner à la mémoire. La clé de protection doit être allouée avec pkey_alloc(2) avant d'être passée à pkey_mprotect(). Pour un exemple d'utilisation de cet appel système, voir pkeys(7).
mprotect() et pkey_mprotect() renvoient 0 s'ils réussissent. En cas d'erreur, ces appels système renvoient -1 et errno est défini pour indiquer l'erreur.
pkey_mprotect() est apparu dans Linux 4.9 ; la bibliothèque glibc le gère depuis la version 2.27.
mprotect() : POSIX.1-2001, POSIX.1-2008, SVr4. POSIX indique que le comportement de mprotect() n'est pas spécifié s'il s'applique à une zone de mémoire non obtenue à l'aide de mmap(2).
pkey_mprotect() est une extension Linux non portable.
Sous Linux, il est toujours autorisé d'appeler mprotect() sur une adresse de l'espace d'adressage du processus (excepté pour la zone vsyscall du noyau). En particulier, il peut être utilisé pour rendre une projection de code existante accessible en écriture.
La différence entre PROT_EXEC et PROT_READ dépend de l'architecture, de la version du noyau et de l'état du processus. Sur certaines, si READ_IMPLIES_EXEC est positionné dans les drapeaux de la personnalité d'un processus (voir personality(2)), le fait d'indiquer PROT_READ ajoutera implicitement PROT_EXEC.
Sur certaines architectures matérielles (comme i386), PROT_WRITE implique PROT_READ.
POSIX.1 indique qu'une implémentation peut autoriser un accès autre que celui donné dans prot, mais doit au minimum autoriser l'accès en écriture si PROT_WRITE était passé, et ne doit autoriser aucun accès si PROT_NONE était passé.
Les applications devraient faire attention quand elles mélangent l'utilisation de mprotect() et de pkey_mprotect(). Sur x86, quand mprotect() est utilisé avec prot positionné sur PROT_EXEC, une pkey peut être allouée et positionnée implicitement sur la mémoire par le noyau, mais uniquement quand la pkey était de 0 précédemment.
Sur les systèmes qui ne gèrent pas les clés de protection dans le matériel, pkey_mprotect() peut toujours être utilisé, mais pkey doit être positionné sur -1. Si elle est appelée ainsi, l'opération pkey_mprotect() est équivalente à mprotect().
Le programme ci-dessous montre l'utilisation de mprotect(). Il alloue quatre pages de mémoire, rend la troisième accessible en lecture seule, puis exécute une boucle qui se déplace en avançant dans la région allouée et en modifiant son contenu.
Voici un exemple d'exécution de ce programme :
$ ./a.out Début de la région : 0x804c000 Reçu SIGSEGV à l'adresse : 0x804e000
#include <malloc.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <sys/mman.h> #include <unistd.h> #define handle_error(msg) \
do { perror(msg); exit(EXIT_FAILURE); } while (0) static char *buffer; static void handler(int sig, siginfo_t *si, void *unused) {
/* Remarque : appeler printf() à partir d'un gestionnaire de signal
n'est pas sûr (vous ne devriez pas le faire dans des programmes en
production) car printf() n'est pas async-signal-safe ; voir
signal-safety(7). Cependant, nous utilisons printf() ici comme façon
simple de montrer que le gestionnaire a été appelé. */ static void
printf("Reçu SIGSEGV à l'adresse : %p\n", si->si_addr);
exit(EXIT_FAILURE); } int main(void) {
int pagesize;
struct sigaction sa;
sa.sa_flags = SA_SIGINFO;
sigemptyset(&sa.sa_mask);
sa.sa_sigaction = handler;
if (sigaction(SIGSEGV, &sa, NULL) == -1)
handle_error("sigaction");
pagesize = sysconf(_SC_PAGE_SIZE);
if (pagesize == -1)
handle_error("sysconf");
/* Allouer un tampon aligné sur une limite de page ;
la protection initiale est PROT_READ | PROT_WRITE. */
buffer = memalign(pagesize, 4 * pagesize);
if (buffer == NULL)
handle_error("memalign");
printf("Début de la région : %p\n", buffer);
if (mprotect(buffer + pagesize * 2, pagesize,
PROT_READ) == -1)
handle_error("mprotect");
for (char *p = buffer ; ; )
*(p++) = 'a';
printf("Boucle terminée\n"); /* Ne devrait jamais arriver */
exit(EXIT_SUCCESS); }
La traduction française de cette page de manuel a été créée par Christophe Blaess <https://www.blaess.fr/christophe/>, Stéphan Rafin <stephan.rafin@laposte.net>, Thierry Vignaud <tvignaud@mandriva.com>, François Micaux, Alain Portal <aportal@univ-montp2.fr>, Jean-Philippe Guérard <fevrier@tigreraye.org>, Jean-Luc Coulon (f5ibh) <jean-luc.coulon@wanadoo.fr>, Julien Cristau <jcristau@debian.org>, Thomas Huriaux <thomas.huriaux@gmail.com>, Nicolas François <nicolas.francois@centraliens.net>, Florentin Duneau <fduneau@gmail.com>, Simon Paillard <simon.paillard@resel.enst-bretagne.fr>, Denis Barbier <barbier@debian.org>, David Prévot <david@tilapin.org> et Jean-Philippe MENGUAL <jpmengual@debian.org>
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 à debian-l10n-french@lists.debian.org.
5 février 2023 | Pages du manuel de Linux 6.03 |