Forum Programmation.c Perdu dans la mémoire (malloc, stack, heap et système)

Posté par  .
Étiquettes : aucune
0
30
août
2005
Bonjour,

Tout d'accord toutes mes excuses pour la longueur du post, mais j'ai ce problème depuis un bon bout de temps sans en voir la solution.

j'ai un problème lié à de l'allocation mémoire par un malloc, mais aussi lié au système et à mes distribs par les limitations soft (ulimit).

Je m'explique : j'ai 3 distribs
  1. Redhat 8.0, noyau 2.4.18-14, glibc-2.2.93-5, aucune limite soft
  2. Mandrake 10.1, noyau 2.6.8.1-12, glibc-2.3.3-21, limité en stack size à 8 Mo
  3. Red Hat Enterprise 3, noyau 2.4.21-27.0.2, glibc-2.3.2-95.33, limité en stack size à 10 Mo.

J'ai le code C suivant (pas parfait):

#include stdlib.h
#include malloc.h
#include stdio.h

int main(void) {
unsigned int base,*decalage,longueur;
unsigned int* addr; int i;
base = 134835592;
longueur = 40000;

addr = malloc(longueur * sizeof(base));
decalage=(addr-base) ;
printf("\ndecalage = %u = %d et base = %u \n",decalage,decalage,base);
printf("decalage = 0x%x et base = 0x%x \n",decalage,base);
printf("addr = %u = %d\n",addr,addr);
printf("addr = 0x%x\n",addr);
printf("&addr = %u = %d\n",&addr,&addr);
printf("&addr = 0x%x\n",&addr);
for (i=0;i<longueur;i+=1024) {addr[i]=i;}
}

En exécutant ce code sur les 3 distribs, j'ai 2 types de résultats différents. les distribs 1 et 2 d'un coté, la 3 de l'autre.

  • distrib 1 : decalage = 534485480 = 534485480 et base = 134835592
    decalage = 0x1fdb99e8 et base = 0x8096d88
    addr = 1073827848 = 1073827848
    addr = 0x40015008
    &addr = 3221222084 = -1073745212
    &addr = 0xbffff2c4
  • distrib 2 : decalage = 535673320 = 535673320 et base = 134835592
    decalage = 0x1fedb9e8 et base = 0x8096d88
    addr = 1075015688 = 1075015688
    addr = 0x40137008
    &addr = 3221221700 = -1073745596
    &addr = 0xbffff144

  • distrib 3 :decalage = 2535635432 = -1759331864 et base = 134835592
    decalage = 0x9722b9e8 et base = 0x8096d88
    addr = 3074977800 = -1219989496
    addr = 0xb7487008
    &addr = 3221214884 = -1073752412
    &addr = 0xbfffd6a4

Sauf que quand je supprime la limitation sur la stacksize de la distrib 3, je me retrouve avec les mêmes types de résultats pour toutes les distribs (je rappelle que la distrib 2 a elle aussi une limite sur la stacksize sans donner de résultats différents de la distrib 1.)
  • distrib 3 :decalage = 535775720 = 535775720 et base = 134835592
    decalage = 0x1fef49e8 et base = 0x8096d88
    addr = 1075118088 = 1075118088
    addr = 0x40150008
    &addr = 3221222068 = -1073745228
    &addr = 0xbffff2b4

