Comment un" débordement de pile " se produit-il et comment l'empêchez-vous?

comment un débordement de pile se produit-il et quelles sont les meilleures façons de s'assurer qu'il ne se produise pas, ou les façons d'en empêcher un, en particulier sur les serveurs web, mais d'autres exemples seraient également intéressants?

67
demandé sur Kapol 2008-08-25 18:49:29

9 réponses

Pile

une pile, dans ce contexte, est le dernier in, premier out buffer que vous placez des données pendant que votre programme exécute. Dernier entré, premier sorti (LIFO) signifie que la dernière chose que vous mettez en est toujours la première chose que vous obtenez de retour - si vous appuyez sur 2 éléments sur la pile, 'A' et 'B', alors la première chose que vous enlevez la pile sera "B", et la prochaine chose est 'Un'.

Lorsque vous appelez une fonction dans votre code, l'instruction suivante après la fonction l'appel est stocké sur la pile, et tout espace de stockage qui pourrait être écrasé par l'appel de fonction. La fonction que vous appelez peut utiliser plus de pile pour ses propres variables locales. Quand il est fait, il libère l'espace de la variable locale de pile qu'il a utilisé, puis retourne à la fonction précédente.

débordement de la cheminée

un débordement de pile est quand vous avez utilisé plus de mémoire pour la pile que votre programme était censé utiliser. Dans les systèmes embarqués, vous ne pouvez avoir 256 octets pour la pile, et si chaque fonction prend 32 octets, alors vous ne pouvez avoir que des appels de fonction 8 deep-function 1 calls function 2 who calls function 3 who calls function 4 .... qui appelle la fonction 8 qui appelle la fonction 9, mais la fonction 9 écrase la mémoire à l'extérieur de la pile. Cela pourrait écraser la mémoire, le code, etc.

beaucoup de programmeurs font cette erreur en appelant la fonction A qui appelle ensuite la fonction B, qui appelle ensuite la fonction C, qui appelle ensuite la fonction A. It peut-travail la plupart du temps, mais une fois la saisie erronée va l'amener à aller dans ce cercle pour toujours jusqu'à ce que l'ordinateur reconnaît que la pile est exagérée.

