mprotect
Section: System Calls (2)
Updated: 5 février 2023
Index
Return to Main Contents
NOM
mprotect, pkey_mprotect - Définir la protection d'une partie de la mémoire
BIBLIOTHÈQUE
Bibliothèque C standard (libc, -lc)
SYNOPSIS
#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);
DESCRIPTION
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 :
- PROT_NONE
-
On ne peut pas accéder du tout à la zone de mémoire.
- PROT_READ
-
On peut lire la zone de mémoire.
- PROT_WRITE
-
On peut modifier la zone de mémoire.
- PROT_EXEC
-
La zone de mémoire peut contenir du code exécutable.
- PROT_SEM (depuis Linux 2.5.7)
-
La mémoire peut être utilisée pour des opérations atomiques. Cet attribut a
été introduit dans l'implémentation de futex(2) (afin de garantir la
possibilité d'effectuer des opérations atomiques exigées par des commandes
comme FUTEX_WAIT), mais il n'est actuellement utilisé sur aucune
architecture.
- PROT_SAO (depuis Linux 2.6.26)
-
La mémoire devrait avoir une forte organisation de son accès. Cette
fonctionnalité est spécifique à l'architecture PowerPC (la version 2.06 de
la spécification de l'architecture ajoute la fonction SAO du processeur,
disponible par exemple sur POWER 7 ou PowerPC A2).
En outre (depuis Linux 2.6.0), il est possible de positionner les attributs
suivants sur prot :
-
-
PROT_GROWSUP
Appliquer le mode de protection jusqu'à la fin d'une projection qui grandit
vers le haut (de telles projections sont créées pour la zone de la pile sur
une architecture — par exemple HP-PARISC — dont la pile a tendance à
s'accroître vers le haut).
- PROT_GROWSDOWN
-
Appliquer le mode de protection vers le bas jusqu'au début d'une projection
qui grandit vers le bas (il pourrait s'agir d'un segment de pile ou d'un
segment projeté avec un drapeau MAP_GROWSDOWN positionné).
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).
VALEUR RENVOYÉE
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.
ERREURS
- EACCES
-
L'accès spécifié n'est pas possible sur ce type de mémoire. Cela se produit
par exemple si vous utilisez mmap(2) pour représenter un fichier en
lecture seule en mémoire, et puis demandez de marquer cette zone avec
PROT_WRITE.
- EINVAL
-
addr n'est pas un pointeur valable, ou ce n'est pas un multiple de la
taille de page du système.
- EINVAL
-
(pkey_mprotect()) pkey n'a pas été alloué avec pkey_alloc(2)
- EINVAL
-
PROT_GROWSUP et PROT_GROWSDOWN étaient indiqués tous les deux dans
prot.
- EINVAL
-
Drapeaux non valables indiqués dans prot.
- EINVAL
-
(Architecture PowerPC) PROT_SAO était indiqué dans prot, mais la
fonctionnalité matérielle SAO n'est pas disponible.
- ENOMEM
-
Impossible d'allouer des structures internes au noyau.
- ENOMEM
-
Les adresses dans l'intervalle [addr, addr+len-1] ne sont pas
valables dans l'espace d'adressage du processus, ou l'intervalle s'étend sur
des pages non projetées (avant Linux 2.4.19, l'erreur EFAULT était
produite à tort dans ce cas).
- ENOMEM
-
La modification de la protection d'une zone de la mémoire ferait dépasser le
nombre maximal autorisé de projections avec des attributs différents (comme
la protection en lecture vs lecture/écriture) (par exemple, positionner une
protection d'une plage PROT_READ au milieu d'une zone protégée par
PROT_READ|PROT_WRITE donnerait trois projections : deux en
lecture/écriture aux extrémités et une en lecture seule au milieu).
VERSIONS
pkey_mprotect() est apparu dans Linux 4.9 ; la bibliothèque glibc le
gère depuis la version 2.27.
STANDARDS
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.
NOTES
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().
EXEMPLES
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
Source du programme
#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);
}
VOIR AUSSI
mmap(2), sysconf(3), pkeys(7)
TRADUCTION
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 à
Index
- NOM
-
- BIBLIOTHÈQUE
-
- SYNOPSIS
-
- DESCRIPTION
-
- VALEUR RENVOYÉE
-
- ERREURS
-
- VERSIONS
-
- STANDARDS
-
- NOTES
-
- EXEMPLES
-
- Source du programme
-
- VOIR AUSSI
-
- TRADUCTION
-
This document was created by
man2html,
using the manual pages.
Time: 06:05:30 GMT, May 23, 2024