Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Heap corruption

16 réponses
Avatar
JKB
Bonjour à tous,

Petite question de méthodologie. Lorsque vous avez un code assez
gros (qui a passé tous les tests unitaires avec succès) et qui sur
un couple architecture/OS précis plante avec une corruption du tas,
comment faites-vous pour isoler le problème ?

Je m'explique. J'ai un gros programme de calcul (qui fait des tas de
fork() et de pthread_create()). Ce programme a été développé puis testé
en long, en large et en travers sous Linux/amd64. Sous
Solaris/sparc64, il plante aléatoirement sur une violation d'accès
(libmalloc et libmtmalloc). En forçant l'utilisation de la libumem,
il échoue toujours avec une corruption du tas. Je pensais
tout d'abord à un sombre problème d'alignement qui faisait qu'un
tableau dépassait quelque part mais cela ne semble pas être le cas.
J'ai essayé de trouver dans quelle partie du programme cette
corruption avait lieu sans succès.

mdb ne m'est d'aucun secours (les différentes tâches sont
synchronisées par des signaux et mdb gère ça très mal). En fait,
l'utilisation de mbd fait que le programme entre dans des deadlocks.
Malgré cela, je me suis acharné et mdb ne m'a montré aucune écriture
illégale à l'adresse en question.

gdb me permet de faire une analyse post-mortem, mais sans me dire ce
qui a bien pu écraser ce bout de mémoire.

libumem provoque un plantage _toujours_ au même moment, mais je
n'arrive pas à voir vec libumem ce qui provoque ce plantage
(l'horodatage renvoyé par mdb ::umem_status n'existe pas dans le
fichier de transaction !).

Partant du principe que l'informatique est quelque chose de
reprodctible, j'ai fait tourner ce programme dans valgrind pas loin
d'une semaine sous Linux/amd64 et valgrind n'a renvoyé aucune erreur
(avec la libumem sous Solaris, ça merdoie dès le début). J'ai aussi
poussé le vice à compiler le programme sous Linux/sparc64 histoire
de confirmer ou d'infirmer le rapport entre le bug et le processeur.
Le programme a tourné là aussi plusieurs jours sans provoquer
d'erreur.

J'ai essayé plus tordu : electric fence, mais le programme en
question manipule énormément de données et il explose sur un "out of
memory".

En fait, mon problème est de savoir si mon code est fautif ou s'il
s'agit de la libpq de PostgreSQL voire de getaddrinfo() de Solaris
(le code plante dans __IPv6_malloc() appelé depuis getaddrinfo()
lui-même appelé depuis une fonction de la libpq.a alors que la pile
IPv6 n'est pas configurée sur le serveur en question).

Merci de vos avis,

JKB

--
Si votre demande me parvient sur carte perforée, je titiouaillerai très
volontiers une réponse...
=> http://grincheux.de-charybde-en-scylla.fr

10 réponses

1 2
Avatar
Lucas Levrel
Le 15 décembre 2010, JKB a écrit :

Merci de vos avis,



Est-ce que c'est le même compilateur pour tous tes tests ?

--
LL
Avatar
JKB
Le Thu, 16 Dec 2010 12:21:44 +0100,
Lucas Levrel écrivait :
Le 15 décembre 2010, JKB a écrit :

Merci de vos avis,



Est-ce que c'est le même compilateur pour tous tes tests ?



Oui. gcc 4.4.3/gfortran 4.4.3. L'éditeur des liens est GNU ld dans
tous les cas. Les bibliothèques sont compilées par mes soins et
liées statiquement.

Je suis en train d'essayer de compiler la chose avec purify mais je
butte sur l'édition des liens (cannot find elf64 machinchose...).

JKB

--
Si votre demande me parvient sur carte perforée, je titiouaillerai très
volontiers une réponse...
=> http://grincheux.de-charybde-en-scylla.fr
Avatar
JKB
Le Thu, 16 Dec 2010 12:21:44 +0100,
Lucas Levrel écrivait :
Le 15 décembre 2010, JKB a écrit :

Merci de vos avis,



Est-ce que c'est le même compilateur pour tous tes tests ?



Je disjoncte... Le programme en question tourne depuis 48 heures
sous Purify et les seules seules erreurs remontées sont :

MSE: Memory segment error (38247 times)
This is occurring while in thread 1:
_syscall6 [libc.so.1]
sigpending [rtlib.o]
Purchase product for additional stack frames and source line
numbers.
Accessing a memory range that crosses a memory segment boundary.
Addressing 0x100f2ec10 for 8 bytes ending at 0x100f2ec18,
which is neither in the heap nor the main stack.

Il n'y a aucune écriture fautive en mémoire capable de générer une
corruption du tas !...

Je crois que je vais installer un Solaris sur mon U60 pour tester
plus facilement (la T1000 est en salle blanche et ce n'est pas
évident) et isoler les paramètres un a un jusqu'à trouver ce qui
coince !

JKB

--
Si votre demande me parvient sur carte perforée, je titiouaillerai très
volontiers une réponse...
=> http://grincheux.de-charybde-en-scylla.fr
Avatar
JKB
Le Thu, 16 Dec 2010 12:21:44 +0100,
Lucas Levrel écrivait :
Le 15 décembre 2010, JKB a écrit :

Merci de vos avis,



Est-ce que c'est le même compilateur pour tous tes tests ?



Quelques nouveautés avec une question...

J'ai fait tourner le programme en question sous Purify et j'obtiens
une erreur ABR sur l'appel suivant :

(*s_objet_argument).objet est un pointeur sur un void.
real8 est un double
erreur est un entier 32 bits.

Appel sur lequel purify râle :

f77lnrp_((real8 *) (*s_objet_argument).objet,
(real8 *) (*s_objet_resultat).objet, &erreur);

La fonction est écrite en Fortran77.

subroutine F77LNRP(ARGUMENT, RESULTAT, ERREUR)

integer*4 ERREUR
real*8 ARGUMENT
real*8 RESULTAT
...
end

et son prototype C est :

void f77lnrp_(real8 *argument, real8 *resultat, integer4 *erreur);

D'où ma question : pourquoi purify râle-t-il ? L'appel à la fonction
se fait bien et les valeurs renvoyées semblent bonnes.

Cordialement,

JKB


--
Si votre demande me parvient sur carte perforée, je titiouaillerai très
volontiers une réponse...
=> http://grincheux.de-charybde-en-scylla.fr
Avatar
Marc Boyer
Le 17-12-2010, JKB a écrit :
Quelques nouveautés avec une question...

J'ai fait tourner le programme en question sous Purify et j'obtiens
une erreur ABR sur l'appel suivant :

(*s_objet_argument).objet est un pointeur sur un void.
real8 est un double
erreur est un entier 32 bits.



Quelques questions naives:
1) est-ce que sizeof(void*) == sizeof(real8*) ?
2) est-ce que ça rale aussi si tu changes le code en
ne passant pas deux fois le même pointeur ? Genre:

