IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

 C Discussion :

my_memset segmentation fault


Sujet :

C

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre confirmé
    Profil pro
    Inscrit en
    Janvier 2013
    Messages
    150
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2013
    Messages : 150
    Par défaut my_memset segmentation fault
    Bonsoir,

    J'essaye de réecrirre la fonction memset en fonction de ce que me dis le man, cependant comment contrôler le fait que len ne dois pas être négative ?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
     
    void *my_memset(void *b, int c, size_t len)
    {
    	unsigned char	*s;
    	size_t	         i;
     
    	i = 0;
    	s = (unsigned char*) b;
    	while (i < len)
    	{
    		s[i] = (unsigned char)c;
    		i++;
    	}
    	return (b);
    }
    Si j'essaye d'appeler la fonction comme ça my_memset(b,2,-1), (len négative) => segmentation fault. Je comprend parfaitement l'erreur, je sors des limites de ma variable s.
    La fonction standard memset quand à elle gére bien ce cas puisqu'elle me renvoi : abort ...

    Merci pour vos explications.

  2. #2
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2009
    Messages
    4 493
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 493
    Billets dans le blog
    1
    Par défaut
    Bonjour,

    Tu n'as pas dû te renseigné correctement sur le type size_t. Ce type est normalement non-signé ; il en existe une version signée, ssize_t. (J'espère ne pas dire de bêtise, il est tard, la journée a été dure).

    Ainsi, tu ne peux pas appeler ta fonction de cette manière sans que ton compilateur émette un warning, *sauf* si tu n'as pas activé les options d'usage pour la génération des warnings (-Wall avec gcc par exemple).

    Tu ne sors pas des limites de s. -1 est simplement converti en valeur positive. Sans doute 0xFFFFFFFF sur une machine 32 bits car c'est la représentation de -1 en complément à 2. Je te laisse imaginer ce que fait ton code avec une telle valeur en entrée et pourquoi tu te choppes une erreur de segmentation.

    Autres remarques :
    • séparer déclarations et initialisations me semble contre-productif ici
    • tu castes le pointeur void* en unsigned char* alors que tu souhaites mettre un int dedans. Pourquoi ? :((

  3. #3
    Membre confirmé
    Profil pro
    Inscrit en
    Janvier 2013
    Messages
    150
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2013
    Messages : 150
    Par défaut
    Merci Bktero pour ta réponse ultra-rapide

    Ainsi, tu ne peux pas appeler ta fonction de cette manière sans que ton compilateur émette un warning, *sauf* si tu n'as pas activé les options d'usage pour la génération des warnings (-Wall avec gcc par exemple).
    Hum, je suis sous Mac, je compile ainsi : gcc -Wall -Werror -Wextra. Aucun warning, même lorsque j'utilise la fonction standard memset cela me renvoi un abord comme dit précédemment.

    Tu ne sors pas des limites de s. -1 est simplement converti en valeur positive. Sans doute 0xFFFFFFFF sur une machine 32 bits car c'est la représentation de -1 en complément à 2. Je te laisse imaginer ce que fait ton code avec une telle valeur en entrée et pourquoi tu te choppes une erreur de segmentation.
    C'est ce que j'appelle effectivement sortir de mon tableau, effectivement en prenant le complément à deux ma valeur deviens positif ( un très gros chiffre - je l'ai afficher ), ce qui m'amène à accéder à une case mémoire n'appartenant pas à ma variable s :s[GRAND NOMBRE] = (unsigned char)c; => segmentation fault

    tu castes le pointeur void* en unsigned char* alors que tu souhaites mettre un int dedans. Pourquoi ? (
    J'ai fais ainsi car le man me dit : "The memset() function writes len bytes of value c (converted to an unsigned char) to the string b."

    Je pense que ma fonction est juste car j'ai comparer la standard avec la mienne, la seule différence réside dans le fait que lorsque j'ai une erreur segmentation fault, la standard répond : [1] 6644 abort ./a.out.

  4. #4
    Membre prolifique
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 832
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 832
    Billets dans le blog
    1
    Par défaut
    Bonjour

    Bktero a bien trouvé le problème. Ton seg_fault vient du fait que -1 converti en size_t (donc probablement en unsigned long) devient 4 294 967 295 et que ta zone à remplir ne fait probablement pas cette taille

    Mais pour répondre à ta question, si ta fonction ne stocke pas la valeur reçue dans un "signed" tu ne peux absolument pas détecter que t'as reçu un négatif. ssize_t ("signed size_t") peut convenir. D'ailleurs je ne pense pas que le memset d'origine (qui stocke lui-aussi la taille dans un size_t) détecte le négatif parce que si c'était le cas, tu n'aurais pas de "abort" (renvoyé par ton OS et non par la fonction). Surtout que chez-moi, ça me fait un segfault preuve qu'il détecte que dalle !!!

    Autre chose: tu peux encore optimiser ta fonction en incrémentant directement le pointeur "s" plutôt que de taper dans s[i] et remplacer initialisation, while et incrément par un simple for (en fait tout tient dans le for qui n'aura alors aucun autre code)

    PS: moi non plus je ne sais pas pourquoi memset (le vrai) demande lui-aussi un int alors qu'il sera stocké fatalement dans un tableau d'octets. Peut-être par soucis d'optimisation lors du passage de paramètres (calage sur les mots machine)...

    PS2: tu ne dois jamais émettre une hypothèse sur un comportement indéterminé (comme par exemple dire que memset() détecte le négatif parce que tu as un abort). Un comportement indéterminé est exactement ce que le terme signifie, c'est à dire "non prévisible". Un coup tu peux avoir "abort", un autre "segfault" (comme chez-moi), un 3° tu peux même avoir ta fonction qui s'exécute correctement et qui ira remplir sans soucis 4 294 967 295 octets à partir de l'adresse reçue (sans soucis "immédiat" s'entend car il est plus que probable que dans ces 4 294 967 295 octets, certains sont utilisés par d'autres variables de ton programme et que la suite du programme s'en ressentira...)
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  5. #5
    Membre confirmé
    Profil pro
    Inscrit en
    Janvier 2013
    Messages
    150
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2013
    Messages : 150
    Par défaut
    Bonjour,

    J'ai bien compris vos explications merci. Cependant mes programmes personnelle ne m'ont jamais renvoyé un abort, j'ai donc ( mal ) pensé que ces memset standard qui détecter le segfault, ça me rassure que ça ne soit pas le cas car je n'ai pas trouvé comment vu le type de len...
    Quoi qu'il en soit merci, je vais prendre vos conseils pour l'optimisation

  6. #6
    Expert éminent
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 395
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 395
    Par défaut
    Je pense que j'ai une petite idée de la vérification pratiquée par memset:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    void *my_memset(void *b, int c, size_t len)
    {
    	unsigned char	*s;
    	size_t	         i;
    	uintptr_t bufferStart;
     
    	bufferStart = (uintptr_t)b;
    	assert((bufferStart + len) >=bufferStart);
     
    	i = 0;
    	s = (unsigned char*) b;
    	while (i < len)
    	{
    		s[i] = (unsigned char)c;
    		i++;
    	}
    	return (b);
    }
    Cela permet de vérifier que b+len ne cause pas d'overflow, et détectera donc un len "négatif".
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  7. #7
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2009
    Messages
    4 493
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 493
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par Crackerz Voir le message
    Merci Bktero pour ta réponse ultra-rapide
    De rien

    Citation Envoyé par Crackerz Voir le message
    Hum, je suis sous Mac, je compile ainsi : gcc -Wall -Werror -Wextra. Aucun warning
    J'ai mis du temps à le retrouver : -Wconversion. Il n'est pas dans -Wall ni dans -Wextra visiblement... Ta ligne de compilation est très bien sinon !

    Citation Envoyé par Crackerz Voir le message
    J'ai fais ainsi car le man me dit : "The memset() function writes len bytes of value c (converted to an unsigned char) to the string b."
    http://linux.die.net/man/3/memset -->
    The memset() function fills the first n bytes of the memory area pointed to by s with the constant byte c.
    https://developer.apple.com/library/.../memset.3.html -->
    The memset() function writes len bytes of value c (converted to an unsigned char) to the string b.
    Hum hum....

    En regardant la norme C99 (page 345) :
    The memset function copies the value of c (converted to an unsigned char) into each of the first n characters of the object pointed to by s.
    Par contre, je pense que cela sous-entend qu'il faut vérifier que l'entier rentre dans un char ? Que l'écriture est fait en plusieurs fois ? Je trouve cette description un peu étonnante.

    @Médinoc : ça ne me parait pas déconnant. Faudrait regarder dans les libc comment c'est implémenté.

  8. #8
    Membre prolifique
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 832
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 832
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par Bktero Voir le message
    @Médinoc : ça ne me parait pas déconnant. Faudrait regarder dans les libc comment c'est implémenté.
    Ben à moi si. Si la valeur passée est -1 alors len vaut 4 294 967 295 et alors (bufferStart + len) est bien supérieur à bufferStart. Donc l'assertion est toujours vérifiée. Et je vois mal une fonction de la libc détecter quelque chose d'incorrect mais ne rien faire et laisser l'OS se démerder tout seul par un abort ou un segfault.

    Euh et puis en plus, mon gcc ne connait pas assert...

    Citation Envoyé par Médinoc Voir le message
    Cela permet de vérifier que b+len ne cause pas d'overflow
    Même si la valeur passée est -4 294 967 295 ???
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  9. #9
    Expert éminent
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 395
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 395
    Par défaut
    Si (size_t)-1 vaut 4294967295, alors c'est un système 32 bits.
    Et dans un système 32 bits, bufferStart+4294967295 causera un overflow, et vaudra alors bufferStart-1!
    Qui est inférieur à bufferStart.

    PS: Un size_t ne peut pas valoir -4294967295 sur un système 32 bits.

    PPS: Je ne prétends pas que la libc puisse détecter un débordement du buffer, loin de là, mais elle peut au moins détecter les débordements numériques (ou "tours de compteur").
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Pb segmentation fault avec glutinit()
    Par pipistrelle dans le forum GLUT
    Réponses: 2
    Dernier message: 17/11/2004, 23h17
  2. [SDL_Image] Img_Load : segmentation fault ....
    Par Mathieu.J dans le forum OpenGL
    Réponses: 6
    Dernier message: 19/10/2004, 23h52
  3. [REDHAT] Segmentation fault systematique
    Par mela dans le forum RedHat / CentOS / Fedora
    Réponses: 2
    Dernier message: 21/09/2004, 06h05
  4. Réponses: 13
    Dernier message: 13/07/2004, 15h41
  5. Comment contrer la "segmentation fault" ?
    Par guillaume_pfr dans le forum C
    Réponses: 15
    Dernier message: 08/08/2003, 13h43

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo