2010-03-30 4 views
3

Le code ci-dessous est dit pour donner une violation de segmentation:Pourquoi ne donne-t-il PAS une violation de segmentation?

#include <stdio.h> 
#include <string.h> 

void function(char *str) { 
    char buffer[16]; 

    strcpy(buffer,str); 
} 

int main() { 
    char large_string[256]; 
    int i; 

    for(i = 0; i < 255; i++) 
    large_string[i] = 'A'; 

    function(large_string); 
    return 1; 
} 

Il est compilé et exécuté comme ceci:

gcc -Wall -Wextra hw.cpp && a.exe 

Mais il y a sortie rien.

NOTE

Le code ci-dessus en effet l'adresse écrase ret et ainsi de suite si vous comprenez vraiment ce qui se passe en dessous.

L'adresse ret sera 0x41414141 pour être spécifique.

Important Cela nécessite une connaissance approfondie de la pile

+11

Le comportement indéfini est un comportement indéfini. –

+1

Non seulement ce comportement n'est pas défini, mais vous n'avez aucun code pour produire quoi que ce soit. –

+0

Est-ce que 2 comportement indéfini fait 1 comportement défini? ;-) La première grande_chaîne elle-même n'est pas terminée par un caractère nul, alors on essaye de copier un tampon source trop grand pour sa destination. : D – KTC

Répondre

6

Vous êtes juste chanceux. Il n'y a aucune raison pour que le code génère une erreur de segmentation (ou tout autre type d'erreur). C'est quand même probablement une mauvaise idée. Vous pouvez probablement l'obtenir pour échouer en augmentant la taille de large_string.

+1

Eh bien, puisque la chaîne large_string n'est pas terminée, qui sait quand strcpy arrête réellement la copie. – KTC

+1

@KTC, bien sûr, mais il est * garanti * de copier plus que sizeof (buffer) ', alors quelle est la différence? –

+0

Non, la taille est suffisante pour écraser l'adresse * ret * – user198729

1

Son UB (comportement indéfini). Strcpy peut avoir copié plus d'octets dans la mémoire pointée par le tampon et cela pourrait ne pas causer de problème à ce moment-là.

1

C'est un comportement non défini, ce qui signifie que tout peut arriver. Le programme peut même sembler fonctionner correctement.

Il semble que vous n'arriviez pas à écraser les parties de la mémoire qui sont encore nécessaires pour le reste du programme (court) (ou qui sont hors de l'espace d'adressage/protégé en écriture/...), donc rien spécial arrive. Au moins rien qui conduirait à une sortie.

0

Il peut y avoir quelque chose dans votre 'char buffer [16]', y compris \ 0. strcpy copies jusqu'à ce qu'il trouve d'abord \ 0 - ne dépassant pas ainsi votre limite de 16 caractères.

+0

strcpy (des, src) Le code est copié de * large_string * à * buffer *, et non l'inverse ... – KTC

+0

Oops :) Je dois être plus prudent avec la lecture de documentation. –

1

Il y a un octet nul sur la pile quelque part qui arrête le strcpy() et il y a assez de place sur la pile pour ne pas atteindre la page protégée. Essayez d'imprimer strlen(buffer) dans cette fonction. Dans tous les cas, le résultat est comportement indéfini.

Prenez l'habitude d'utiliser strlcpy(3) famille de fonctions.

3

Probablement dans votre implémentation buffer est immédiatement au-dessous de large_string sur la pile. Donc, quand l'appel à strcpy déborde buffer, il écrit simplement la plupart du temps dans large_string sans faire de dégâts particuliers. Il écrira au moins 255 octets, mais s'il écrit plus dépend de ce qui est au dessus de large_string (et de la valeur non initialisée du dernier octet de large_string). Il semble s'être arrêté avant de faire des dommages ou de segfaulting. Par défaut, l'adresse de retour de l'appel à function n'est pas en cours de suppression. Soit c'est au-dessous de buffer sur la pile ou c'est dans un registre, ou peut-être que la fonction est inline, je ne me souviens pas de ce qu'aucune optimisation ne fait. Si vous ne pouvez pas prendre la peine de vérifier le démontage, je ne peux pas non plus ;-). Donc, vous revenez et vous sortez sans problèmes.