real8 res;
f77lnrp_((real8 *) (*s_objet_argument).objet, &res, &erreur);
memcpy( (*s_objet_argument).objet , &res, sizeof(real8) );
// ou
*((real8 *) (*s_objet_argument).objet)= res;

Marc Boyer, pas inspiré du tout
--
En prenant aux 10% des francais les plus riches 12% de leurs revenus,
on pourrait doubler les revenus des 10% les plus pauvres.
http://www.inegalites.fr/spip.php?article1&id_mot0
Avatar
JKB
Le Fri, 17 Dec 2010 14:14:59 +0000 (UTC),
Marc Boyer écrivait :
Le 17-12-2010, JKB a écrit :
Quelques nouveautés avec une question...

J'ai fait tourner le programme en question sous Purify et j'obtiens
une erreur ABR sur l'appel suivant :

(*s_objet_argument).objet est un pointeur sur un void.
real8 est un double
erreur est un entier 32 bits.



Quelques questions naives:
1) est-ce que sizeof(void*) == sizeof(real8*) ?
2) est-ce que ça rale aussi si tu changes le code en
ne passant pas deux fois le même pointeur ? Genre:



Sauf erreur de ma part (ce qui peut arriver...), je ne passe pas
deux fois le même pointeur.

Comment pourrais-avoir deux tailles de pointeurs différentes ? En
fait, je ne vois pas pourquoi sizeof(void*) pourrait être différente
de sizeof(real8*)...

real8 res;
f77lnrp_((real8 *) (*s_objet_argument).objet, &res, &erreur);
memcpy( (*s_objet_argument).objet , &res, sizeof(real8) );
// ou
*((real8 *) (*s_objet_argument).objet)= res;

Marc Boyer, pas inspiré du tout



Moi non plus, d'autant que je viens d'avoir un message de purify
qui me signale un passage dans une fonction qui n'est _pas_ utilisée
dans le code en question... Je commence à me poser la question de la
fiabilité du truc. La question initiale du thread reste donc
d'actualité.

Histoire de séparer les problèmes, je suis ne train d'installer un
Solaris 10 et un 11 sur mon U60 pour vérifier si ça vient de l'OS ou
non. Je commence à avoir de sérieux doutes sur la question.

Cordialement,

JKB

--
Si votre demande me parvient sur carte perforée, je titiouaillerai très
volontiers une réponse...
=> http://grincheux.de-charybde-en-scylla.fr
Avatar
Marc Boyer
Le 17-12-2010, JKB a écrit :
Le Fri, 17 Dec 2010 14:14:59 +0000 (UTC),
Marc Boyer écrivait :
Le 17-12-2010, JKB a écrit :
Quelques nouveautés avec une question...

J'ai fait tourner le programme en question sous Purify et j'obtiens
une erreur ABR sur l'appel suivant :

