Erreur de segmentation
Une erreur de segmentation (en anglais segmentation fault, en abrégé segfault) est un plantage d'une application qui a tenté d'accéder à un emplacement mémoire qui ne lui était pas alloué.
Les applications, lorsqu'elles s'exécutent, ont besoin de mémoire vive, allouée par le système d'exploitation. Une fois allouée à l'application, aucune autre application ne peut avoir accès à cette zone ; cela garantit une sûreté de fonctionnement pour chaque application contre les erreurs des autres. Ainsi, si une application tente le moindre accès à une zone mémoire qui ne lui est pas allouée, le système d'exploitation le détecte et stoppe immédiatement son exécution.
La très grande majorité des erreurs de segmentation ne sont pas volontaires (si elles le sont, il y a de fortes chances que cela soit dans un but malicieux) ; elles sont dues à une mauvaise conception ou réalisation de l'application.
Exemples
Lorsqu'un programme s'exécute, le système d'exploitation lui alloue de la mémoire. Mais il arrive qu'au cours de son exécution, pour ses besoins de traitements, l'application ait besoin de mémoire supplémentaire. Elle demande alors au système d'exploitation de lui allouer une certaine quantité de mémoire. C'est ensuite à la charge de l'application d'utiliser cette mémoire et de faire attention à ne pas écrire ou lire en dehors de la zone mémoire allouée.
Tout le problème est de bien savoir où l'on se trouve lorsque l'on utilise cette mémoire. Et c'est à ce moment-là que si l'on n'y prend pas garde, on déborde de la mémoire et l'application se termine. C'est ce que l'on nomme un dépassement de tampon.
Une autre cause d'erreur de segmentation est la mauvaise initialisation d'un pointeur. Ce dernier pointe alors sur une zone mémoire quelconque et lorsqu'on l'utilise, il y a de forte chances pour que ce dernier contienne une adresse qui n'est pas allouée à l'application. Et comme précédemment, c'est une erreur de segmentation que le système d'exploitation génère.
Pour éviter tout problème avec les pointeurs, il est nécessaire de les initialiser avec une adresse valide ou à NULL (le code devra le traiter correctement) avant toute utilisation.
Programmes produisant une erreur de segmentation
Voici des exemples de programmes en C qui peuvent produire une telle erreur.
Exemple avec une variable
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int *variable_entiere;
scanf("%d", variable_entiere);
return EXIT_SUCCESS;
}
La fonction scanf cherche à récupérer un entier sur l'entrée standard (par défaut, le clavier) et stocke cette valeur dans une variable. Pour pouvoir y stocker la donnée, scanf a besoin de connaître l'adresse de la variable (dans notre cas : variable_entiere).
Or, dans notre cas, la valeur de variable_entiere
n'est pas initialisée et a donc une valeur quelconque. La fonction scanf tente alors d'accéder à la zone mémoire représentée par la valeur contenue dans variable_entiere
et provoquera fort probablement une erreur de segmentation.
Autre Exemple
int main(void)
{
int *pointeur, valeur;
valeur = 3;
*pointeur = valeur * valeur;
return EXIT_SUCCESS;
}
Le problème ici est que l'on veut stocker le résultat de l'opération valeur * valeur
et y avoir accès par un pointeur (nommé ici pointeur
). Or, un pointeur n'allouant aucun espace mémoire autre que pour lui-même, on ne peut lui affecter une valeur tant qu'il ne pointe pas sur un espace mémoire correctement alloué (comme une variable) sans risquer de provoquer une erreur de segmentation car un pointeur non initialisé pointe à un endroit aléatoire de la mémoire.
Une solution est de créer une seconde variable (nommée ici resultat
) vers laquelle pointe le pointeur :
int main(void)
{
int *pointeur, valeur, resultat;
pointeur = &resultat;
valeur = 3;
*pointeur = valeur * valeur; /* maintenant la variable resultat contient 9 */
return EXIT_SUCCESS;
}
Une autre solution consisterait à l'allocation dynamique de mémoire c'est-à -dire de demander au système d'exploitation de la mémoire et de faire pointer le pointeur sur ce nouvel espace mémoire :
#include <stdlib.h>
int main(void)
{
int *pointeur, valeur;
pointeur = malloc(sizeof(*pointeur));
if (pointeur == NULL) /* La fonction malloc() retourne NULL en cas d'Ă©chec. */
return EXIT_FAILURE;
valeur = 3;
*pointeur = valeur * valeur;
free(pointeur); /* On libère la mémoire. */
return EXIT_SUCCESS;
}
Valeur renvoyée
Lors d'une erreur de segmentation, l'erreur donnée par un shell est 139
: la fin du processus étant causée par un signal, le shell ajoute 128
au code du signal[1]. SIGSEGV
valant 11
[2], l'erreur reçue est 139
.
Notes et références
- « bash(1): GNU Bourne-Again SHell - Linux man page », sur linux.die.net (consulté le )
- « signal(7) - Linux manual page », sur man7.org (consulté le )