Forum |  HardWare.fr | News | Articles | PC | S'identifier | S'inscrire | Shop Recherche
2969 connectés 

  FORUM HardWare.fr
  Programmation
  C

  analyseur de code pour dépassement mémoire

 


 Mot :   Pseudo :  
 
Bas de page
Auteur Sujet :

analyseur de code pour dépassement mémoire

n°1406689
adrienj
Posté le 13-07-2006 à 21:15:55  profilanswer
 

Bonjour à tous,
 
j'utilise Splint pour analyser mon code C et je souhaite que le résultat de l'analyse me dise les failles de gestion mémoire du genre :

Code :
  1. int *pointeur;
  2. pointeur = malloc(10*sizeof(int));
  3. pointeur[15] = 3;
  4. free(pointeur);


Comme on le sait, ce code passe à la compilation mais provoque des erreurs à l'exécution. C'est pourquoi le mieux est de le détecter avant.
 
Splint me dit bien qu'il y a une possibilité que pointeur soit NULL, là impécable, je suis d'accords, il faut donc ajouter un if(!NULL) avant d'utiliser le pointeur.
 
Mais ce que je veux c'est qu'il m'indique que la ligne pointeur[15] = 3; provoque un déppassement, comme il le fait si j'utilise des tableau :

Code :
  1. int tableau[10];
  2. tableau[15] = 3;


dans cet exemple, Splint m'indique bien qu'il y a dépassement car 15 est plus grand que 10-1.
 
J'ai essayé avec des commandes Splint du genre

Code :
  1. int *pointeur;
  2. pointeur = malloc(10*sizeof(int));
  3. /*@requires maxSet(pointeur) == 10-1@*/
  4. pointeur[15] = 3;
  5. free(pointeur);


mais Splint ne semble pas comprendre ce que je lui dis.
 
Quelqu'un à t'il une idée de comment faire ?
Est-ce possible ?  
Existe-t-il un analyseur statique qui sache le faire ?

mood
Publicité
Posté le 13-07-2006 à 21:15:55  profilanswer
 

n°1406700
Sve@r
Posté le 13-07-2006 à 21:59:25  profilanswer
 

adrienj a écrit :

Comme on le sait, ce code passe à la compilation mais provoque des erreurs à l'exécution. C'est pourquoi le mieux est de le détecter avant.


Non. Le mieux est de faire gaffe à ce qu'on code (surtout que si c'est moi qui écrit "tab=malloc(10)" je vais pas aller m'amuser à faire "tab[15]=..." )
 

adrienj a écrit :

il faut donc ajouter un if(!NULL) avant d'utiliser le pointeur.


Ca c'est de l'alternative de haut vol. T'as mis un "else" au moins... :pt1cable:

Message cité 1 fois
Message édité par Sve@r le 13-07-2006 à 22:01:33

---------------
Vous ne pouvez pas apporter la prospérité au pauvre en la retirant au riche.
n°1406779
el muchach​o
Comfortably Numb
Posté le 14-07-2006 à 01:20:17  profilanswer
 

adrienj a écrit :


Existe-t-il un analyseur statique qui sache le faire ?


µOui, mais ils ne sont pas gratuits.  [:itm] Pour les problèmes de mémoire et pointeurs, c'est d'utiliser un malloc debugger du genre efence ou mempatrol et de tester jusqu'à plus soif. Ca a l'avantage / splint de ne pas modifier le code. Par contre, Splint oblige à réfléchir un peu plus, ce qui n'est pas forcément un mal.


---------------
Les aéroports où il fait bon attendre, voila un topic qu'il est bien
n°1406782
adrienj
Posté le 14-07-2006 à 01:44:16  profilanswer
 

Sve@r a écrit :

Non. Le mieux est de faire gaffe à ce qu'on code (surtout que si c'est moi qui écrit "tab=malloc(10)" je vais pas aller m'amuser à faire "tab[15]=..." )
 
 
Ca c'est de l'alternative de haut vol. T'as mis un "else" au moins... :pt1cable:


 
Bien sur que si c'est aussi simple qu'un tab[15], c'était pour l'exemple et pour tester les capacités de Splint. Ici, il n'y a pas de problèmes. Là ou ca se complique c'est lorsque qu'on a un tab[index] où index est une variable qui varie lors de l'exécution du prog. Et il arrive des cas où c'est vraiement très complex de prévoir tous de tête  ;) d'où l'utilisation de softs d'analyse faits pour.
 