Celui qui a dit que le code donnerait une erreur de segmentation n'est probablement pas fiable. Il en résulte un comportement indéfini. À cette occasion, le comportement était de ne rien produire et de sortir. [Edit: J'ai vérifié sur mon compilateur (GCC sur cygwin), et pour ce code il utilise la convention d'appel x86 standard et le code d'entrée/sortie. Et il segfault.]

+0

J'utilise GCC + MinGW – user198729

2

Vous compilez un programme .cpp (C++) en appelant gcc (au lieu de g ++) ... pas sûr si c'est la cause, mais sur un système Linux (il semble que vous êtes en train de courir sur les fenêtres en raison de la valeur par défaut .exe sortie), il jette l'erreur suivante lorsque vous essayez de compiler comme vous l'avez dit:

/tmp/ccSZCCBR.o:(.eh_frame+0x12): undefined reference to `__gxx_personality_v0' collect2 : ld retourné 1 état de sortie

+0

Merci d'avoir fourni la sortie sous linux, + 1 :) – user198729

1

Vous pouvez le tester dans d'autres façons:

#include <stdlib.h> 
int main() { 
    int *a=(int *)malloc(10*sizeof(int)); 
    int i; 
    for (i=0;i<1000000; i++) a[i] = i; 
    return 0; 
} 

Dans ma machine, ceci cause SIGSEGV seulement autour de i = 37000! (testé en inspectant le noyau avec gdb). Pour vous prémunir de ces problèmes, testez vos programmes en utilisant un débogueur malloc ... et utilisez beaucoup de mallocs, car je ne connais pas de librairies de débogage de mémoire capables de regarder dans la mémoire statique. Exemple: Electric Fence

gcc -g -Wall docore.c -o c -lefence 

Et maintenant, le SIGSEGV est déclenché dès i=10, comme on pouvait s'y attendre.

1

Comme tout le monde le dit, votre programme a un comportement indéfini. En fait, votre programme a plus de bugs que vous ne le pensiez, mais après qu'il est déjà indéfini, il n'a plus de bugs.

Voici ma raison pour laquelle il n'y avait pas de sortie. Vous n'avez pas complètement désactivé l'optimisation. Le compilateur a vu que le code de function() n'a aucun effet défini sur le reste du programme. Le compilateur a optimisé l'appel de function().

+0

Y at-il une option permettant à gcc de désactiver l'optimisation? – user198729

+0

OK, http://linux.die.net/man/1/gcc est trop long à lire en un jour, mais recherchez -O0 (c'est un moins suivi de O majuscule pour l'optimisation et de 0 pour rien). –

1

Les chances sont que la chaîne longue est, en fait, terminée par le zéro octet de i. En supposant que les variables dans main sont disposées dans l'ordre où elles sont déclarées - ce qui n'est pas requis par quelque chose dans la spécification de langage que je connais mais semble probable dans la pratique - alors la chaîne large sera la première en mémoire, suivie par i . La boucle définit i à 0 et compte jusqu'à 255. Si i est stocké big-endian ou little-endian, de toute façon il a un octet zéro dedans. Ainsi, en traversant la chaîne large, au niveau de l'octet 256 ou 257, vous obtiendrez un octet nul. Au-delà de cela, je devrais étudier le code généré pour comprendre pourquoi cela n'a pas soufflé. Comme vous semblez l'indiquer, je m'attendrais à ce que la copie au tampon écrase l'adresse de retour du strcpy, alors quand il essayait de revenir, vous alliez dans l'espace lointain et vous explosiez rapidement quelque chose.

Mais comme d'autres le disent, "indéfini" signifie "imprévisible".

Questions connexes