fonctions récursives sont également une cause pour cela, mais si vous écrivez récursivement (c'est-à-dire, votre fonction appelle lui-même), alors vous devez être conscient de cela et utiliser des variables statiques/globales pour empêcher la récursion infinie.

généralement, le système D'exploitation et le langage de programmation que vous utilisez pile, et il est hors de vos mains. Vous devriez regarder votre graphique d'appel (une structure d'arbre qui montre de votre main ce que chaque fonction appelle) pour voir la profondeur de vos appels de fonction vont, et pour détecter les cycles et la récursion qui ne sont pas prévus. Les cycles intentionnels et la récursion doivent être artificiellement vérifiés pour faire une erreur s'ils s'appellent l'un l'autre trop souvent.

au-delà des bonnes pratiques de programmation, des tests statiques et dynamiques, il n'y a pas grand chose que vous pouvez faire sur ces système.

systèmes Embarqués

dans le monde embarqué, en particulier dans le code de haute fiabilité (Automobile, Avion, espace) vous faites des revues de code et de vérification approfondies, mais vous faites aussi ce qui suit:

  • Interdire la récursivité et les cycles de l'exécution par la politique et les tests
  • conserver le code et la pile très éloignés les uns des autres (code en flash, pile en RAM, et jamais les deux doivent respecter)
  • Placez des bandes de garde autour de la pile - zone vide de mémoire que vous remplissez avec un nombre magique (habituellement une instruction d'interruption de logiciel, mais il y a beaucoup d'options ici), et des centaines ou des milliers de fois par seconde vous regardez les bandes de garde pour s'assurer qu'ils n'ont pas été écrasés.
  • utiliser la protection de mémoire (c.-à-d., pas d'exécution sur la pile, pas de lecture ou d'écriture juste à l'extérieur de la pile)
  • les Interruptions de ne pas appeler les fonctions secondaires - ils définir des indicateurs, copie données, et laisser l'application s'occuper de son traitement (sinon vous pourriez obtenir 8 profondément dans votre arbre d'appel de fonction, avoir une interruption, et puis sortir quelques autres fonctions à l'intérieur de l'interruption, provoquant la panne). Vous avez plusieurs appels arbres pour les principaux processus, et un pour chaque interruption. Si vos interruptions peuvent s'interrompre... eh bien, il y a des dragons...

les langages de Haut niveau et des systèmes

mais en haut niveau les langues fonctionnent sur les systèmes d'exploitation:

  • réduisez votre stockage de variables locales (les variables locales sont stockées sur la pile-bien que les compilateurs soient assez intelligents à ce sujet et vont parfois mettre de grands locaux sur le tas si votre arbre d'appel est peu profond)
  • éviter ou limiter strictement la récursion
  • ne divisez pas vos programmes trop loin en petites et petites fonctions - même sans compter les variables locales chaque fonction l'appel consomme jusqu'à 64 octets sur la pile (processeur 32 bits, économisant la moitié des registres CPU, drapeaux, etc)
  • Gardez votre arborescence des appels à faible profondeur (similaire à la déclaration ci-dessus)

serveurs Web

cela dépend du "bac à sable" que vous avez, Si vous pouvez contrôler ou même voir la pile. Les Chances sont bonnes, vous pouvez traiter les serveurs web comme vous le feriez n'importe quel autre langage de haut niveau et le système d'exploitation - il est largement hors de votre mains, mais vérifiez la langue et la pile de serveur que vous utilisez. Il est possible de souffler la pile sur votre serveur SQL, par exemple.

- Adam

90
répondu Adam Davis 2008-08-26 00:37:24

un débordement de pile en code réel se produit très rarement. La plupart des situations dans lesquelles elle se produit sont des récursions où la fin a été oubliée. Cela peut toutefois se produire rarement dans des structures très imbriquées, par exemple des documents XML particulièrement volumineux. La seule vraie aide ici est de reformuler le code pour utiliser un objet stack explicite au lieu de la pile d'appels.

8
répondu Konrad Rudolph 2008-08-25 14:55:01

la récursion infinie est un moyen courant d'obtenir une erreur de débordement de pile. Pour empêcher - toujours s'assurer qu'il y a un chemin de sortie que sera touché . :- )

une autre façon d'obtenir un débordement de pile (en C/C++, au moins) est de déclarer une variable énorme sur la pile.

char hugeArray[100000000];

ça ira.

6
répondu Matt Dillard 2008-08-25 14:55:10

la plupart des gens vous diront qu'un débordement de pile se produit avec une récursion sans chemin de sortie - alors que la plupart du temps vrai, si vous travaillez avec des structures de données assez grandes, même un chemin de sortie de récursion approprié ne vous aidera pas.

quelques options dans ce cas:

6
répondu Greg Hurlman 2008-08-25 15:00:35

habituellement, un débordement de pile est le résultat d'un appel récursif infini (étant donné la quantité habituelle de mémoire dans les ordinateurs standards de nos jours).

lorsque vous faites un appel à une méthode, une fonction ou une procédure, la manière "standard" ou faire l'appel consiste en:

  1. en Poussant le retour de la direction pour l'appel dans la pile(c'est la phrase suivante après l'appel)
  2. habituellement l'espace pour la valeur de retour obtenir réservé dans la pile
  3. Pousser chaque paramètre dans la pile (de l'ordre diverge et dépend du compilateur, aussi certains d'entre eux sont parfois stockées sur les registres du CPU pour améliorer les performances)
  4. fait l'appel réel.

ainsi, Généralement cela prend quelques octets en fonction du nombre et du type des paramètres ainsi que de l'architecture de la machine.

vous verrez alors que si vous commencez à faire des appels récursifs la pile va commencer à croître. Maintenant, la pile est généralement réservé à la mémoire de telle manière qu'il pousse dans la direction opposée à la tas, car un grand nombre d'appels sans "retour" de la pile commence à faire le plein.

maintenant, sur les temps plus anciens le débordement de pile pourrait se produire simplement parce que vous avez épuisé toute la mémoire disponible, juste comme cela. Avec le modèle de mémoire virtuelle (jusqu'à 4 Go sur un système X86) qui était hors de portée donc habituellement, si vous obtenez un erreur de débordement de pile, recherchez un appel récursif infini.

4
répondu Jorge Córdoba 2008-08-25 15:01:22

un débordement de pile se produit lorsque Jeff et Joel veulent donner au monde un meilleur endroit pour obtenir des réponses à des questions techniques. Il est trop tard pour empêcher ce débordement de pile. Cet "autre site" aurait pu le prévenir en n'étant pas étourdi. ;)

4
répondu Haacked 2008-08-25 16:28:12

mis à part la forme de débordement de pile que vous obtenez d'une récursion directe (par exemple Fibonacci(1000000) ), une forme plus subtile de celui-ci que j'ai expérimenté plusieurs fois est une récursion indirecte, où une fonction appelle une autre fonction, qui appelle une autre, et puis une de ces fonctions appelle la première à nouveau.

cela peut se produire dans des fonctions qui sont appelées en réponse à des événements, mais qui elles-mêmes peuvent générer de nouveaux événements, par exemple:

void WindowSizeChanged(Size& newsize) {
  // override window size to constrain width
    newSize.width=200;
    ResizeWindow(newSize);
}

dans ce cas, l'appel à ResizeWindow peut déclencher à nouveau le WindowSizeChanged() , qui appelle ResizeWindow à nouveau, jusqu'à ce que vous soyez à court de pile. Dans des situations comme celles-ci, vous devez souvent reporter la réponse à l'événement jusqu'à ce que le cadre de la pile soit revenu, par exemple en affichant un message.

3
répondu the_mandrill 2013-05-22 11:52:52

quoi? Personne n'aime ceux qui sont enfermés dans une boucle infinie?

do
{
  JeffAtwood.WritesCode();
} while(StackOverflow.MakingMadBank.Equals(false));
3
répondu Ian Patrick Hughes 2017-11-21 00:20:45

considérant que ceci a été étiqueté avec "hacking", je soupçonne que le" stack overflow " auquel il fait référence est un overflow de pile d'appel, plutôt qu'un overflow de pile de niveau supérieur comme ceux mentionnés dans la plupart des autres réponses ici. Il ne s'applique pas vraiment à tous les environnements gérés ou interprétés tels que .NET, Java, Python, Perl, PHP, etc, dans lesquels les applications web sont typiquement écrites, donc votre seul risque est le serveur web lui-même, qui est probablement écrit en C ou C++.

contrôle de ce fil:

https://stackoverflow.com/questions/7308/what-is-a-good-starting-point-for-learning-buffer-overflow

2
répondu Steve M 2017-05-23 12:34:26