if !NULL c'est encore pour l'exemple... et toi tu mets quoi pour vérifier que malloc ne te retourne pas NULL ? Tu le vérifies au moins, non ? :) On peut s'en passer pour des petits softs maison, mais pour un soft embarqué sur un calculo d'avion, je dis quoi aux familles des disparus si le calculo de contrôle moteur me renvoit un NULL pour mon malloc ??  :pt1cable: sans doute que j'avais pourtant fait gaffe à ce que je codais  :D surtout quand il y a une dizaine de développeurs qui ont travaillés sur ce soft et qu'on est pas à l'abris de l'erreur humaine.
Un exemple qui me vient en tête : le crach d'Ariane 5 qui était dû à un tout petit out of bounds. :)
 
On voit bien que pour certaines applications, il est nécessaire de passer le code à la moulinette pour le bétonner au max.
------------
 
el muchacho, tu as raison mais le pb du malloc debugger c'est qu'encore une fois, il faut faire tourner le soft dans tous les cas possibles. Je cherche une alternative. Sur le site www.splint.org, ils nous disent que c'est possible, mais pour mon exemple trivial mais précis, j'ai pas trouvé comment ils font. En tous les cas, merci pour tes infos!
 
 

Message cité 2 fois
Message édité par adrienj le 14-07-2006 à 02:12:10
n°1406836
Sve@r
Posté le 14-07-2006 à 11:30:26  profilanswer
 

adrienj a écrit :

Là ou ca se complique c'est lorsque qu'on a un tab[index] où index est une variable qui varie lors de l'exécution du prog. Et il arrive des cas où c'est vraiement très complex de prévoir tous de tête  ;) d'où l'utilisation de softs d'analyse faits pour.


T'as tout à fait raison. Mais même le meilleur analyseur ne pourra pas prévoir tous les cas. Par exemple, si tu demandes "tab[f()]" avec "f()" qui peut te renvoyer 15, je ne pense pas qu'un soft te trouve ça. D'où la solution d'essayer de toujours faire gaffe...

adrienj a écrit :

if !NULL c'est encore pour l'exemple... et toi tu mets quoi pour vérifier que malloc ne te retourne pas NULL ? Tu le vérifies au moins, non ? :) On peut s'en passer pour des petits softs maison, mais pour un soft embarqué sur un calculo d'avion, je dis quoi aux familles des disparus si le calculo de contrôle moteur me renvoit un NULL pour mon malloc ??


N'exagérons pas. Les avions ont toujours 2 calculateurs travaillant en parallèles. Mais je comprends ton point de vue. C'est juste que j'ai été très amusé de voir ce "if(!NULL)" bien que je me doute qu'étant pas un débutant, tu ne dois pas faire de tests aussi bateaux...


---------------
Vous ne pouvez pas apporter la prospérité au pauvre en la retirant au riche.
n°1407182
el muchach​o
Comfortably Numb
Posté le 15-07-2006 à 12:00:28  profilanswer
 

adrienj a écrit :


el muchacho, tu as raison mais le pb du malloc debugger c'est qu'encore une fois, il faut faire tourner le soft dans tous les cas possibles. Je cherche une alternative. Sur le site www.splint.org, ils nous disent que c'est possible, mais pour mon exemple trivial mais précis, j'ai pas trouvé comment ils font. En tous les cas, merci pour tes infos!


Hmm, certes, il faut bcp tester. Et pour l'aviation, c'est même obligatoire, quitte à y mettre énormément de ressources.  
Je ne suis pas sûr cependant que splint fasse de la couverture complète en analyse statique; ne l'ayant pas testé, je ne connais pas ses possibilités. Les seuls logiciels qui font cela sont Clokwork et Coverity, mais c'est pas donné du tout (c'est même très très cher pour ce dernier). Par contre, ces softs marchent pas mal.
 
http://www.spinroot.com/static/

Message cité 1 fois
Message édité par el muchacho le 15-07-2006 à 12:06:15

---------------
Les aéroports où il fait bon attendre, voila un topic qu'il est bien
n°1407299
adrienj
Posté le 15-07-2006 à 21:20:40  profilanswer
 

Sve@r a écrit :

T'as tout à fait raison. Mais même le meilleur analyseur ne pourra pas prévoir tous les cas. Par exemple, si tu demandes "tab[f()]" avec "f()" qui peut te renvoyer 15, je ne pense pas qu'un soft te trouve ça. D'où la solution d'essayer de toujours faire gaffe...


Bonne question, je viens de le tester le code suivant (encore un code hyper simple :)) avec Splint :  

Code :
  1. int ComputeIndex(void) {
  2. return 15;
  3. }
  4. int main (void) {
  5.   int iTableau[5];
  6.   iTableau[ComputeIndex] = 3;
  7.   return 0;
  8. }


et la réponse est.... oui, Splint détect bien le bug et met un warning out of bounds.
Tu as cependant raison, on ne peut pas faire aveuglément confiance à Splint, surtout qu'il n'est pas validé par des organisme type ISO et autres.
Le schéma classique de validation statique est: relecture du code par une autre équipe de développeurs, puis en dernier, un passage dans l'analyseur pour une ultime "relecture" avant le passage à la validation dynamique.  

Sve@r a écrit :


N'exagérons pas. Les avions ont toujours 2 calculateurs travaillant en parallèles. Mais je comprends ton point de vue. C'est juste que j'ai été très amusé de voir ce "if(!NULL)" bien que je me doute qu'étant pas un débutant, tu ne dois pas faire de tests aussi bateaux...


Exact, toujours deux calculo en parallèles qui ont des taux d'erreur "acceptable" celon la DO178b pour l'avion civile.
if(!NULL) test bateau... pourquoi, tu n'utilises jamais if(!NULL) ? tu ne l'aime pas :). Alors comment fais-tu pour tester ton retour de malloc? qui est, soit dit en passant, défini dans la litérature comme faille de sécurité si == NULL.
 

el muchacho a écrit :

Hmm, certes, il faut bcp tester. Et pour l'aviation, c'est même obligatoire, quitte à y mettre énormément de ressources.  
Je ne suis pas sûr cependant que splint fasse de la couverture complète en analyse statique; ne l'ayant pas testé, je ne connais pas ses possibilités. Les seuls logiciels qui font cela sont Clokwork et Coverity, mais c'est pas donné du tout (c'est même très très cher pour ce dernier). Par contre, ces softs marchent pas mal.
 
http://www.spinroot.com/static/


Tu as encore raion, l'analyse statique n'est qu'une étape dans la validation avant de passer à la validation dynamique des cartes du calculo sur testeur fonctionnel puis du calculo complet sur banc de validation.
Merci pour l'adresse, je vais aller lire tout ca. Splint est dans la liste sous son ancienne dénomination LINT.
Plusieurs softs commerciaux d'analyse statique sont dérivés de SPLint (ou LINT) qui est à la base un projet universitaire de l'université de Virginie (et oui, merci la licence GNU).

Message cité 3 fois
Message édité par adrienj le 15-07-2006 à 21:36:08
n°1407515
el muchach​o
Comfortably Numb
Posté le 16-07-2006 à 13:40:04  profilanswer
 

adrienj a écrit :


Tu as encore raion, l'analyse statique n'est qu'une étape dans la validation avant de passer à la validation dynamique des cartes du calculo sur testeur fonctionnel puis du calculo complet sur banc de validation.
Merci pour l'adresse, je vais aller lire tout ca. Splint est dans la liste sous son ancienne dénomination LINT.
Plusieurs softs commerciaux d'analyse statique sont dérivés de SPLint (ou LINT) qui est à la base un projet universitaire de l'université de Virginie (et oui, merci la licence GNU).


Oui, je sais, mais Lint ne fait pas une analyse complète comme le fait Coverity ou Klocwork par ex.  
Ce genre de soft, non seulement t'indique un bug, mais aussi tout le cheminement logique qui le reproduit.
CodeSurfer a l'air aussi d'être assez intéressant pour le débogage.


---------------
Les aéroports où il fait bon attendre, voila un topic qu'il est bien
n°1407755
Emmanuel D​elahaye
C is a sharp tool
Posté le 16-07-2006 à 22:21:16  profilanswer
 

adrienj a écrit :

if(!NULL) test bateau... pourquoi, tu n'utilises jamais if(!NULL) ? tu ne l'aime pas :). Alors comment fais-tu pour tester ton retour de malloc? qui est, soit dit en passant, défini dans la litérature comme faille de sécurité si == NULL.


J'aurais plutôt tendance à utiliser

Code :
  1. if (p != NULL)
  2.    {
  3.       /* OK */
  4.    }
  5.    else
  6.    {
  7.       /* ERR */
  8.    }

Message cité 2 fois
Message édité par Emmanuel Delahaye le 16-07-2006 à 22:29:50

---------------
Des infos sur la programmation et le langage C: http://www.bien-programmer.fr Pas de Wi-Fi à la maison : http://www.cpl-france.org/
n°1408164
adrienj
Posté le 17-07-2006 à 21:09:31  profilanswer
 

el muchacho a écrit :


CodeSurfer a l'air aussi d'être assez intéressant pour le débogage.


