43 votes

Comment détecter les problèmes éventuels / potentiels de débordement de pile dans un programme c / c++ ?

Existe-t-il un moyen standard de voir combien d'espace de pile votre application a et quel est le filigrane le plus élevé pour l'utilisation de la pile pendant une exécution ?

Et dans le cas redouté d'un débordement réel, que se passe-t-il ?

Est-ce qu'il se bloque, déclenche une exception ou un signal ? Existe-t-il une norme ou est-elle différente sur tous les systèmes et compilateurs ?

Je recherche spécifiquement Windows, Linux et Macintosh.

0 votes

0 votes

"Je recherche spécifiquement Windows, Linux et Macintosh" - assez précis alors :)

16voto

jussij Points 7391

Sur Windows un débordement de pile exception sera généré.

Le code Windows suivant illustre cela :

#include <stdio.h>
#include <windows.h>

void StackOverFlow()
{
  CONTEXT context;

  // we are interested control registers
  context.ContextFlags = CONTEXT_CONTROL;

  // get the details
  GetThreadContext(GetCurrentThread(), &context);

  // print the stack pointer
  printf("Esp: %X\n", context.Esp);

  // this will eventually overflow the stack
  StackOverFlow();
}

DWORD ExceptionFilter(EXCEPTION_POINTERS *pointers, DWORD dwException)
{
  return EXCEPTION_EXECUTE_HANDLER;
}

void main()
{
  CONTEXT context;

  // we are interested control registers
  context.ContextFlags = CONTEXT_CONTROL;

  // get the details
  GetThreadContext(GetCurrentThread(), &context);

  // print the stack pointer
  printf("Esp: %X\n", context.Esp);

  __try
  {
    // cause a stack overflow
    StackOverFlow();
  }
  __except(ExceptionFilter(GetExceptionInformation(), GetExceptionCode()))
  {
    printf("\n****** ExceptionFilter fired ******\n");
  }
}

Lorsque cet exe est exécuté, la sortie suivante est générée :

Esp: 12FC4C
Esp: 12F96C
Esp: 12F68C
.....
Esp: 33D8C
Esp: 33AAC
Esp: 337CC

****** ExceptionFilter fired ******

0 votes

Corrigez-moi si je me trompe, car je ne suis pas sûr. Votre code illustre le cas simple. Mais si la pile déborde temporairement dans le tas et qu'elle est traitée à nouveau, cela ne déclenchera pas toujours l'exception de débordement car ce mécanisme de détection est exécuté dans un autre thread.

0 votes

Je ne suis certainement pas un expert, mais j'aurais pensé qu'une exception de pile est générée lorsqu'une demande de déplacement du pointeur de pile a pour résultat que ce pointeur fait référence à une mémoire non valide. Les variables sur la pile peuvent corrompre la pile mais je ne pense pas que cela provoque une exception de débordement de pile.

0 votes

Dans l'exemple que j'ai posté, chaque appel à la fonction StackOverFlow fait avancer le pointeur de la pile (comme le montre l'impression) et finalement ce pointeur atteint une mémoire invalide.

16voto

adl Points 7294

Sous Linux, vous obtenez une erreur de segmentation si votre code essaie d'écrire au-delà de la pile.

La taille de la pile est une propriété héritée entre les processus. Si vous pouvez la lire ou la modifier dans le shell à l'aide de commandes telles que ulimit -s (en sh , ksh , zsh ) ou limit stacksize ( tcsh , zsh ).

A partir d'un programme, la taille de la pile peut être lue en utilisant

#include <sys/resource.h>
#include <stdio.h>

int main() {
    struct rlimit l;
    getrlimit(RLIMIT_STACK, &l);
    printf("stack_size = %ld\n", l.rlim_cur);
    return 0;
}

Je ne connais pas de méthode standard pour obtenir la taille de la pile disponible.

La pile commence par argc suivi du contenu de argv et une copie de l'environnement, puis vos variables. Cependant, comme le noyau peut randomiser l'emplacement du début de la pile, et qu'il peut y avoir des valeurs factices au-dessus de argc il serait erroné de supposer que vous avez l.rlim_cur octets disponibles ci-dessous &argc .

Une façon de récupérer l'emplacement exact de la pile est de regarder le fichier /proc/1234/maps (où 1234 est l'ID du processus de votre programme). Une fois que vous connaissez ces limites, vous pouvez calculer la quantité de votre pile utilisée en regardant l'adresse de la dernière variable locale.

