Le système d'exploitation réserve-t-il la quantité fixe d'espace virtuel valide pour la pile ou autre chose? Suis-je capable de produire un débordement de pile simplement en utilisant de grandes variables locales?
J'ai écrit un petit C
programme pour tester mon hypothèse. Il fonctionne sur X86-64 CentOS 6.5.
#include <string.h>
#include <stdio.h>
int main()
{
int n = 10240 * 1024;
char a[n];
memset(a, 'x', n);
printf("%x\n%x\n", &a[0], &a[n-1]);
getchar();
return 0;
}
L'exécution du programme donne &a[0] = f0ceabe0
et&a[n-1] = f16eabdf
Les cartes proc montrent la pile: 7ffff0cea000-7ffff16ec000. (10248 * 1024B)
J'ai ensuite essayé d'augmenter n = 11240 * 1024
L'exécution du programme donne &a[0] = b6b36690
et&a[n-1] = b763068f
Les cartes proc montrent la pile: 7fffb6b35000-7fffb7633000. (11256 * 1024B)
ulimit -s
imprime 10240
sur mon PC.
Comme vous pouvez le voir, dans les deux cas, la taille de la pile est plus grande que ce qui ulimit -s
donne. Et la pile grandit avec une plus grande variable locale. Le haut de la pile est en quelque sorte de 3 à 5 Ko de plus &a[0]
(AFAIK, la zone rouge est de 128B).
Alors, comment cette carte de pile est-elle allouée?
ulimit -s
donnant 10240, comme dans les conditions de l'OP, et j'obtiens un SIGSEGV comme prévu (c'est ce qui est requis par POSIX: "Si cette limite est dépassée, SIGSEGV doit être généré pour le thread. "). Je soupçonne un bogue dans le noyau de l'OP.Noyau Linux 4.2
rlim[RLIMIT_STACK]
ce qui correspond au POSIXgerlimit(RLIMIT_STACK)
acct_stack_growth
Programme de test minimal
Nous pouvons ensuite le tester avec un programme NASM 64 bits minimal:
Assurez-vous que vous désactivez ASLR et supprimez les variables d'environnement car celles-ci iront sur la pile et prendront de l'espace:
La limite est quelque part légèrement en dessous de ma
ulimit -s
(8 Mo pour moi). Il semble que cela soit dû aux données supplémentaires spécifiées par System V initialement placées sur la pile en plus de l'environnement: Paramètres de ligne de commande Linux 64 dans Assembly | Débordement de pileSi vous êtes sérieux à ce sujet, TODO crée une image initrd minimale qui commence à écrire à partir du haut de la pile et descend, puis exécutez-la avec QEMU + GDB . Mettez un
dprintf
sur la boucle imprimant l'adresse de la pile et un point d'arrêt àacct_stack_growth
. Ce sera glorieux.En relation:
la source
Par défaut, la taille maximale de la pile est configurée pour être de 8 Mo par processus,
mais elle peut être modifiée à l'aide de
ulimit
:Affichage de la valeur par défaut en Ko:
Réglez sur illimité:
ulimit -s unlimited
affectant le shell et les sous-shell actuels et leurs processus enfants.
(
ulimit
est une commande intégrée au shell)Vous pouvez afficher la plage d'adresses de pile réelle utilisée avec:
cat /proc/$PID/maps | grep -F '[stack]'
sous Linux.
la source
ulimit -s
Ko valide pour le programme. Dans mon cas, c'est 10240 Ko. Mais lorsque je déclare un tableau localchar a[10240*1024]
et que je définisa[0]=1
, le programme se ferme correctement. Pourquoi?int n = 10240*1024; char a[n]; memset(a,'x',n);
... faute de seg.a[]
n'a pas été alloué dans votre pile de 10 Mo. Le compilateur a peut-être vu qu'il ne pouvait pas y avoir d'appel récursif et a effectué une allocation spéciale, ou quelque chose d'autre comme une pile discontinue ou une indirection.