(*s_objet_argument).objet est un pointeur sur un void.
real8 est un double
erreur est un entier 32 bits.



Quelques questions naives:
1) est-ce que sizeof(void*) == sizeof(real8*) ?
2) est-ce que ça rale aussi si tu changes le code en
ne passant pas deux fois le même pointeur ? Genre:



Sauf erreur de ma part (ce qui peut arriver...), je ne passe pas
deux fois le même pointeur.



Désolé, on est vendredi.

Comment pourrais-avoir deux tailles de pointeurs différentes ? En
fait, je ne vois pas pourquoi sizeof(void*) pourrait être différente
de sizeof(real8*)...



Moi non plus, mais puisque ça rale dans un appel C avec une conversion
qui est convertit en appel Fortran, je me demande. D'autant que dans
mon souvenir, les conventions de passage de paramètres sont différents
sur sparc et Intel, donc peut-être moins testés sur sparc.
D'où l'idée de repasser par des variables intermédaires.

Moi non plus, d'autant que je viens d'avoir un message de purify
qui me signale un passage dans une fonction qui n'est _pas_ utilisée
dans le code en question... Je commence à me poser la question de la
fiabilité du truc. La question initiale du thread reste donc
d'actualité.



J'aimerai pas être à ta place.
Je pense que mes compétences ont été dépassées.

Bonne chance,
Marc Boyer
--
En prenant aux 10% des francais les plus riches 12% de leurs revenus,
on pourrait doubler les revenus des 10% les plus pauvres.
http://www.inegalites.fr/spip.php?article1&id_mot0
Avatar
Marc
JKB wrote:

Petite question de méthodologie. Lorsque vous avez un code assez
gros (qui a passé tous les tests unitaires avec succès) et qui sur
un couple architecture/OS précis plante avec une corruption du tas,
comment faites-vous pour isoler le problème ?



En plus de ce qui est déjà fait, j'aurais sans doute essayé de faire
tourner dans dbx avec check -all. Mais sans conviction.
Avatar
JKB
Le Fri, 17 Dec 2010 14:56:16 +0000 (UTC),
Marc Boyer écrivait :
Le 17-12-2010, JKB a écrit :
Le Fri, 17 Dec 2010 14:14:59 +0000 (UTC),
Marc Boyer écrivait :
Le 17-12-2010, JKB a écrit :
Quelques nouveautés avec une question...

J'ai fait tourner le programme en question sous Purify et j'obtiens
une erreur ABR sur l'appel suivant :

(*s_objet_argument).objet est un pointeur sur un void.
real8 est un double
erreur est un entier 32 bits.



Quelques questions naives:
1) est-ce que sizeof(void*) == sizeof(real8*) ?
2) est-ce que ça rale aussi si tu changes le code en
ne passant pas deux fois le même pointeur ? Genre:



Sauf erreur de ma part (ce qui peut arriver...), je ne passe pas
deux fois le même pointeur.



Désolé, on est vendredi.

Comment pourrais-avoir deux tailles de pointeurs différentes ? En
fait, je ne vois pas pourquoi sizeof(void*) pourrait être différente
de sizeof(real8*)...



Moi non plus, mais puisque ça rale dans un appel C avec une conversion
qui est convertit en appel Fortran, je me demande. D'autant que dans
mon souvenir, les conventions de passage de paramètres sont différents
sur sparc et Intel, donc peut-être moins testés sur sparc.
D'où l'idée de repasser par des variables intermédaires.

Moi non plus, d'autant que je viens d'avoir un message de purify
qui me signale un passage dans une fonction qui n'est _pas_ utilisée
dans le code en question... Je commence à me poser la question de la
fiabilité du truc. La question initiale du thread reste donc
d'actualité.



J'aimerai pas être à ta place.
Je pense que mes compétences ont été dépassées.



Je crois que les miennes sont dépassées depuis longtemps... Merci
d'avoir essayé.

JKB

--
Si votre demande me parvient sur carte perforée, je titiouaillerai très
volontiers une réponse...
=> http://grincheux.de-charybde-en-scylla.fr
Avatar
JKB
Le Fri, 17 Dec 2010 15:41:47 +0000 (UTC),
Marc écrivait :
JKB wrote:

Petite question de méthodologie. Lorsque vous avez un code assez
gros (qui a passé tous les tests unitaires avec succès) et qui sur
un couple architecture/OS précis plante avec une corruption du tas,
comment faites-vous pour isoler le problème ?



En plus de ce qui est déjà fait, j'aurais sans doute essayé de faire
tourner dans dbx avec check -all. Mais sans conviction.



Tiens, je vais essayer après la fin du purify en cours.

Cordialement,

JKB

--
Si votre demande me parvient sur carte perforée, je titiouaillerai très
volontiers une réponse...
=> http://grincheux.de-charybde-en-scylla.fr
1 2