Comportement surprenant de strcpy - C - Programmation
Marsh Posté le 09-03-2009 à 21:55:06
tu as oublié d'enlever le "-2" après le sizeof dans ton code
Marsh Posté le 09-03-2009 à 21:56:32
Sur ma plateforme (Linux x86), pour le premier cas, j'ai ceci:
Citation : sizeof src 37, sizeof dst 36 |
De plus, valgrind n'indique aucune erreur !
Dans le second cas, valgrind n'indique toujours aucune erreur !!!
Par contre, le runtime détecte une corruption de la pile.
J'ai exactement ceci:
Citation :
|
Je n'arrive pas trop à m'expliquer ce comportement.
Marsh Posté le 09-03-2009 à 21:59:41
sligor a écrit : tu as oublié d'enlever le "-2" après le sizeof dans ton code |
Oups, corrigé.
Marsh Posté le 09-03-2009 à 22:05:47
moi dans le "-1" j'ai:
Citation : sizeof src 37, sizeof dst 36 |
valgrind râle:
Citation :
|
et dans le "-2" j'ai:
Citation :
|
valgrind râle:
Citation :
|
chez moi c'est beaucoup plus compréhensible
je dirais que dans ta pile src et dst sont inversés par rapport à la mienne
edit: tu peux essayer de rajouter printf("dst-src=%d\n",dst-src); pour voir ?
Marsh Posté le 09-03-2009 à 22:08:44
Euh, c'est bien le même programme, là ? Parce que l'overlap, je vois vraiment pas pourquoi
Marsh Posté le 09-03-2009 à 22:11:56
el muchacho a écrit : Euh, c'est bien le même programme, là ? Parce que l'overlap, je vois vraiment pas pourquoi |
si dst est juste avant src dans la pile, ce qui est le cas chez moi
Marsh Posté le 09-03-2009 à 22:12:58
sligor a écrit : |
Code :
|
Résultat:
dst-src=-37
dst-src=-37
(résultat attendu)
Marsh Posté le 09-03-2009 à 22:14:21
sligor a écrit :
|
Ah oui, ok, pas con. C'est quoi, ta machine, à titre indicatif ?
Marsh Posté le 09-03-2009 à 22:19:06
el muchacho a écrit :
Résultat: |
donc tu as aussi dst avant src, tu as la même organisation de pile que moi, du coup je ne comprends pas pourquoi tu n'as pas 0 et 1 comme longueur de dst avec "-1" et "-2", à moins que le compilateur change l'organisation sur les autres valeurs.
Tu peux aussi tester avec les "-1" et "-2" ?
ma config: debian lenny, gcc 4.3.2, x86 32bits
Marsh Posté le 09-03-2009 à 22:22:06
sligor a écrit : |
J'ai la même version de gcc que toi, sous Ubuntu.
Citation : Tu peux aussi tester avec les "-1" et "-2" ? |
J'obtiens la même chose qu'avant.
Taz a écrit : Y a quoi de surprenant dans un UB ? |
Un quoi ??
Marsh Posté le 09-03-2009 à 22:34:01
el muchacho a écrit :
|
Tu altères la pile, et le résultat est "indéfini" (dépendant de comment le compilateur va optimiser le code, l'ordre des variables, et probablement l'architecture aussi). Le seul moyen de vraiment en être sur, c'est de regarder le code compilé.
el muchacho a écrit : Que me sort valgrind ? |
Valgrind a beaucoup de mal à détecter le petage de pile, vu qu'il ne fait pas de bound checking sur des agrégats en pile ou statique.
C'est particulièrement difficile à détecter de toute manière, il n'y a pas autant de liberté que dans le tas ou on peut facilement placer des tokens ou des pages invalides pour provoquer des fautes d'accès.
Ce cas là de toute manière, tu peux l'empecher avec des canary. Compile avec -fstack-protector...
Marsh Posté le 09-03-2009 à 22:35:45
el muchacho a écrit : Attention, question piège spécial experts. Voici un programme tout con.
Ca me sort: sizeof src 37, sizeof dst 37 Normal. Maintenant, si je remplace la ligne 8 par A votre avis, que se passe-t'il à l'exécution ? Que me sort valgrind ? Même question si je remplace la ligne par Appelez-moi noob, mais le résultat est tout de même assez surprenant. |
Rien de surprenant.
Les deux résultats sont identiques avec -1 et -2 (aléatoirement), et valgrind devrait te sortir un truc du genre "read out of bound" ou ce genre de chose.
strcpy() recopie src dans dst jusqu'à trouver un '\0'. Sans se préoccuper de la taille de dst. Donc ça recopie src dans dst en dépassant les capacités de cette dernière variable.
edit : ah ok j'avais pas lu la suite, et je me serais attendu aussi à ce que valgrind râle. Oubliez mon post qui arrive comme un cheveu sur la soupe.
Marsh Posté le 09-03-2009 à 22:36:49
Gf4x3443 a écrit : Tu altères la pile, et le résultat est "indéfini" (dépendant de comment le compilateur va optimiser le code, l'ordre des variables, et probablement l'architecture aussi). Le seul moyen de vraiment en être sur, c'est de regarder le code compilé. |
sauf que si dst-src=-37 il ne peut pas péter la pile avec ce qu'il fait
donc j'aimerais bien qu'il revérifie avec sizeof(src)-1 et sizeof(src)-2
Marsh Posté le 09-03-2009 à 22:42:25
Gf4x3443 a écrit :
|
Citation : Valgrind a beaucoup de mal à détecter le petage de pile, vu qu'il ne fait pas de bound checking sur des agrégats en pile ou statique. C'est particulièrement difficile à détecter de toute manière, il n'y a pas autant de liberté que dans le tas ou on peut facilement placer des tokens ou des pages invalides pour provoquer des fautes d'accès. |
C'est ce que je me disais aussi.
Citation : Ce cas là de toute manière, tu peux l'empecher avec des canary. Compile avec -fstack-protector... |
je vais essayer ça.
Taz a écrit : Undefined Behaviour |
OK.
sligor a écrit :
|
J'ai 37 à chaque fois, et j'ai la même chose qu'auparavant pour le reste du code.
Et par ailleurs, j'ai la même version de gcc que toi.
Marsh Posté le 09-03-2009 à 22:45:19
Ah ouais, tiens, dans le même genre. Qu'est ce qu'il y a de foireux avec ce code :
Code :
|
Spoiler : En fait il n'y a rien de vraiment faux dans ce code. |
Marsh Posté le 09-03-2009 à 22:49:04
Ah putaing, cong, en fait, sligor a raison : avec le -1, j'ai +37 ! gcc change l'ordre des chaînes dans la pile !
Marsh Posté le 09-03-2009 à 22:51:13
donc du coup la corruption de pile est plus logique, tout s'explique
Marsh Posté le 09-03-2009 à 22:52:15
ReplyMarsh Posté le 09-03-2009 à 22:52:35
tpierron a écrit : Ah ouais, tiens, dans le même genre. Qu'est ce qu'il y a de foireux avec ce code : |
Ayé, je compatis. J'ai eu aussi des problèmes avec des LARGE_INTEGER avec MinGW. Je me rappelle avoir passé ma semaine à essayer de comprendre pourquoi je n'avais pas de bonnes valeurs affichées dans des printf().
En fait, c'est là tout le challenge: entre un code source et le code compilé, c'est non trivial de qualifier du code aujourd'hui. Enfin du moins, dans de l'embarqué, ca m'a valu quelques crises
Marsh Posté le 09-03-2009 à 22:53:16
tpierron a écrit : Ah ouais, tiens, dans le même genre. Qu'est ce qu'il y a de foireux avec ce code :
|
Hm. C'est pas un problème de norme plutôt ? Je zone les manpages depuis 5min, et il y en a avec hh, et d'autres sans.
Pour rire j'ai regardé sur Solaris 8 (ok, côté norme c'est pas ce qu'il y a de plus strict cet OS ), pas de hh. Sur Solaris 10 par contre, il y est.
edit : trouvé. "hh" c'est bien C99, d'où les différences de comportement avec certains compilateurs pas à jour.
Marsh Posté le 09-03-2009 à 22:53:40
Sinon, le -fstack-protector, j'ai pas trop vu son effet.
Marsh Posté le 09-03-2009 à 22:57:21
Gf4x3443 a écrit : |
Clair, ce qu'il y a de bien avec le C, c'est que c'est supêr simple, mais on peut passer une vie entière sur ce genre de conneries.
Marsh Posté le 09-03-2009 à 22:57:22
Elmoricq a écrit : Hm. C'est pas un problème de norme plutôt ? Je zone les manpages depuis 5min, et il y en a avec hh, et d'autres sans. |
d'où l'utilité de l'option "-pedantics" avec gcc
mais sur un gros projet développé sans cette option dés le dépat, on a vite d'innombrables warnings
Citation :
|
Marsh Posté le 09-03-2009 à 23:02:28
Il n'aborte pas avec -1?
toulouse$ grep "dst" test.c |
Marsh Posté le 09-03-2009 à 23:06:14
c'est indéterminé ça dépends de plein de choses, on était plus parti pour comprendre ce qu'il se passait plus exactement sur la machine de el muchacho
Marsh Posté le 09-03-2009 à 23:08:46
Chez moi, il n'en a rien à secouer. Je suppose que c'est exactement pour la même raison qu'au-dessus, à savoir que l'ordre des chaînes sur la pile est bizarrement inversé quand je réduis la taille de dst. Ce qui est bizarre, c'est que gcc se comporte comme ça que chez moi (avec les options de compilation par défaut).
Marsh Posté le 09-03-2009 à 23:10:35
Ca c'est fun. C'est quelle version 4 de gcc? 4.1.2 ici (sous NetBSD), pas d'inversion dans les adresses des tableaux, ca fait -37, -36, -35, ... quand je réduis dst.
Marsh Posté le 09-03-2009 à 23:14:20
Citation : gromit:~/dev/SPString$ gcc -v |
Le plus marrant, c'est que c'est le même compilo que sligor, et chez lui, il n'obitent pas la même chose.
sligor, ça donne quoi, chez toi
gcc -v
?
Marsh Posté le 10-03-2009 à 08:24:39
el muchacho a écrit : Attention, question piège spécial experts.
|
Le comportement est indéfini. tout peut arriver.
Citation : A votre avis, que se passe-t'il à l'exécution ? Que me sort valgrind ? |
Personne ne peut le prévoir. Mais le code est faux, c'est certain.
Citation : |
Je ne sais pas de quel résultat tu parles, mais on a pas à s'étonner d'un comportement indéfini. Je répète TOUT peut arriver, y compris un comportement d'apparence normale.
Un code est correct si
1 - il est écrit correctement
2 - il se comporte comme prévu.
Si le #1 est faux tout le reste est sans objet.
Marsh Posté le 10-03-2009 à 08:32:29
tpierron a écrit : En fait il n'y a rien de vraiment faux dans ce code. |
C'est pas un problème de codage, mais de bibliothèque C sous Windows qui, rappelons le une bonne fois pour toute : ne supporte pas C99...
(même si certaines concessions semblent avoir été faites sous Vista SP1, comme le support de "%ll*" )
Marsh Posté le 10-03-2009 à 22:06:43
Emmanuel Delahaye a écrit : |
Le pire c'est que j'avais fait un test rapide pour voir si ça fonctionnait. Comme l'ordre des octets était little-endian, la seule chose qui merdait était le stack overflow (que je n'avais pas pensé à vérifié dans mon test). En big endian, j'aurais vu directement le problème, puisque tout aurait valu 0 (vu que je convertissais des nombres < 10). En fait, le comportement que j'attendais était que le code de retour reflète les formats de conversion non reconnu, jamais j'aurais imaginé que cette lib puisse écrire un int dans un char .
Marsh Posté le 09-03-2009 à 21:52:15
Attention, question piège spécial experts.
Voici un programme tout con.
Si je compile (gcc 4)
Ca me sort:
sizeof src 37, sizeof dst 37
src abcdefghijklmnopqrstuvwxyz0123456789 36
dst abcdefghijklmnopqrstuvwxyz0123456789 36
Normal.
Maintenant, si je remplace la ligne 8 par
char dst[sizeof(src)-1] = "";
A votre avis, que se passe-t'il à l'exécution ? Que me sort valgrind ?
Même question si je remplace la ligne par
char dst[sizeof(src)-2] = "";
Appelez-moi noob, mais le résultat est tout de même assez surprenant.
Message édité par el muchacho le 09-03-2009 à 22:00:37
---------------
Les aéroports où il fait bon attendre, voila un topic qu'il est bien