Par même type de résultats, je veux dire que l'allocation malloc se fait dans les environs de la même adresse. Ceci est important, car les adresses représentées non signées sont négatives, ce qui peut entraîner des débordements lorsqu'elles sont utilisées en valeur absolue (c'est pas moi qui programme !)dans une autre partie de code.
Détail amusant : lancé plusieurs fois, le code donne exactement les mêmes résultats pour les distribs 1 et 2 ; pour la distrib 3 (limitée ou pas) les résultats diffèrent à chaque exécution.

Bref, mes questions sont les suivantes :
  • quelle est le lien entre la stacksize et le malloc ?
  • pourquoi la limitation stacksize n'a pas d'influence sur la distrib 2 alors qu'elle en a sur la distrib 3 ?

Par avance, merci à tous ceux qui auront pris le temps de me lire, et surtout de me répondre.

PS : si quelqu'un pouvait m'expliquer comment on fait "<" et ">" ça m'arrangerait, j'y suis pas arrivé. Merci.
  • # Patches de sécurité

    Posté par  (site web personnel) . Évalué à 6.

    Hello,

    Je pense que la distrib 3 doit utiliser des patches de sécurité (genre PaX ou grsec) qui "randomizent" la mémoire, c'est à dire mettent la pile et les autres segments à des endroits aléatoires afin d'offrir une sécurité contre l'exploitation de failles dans les programmes (cela rend les calculs d'adresse difficiles pour les pirates).
  • # Re : Perdu dans la mémoire (malloc, stack, heap et système)

    Posté par  . Évalué à 2.

    je ne repond pas a ton pb, mais l'adresse n'est pas negative, c'est quand tu fais ton printf avec un %d que tu la vois comme ca, je te dirais donc pour te rassurer d'utiliser %ld pour afficher des variables de "grande" taille :

    $ cat foo.c
    #include <stdio.h>

    int main()
    {
    printf("%d %d\n", sizeof(void*), sizeof(int));
    }

    sur une archi 64 bits :
    $ gcc foo.c && ./a.out
    8 4

    sur une archi 32 bits :
    $ gcc foo.c && ./a.out
    4 4

    • [^] # Re: Re : Perdu dans la mémoire (malloc, stack, heap et système)

      Posté par  . Évalué à 1.

      Je suis d'accord.
      Mais mon programme est appelé par un programme fortran, qui passe des adresses à ma routine C. Et si je ne me trompe pas, ces adresses sont des integer. J'aurai pour ma part préféré des unsigned int.

      Donc comme j'ai des int, ma variable "decalage" est donc prise, en retour de ma routine C, dans le fortran comme un integer. Et peut donc être négative. Aucun problème si ce n'est que le traitement mémoire se fait derrière sur sa valeur absolue : on tape donc dans des zones mémoires aléatoires.

      Le mec qui a codé le fortran ne s'y attendait surement pas.
      • [^] # Re: Re : Perdu dans la mémoire (malloc, stack, heap et système)

        Posté par  . Évalué à 3.

        Waou, c'est vraiment pas clair ton problème !

        > Mais mon programme est appelé par un programme fortran

        Euh, rassure-moi tu voulais dire "ma bibliothèque de fonction est appelé par un programme fortran", parce que l'espace d'adressage d'un processus à l'autre est complétement différent.

        > Ces adresses sont des integer. J'aurai pour ma part préféré des unsigned int.

        Euh, ce n'est qu'une question de convention. Un simple cast, et hop, tes int se transforment en unsigned int :
        unsigned int i = (unsigned int) -1;
        printf ("i = %u\n", i); // 4294967295

        Ou utiliser un entier signé comme une adresse mémoire :
        int i;
        long addr = (long) &i; // là tu as une adresse sour forme d'entier signé

        ((int *)addr)[0] = 1234; // Positionne i à 1234

        > Donc comme j'ai des int [...] zones mémoires aléatoires.
        Ou tu débutes en programmation, ou tu expliques super mal, ou tu cherches à faire compliquer quand on peut faire simple. De ce que j'ai compris, tu as un programme fortran, qui te passes une zone mémoire sous forme d'entier (signé ou pas, il ne devrait pas y avoir de problème).

        Toi tu veux allouer un nouveau buffer et renvoyer au programme fortran l'adresse de ce nouveau buffer sous forme d'offset par rapport à l'ancien (si ma mémoire est bonne, Fortran ne gère pas l'allocation dynamique).

        Pourquoi ne pas simplement retourner l'adresse du buffer sous forme d'entier (signé ou pas, on s'en fous) ?

        Dans tous les cas, n'espère surtout pas avoir une valeur constante dans ta variable "decalage".
        • [^] # Re: Re : Perdu dans la mémoire (malloc, stack, heap et système)

          Posté par  . Évalué à 1.

          Merci. Je vais cogiter la dessus. Je vais tester. Et peut-être que la solution que je recherche serait alors de faire le cast de manière à avoir des adresses qui "collent" ?

          Derrière ce problème anodin, il y a un gros code industriel (oui oui, mais pas connu du public).
      • [^] # Re: Re : Perdu dans la mémoire (malloc, stack, heap et système)

        Posté par  . Évalué à 3.

        les adresses (pointeurs) ne sont pas des unsigned int, la taille d'un int et d'un unsigned int est la meme, et sur l'archi 64 bits, on voit qu'un int est de 4 octets, et un pointeur de 8 octets (attention, je ne dit pas que c'est le cas sur tous les proc 64b, c'est juste le cas sur mon exemple...)

        dc dans ton prog, la taille de addr n'est pas focement la taille d'un int (ce que tu utilise par le %d des printf), ni celle dun unsigned int (%u), sur mon athlon 32 bits, c'est le cas, mais c'est pas forcement une verite absolue...

        enfin, comme dis dans un autre commentaire, un cast et ca repart... le codage de l'adresse n'intervient pas, il suffit de bien utiliser la valeur (dc connaitre le codage intervient en fait ^^ )

        ensuite, il faut aussi voir que l'adresse que tu obtiens avec le malloc, c'est une adresse "virtuelle", plusieurs processus peuvent obtenir une meme adresse, et ecrire a des endroits differents dans la memoire, mais a mon avis, ca ne concerne en rien ton probleme, c'est juste au noyau de se debrouiller avec...

  • # Hum

    Posté par  . Évalué à 3.

    Je pense qu'il n'y a aucun lien entre la taille de ta pile et malloc (à supposé qu'il y en ait un, ce serait un effet de bord quasi inexploitable).

    À vrai dire, je ne vois pas quel est ton problème. Les valeurs de malloc() changent entre plusieurs distributions ? Ben, moi je dirais que les valeurs devraient changer même lorsque tu testes sur le même système.

    À part quelques besoins ultra pointus (canaux DMA d'une carte d'acquisition par exemple), il n'y a aucun intérêt à allouer de la mémoire à adresse fixe. Qui plus est, la seule certitude que tu peux avoir avec malloc(), c'est soit d'avoir une valuer nulle (et encore sous Linux ton process sera flingué d'office) ou soit une valeur différente de 0. Tout le reste n'est qu'effet de bord ultra suicidaire à exploiter, lié à je ne sais trop quel coïncidence entre l'ordonnanceur du système et la pagination mémoire.

    Bref, les résultats que tu obtiens me semblent normaux.
    • [^] # Re: Hum

      Posté par  . Évalué à 0.

      J'ai essayé avec ma Ubuntu.
      Le test lancé plusieurs fois de suite me donne les mêmes adresses.

      Et pareil, ma Ubuntu est limitée en stacksize à 8 Mo. Si j'enlève cette limite, le test donne des adresses différentes de celles qu'elles étaient avec la limitation. Et de nouveau, plusieurs tests à la suite donnent des adresses identiques.

      Je suis d'accord sur le fait que les valeurs peuvent être différentes d'une distrib à une autre. D'ailleur je ne le remettait pas en cause. La chose qui me gène c'est une telle disparité dans les adresses : d'un coté j'ai : 0x40015008 (1), 0x40137008 (2), 0x40157008 (Ubuntu unlimited), 0x40150008 (3 unlimited) et de l'autre j'ai : 0xb7e82008 (Ubuntu limited) et 0xb7487008 (3 limited).

      Je n'arrive pas à avoir une explication de ce phénomène (surtout entre limited et unlimited).
      • [^] # Re: ??! \_O< ...

        Posté par  . Évalué à 3.

        > La chose qui me gène c'est une telle disparité dans les adresses

        Parce que tu voudrais que ces adresses soient identiques d'un lancement à l'autre ?

        Bon, je te le dis tout de suite : c'est impossible. C'est le cas pour quelques uns de tes tests, mais ça tiens plus du coup de chance, certainement lié au fait que tu venais de lancer ton programme plusieurs fois de suite et le fait que la pagination a *tendance* à utiliser les mêmes schéma d'adresse (0x8...... ou 0xb....).

        Je persiste à dire, que tes résultats sont tout à fait normaux.
        • [^] # Re: ??! \_O< ...

          Posté par  . Évalué à 1.

          D'accord. J'admet que cela est normal.
          Mais pourquoi la délimitation sur la stacksize change t-elle autant les valeurs ?
          Surtout que pour la Mandrake 10.1, limité ou pas c'est les mêmes résultats.

          Je ne suis pas hyper calé en mémoire, mais il me semble que la stack et le malloc c'est pas au même endroit...
  • # comprends rien ...

    Posté par  (site web personnel) . Évalué à 3.

    Je vois pas trop ce que tu cherches à faire.

    elle sort d'où ta valeur de base ? pourquoi tu la soustrait à addr ? t'espère obtenir quoi ?

    adresses représentées non signées sont négatives
    si c'est non signé, ça risque pas d'être négatif !
    • [^] # Re: comprends rien ...

      Posté par  . Évalué à 1.

      Effectivement, je me suis trompé.
      C'est signé que je voulais dire. C'est le fait qu'en étant int, mon adresse puisse être négative, et que retraitée ensuite dans le fortran en valeur absolue on pointe alors sur la mauvaise zone allouée.

      En fait "base" est une adresse donnée par la routine fortran, et "decalage" sert ensuite dans le fortran à manipuler la mémoire à partir de "base".

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.