67 votes

Que se passe-t-il lorsque la pile et le tas entrent en collision ?

Je suis curieux de savoir ce qui se passe lorsque la pile et le tas entrent en collision. Si quelqu'un a déjà rencontré ce problème, il peut m'expliquer le scénario.

8 votes

Je suis presque sûr que ça arrive quand on ne peut pas arrêter la douleur à l'intérieur.

1 votes

Si vous êtes à court de mémoire de pile et de mémoire de tas, une exception OutOfMemoryException pourrait être levée ?

22 votes

Je ne suis pas d'accord avec les votes serrés (NaRQ). La situation décrite est réel, est important dans les petits systèmes, même aujourd'hui, est mauvais, et est qui mérite d'être compris.

74voto

Norman Ramsey Points 115730

Dans un langage moderne fonctionnant sur un système d'exploitation moderne, vous obtiendrez soit un débordement de pile (hourra !), soit un message d'erreur. malloc() o sbrk() o mmap() échouera quand vous essayerez de faire croître le tas. Mais tous les logiciels ne sont pas modernes, alors examinons les modes d'échec :

  • Si la pile se développe dans le tas, le compilateur typiquement C commencera silencieusement à écraser les structures de données du tas. Sur un système d'exploitation moderne, il existe une ou plusieurs mémoires virtuelles. pages de garde qui empêchent la pile de croître indéfiniment. Tant que la quantité de mémoire dans les pages de garde est au moins aussi grande que la taille de l'enregistrement d'activation de la procédure en croissance, le système d'exploitation vous garantit un défaut de segmentation. Si vous êtes un DOS fonctionnant sur une machine sans MMU, vous êtes probablement fichu.

  • Si le tas s'étend sur la pile, le système d'exploitation doit toujours être conscient de la situation et une sorte d'appel système échouera. L'implémentation de malloc() remarque presque certainement l'échec et retourne NULL . Ce qui se passe ensuite dépend de vous.

Je suis toujours étonné par la volonté des auteurs de compilateurs d'espérer que le système d'exploitation mette en place des pages de garde pour éviter les débordements de pile. Bien sûr, cette astuce fonctionne bien jusqu'à ce que vous commenciez à avoir des milliers de threads, chacun avec sa propre pile...

2 votes

Même avec un seul thread, il est possible d'obtenir une collision pile-tapis non détectée : gcc.gnu.org/ml/gcc-help/2014-07/msg00076.html (bien qu'il soit assez peu probable que cela se produise en pratique, sauf peut-être en cas d'attaques spécifiques). La page de manuel de GCC conseille d'utiliser l'option -fstack-check avec les programmes multithreads (et ce drapeau permet également de détecter la collision dans mon exemple).

0 votes

@vinc17 Ceci est également détecté avec -fsanitise=thread -g (le -g est d'ajouter des numéros de ligne). Le programme s'exécute de 2 à 20 fois plus lentement, mais il détecte un grand nombre de problèmes de threading-safety. Également détectés avec -fsanitise=address . Pour chaque problème, il affiche le numéro de ligne exact où il s'est produit. L'exécution du programme d'exemple à partir du lien indiqué par @vinc17 sous Ubuntu 18.04 + gcc 9.3.0 ne révèle aucun problème : le programme correct se termine immédiatement avec un segmentation fault même si elle est compilée avech -O3 . Ajout de la -fsanitise=X (ci-dessus) révèle la ligne de code exacte.

1 votes

@Contango Avec mon test effectué avec GCC 4.9.1 en 2014-07, le programme s'est terminé avec succès. Maintenant, j'obtiens un défaut de segmentation pendant le GETADDR(c) avant son printf même avec GCC 4.6.4 (publié en 2013-04). Il semble donc que quelque chose ait changé dans le système pour permettre une protection rapide contre la collision pile-heap, indépendamment de GCC (binutils ? noyau Linux ?). Je serais intéressé de le savoir. Les choses ont peut-être aussi changé dans GCC, mais plus tard (2017+), de sorte que GCC 4.6.4 n'est pas concerné : gcc.gnu.org/legacy-ml/gcc-patches/2017-06/msg01343.html

43voto

Jim Dennis Points 5454

Cela dépendrait de la plate-forme. Sur de nombreuses plateformes, cela ne peut pas se produire du tout (le tas et la pile sont alloués dans des pages différentes et les deux ne se rencontreront jamais).

Gardez à l'esprit que l'idée de la croissance du tas vers le haut et de la pile vers le bas n'est que conceptuelle. Sur les très petits systèmes (comme les anciens micros 8 bits qui exécutaient CP/M) et sur certains PIC et autres systèmes à modèle de mémoire plat (ceux qui n'ont pas de MMU ni aucun autre support de mémoire virtuelle ou protégée), le tas et la pile pourraient être réellement implémentés de cette façon. Dans ce cas, le comportement serait indéfini ... mais il y aurait presque certainement un crash dès que le code essaierait de retourner à une adresse au sommet de la pile corrompue ou de suivre un pointeur indirect d'une partie du tas à une autre ou ...