0 votes

Je ne crois pas qu'il existe un moyen standard d'obtenir la taille de la pile disponible. La norme définit-elle même l'existence d'une pile ?

0 votes

Je viens de regarder la norme C et en effet, elle n'utilise même pas le mot pile . C'est amusant. Il fait la distinction entre statique , automatique o alloué Cependant, je n'ai pas trouvé d'endroit où il serait suggéré que l'appel d'une fonction peut échouer en raison de contraintes de mémoire.

0 votes

@GregD il y a un moyen indirect 1. obtenir la taille maximale de la pile 2. obtenir la taille actuelle de la pile 3. faire A-B

11voto

Kknd Points 1244

Gcc place un bloc de mémoire supplémentaire entre l'adresse de retour et les variables normales dans les appels de fonctions "non sécurisées", comme (dans cet exemple, la fonction est void test() {char a[10] ; b[20]} :

call stack:
-----------
return address
dummy
char b[10]
char a[20]

Si la fonction écrit 36 octets dans le pointeur "a", le dépassement de capacité "corrompra" l'adresse de retour (violation possible de la sécurité). Mais il changera également la valeur du 'dummy', qui se trouve entre le pointeur et l'adresse de retour, et le programme se plantera avec un avertissement (vous pouvez désactiver cela avec un -fno-stack-protector).

6voto

deemok Points 2137

Sous Windows, la pile (pour un thread spécifique) croît à la demande jusqu'à ce que la taille de la pile spécifiée pour ce thread avant sa création soit atteinte.

La croissance à la demande est stimulée par l'utilisation de pages de garde, en ce sens qu'il n'y a qu'un fragment de pile disponible au départ, suivi d'une page de garde qui, lorsqu'elle est atteinte, déclenche une exception - cette exception est spéciale et est gérée par le système pour vous - la gestion augmente l'espace de pile disponible (également vérifié si une limite a été atteinte !) et l'opération de lecture est relancée.

Une fois la limite atteinte, il n'y a plus de croissance, ce qui entraîne une exception de débordement de pile. La base et la limite actuelles de la pile sont stockées dans le bloc d'environnement du thread, dans une structure appelée _NT_TIB (bloc d'information sur les fils). Si vous avez un débogueur à portée de main, voici ce que vous voyez :

0:000> dt ntdll!_teb @$teb nttib.
   +0x000 NtTib  : 
      +0x000 ExceptionList : 0x0012e030 _EXCEPTION_REGISTRATION_RECORD
      +0x004 StackBase : 0x00130000 
      +0x008 StackLimit : 0x0011e000 
      +0x00c SubSystemTib : (null) 
      +0x010 FiberData : 0x00001e00 
      +0x010 Version : 0x1e00
      +0x014 ArbitraryUserPointer : (null) 
      +0x018 Self   : 0x7ffdf000 _NT_TIB

L'attribut StackLimit sera mis à jour à la demande. Si vous vérifiez les attributs de ce bloc mémoire, vous verrez quelque chose de similaire :

0:000> !address 0x0011e000 
    00030000 : 0011e000 - 00012000
                    Type     00020000 MEM_PRIVATE
                    Protect  00000004 PAGE_READWRITE
                    State    00001000 MEM_COMMIT
                    Usage    RegionUsageStack
                    Pid.Tid  abc.560

Et le fait de vérifier une page à côté révèle l'attribut de garde :

0:000> !address 0x0011e000-1000
    00030000 : 0011d000 - 00001000
                    Type     00020000 MEM_PRIVATE
                    Protect  00000104 PAGE_READWRITE | PAGE_GUARD
                    State    00001000 MEM_COMMIT
                    Usage    RegionUsageStack
                    Pid.Tid  abc.560

J'espère que cela vous aidera.

4voto

Rob Walker Points 25840

Le débordement de pile est probablement le type d'exception le plus difficile à gérer, car votre gestionnaire d'exception doit traiter une quantité minimale de pile (généralement une seule page est réservée à cet effet).

Pour une discussion intéressante sur les difficultés à gérer ce type d'exception, consultez ces articles de blog : 1 y 2 de Chris Brumme qui se concentrent sur la question du point de vue de .NET, en particulier sur l'hébergement du CLR.

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