analyseur de code pour dépassement mémoire - C - Programmation
Marsh Posté le 13-07-2006 à 21:59:25
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...
Marsh Posté le 14-07-2006 à 01:20:17
adrienj a écrit : |
µOui, mais ils ne sont pas gratuits. 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.
Marsh Posté le 14-07-2006 à 01:44:16
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]=..." ) |
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 ?? sans doute que j'avais pourtant fait gaffe à ce que je codais 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!
Marsh Posté le 14-07-2006 à 11:30:26
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...
Marsh Posté le 15-07-2006 à 12:00:28
adrienj 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/
Marsh Posté le 15-07-2006 à 21:20:40
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 :
|
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 : |
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. |
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).
Marsh Posté le 16-07-2006 à 13:40:04
adrienj a écrit : |
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.
Marsh Posté le 16-07-2006 à 22:21:16
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 :
|
Marsh Posté le 17-07-2006 à 21:09:31
el muchacho a écrit : |
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
|
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
Marsh Posté le 18-07-2006 à 23:20:06
adrienj a écrit : Bonne question, je viens de le tester le code suivant (encore un code hyper simple ) avec Splint :
|
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" )...
Marsh Posté le 19-07-2006 à 02:31:42
Sve@r 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.
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! 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 :
|
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.
Marsh Posté le 19-07-2006 à 03:12:10
Emmanuel Delahaye a écrit : J'aurais plutôt tendance à utiliser
|
T'as pas un "risque" de te retrouver avec une indentation à perpète si tu fais ca plusieurs fois dans une même fonction ?
Marsh Posté le 19-07-2006 à 03:46:45
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
Marsh Posté le 19-07-2006 à 20:04:46
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é...
adrienj a écrit : ok, ok, ... je me relirai la prochaine fois... décidément! en oubliant les parenthèses, c'est l'adresse de la fonction qui est renvoyée (au contraire d'autres langages plus "évolués" ). .
|
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(...) |
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(...) |
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"
Marsh Posté le 19-07-2006 à 20:29:47
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.
Marsh Posté le 20-07-2006 à 04:20:52
Sve@r a écrit : héhé...
|
Y'a aussi les goto
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.
Marsh Posté le 20-07-2006 à 04:36:31
0x90 a écrit : Y'a aussi les goto |
J'y pensais également... ca permet d'avoir une seule sortie et d'alléger le code.
Avec une macro de type :
Code :
|
Marsh Posté le 25-07-2006 à 20:35:04
Voici un nouveau test sous splint pour la détection de dépassement mémoire:
Code :
|
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 :
|
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 ?
Marsh Posté le 25-07-2006 à 21:15:19
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.
Marsh Posté le 25-07-2006 à 21:49:41
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.
Marsh Posté le 13-07-2006 à 21:15:55
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 :
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 :
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
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 ?