J'a été faire un tour sur http://www.grammatech.com/products/codesurfer
Il est pas mal pour surfer dans les sources, surtout qu'il comprend les pointeurs. Donc, c'est un bon outils pour les projets qui commencent à peser, et très bien pour faire du retro-engineering également car on tombe quelque fois sur des softs non documentés qu'il faut reprendre...
 

Emmanuel Delahaye a écrit :

J'aurais plutôt tendance à utiliser

Code :
  1. if (p != NULL)
  2.    {
  3.       /* OK */
  4.    }
  5.    else
  6.    {
  7.       /* ERR */
  8.    }



very good ! :) je ne pense pas qu'il y est d'alternative à cela pour tester le résultat d'un malloc... on peut faire une macro ou une fonction pour enrubanner le tout  :D


Message édité par adrienj le 17-07-2006 à 21:12:34
mood
Publicité
Posté le 17-07-2006 à 21:09:31  profilanswer
 

n°1408945
Sve@r
Posté le 18-07-2006 à 23:20:06  profilanswer
 

adrienj a écrit :

Bonne question, je viens de le tester le code suivant (encore un code hyper simple :)) avec Splint :  

Code :
  1. int ComputeIndex(void) {
  2. return 15;
  3. }
  4. int main (void) {
  5.   int iTableau[5];
  6.   iTableau[ComputeIndex] = 3;
  7.   return 0;
  8. }


et la réponse est.... oui, Splint détect bien le bug et met un warning out of bounds.


Evidemment !!! ici tu demandes "iTableau[adresse début_du_code fonction ComputeIndex]". Comme cette adresse dépasse probablement 5, splint te détecte le bug. Mais ce n'est pas le test dont j'ai parlé...
 

adrienj a écrit :

Alors comment fais-tu pour tester ton retour de malloc?


Comme l'a dit Emmanuel, je stocke ce retour dans une variable (de préférence de type "pointeur sur qqchose" ) et je teste la valeur de cette variable
 

adrienj a écrit :

if(!NULL) test bateau... pourquoi, tu n'utilises jamais if(!NULL) ? tu ne l'aime pas :)


Non, je n'écris jamais de test qui renvoient systématiquement "vrai" (ou "faux" )... :sol:

Message cité 1 fois
Message édité par Sve@r le 18-07-2006 à 23:21:10
n°1409069
adrienj
Posté le 19-07-2006 à 02:31:42  profilanswer
 

Sve@r a écrit :


Comme l'a dit Emmanuel, je stocke ce retour dans une variable (de préférence de type "pointeur sur qqchose" ) et je teste la valeur de cette variable
Non, je n'écris jamais de test qui renvoient systématiquement "vrai" (ou "faux" )... :sol:


bein oui évidemment... un if(!NULL) est toujours vrai, donc il execute toujours le code :). Pour ma défence, je sais que ca fait con et pourtant c'est bien vrai, j'entendais bien if(p!=NULL), erreur de frappe, ce qui entraine toute la mécompréhension... ;) Du coup, tu as bien raison d'insister car écrit comme ca c'est logiquement débile.
 

Sve@r a écrit :

Evidemment !!! ici tu demandes "iTableau[adresse début_du_code fonction ComputeIndex]". Comme cette adresse dépasse probablement 5, splint te détecte le bug. Mais ce n'est pas le test dont j'ai parlé...


