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

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

    Informations forums :
    Inscription : Janvier 2013
    Messages : 150
    Points : 157
    Points
    157
    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
    Développeur en systèmes embarqués
    Inscrit en
    Juin 2009
    Messages
    4 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 481
    Points : 13 679
    Points
    13 679
    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 habitué
    Profil pro
    Inscrit en
    Janvier 2013
    Messages
    150
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2013
    Messages : 150
    Points : 157
    Points
    157
    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
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 690
    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 690
    Points : 30 985
    Points
    30 985
    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 habitué
    Profil pro
    Inscrit en
    Janvier 2013
    Messages
    150
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2013
    Messages : 150
    Points : 157
    Points
    157
    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 sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 519
    Points
    41 519
    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
    Développeur en systèmes embarqués
    Inscrit en
    Juin 2009
    Messages
    4 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 481
    Points : 13 679
    Points
    13 679
    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
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 690
    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 690
    Points : 30 985
    Points
    30 985
    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 sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 519
    Points
    41 519
    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.

  10. #10
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 690
    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 690
    Points : 30 985
    Points
    30 985
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par Médinoc Voir le message
    PS: Un size_t ne peut pas valoir -4294967295 sur un système 32 bits.
    Ce n'est pas ce que j'ai dit !!! J'ai dit "si la valeur passée" !!!

    Voici un petit code de démo pour que tu en tires les conclusions que ça donnera sur ta "détection d'overflow"

    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    #include <stdio.h>
    #include <sys/types.h>
     
    void demo(size_t x)
    {
    	printf("%lu %ld\n", x, x);
    }
     
    int main(){
     
    	size_t x;
    	demo(-4294967295);
    }

    PS: merci pour ton "-1". Je ne sais pas pourquoi je suis sûr que c'est toi. Je ne pensais pourtant pas avoir dit de conneries...
    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]

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

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 519
    Points
    41 519
    Par défaut
    En effet, (size_t)-4294967295 donne 1 (avec warning "large integer implicitly truncated to unsigned type" de gcc).
    Néanmoins, cette valeur ne causera pas de tour de compteur dans le my_memset(), donc j'ai du mal à comprendre ce que tu cherches à prouver.

    ...Mais je te retire ton pouce bas quand même, va!
    Edit: En fait non, parce que "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. " reste une connerie, pour les raisons que j'ai expliqué plus haut.
    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.

  12. #12
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 690
    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 690
    Points : 30 985
    Points
    30 985
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par Médinoc Voir le message
    Citation Envoyé par Médinoc Voir le message
    Citation Envoyé par Crackerz Voir le message
    cependant comment contrôler le fait que len ne dois pas être négative ?
    Je pense que j'ai une petite idée de la vérification pratiquée par memset:
    En effet, (size_t)-4294967295 donne 1 (avec warning "large integer implicitly truncated to unsigned type" de gcc).
    Néanmoins, cette valeur ne causera pas de tour de compteur dans le my_memset(), donc j'ai du mal à comprendre ce que tu cherches à prouver.
    Je cherche à prouver que ton assertion sensée montrer comment memset() détecte un négatif (tu étais bien en train de répondre au problème originel n'est-ce pas ???) ne le détecte pas dans ce cas là (et dans bien d'autres).

    Accessoirement si memset() avait un assert qui échouait, ça ressortirait à l'exécution
    Accessoirement2, si memset() avait été pensée pour détecter "certains négatifs" (tant pis pour les autres), alors ça aurait été pensé de façon plus "professionnelle" et passerait par une autre méthode que assert qui plante le programme. Par exemple ça pourrait être un truc de ce type
    Code c : 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;
    	if ((bufferStart + len) < bufferStart) return NULL;
     
    	i = 0;
    	s = (unsigned char*) b;
    	while (i < len)
    	{
    		s[i] = (unsigned char)c;
    		i++;
    	}
    	return (b);
    }

    Citation Envoyé par Médinoc Voir le message
    Edit: En fait non, parce que "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. " reste une connerie, pour les raisons que j'ai expliqué plus haut.
    As you want. Je n'oublierai pas.
    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]

  13. #13
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Juin 2009
    Messages
    4 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 481
    Points : 13 679
    Points
    13 679
    Billets dans le blog
    1
    Par défaut
    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
    19
    20
    21
    22
    23
    24
    25
    #include <stdio.h>
    #include <stdbool.h>
     
    bool check(void* ptr, size_t offset)
    {
        return ((int *)ptr + offset) >= (int *)ptr;
    }
     
    size_t offsets[] = {0,
                        0xF,
                        0xFFFF,
                        -5,
                        -1,
                        -4294967295};
     
    int main()
    {
        printf("sizeof int* %u\n", sizeof (int*));
        int array[10];
     
        for(unsigned int i = 0; i < (sizeof offsets / sizeof offsets[0]); i++)
        {
            printf("Offset = 0x%X\t--> %d\n", offsets[i], check(array, offsets[i]));
        }
    }
    donne :
    sizeof int* 4
    Offset = 0x0    --> 1
    Offset = 0xF    --> 1
    Offset = 0xFFFF --> 1
    Offset = 0xFFFFFFFB     --> 0
    Offset = 0xFFFFFFFF     --> 0
    Offset = 0x1    --> 1
    
    La technique n'est faite pour détecter une longueur négative mais bien une addition qui fait un overflow.

    De toute façon, on ne peut pas détecter le passage d'un nombre négatif. Interpréter 32 bits comme signé ou pas, ça reste bien qu'une question d'interprétation. Qu'est ce qui fait dire que le nombre passé est négatif dans l'appelant ? La seule façon possible est de voir une constante littérale négative (avec le warning que j'ai indiqué) ou alors un risque possible lors de la conversion d'un entier signé vers un entier non-signé. Or :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    int main()
    {
        int a = -42;
        unsigned int b = a;
    }
    génère :
    D:\Users\pgradot\Documents\C\definitions.c|10|warning: conversion to 'unsigned int' from 'int' may change the sign of the result [-Wsign-conversion]|
    Accessoirement2, si memset() avait été pensée pour détecter "certains négatifs" (tant pis pour les autres), alors ça aurait été pensé de façon plus "professionnelle" et passerait par une autre méthode que assert qui plante le programme
    Captain Obvious ?
    Le man ne précise aucun type de retour. Peut-on imaginer que la fonction ne fasse pas de vérification ?

  14. #14
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 690
    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 690
    Points : 30 985
    Points
    30 985
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par Bktero Voir le message
    La technique n'est faite pour détecter une longueur négative mais bien une addition qui fait un overflow.
    La question d'origine était "cependant comment contrôler le fait que len ne dois pas être négative" et il a répondu "Je pense que j'ai une petite idée de la vérification pratiquée par memset". Soit il répondait à la question d'origine, soit il répondait à je ne sais pas quoi vu que chez-moi, memset() ne fait aucune vérification car memset(pt, 'x', -1) me fait un segfault et que memset(pt, 'x', -4294967295) fonctionne et remplit un (seul) octet de ma zone.

    Citation Envoyé par Bktero Voir le message
    De toute façon, on ne peut pas détecter le passage d'un nombre négatif. Interpréter 32 bits comme signé ou pas, ça reste bien qu'une question d'interprétation. Qu'est ce qui fait dire que le nombre passé est négatif dans l'appelant ?
    Euh oui, il me semble l'avoir dit aussi quoiqu'avec d'autres mots. Lieutnant OMG ?

    Citation Envoyé par Bktero Voir le message
    Le man ne précise aucun type de retour.
    Euh si, moi mon man me dit que c'est de type "void*" et que
    Citation Envoyé par man
    RETURN VALUE: The memset() function returns a pointer to the memory area s.
    ...

    Citation Envoyé par Bktero Voir le message
    Peut-on imaginer que la fonction ne fasse pas de vérification ?
    C'est un des fondements du C: pour aller le plus vite possible il n'y aura aucun garde-fou, aucun contrôle, le programmeur sait ce qu'il fait...
    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]

  15. #15
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Juin 2009
    Messages
    4 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 481
    Points : 13 679
    Points
    13 679
    Billets dans le blog
    1
    Par défaut
    Euh oui, il me semble l'avoir dit aussi quoiqu'avec d'autres mots. Lieutnant OMG ?
    On est OK.

    Euh si, moi mon man me dit que c'est de type "void*" et que
    En fait je voulais dire pas de retour particulier (retourner la valeur en paramètre permet juste de chaîner les utilisations), mais j'ai lu et écrit un peu vite.

    C'est un des fondements du C: pour aller le plus vite possible il n'y aura aucun garde-fou, aucun contrôle, le programmeur sait ce qu'il fait...
    On est OK.


  16. #16
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 690
    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 690
    Points : 30 985
    Points
    30 985
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par Bktero Voir le message
    On est OK.
    Oui, tous les deux on est ok. C'est l'autre qui a émis une hypothèse à laquelle j'ai répondu pour indiquer que je ne la partageais pas avec un contre-exemple à la clef qui démontrait pourquoi je ne la partageais pas (même si j'ai fait une petite erreur d'analyse parce que je ne savais pas que la plage d'adresse était circulaire mon contre-exemple reste quand-même valable) ; tout en essayant d'aténuer par des émoticons humoristiques le ressenti qu'il pourrait avoir pour ça. Et là il se vexe et me met un -1. Ben là aussi on est ok !!!
    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]

  17. #17
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Juin 2009
    Messages
    4 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 481
    Points : 13 679
    Points
    13 679
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par Sve@r Voir le message
    Et là il se vexe et me met un -1.
    Pov' chou

  18. #18
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 690
    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 690
    Points : 30 985
    Points
    30 985
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par Bktero Voir le message
    Pov' chou
    Ben oui, ça fait jamais plaisir. Après-tout, si j'avais dû mettre des -1 à tous ceux à qui j'ai dit "non non tu fais erreur, c'est pas comme ça que ça se passe..." surtout que j'étais généreux avec lui (ceci dit j'ai revu certains de mes "+1" gratuits sur ses récents posts).
    Toutefois ça m'a fait réfléchir. C'est vrai que j'aurais dû réaliser immédiatement que la plage d'adresse était circulaire vu que les nombres le sont. Mais on peut alors en tirer aussi immédiatement les conséquences que ça entraine. En effet, cet assert, s'il était réellement présent, aurait alors empêché tout programmeur d'utiliser memset pour une zone dépassant les 4160453461 octets. Pourquoi 4160453461 (0xf7fb7b55) et non 2147483648 comme ça me semblait logique vu la plage de valeur des size_t par rapport aux ssize_t, ça je n'ai pas encore compris, ce nombre est venu suite à une boucle de recherche pour savoir à quel moment un (uintptr_t)pt + x < (uintptr_t)pt (sur un os 32 bits).

    Accessoirement j'ai aussi fait erreur quand j'ai dit qu'on ne pouvait pas détecter un len négatif. En effet, même dans la toute première fonction présentée par Crackerz, rajouter (ssize_t)len < 0 lui permettra de détecter quand on lui passe -1. Malheureusement il renverra aussi le même résultat quand on lui passe 4294967295. Putain d'horloge !!!
    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]

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

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 519
    Points
    41 519
    Par défaut
    J'ai mentionné l'assert parce que ça collait au comportement constaté par Crackerz: Un abort quand on passe -1 (qui devient 4294967295, d'où tour de compteur) au paramètre de taille, pour un pointeur non-nul.

    Citation Envoyé par Sve@r Voir le message
    En effet, cet assert, s'il était réellement présent, aurait alors empêché tout programmeur d'utiliser memset pour une zone dépassant les 4160453461 octets. Pourquoi 4160453461 (0xf7fb7b55) et non 2147483648 comme ça me semblait logique vu la plage de valeur des size_t par rapport aux ssize_t, ça je n'ai pas encore compris, ce nombre est venu suite à une boucle de recherche pour savoir à quel moment un (uintptr_t)pt + x < (uintptr_t)pt (sur un os 32 bits).
    Là, il va falloir que tu m'en dises plus sur comment tu as obtenu ce nombre.
    Parce qu'en théorie, un my_memset(NULL, 42, -1); passerait l'assert (car il ne cause pas de tour de compteur).
    Et si on avait le droit d'écrire sur toute la mémoire, il fonctionnerait (enfin, jusqu'au point où il écraserait son propre code).

    Edit: L'assert possède un inconvénient par contre, c'est qu'il interdit à la fonction de modifier le dernier octet de la mémoire (car bufferStart+len sera alors nul).
    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.

  20. #20
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 690
    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 690
    Points : 30 985
    Points
    30 985
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par Médinoc Voir le message
    J'ai mentionné l'assert parce que ça collait au comportement constaté par Crackerz: Un abort quand on passe -1 (qui devient 4294967295, d'où tour de compteur) au paramètre de taille, pour un pointeur non-nul.
    Crackerz n'a pas mentionné un quelconque message provenant de l'assert...

    Citation Envoyé par Médinoc Voir le message
    Là, il va falloir que tu m'en dises plus sur comment tu as obtenu ce nombre.
    Ben j'avais parlé d'une boucle de recherche. Bon je l'ai retapée car entre temps j'avais fait d'autres trucs mais je pense qu'elle devait ressembler à ceci:
    Code c : 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
    19
    20
    21
    22
    #include <stdio.h>
    #include <stdint.h>
    #include <stdlib.h>
    #include <string.h>
    #include <assert.h>
    #include <sys/types.h>
    #include <sys/types.h>
     
    int test(char *adr, size_t i)
    {
    	uintptr_t *pt=(uintptr_t*)adr;
     
    	return ((pt + i) > pt);
    }
     
    int main(){
     
    	char pt[10];
    	size_t i;
    	for (i=1; test(pt, i); i++);
    	printf("i=%lu (%x)\n", i, i);
    }

    Sauf que là, je l'ai lancée plusieurs fois et voici ce que j'ai eu
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    ~/tmp$ ./essai
    i=269228476 (100c19bc)
    ~/tmp$ ./essai
    i=269393864 (100e9fc8)
    ~/tmp$ ./essai
    i=269329524 (100da474)
    ~/tmp$ ./essai
    i=268961052 (1008051c)
    ~/tmp$ ./essai
    i=268932548 (100795c4)
    Effectivement la valeur change à chaque appel puisque l'adresse de départ change aussi.

    Citation Envoyé par Médinoc Voir le message
    Parce qu'en théorie, un my_memset(NULL, 42, -1); passerait l'assert (car il ne cause pas de tour de compteur).
    Et si on avait le droit d'écrire sur toute la mémoire, il fonctionnerait (enfin, jusqu'au point où il écraserait son propre code).
    Oui, c'est ce que j'ai eu quand j'ai passé NULL au lieu de pt
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    gcc     essai.c   -o essai
    ~/tmp$ ./essai
    i=1073741824 (40000000)
    ~/tmp$ ./essai
    i=1073741824 (40000000)
    ~/tmp$ ./essai
    i=1073741824 (40000000)
    ~/tmp$ ./essai
    Et quand j'ai remplacé ">" dans le test par ">=" (ton assert), alors je n'ai plus eu de retour pour NULL (effectivement, dans ce cas le test reste toujours vrai)...
    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]

+ Répondre à la discussion
Cette discussion est résolue.
Page 1 sur 2 12 DernièreDernière

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