Répondre à la discussion
Affichage des résultats 1 à 10 sur 10

Programation en C



  1. #1
    Tookiz

    Programation en C


    ------

    Bonjour,
    Voila j'ai un souci. J'ai fait un programme, il fonctionne bien sauf à la fin.
    Quand on met "n", le programme quitte bien, sauf que il me met cette erreur:
    Run-Time Check Failure #2 - Stack around the variable 'rep' was corrupted.
    Je suis débutant et je n'ai pas réussit a trouver la solution.
    Si vous avez la gentillesse de m'aider
    Merci
    Code:
    //Muliplication avec addition
    #include "stdio.h"
    #include "stdlib.h"
    int main(int argc, char *argv[])
    {
        int a = 0,b = 0,p = 0,c = 0;
        char rep = 'o';
    //Debut du programme
        printf("Programme de multiplication\n");
    while (rep == 'o')
        {
        printf("Veuillez entrer deux nombres positifs :\n");
        scanf("%ld%ld",&a,&b);
             
        
        //Boucle si nombre pas positifs
              while ((a<0)||(b<0))
             {
                 printf("Veuillez entrer deux nombres positifs :\n");
                 scanf("%ld%ld",&a,&b);
              }
     p = 0;            
              
              //Calcul du produit
                             for(c = 0 ; c<b ;c++)
                             {p = p+a;}
                             
        //Affichage du resultat
        printf("Le resultat de %ldx%ld est %ld\n",a,b,p);
    
        //Demande et saisie réponse repeat
        printf("Voulez-vous recommencer?(o/n)\n");
        scanf("%s",&rep);
    
    }    printf("Aurevoir et merci.\n");
        system("PAUSE");
        return 0;
    }

    -----

  2. Publicité
  3. #2
    Towl

    Re : Programation en C

    Youhou un stack overflow ! Et une faille de sécurité, une (bon à priori pas exploitable (ou pas facilement))

    Petite explication technique (voir la solution plus bas si pas interressé ):
    En gros le message veux dire que la pile (stack) est corrompue et ne contient pas ce qu'elle devrait.
    Il existe deux endroit pour stocker les variables, la pile (stack) pour les variables statiques (a,b,p,c,prep dans ton exemple) et le tas (heap) pour les variables dynamiques (qui ont besoin d'un malloc).

    Chaque variable à une taille maximale :
    - 1 octet pour un char
    - 2 octets pour un shot
    - 4 octets pour un int / float
    - 8 octets pour un double.
    ...

    Chaque variable est déclarée à la suite les unes des autres. Ainsi, dans ton programme, ta pile ressemble à cela (chaque ligne représente 1 octet) :
    Code:
    ---------
    |   a   |
    |   b   |
    |   p   |
    |   c   |
    |  rep  |
    ---------
    Maintenant, regardons l'état de ta pile juste avant la demande et saisie réponse repeat (avec a = 1, b = 2):
    Code:
    ---------
    |   1   |
    |   2   |
    |   2   |
    |   1   |
    |  'o'  |
    ---------
    Ta pile est donc correcte.

    Ensuite, regarde ce que tu fais dans ton scanf : tu as un %s qui va lire une chaine de caractère et non un caractère. Plus précisément, ta chaine de caractère va contenir : 'n\n\0'. Le premier caractère correspond à ta réponse 'n', le second à ton appuie sur la touche entrée pour valider ('\n') et le troisième indique la fin de ta chaine de caractère ('\0').

    Or tu vas copier 3 caractères dans ta pile, sur une place ou il n'y en avait que pour 1. Un ordinateur, surtout en C, c'est très bete, cela fait exactement ce que tu lui dit. Ainsi ta pile va valoir juste apres le scanf :
    Code:
    ---------
    |   1   |
    |   2   |
    |  '\0' |
    |  '\n' |
    |  'n'  |
    ---------
    Comme tu peux le voir, tu as écraser c et p. Comme tu as utilisé un compilateur en mode debug, il va détecter cette corruption de pile et t'avertir.

    Solution :
    Remplacer
    Code:
    printf("Voulez-vous recommencer?(o/n)\n");
    scanf("%s",&rep);
    par
    Code:
    printf("Voulez-vous recommencer?(o/n)\n");
    scanf("%c",&rep);
    fflush(stdin);
    Le fflush te permettant de vider la mémoire des caractères lu (donc le '\n').
    The only limiting factor of the Linux operating system, is his user. - Linus Torvalds

  4. #3
    Philou67

    Re : Programation en C

    Citation Envoyé par Towl Voir le message
    Le fflush te permettant de vider la mémoire des caractères lu (donc le '\n').
    Mmmmh... pas sûr que ce soit suffisant et que cela fonctionne dans tous les cas (du moins, ça ne l'est pas dans le cas que j'ai testé localement : gcc sous cygwin sous WinXP).
    La solution d'une lecture dans un tableau et analyse du premier caractère saisi me semble plus sure :

    Code:
    char reponse[2];
    scanf("%1s", reponse);
    if (reponse[0] == "o")
    ...
    :'( Plus j'apprends, et plus je mesure mon ignorance

  5. #4
    Tookiz

    Re : Programation en C

    bonjour,
    Merci pour vos réponses plus que complètes
    J'ai appris des choses.
    Et pour mon souci, j'ai donc vu le probleme avec ma prof d'algo, et l'erreure etait simple.
    Code:
        //Demande et saisie réponse repeat
        printf("Voulez-vous recommencer?(o/n)\n");
        scanf("%s",&rep);
    
    }    printf("Aurevoir et merci.\n");
        system("PAUSE");
        return 0;
    }
    La solution etais enfait de remplacer mon "%s" par un " %c" en oubliant pas l'espace.
    Donc ca donne ca :
    Code:
        //Demande et saisie réponse repeat
        printf("Voulez-vous recommencer?(o/n)\n");
        scanf(" %c",&rep);
    
    }    printf("Aurevoir et merci.\n");
        system("PAUSE");
        return 0;
    }
    Voila si ca peux aider certains.
    merci pour votre rapidité.
    Cordialement

  6. A voir en vidéo sur Futura
  7. #5
    Tookiz

    Re : Programation en C

    Ah j'ai oublier, je voudrais bien une explication concernant la faille de sécurité.

  8. #6
    Towl

    Re : Programation en C

    Mmmmh... pas sûr que ce soit suffisant et que cela fonctionne dans tous les cas (du moins, ça ne l'est pas dans le cas que j'ai testé localement : gcc sous cygwin sous WinXP).
    en fait, c'est exact, il manque un fflush(stdin) avant le scanf. En effet, ton buffer contient encore le retour à la ligne de ta demande précédente (ici demande d'entiers).

    La solution d'une lecture dans un tableau et analyse du premier caractère saisi me semble plus sure :
    C'est rajouter des étapes gourmandes en temps et en mémoire. D'ailleur l'idéal serait de remplacer le scanf par un getchar()

    La solution etais enfait de remplacer mon "%s" par un " %c" en oubliant pas l'espace.
    En fait l'espace te permet de virer le retour à la ligne jusqu'au prochain caractère, ce qui n'est pas forcément voulu. Pour vider completement tout ce qui a été saisi, utilise fflush(stdin);

    Exemple de cas ou ton scanf(" %c") pourrait ne pas fonctionner comme voulu :
    Code:
    Programme de multiplication
    Veuillez entrer deux nombres positifs :
    1 2 3
    Le resultat de 1x2 est 2
    Voulez-vous recommencer?(o/n)
    *** DBG (read '3')
    Aurevoir et merci.
    Comme tu peux le voir, dans ce cas ci, tu lis le 3 qui suis et donc la réponse à la question voulez vous recommencer est systématiquement non



    Edit:
    Je tente de faire la réponse sur la (possible) faille de sécu d'ici à demain, si j'ai le tps
    The only limiting factor of the Linux operating system, is his user. - Linus Torvalds

  9. Publicité
  10. #7
    Philou67

    Re : Programation en C

    Citation Envoyé par Towl Voir le message
    en fait, c'est exact, il manque un fflush(stdin) avant le scanf. En effet, ton buffer contient encore le retour à la ligne de ta demande précédente (ici demande d'entiers).
    Pas mieux...
    Citation Envoyé par Towl Voir le message
    C'est rajouter des étapes gourmandes en temps et en mémoire. D'ailleur l'idéal serait de remplacer le scanf par un getchar()
    A force d'ajouter des fflush, je ne crois pas que la solution soit ne soit plus optimale
    Étant donné que scanf et getchar sont des fonctions de la librairie d'entrée-sortie bufferisée, je doute que sur la majorité des systèmes d'exploitation, l'usage de fflush sur une entrée standard ait un quelconque effet sur la réception d'une entrée terminée par "\n". Le buffer d'entrée en rempli avec "\n", et fflush ne le vide pas. Ne crois-tu pas ?
    D'ailleurs, fflush n'a pas pour but de vider les buffers, mais de "terminer" une opération bufferisée. En sortie, c'est écrire les données sur le device (désolé), et en entrée, ça pourrait être : "lire le device" pour compléter le buffer (mais la doc de GNU libc n'est pas loquace sur l'usage de fflush en entrée).
    :'( Plus j'apprends, et plus je mesure mon ignorance

  11. #8
    Towl

    Re : Programation en C

    Le getchar() comparé au scanf("%c") est beaucoup plus performant. L'exemple flagrant est qu'il n'y a pas l'analyse du "%c" à faire.

    Ensuite, que tu sois en scanf() ou getchar(), tu dois vider le tampon du clavier. Le positionnement du 'vidage' du tampon est a choisir judicieusement pour éviter de le faire trop de fois

    Comme tu le dit, le fflush(stdin) n'est pas dans la norme, donc en théorie à éviter. Néanmoins, je n'ai jamais eu de soucis avec les compilos que j'ai utilisé (icc, gcc, vc++, borland c++ ou pcc)

    Sinon, pour faire plus standard, la seule méthode pour vider le buffer du clavier, c'est le bon vieux :
    Code:
    while(getchar() != '\n');
    Le ';' à la fin est important


    Edit:
    D'ailleurs, je viens de voir que j'avais dans mes bibliothèques redéfinies fflush() pour justement être standard (surement pour cela que mon code est portable ) :
    Code:
    int _fflush(FILE * fp)
    {
        if (fp != stdin)
            return fflush(fp);
        while(getchar() != '\n');
        return 0;
    }
    Ca faisait vraiment logntemps que j'avais pas utilisé cela, donc je me souvenais plus de cette subtilité
    The only limiting factor of the Linux operating system, is his user. - Linus Torvalds

  12. #9
    Towl

    Re : Programation en C

    Petite explication rapide sur la possible faille de sécurité (la elle est a priori pas exploitable comme cela, mais il s'en est fallut de peu ). Pour lpus de détail regerder les articles sur les buffers overflow, notamment le très fameux "Smashing The Stack For Fun And Profit" par Aleph1 : http://www.phrack.org/issues.html?is...&id=14#article

    Un ordinateur effectue des actions les unes à la suite des autres. Or lorsque l'on fait une fonction, c'est le même bout de code qui est utilisé à différents endroit. Pour que l'on sache où aller apres la fonction, l'adresse de l'instruction suivante est stockée dans la pile, apres les arguments.

    Un petit code (volontairement simplifié et "faux" dans le calcul des adresses (flemme )) d'un appel de fonction en assembleur (produit par du C)
    Code:
    0x00400001    mov eax, 3 ; On mets 3 dans eax
    0x00400002    push eax   ; On stocke eax dans la pile
    0x00400003    call myfunc ; on appelle la fonction myfunc
    	            ; revient à faire un myfunc(3) en C
    0x00400004    cmp eax, 3 ; On compare eax à 3 (instruction suivante à l'appelle de fonction, adresse 0x00400004
    ......
    
    
    0x00400100 myfunc:      ; définition de myfunc
    ....         ; simplification volontaire :p
    0x0040010A    ret      ; fin de la fonction

    Comme dit précedement, pour "sauter" au bon endroit dans ton code apres la fonction, l'adresse est stockée dans ta pile. Ce qui donne :
    Code:
    Avant appel    Pendant Appel     Apres appel
    ---------      --------------    ---------
    |   3   |      |      3     |    |   3   |
    ---------      | 0x00400004 |    ---------
                   | variables  |
                   |    de      |
    		| fonctions  |
    	        --------------
    comme tu peux le remarquer, l'instruction ret (et avant) nettoye la pile (vire les variables de fonction) et saute ou il le faut (en 0x00400004)

    Maintenant, si tu te souviens de ton code, tu écrasais la pile. L'idée de la faille de sécu, c'est d'écraser l'adresse de retour pour mettre une adresse qui contient le code que l'on souhaite executer (shellcode).

    Un exemple de code vulnérable
    Code:
    int vuln()
    {
        char c[2];
    	scanf("%s", c);
    }
    Si tu donnes une chaine trop longue en entrée, tu vas exploser ton tableau (variable de fonction), puis lorsqu'il n'y aura plus de variables de fonctions, tu vas écraser l'adresse de retour, etc..
    Donc en visant juste (ie en tombant sur adresse de retour), tu es capable de modifier le comportement du programme et d'en faire presque tout ce que tu veux
    The only limiting factor of the Linux operating system, is his user. - Linus Torvalds

  13. #10
    JPL
    Responsable des forums

    Re : Programation en C

    J'ajoute que de très nombreux malwares et failles signalées dans des programmes variés reposent sur un stack overflow.
    Rien ne sert de penser, il faut réfléchir avant - Pierre Dac

Discussions similaires

  1. programation automate
    Par bougadul dans le forum Technologies
    Réponses: 6
    Dernier message: 07/11/2008, 19h33
  2. programation
    Par amineln dans le forum Électronique
    Réponses: 2
    Dernier message: 10/09/2007, 20h03
  3. programation orientée
    Par c_a_r_a_83 dans le forum Logiciel - Software - Open Source
    Réponses: 2
    Dernier message: 31/08/2007, 12h59
  4. programation de microcontroleur
    Par justadream dans le forum Technologies
    Réponses: 8
    Dernier message: 12/01/2007, 22h30
  5. Programation en Fortran
    Par $ µ L v @ i N dans le forum Logiciel - Software - Open Source
    Réponses: 6
    Dernier message: 05/11/2006, 10h49
Découvrez nos comparatifs produits sur l'informatique et les technologies.