ok, ok, ... je me relirai la prochaine fois... décidément!  :pt1cable: en oubliant les parenthèses, c'est l'adresse de la fonction qui est renvoyée (au contraire d'autres langages plus "évolués" ). .
comme ca c'est mieux :

Code :
  1. int ComputeIndex(void){
  2. return 15;
  3. }
  4. int main (void){
  5.   int iTableau[5];
  6.   iTableau[ComputeIndex()] = 3;
  7.   return 0;
  8. }


Alors pour le résultat de l'analyse de Splint sur ce petit bout de code, la réponse est :
oui, Splint m'indique un warning out of bounds.
Cependant, il m'indique un risque tout en étant incapable d'évaluer la valeur de ComputeIndex() (qui est 15).
Avec les notations lu "langage" splint pour encadre et donner un peut plus d'info à splint, il devrai être, je crois?, être en mes de donner une évaluation exacte.
Je fais une petite recherche dans la doc pour trouver la bonne synthax et je te dis ca ca marche ou pas.
 

n°1409071
0x90
Posté le 19-07-2006 à 03:12:10  profilanswer
 

Emmanuel Delahaye a écrit :

J'aurais plutôt tendance à utiliser

Code :
  1. if (p != NULL)
  2.    {
  3.       /* OK */
  4.    }
  5.    else
  6.    {
  7.       /* ERR */
  8.    }



 
T'as pas un "risque" de te retrouver avec une indentation à perpète si tu fais ca plusieurs fois dans une même fonction ?


---------------
Me: Django Localization, Yogo Puzzle, Chrome Grapher, C++ Signals, Brainf*ck.
n°1409073
adrienj
Posté le 19-07-2006 à 03:46:45  profilanswer
 

0x90 a écrit :

T'as pas un "risque" de te retrouver avec une indentation à perpète si tu fais ca plusieurs fois dans une même fonction ?


En soit, l'identation imbiquée à l'exrème n'est pas un risque pour l'exécution du programme mais elle est génante pour la lecture du code, c'est vrai. Ce qui est pesant et donc source de perte de temps si ce n'est de bugs pour les modifications futures du code surtout si elles sont effectuées par d'autres codeurs.  
Dans ces cas là, dans le soucis d'une bonne maintenance du code, tu peux blinder le code de commentaires ou alléger le code en le déportant dans une fonction qui dans ce cas n'aura que pour vocation la lisibilité et non la capitalisation.
 
Ou alors, tu fais le sacrifice de la sécurité de ton code au profit d'une identation mini...  
Et puis je dirais que ca dépend du logiciel que tu fais :  
logiciel maison : pas bien méchant,  
logciel commercial : moyen, car tout dépend l'environemment d'éxecution du soft, et ca peut également engendrer une faille de sécurité,
logiciel embarqué de type contrôle moteur, énergie, aéronautique, militaire et autres : la sécurité avant tout

n°1409720
Sve@r
Posté le 19-07-2006 à 20:04:46  profilanswer
 

adrienj a écrit :

bein oui évidemment... un if(!NULL) est toujours vrai, donc il execute toujours le code :). Pour ma défence, je sais que ca fait con et pourtant c'est bien vrai, j'entendais bien if(p!=NULL), erreur de frappe, ce qui entraine toute la mécompréhension... ;) Du coup, tu as bien raison d'insister car écrit comme ca c'est logiquement débile.


héhé... :sol:  
 

adrienj a écrit :

ok, ok, ... je me relirai la prochaine fois... décidément!  :pt1cable: en oubliant les parenthèses, c'est l'adresse de la fonction qui est renvoyée (au contraire d'autres langages plus "évolués" ). .
comme ca c'est mieux :

Code :
  1. int ComputeIndex(void){
  2. return 15;
  3. }
  4. int main (void){
  5.   int iTableau[5];
  6.   iTableau[ComputeIndex()] = 3;
  7.   return 0;
  8. }


Alors pour le résultat de l'analyse de Splint sur ce petit bout de code, la réponse est :
oui, Splint m'indique un warning out of bounds.
Cependant, il m'indique un risque tout en étant incapable d'évaluer la valeur de ComputeIndex() (qui est 15).


Normal, il faudrait qu'il exécute le code ce que je pense qu'il ne peut pas faire. Il devrait d'ailleurs te donner le même résultat si tu remplaces "return 15" par "return 2"...
 

0x90 a écrit :

T'as pas un "risque" de te retrouver avec une indentation à perpète si tu fais ca plusieurs fois dans une même fonction ?


J'ai déjà discuté avec Emmanuel de ce pb. Grosso-modo, t'as 2 façons d'écrire un code de ce genre
façon 1:

void fct(...)
{
    if ((p1=malloc(...)) == NULL)
    {
        <...gestion pb p1...>
        return;
    }
    if ((p2=malloc(...)) == NULL)
    {
        free(p1);
        <...gestion pb p2...>
        return;
    }
    if ((f=fopen(...)) == NULL)
    {
        free(p1);
        free(p2);
        <...gestion pb f...>
        return;
    }
    <... travail réel de la fonction...>
    free(p1);
    free(p2);
    fclose(f);
}


Avantages:
- chaque gestion des pb se fait immédiatement sous l'instruction délicate. Ca facilite la relecture
- lorsqu'on commence à coder le travail réel, on est bien à gauche. On a toute la place qu'on veut pour indenter des structures alternatives ou répétitives nécessaires au travail
Inconvénients:
- obligation de répéter les instructions de libération des ressources => risque d'oubli
- sortie de la fonction en plein milieu (certains n'aiment pas)
     
façon 2:

void fct(...)
{
    if ((p1=malloc(...)) != NULL)
    {
        if ((p2=malloc(...)) != NULL)
        {
            if ((f=fopen(...)) != NULL)
            {
                <... travail réel de la fonction...>
                fclose(f);
            }
            else
            {
                 <...gestion pb f...>
            }
            free(p2);
        }
        else
        {
             <...gestion pb p2...>
        }
        free(p1);
    }
    else
    {
         <...gestion pb p1...>
    }
}


 
Avantages:
- pas de répétition des instructions de libération des ressources
- une seule sortie en fin de fonction
Inconvénients:
- les indentations multiples t'obligent à commencer le code "utile" en plein milieu de l'écran. Si on mettait 8 malloc et 12 fopen, il te resterait 3 caractères par ligne pour commencer le code
- la gestion du tout premier "malloc" échoué se fait tout à la fin de la fonction ce qui ne facilite pas la relecture ultèrieure surtout si le code "utile" fait 150 pages
 
Pour le reste c'est une question de choix personnel. C'est toi qui code...
 

adrienj a écrit :

Dans ces cas là, dans le soucis d'une bonne maintenance du code, tu peux blinder le code de commentaires ou alléger le code en le déportant dans une fonction qui dans ce cas n'aura que pour vocation la lisibilité et non la capitalisation.


Cela se fait déjà pas mal car, en plus d'améliorer la lisibilité, le fait de découper les gros pb en tâches simples facilite le passage intellectuel à l'approche "objet"

Message cité 1 fois
Message édité par Sve@r le 19-07-2006 à 20:05:10

---------------
Vous ne pouvez pas apporter la prospérité au pauvre en la retirant au riche.
n°1409752
adrienj
Posté le 19-07-2006 à 20:29:47  profilanswer
 

Pour eviter toutes confusions, voici une petite precision :
splint est un analyseur statique de code. En gros, il analyse le code a la maniere du compilateur en allant plus loin. Il indique bien plus de types de warning qu'un compilateur comme gcc par exemple. Bien souvent, pour aider splint, il faut anoter le code, en indiquant par exemple qu'un pointeur a le droit d'etre nul en argument de fonction, ou pour indiquer la valeur max que devrait avoir un index parcourant un tableau...
Cependant, l'analyse statique a des limites, celles du statisme justement. On ne peut pas aller aussi loin dans l'analyse qu'en runtime. Mais, au meme titre que de prendre en compte les warnings du compilateur, c'est un outil qui va plus loin dans la securite aussi bien au niveau de la structure que de la precision du code. Et puis j'ajouterai formateur.
 
Voici deux articles qui en parlent (en anglais) :
https://pse.cheme.cmu.edu/wiki/view [...] deAnalysis
qui nous parle de trois approches pour le debug statique du code avec les bons et les mauvais côtés : ne pas s'occuper des warnings du compilateur, les prendre en compte et faire les modifications en consequence ou debusquer encore plus de warnings en utilisant splint.
http://palisade.plynt.com/issues/2 [...] /?show=ans : les mesures a prendre pour se premunir et detecter les depassements memoire.
 
Pourquoi une analyse statique ?
Postula : tester tous les chemins possibles en runtime est difficile. Quand un bug est mis en évidance, il peut être difficilement localisable dans les sources. Autre cas : tester un logiciel communiquant avec un instrument ou périphérique sans ce dernier est problématique.
L'analyse statique peut apporter des solutions à ces problèmes :
- tous les chemins d'exécution peuvent être analysés sans exécuter le programme ;
- découvrir la racine éxacte du problème qui peut alors être aisément supprimé ;
mais aussi ses manques :
- l'analyse heuristique peut être imprécise ;
- il peut y avoir des warnings qui ne le sont pas vraiment (l'analyse suit rigoureusement la norme C ANSI) ;
- détecter 100% des erreurs est théoriquement impossible ;
- elle demande quelques fois que le dévoloppeur ajoute des anotations dans le code pour guider l'analyse.
 
Une liste de logiciels d'analyse statique :
- FlawFinder, ITS4, RATS : analyse lexicale : exemple, va toujours donner une warning sur un strcpy sans se soucier des arguments (à la différence d'un strncpy).
- Splint : analyse sémantique : exemple, peut analyser la taille et le type des argument d'un strcpy pour indiquer ou non un warning,  mais nécessite l'aide d'anotations dans certain cas.
- Sparse : je ne le connais pas mais c'est un analyseur statique qu'avait développé initialement Linus Torvalds pour déboguer le noyaux de Linux.
- Cqual, Csur, CCured : pas testés.
 
J'essais d'évaluer splint en le poussant dans ses retranchements, afin de connaitre réellement les limites de l'analyse statique et de ce logicel en particulier.


Message édité par adrienj le 20-07-2006 à 04:11:00
n°1409865
0x90
Posté le 20-07-2006 à 04:20:52  profilanswer
 

Sve@r a écrit :

héhé... :sol:  
 
 
Normal, il faudrait qu'il exécute le code ce que je pense qu'il ne peut pas faire. Il devrait d'ailleurs te donner le même résultat si tu remplaces "return 15" par "return 2"...
 
 
J'ai déjà discuté avec Emmanuel de ce pb. Grosso-modo, t'as 2 façons d'écrire un code de ce genre
façon 1:

void fct(...)
{
    if ((p1=malloc(...)) == NULL)
    {
        <...gestion pb p1...>
        return;
    }
    if ((p2=malloc(...)) == NULL)
    {
        free(p1);
        <...gestion pb p2...>
        return;
    }
    if ((f=fopen(...)) == NULL)
    {
        free(p1);
        free(p2);
        <...gestion pb f...>
        return;
    }
    <... travail réel de la fonction...>
    free(p1);
    free(p2);
    fclose(f);
}


Avantages:
- chaque gestion des pb se fait immédiatement sous l'instruction délicate. Ca facilite la relecture
- lorsqu'on commence à coder le travail réel, on est bien à gauche. On a toute la place qu'on veut pour indenter des structures alternatives ou répétitives nécessaires au travail
Inconvénients:
- obligation de répéter les instructions de libération des ressources => risque d'oubli
- sortie de la fonction en plein milieu (certains n'aiment pas)
     
façon 2:

void fct(...)
{
    if ((p1=malloc(...)) != NULL)
    {
        if ((p2=malloc(...)) != NULL)
        {
            if ((f=fopen(...)) != NULL)
            {
                <... travail réel de la fonction...>
                fclose(f);
            }
            else
            {
                 <...gestion pb f...>
            }
            free(p2);
        }
        else
        {
             <...gestion pb p2...>
        }
        free(p1);
    }
    else
    {
         <...gestion pb p1...>
    }
}


 
Avantages:
- pas de répétition des instructions de libération des ressources
- une seule sortie en fin de fonction
Inconvénients:
- les indentations multiples t'obligent à commencer le code "utile" en plein milieu de l'écran. Si on mettait 8 malloc et 12 fopen, il te resterait 3 caractères par ligne pour commencer le code
- la gestion du tout premier "malloc" échoué se fait tout à la fin de la fonction ce qui ne facilite pas la relecture ultèrieure surtout si le code "utile" fait 150 pages
 
Pour le reste c'est une question de choix personnel. C'est toi qui code...
 
 
Cela se fait déjà pas mal car, en plus d'améliorer la lisibilité, le fait de découper les gros pb en tâches simples facilite le passage intellectuel à l'approche "objet"


 
Y'a aussi les goto [:cupra]
Jles conseillerais pas en temps normal, mais pour ce type de gestion d'erreur ca rends souvent le bouzin vachement clair, c'est fait comme ca dans le kernel linux et uniquement pour ca, et ca passe très bien.


---------------
Me: Django Localization, Yogo Puzzle, Chrome Grapher, C++ Signals, Brainf*ck.
n°1409866
adrienj
Posté le 20-07-2006 à 04:36:31  profilanswer
 

0x90 a écrit :

Y'a aussi les goto [:cupra]
Jles conseillerais pas en temps normal, mais pour ce type de gestion d'erreur ca rends souvent le bouzin vachement clair, c'est fait comme ca dans le kernel linux et uniquement pour ca, et ca passe très bien.


J'y pensais également... :hello: ca permet d'avoir une seule sortie et d'alléger le code.
Avec une macro de type :  

Code :
  1. #define CHKERROR(x)          if(x == NULL) goto Error;


n°1413383
adrienj
Posté le 25-07-2006 à 20:35:04  profilanswer
 

Voici un nouveau test sous splint pour la détection de dépassement mémoire:

Code :
  1. void FillBuffer(int *piBuffer)
  2. {
  3. int i;
  4. for(i=0;i<10;i++)
  5.  piBuffer[i] = 3;
  6. }
  7. int main (void)
  8. {
  9. int tiTableau[5];
  10. FillBuffer(tiTableau);
  11. return 0;
  12. }


Ici Splint ne détecte qu'il y a possibilité de dépassement mémoire, au niveau de la boucle for de la fonction FillBuffer. On peut se dire alors que ca marche ! Mais, en remplacant le 10 par 5, Splint ne devrait alors ne pas signaler de bug de dépassement mémoire. Or, splint m'indique toujours qu'il y a possibilité de dépassement mémoire.
C'est là que les anotations spéciales de splint entrent en jeux.
J'insère cette anotation : /*@requires maxSet(piBuffer) >= 4@*/, pour indiquer à Splint que le que le pointeur piBuffer doit avoir une taille d'au moins 5 (de 0 à 4)
Ce qui donne :

Code :
  1. void FillBuffer(int *piBuffer)
  2. /*@requires maxSet(piBuffer) >= 4@*/
  3. {
  4. int i;
  5. for(i=0;i<5;i++)
  6.  piBuffer[i] = 3;
  7. }
  8. int main (void)
  9. {
  10. int tiTableau[5];
  11. FillBuffer(tiTableau);
  12.   return 0;
  13. }

 
Splint ne m'indique plus la possibilité de dépassement de mémoire. Ca marche ! :)
Par contre, si je retourne à mon 1er test, en remplacant 5 par 10 dans la condition de la boucle for, alors splint m'indique bien qu'il y a possibilité de dépassement mémoire.
Dans ce cas précis, Splint arrive donc à détecter le dépassement mémoire mais nécessite un coup de main : l'anotation du code.
 
Qu'en penssez vous ?  

n°1413395
el muchach​o
Comfortably Numb
Posté le 25-07-2006 à 21:15:19  profilanswer
 

Si on est obligé d'annoter tous les buffers dans toutes les fonctions, c'est pas étonnant que ce type d'outil ne soit pas plus utilisé. :/
En fait, c'est une tentative d'améliorer les compilos dans un langage qui n'est pas du tout sémantiquement protégé contre les erreurs.

Message cité 1 fois
Message édité par el muchacho le 25-07-2006 à 21:18:05

---------------
Les aéroports où il fait bon attendre, voila un topic qu'il est bien
n°1413407
adrienj
Posté le 25-07-2006 à 21:49:41  profilanswer
 

el muchacho a écrit :

En fait, c'est une tentative d'améliorer les compilos dans un langage qui n'est pas du tout sémantiquement protégé contre les erreurs.


splint reconnait ses anotations avec ces tags : /*@ anotation @*/
Il vérifie ensuite la synthax de l'anotation et indique s'il ne comprend pas l'anotation.
 

el muchacho a écrit :

Si on est obligé d'annoter tous les buffers dans toutes les fonctions, c'est pas étonnant que ce type d'outil ne soit pas plus utilisé. :/


Oui, c'est vrai que c'est plus lourd qe de ne rien mettre. C'est comme certains outils de gestion de configuration comme CONTINIUS qui dans certains cas nécessitent des anotations dans le code.  
Je me dis aussi qu'il y a des cas où c'est quand même intéressant, non ? D'un autre côté, pour ces application hyper sécurisées, on peut remplacer l'utilisation de ce genre d'outils en codant "secure" : des if dans tous les sens, ne pas utiliser/coder de fonctions qui ne demandent pas de limites en argument (comme  la différence qu'il y a entre strcpy et strncpy, le dernier demande une limite en argument).
 
"Tu te serviras fréquemment de lint et ses dires tu étudiras avec attention, parce qu'en vérité sa perception et son jugement souvent dépassent les tiens."
-Henry Spencer, "Les dix commandements du programmeur C"  
Lire ce court article http://www.linux-kheops.com/doc/lg [...] -fr-6.html, paru sur le site de la distribution linux-kheops qui nous parle de l'analyse statique et surtout de lint et LcLint (nomé maintenant splint).
Comme dit l'article en conclusion, "Lint, et tout particulièrement une version aussi puissante que LCLint, peut être utilisé pour en apprendre plus sur la programmation du langage C. Le simple fait de réfléchir sur les messages d'erreur et tenter de les faire disparaître vous en donnera un bon apperçu."
Voir également de livre de Peter van der Linden, "Expert C programming - Deep C secrets", qui en parle également.


Message édité par adrienj le 25-07-2006 à 21:56:39
mood
Publicité
Posté le   profilanswer
 


Aller à :
Ajouter une réponse
  FORUM HardWare.fr
  Programmation
  C

  analyseur de code pour dépassement mémoire

 

Sujets relatifs
code a peufiner juste un petit bug....macro qui compare 2 classeurscomment interagir avec dos par le code ?
explication du code "Red Pill" / instruction SIDTnettoyage de code vba access
code pour manager IIS avec asp.net/c#cherche code source virus
Adapter un code généré sous Dev C++ vers du Solaris 5.8 (Unix)code unicode dans un script => non compris par le navigateur ?
code d'erreur copie de fichier[Script bat]Faire un programme de teste de carte mémoire
Plus de sujets relatifs à : analyseur de code pour dépassement mémoire


Copyright © 1997-2022 Hardware.fr SARL (Signaler un contenu illicite / Données personnelles) / Groupe LDLC / Shop HFR