En tout cas, vous ne le verrez pas sur une station de travail ou un serveur moderne à usage général. Vous atteindrez une limite de ressources et obtiendrez des échecs de mallocs, ou vous vous heurterez à la mémoire virtuelle et le système finira par se transformer en un tas tremblant de "hit the red switch".

11voto

Paul Dixon Points 122033

Dans des moments comme ceux-là, il est temps de se tourner vers les sages paroles du Dr Egon Spengler.....

  • Dr. Egon Spengler : Il y a quelque chose de très important que j'ai oublié de vous dire.
  • Dr. Peter Venkman : Quoi ?
  • Dr. Egon Spengler : Ne laissez pas le tas entrer en collision avec la pile.
  • Dr. Peter Venkman : Pourquoi ?
  • Dr. Egon Spengler : Ce serait mauvais.
  • Dr. Peter Venkman : Je suis un peu confus sur l'ensemble "bon/mauvais" chose ici. Qu'est-ce que vous voulez dire, "mauvais" ?
  • Dr. Egon Spengler : Essayez d'imaginer que toute vie telle que vous la connaissez s'arrête instantanément et que chaque molécule de votre corps explose à la vitesse de la lumière.
  • Dr. Ray Stantz : Inversion protonique totale !
  • Dr. Peter Venkman : C'est mauvais. Ok. Très bien, conseil de sécurité important. Merci, Egon.

1 votes

Bizarrement, la phrase à laquelle je pensais était "chiens et chats vivant ensemble".

0 votes

Belle illustration. Espérons que universed -debug libère toute sa mémoire !

7voto

Preet Sangha Points 39414

Si vous avez de la chance, vous obtenez une exception de mémoire ou de pile. Si vous êtes malchanceux, le programme se dirige vers une mémoire invalide et lève une exception de mauvaise mémoire. Si vous êtes extrêmement malchanceux, le programme continue et détruit quelque chose qu'il ne devrait pas et vous ne saurez jamais pourquoi votre programme a échoué.

Enfin, bien sûr, l'univers peut se fissurer.

2 votes

+1 L'univers MS-DOS a craqué mais nous n'avons pas encore vu le boom

1 votes

Notez que le dépassement de pile ne peut pas être une exception C++.

0 votes

@atorras - L'univers MSDOS ne craquera pas tant qu'ils n'auront pas arraché la commande for de mes mains mortes :-)

2voto

Sachin Points 345

Vous obtiendrez un défaut de segmentation ou une erreur d'allocation de mémoire si un dépassement de pile/heap se produit. Voici un exemple :

void recursiveFun ()
{
    static int i;
//  char *str= (char *)malloc (100);
    printf ("%d\t", i++);
    recursiveFun ();
// free (str);
}

Supposons que vous appelez la fonction ci-dessus, il n'y aura plus de pile et le programme se plantera. Maintenant, supprimez les lignes commentées et appelez la fonction à nouveau, vous constaterez que le défaut de segmentation se produit en moins de temps et moins de récursion que la version précédente. [ Dans mon environnement de test, le Stack Overflow s'est produit après 5237765 récursions dans le premier cas, alors que dans le second scénario, il s'est produit après 2616325 récursions].

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X