Erreur inattendue en utilisant la fonction realloc()

Le problème exposé dans ce sujet a été résolu.

Bonjour,

Dans le cadre d’un projet, j’ai identifié une ligne de code qui posait problème avec la fonction realloc.
J’aimerais donc vérifier ma bonne compréhension de cette fonction.

Êtes-vous d’accord que le code suivant est correct, ne présente aucune fuite de mémoire et utilise correctement la fonction realloc ?

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    int* p = malloc(2*sizeof(int));
    
    if (!p)
        return EXIT_FAILURE;
    
    p[0] = 42;
    p[1] = 63;
    
    p = realloc((void*) p, (size_t) 530);
    
    if (!p)
        return EXIT_FAILURE;
    
    p[487] = 74;
    
    // Affiche : [42, 63, 74]
    printf("[%d, %d, %d]", p[0], p[1], p[487]);

    free(p);
    return EXIT_SUCCESS;
} 

(Si oui, si j’ai bien compris realloc, alors j’expliquerai ce que j’ai eu comme erreur : realloc qui corrompt les données en mémoire…)

Ton code comporte deux erreurs. La première est celle qui t’intéresse.

int* p = malloc(2*sizeof(int));
/// ...
p = realloc((void*) p, (size_t) 530);

Regarde ces deux lignes. Pourquoi ne passes-tu pas une taille mémoire de la même façon à ces deux fonctions? L’erreur vient du fait que 530 correspond à un nombre d’octets et 2 * sizeof (int) un nombre d’objets. Quelle est la bonne méthode d’après toi?

Deuxièmement, quand realloc échoue, elle renvoie NULL et c’est à toi de libérer la mémoire précédemment allouée, or tu ne fais pas cette opération ici.

En espérant avoir pu t’aider. Si tu ne vois vraiment pas, n’hésite pas à redemander. J’ai volontairement été vague car de ce que je vois, tu es capable de comprendre ton erreur mais je peux être plus précis.

Lu’!

Êtes-vous d’accord que (1) le code suivant est correct, (2) ne présente aucune fuite de mémoire et (3) utilise correctement la fonction realloc ?

  1. Non
  2. Discutable
  3. Discutable

(1) Tu as alloué une zone mémoire de 530 octets avec ton realloc et tu accèdes à la cellule (de type int) 487. 487*4 octets : 1948. Tu débordes légèrement.

(2) Si realloc renvoie NULL, l’ancienne zone mémoire est conservée. Ici, comme tu fais un exit, l’OS récupère la mémoire donc il n’y a pas foncièrement de fuite, mais bon … c’est douteux.

(3) Tu devrais passer par un pointeur différent pour le résultat, pour pouvoir gérer le cas expliqué dans (2).

Quand tu as un code comme ça, exécute le avec un debugger ou un outil statique (outils externes, ou encore les options -fsanitize des compilos). En C, on ne peut pas faire confiance aux affichages pour le debut et en plus un debugger te donnerait beaucoup plus d’information.

Regarde ces deux lignes. Pourquoi ne passes-tu pas une taille mémoire de la même façon à ces deux fonctions? L’erreur vient du fait que 530 correspond à un nombre d’octets et 2 * sizeof (int) un nombre d’objets. Quelle est la bonne méthode d’après toi?

Ah merci, c’est précisément l’erreur également commise dans mon projet, je rectifie ça ! :magicien:

Quand tu as un code comme ça, exécute le avec un debugger ou un outil statique (outils externes, ou encore les options -fsanitize des compilos). En C, on ne peut pas faire confiance aux affichages pour le debut et en plus un debugger te donnerait beaucoup plus d’information.

Mon compilateur ne reconnaît pas cette commande, est-ce normal ?

gcc -fsanitize -pedantic -Wall -o "_test" "_test.c"
gcc: error: unrecognized command line option ‘-fsanitize’

On passe des paramètres à -fsanitize. Par exemple, address pour vérifier les accès :

gcc -fsanitize=address -Wall -Wextra -pedantic -o exec file.c

Si ton compilateur ne reconnaît pas cette option, il faudra le ramener chez l’antiquaire et en prendre un plus récent. Dans tous les cas, l’utilisation d’un debugger (comme GDB, par exemple) est plus que souhaitable en C.

Bonjour,

En compilant et exécutant mon code avec -Wextra, -Wall, -fsanitize=address ou -fsanitize=leak, aucune erreur n’est renvoyée.

Mais une erreur se produit lorsque j’exécute mon code compilé avec l’option -fsanitize=thread :

FATAL: ThreadSanitizer: unexpected memory mapping 0x565555553000-0x565555555000

Que s’est-il passé ? Est-ce une erreur à réparer dans mon code ?

edit : c’est un bug du compilo

+0 -0

Avec address tu n’as pas d’erreur ? C’est curieux quand même. Normalement pour un buffer overflow aussi trivial, c’est détecté assez facilement. Après, pour avoir plus de précision, le mieux reste d’utiliser un debugger et de placer des breakpoints proches de l’endroit que l’on suspecte d’exploser (Il y a aussi l’option analyseur statique sound, mais c’est plus lourd à mettre en place).

Je ne le recommanderai pas toujours (surtout face au sanitizer) mais j’utilise parfois valgrind, qui détecte très bien cette erreur.

==13982== Memcheck, a memory error detector
==13982== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==13982== Using Valgrind-3.14.0 and LibVEX; rerun with -h for copyright info
==13982== Command: ./test
==13982== 
==13982== Invalid write of size 4
==13982==    at 0x1091D1: main (in /home/boris/test)
==13982==  Address 0x4a2782c is 1,340 bytes inside an unallocated block of size 4,193,520 in arena "client"
==13982== 
==13982== Invalid read of size 4
==13982==    at 0x1091E1: main (in /home/boris/test)
==13982==  Address 0x4a2782c is 1,340 bytes inside an unallocated block of size 4,193,520 in arena "client"
==13982== 
[42, 63, 74]==13982== 
==13982== HEAP SUMMARY:
==13982==     in use at exit: 0 bytes in 0 blocks
==13982==   total heap usage: 3 allocs, 3 frees, 1,562 bytes allocated
==13982== 
==13982== All heap blocks were freed -- no leaks are possible
==13982== 
==13982== For counts of detected and suppressed errors, rerun with: -v
==13982== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)

Je ne dis pas que cette solution est la meilleure, valgrind a évidemment des défauts, mais je pense qu’il faut savoir que ce genre d’outil existe.

Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte