2010-05-21 6 views
0
int main(void) { 
    problem2(); 
} 

void doit2(void) { 
    int overflowme[16]; 
    //overflowme[37] =0; 
} 

void problem2(void) { 
    int x = 42; 
    doit2(); 
    printf("x is %d\n", x); 
    printf("the address of x is 0x%x\n", &x); 
} 

Est-ce que quelqu'un pourrait m'aider à comprendre pourquoi overflowme [37] = 0; de la fonction doit2 écrasera la valeur de x? (veuillez inclure le compteur de programme et le pointeur de trame de la fonction doit2 dans votre explication) Merci!technique de débordement dans la pile

Il fonctionne à chaque fois sur une machine Windows x86 (ok là!) Avec Propriétés du projet-> Propriétés de configuration-> C/C++ -> Génération de code-> Vérifications d'exécution de base définies sur "Par défaut". ce n'est donc pas un comportement indéfini.

+0

non, 37 fonctionne à chaque fois! – user133466

+2

Est-ce que ce sont les devoirs? –

+4

Ce ne sera pas nécessairement - cela ne se produira qu'avec un compilateur spécifique, CPU, drapeaux de compilateur, etc –

Répondre

5

Comme tout le monde l'a dit, cela dépend de la cible et du complicateur, mais pour vous, ceux-ci restent constants et il n'y a pas d'autres choses dans ce code qui semblent introduire un caractère aléatoire dans la pile. parlant), donc il fera la même chose à chaque fois.

La pile système augmente généralement d'une adresse élevée à une adresse inférieure. Si le pointeur de la pile est 0x1234 et que vous appuyez sur une valeur (sur un système de 32 bits {4 octets}), le pointeur de pile devient 0x1230.

Les tableaux sont adressés de l'adresse la plus basse à l'adresse la plus élevée. Si vous avez

char a[2]; 

et [0] est à 0x0122 alors sera à 0x[1].

Votre tableau en doit2 est une variable automatique, ce qui signifie qu'il est créé à l'entrée de la fonction et supprimé à la sortie de la fonction. Les variables automatiques doivent soit vivre sur la pile, soit dans des registres. Comme il s'agit d'un tableau, il est beaucoup moins compliqué pour le compilateur de le placer dans la RAM plutôt que dans les registres (cela facilite l'indexation car il ajoute simplement la taille de l'index * à l'adresse du premier membre du tableau). Puisque la pile est en RAM, le compilateur place le tableau sur la pile. L'allocation d'espace sur la pile pour cette matrice signifie que le pointeur de pile est sizeof(int)*16 inférieur à ce qu'il serait si ce tableau n'était pas présent. Le pointeur de pile pointe le plus probablement vers overflowme[0] tandis que dans doit2.

Il y a d'autres choses qui pourraient être sur la pile et quelques choses qui devaient être sur la pile. Les choses qui devaient être sur la pile sont les pointeurs de retour qui ont été poussés là quand les fonctions ont été appelées.Sur un système 32 bits, ceux-ci devraient occuper 4 octets chacun. Les choses qui auraient pu être sur la pile (si le compilateur voulait l'utiliser) est le pointeur d'image précédent. (Explicit *) Les cadres empilés sur x86-32 ne sont que l'espace entre ESP et EBP, mais ils ne sont pas nécessaires si souvent qu'ils ne sont pas utilisés et EBP est simplement utilisé comme un registre généraliste (des registres plus généraux sont disponibles généralement bon). L'utilisation de cadres de pile est utile, car ils facilitent le débogage car ESP et EBP agissent comme des marqueurs pour les bords des variables locales. Les cadres de pile sont parfois nécessaires, comme lorsque vous utilisez alloca ou les tableaux automatiques de taille variable C99 car ils permettent à l'espace variable local pour une fonction d'être supprimé par mov EPB, ESP ou une instruction équivalente plutôt que sub size_of_local_variable, ESP afin que le compilateur n'ait pas besoin de connaître la taille du cadre. Ils permettent également d'adresser les variables locales par rapport à l'EBP plutôt qu'à l'ESP qui, dans le cas de alloca, change. EBP dans ce cas ne changera pas jusqu'à la fin de la fonction en cours, sauf si elle a été modifiée et restaurée par les fonctions d'appel.

Lors de la compilation sans optimisations, les compilateurs activés utilisent souvent toujours des trames de pile car elles facilitent le débogage. Il est également plus facile de modéliser le code avec des cadres de pile, puis de transformer le code pour ne pas les utiliser après avoir prouvé qu'ils ne sont pas nécessaires.

Ainsi, la valeur précédente de RASE peut ou ne peut pas résider sur la pile entre l'adresse de retour (quelque part dans problem2) et le dernier élément de l » overflowmedoit2. Le compilateur est également libre de mettre n'importe quoi d'autre sur la pile, donc qui sait quoi d'autre pourrait être là. La variable locale int x peut être placée dans un registre ou dans la pile. Lors de la compilation sans optimisations activées, les variables locales vont souvent sur la pile même lorsqu'elles peuvent entrer dans les registres.

Alors, laisse supposer qu'il ya « tableau overflowme s, un ancien pointeur de trame, une adresse de retour, et problem2 » doit2 s x sur la pile (et un peu plus de choses sous {qui est vraiment à une adresse supérieure} elle).

Depuis &(overflowme[i]) est le même que l'ajout de l'adresse du premier élément de overflowme (i * {la taille de int}) et l'ancien RASE mensonges après le dernier élément de overflowme et une adresse de retour se trouve après l'ancien EBP et int x se trouve après l'adresse de retour, x est définitivement debout dans le chemin d'être écrasé par un dépassement de tampon. Pourquoi cela se produit pour l'index de 37 n'est pas clair. Le pointeur mathématique (en supposant que seuls les éléments indiqués ci-dessus se trouvent sur la pile entre le tableau et x) ne suggère pas qu'il devrait être basé sur des pointeurs de 4 octets (machine 32 bits), mais s'il s'agit d'un système de 8 octets (Machine 64 bits), puis le calcul est plus proche de l'adresse que je m'attendais x à être à sizeof(int) == 8. Le compilateur aurait également pu aller de l'avant et attribuer l'espace de pile pour les appels à printf (les arguments variables après que la chaîne de format doit aller sur la pile) ce qui affecterait le calcul (et encouragerait le compilateur à placer x sur la pile car il le ferait dois y pousser de toute façon).

Si vous voulez une réponse plus détaillée à votre question, regardez l'assemblage pour ce code et établissez l'adressage exact.

  • Vous pouvez considérer que le cadre de pile est présent même si EBP n'est pas utilisé comme pointeur de base de trame, mais que le cadre ne sera pas encadré.
+0

ggrrrrr. Bonne réponse.. –

3

Ce n'est peut-être pas le cas. L'emplacement des variables dans la pile dépend du compilateur et de la plateforme.

+0

fonctionne à chaque fois avec Propriétés du projet-> Propriétés de configuration-> C/C++ -> Génération de code-> Vérifications d'exécution de base définies sur "Par défaut" – user133466

+8

@metashockwave: juste parce qu'elle fonctionne sur ** un ** plate-forme/compilateur/etc très spécifique , cela ne signifie pas que c'est généralement vrai –

+0

Non, l'idée de ce que ces paramètres sont, mais sonne comme certains trucs Windows, et probablement x86 ou plus. – johannes

0

Il n'a pas:

x is 42 
the address of x is 0xbff9ea1c 

Le produit ci-dessus chaque fois que le compilateur Un vrai et la plate-forme (changements d'adresse), vous êtes si clairement que ce droit n'est pas un comportement non défini.

+1

Je pense que vous avez frappé le clou carrément sur la tête là-bas. –

2

Votre pile va ressembler à ceci:

char overflowme[16] 
return address to problem2() from calling doit2() 
parameters for doit2(), if it had any 
int x = 42 
return address to main() from calling problem2() 
parameters for problem2(), if it had any 
local variables for main(), if it had any 

Lorsque vous écrivez à overflowme[37], vous allez passer devant la fin de overflowme (puisqu'il est seulement 16 octets) et passé l'adresse de retour à partir de l'appel doit2() et réécrire x.

Comme d'autres l'ont mentionné, cela dépend fortement de la plate-forme et du compilateur, mais devrait vous donner une bonne visualisation du problème. Essayez de parcourir votre code avec une fenêtre de débogage ouverte et en affichant la pile.

Questions connexes