3 votes

doublement libre sans aucune allocation de mémoire dynamique

En général, qu'est-ce qui pourrait causer un double free dans un programme qui ne contient pas d'allocation dynamique de mémoire ?

Pour être plus précis, aucun des mon utilise l'allocation dynamique. J'utilise la STL, mais il est beaucoup plus probable que ce soit quelque chose que j'ai mal fait que ce soit une implémentation défectueuse de G++/glibc/STL.

J'ai fait des recherches pour essayer de trouver une réponse à cette question, mais je n'ai pas pu trouver d'exemple de cette erreur générée sans aucune allocation de mémoire dynamique.

J'aimerais partager le code qui génère cette erreur, mais je ne suis pas autorisé à le publier et je ne sais pas comment réduire le problème à quelque chose d'assez petit pour être donné ici. Je vais faire de mon mieux pour décrire l'essentiel de ce que faisait mon code.

L'erreur était déclenchée à la sortie d'une fonction, et la trace de la pile montrait qu'elle provenait du destructeur d'une fonction de type std::vector<std::set<std::string>> . Un certain nombre d'éléments du vecteur ont été initialisés par emplace_back() . Dans une ultime tentative, je l'ai changé en push_back({{}}) et le problème a disparu. Le problème peut également être évité en définissant la variable d'environnement MALLOC_CHECK_=2 . D'après ce que j'ai compris, cette variable d'environnement aurait dû faire en sorte que la glibc abandonne avec plus d'informations plutôt que de faire disparaître l'erreur.

Cette question n'est posée que pour satisfaire ma curiosité, je me contenterai donc d'une réponse approximative. Le mieux que j'ai pu trouver est qu'il s'agit d'un bug du compilateur, mais c'est toujours ma faute .

3voto

billz Points 28166

En général, qu'est-ce qui pourrait causer un double free dans un programme qui ne contient pas d'allocation dynamique de mémoire ?

Normalement, lorsque vous faites une copie d'un type qui alloue dynamiquement de la mémoire, mais qui ne respecte pas les règles de l'art, vous devez faire une copie de la mémoire. règle de trois

struct Type
{
   Type() : ptr = new int(3) { }
   ~Type() { delete ptr; }
   // no copy constructor is defined
   // no copy assign operator is defined

private:
   int * ptr;
};

void func()
{       
   { 
     std::vector<Type> objs;
     Type t; // allocates ptr
     objs.push_back(t); // make a copy of t, now t->ptr and objs[0]->ptr point to same memory location
     // when this scope finishes, t will be destroyed, its destructor will be called and it will try to delete ptr;
     // objs go out of scope, elements in objs will be destroyed, their destructors are called, and delete ptr; will be executed again. That's double free on same pointer.
   }    
}

1voto

logion Points 93

J'ai extrait un exemple présentable montrant la faute que j'ai faite et qui a conduit à l'erreur d'exécution "double free or corruption". Notez que la structure n'utilise pas explicitement d'allocations de mémoire dynamique, alors qu'en interne std::vector le fait (puisque son contenu peut croître pour accueillir plus d'éléments). Par conséquent, ce problème a été un peu difficile à diagnostiquer car il ne viole pas le principe de la "règle des 3".

#include <vector>
#include <string.h>

typedef struct message {
  std::vector<int> options;
  void push(int o) { this->options.push_back(o); }
} message;

int main( int argc, const char* argv[] )
{
  message m;
  m.push(1);
  m.push(2);

  message m_copy;
  memcpy(&m_copy, &m, sizeof(m));
  //m_copy = m; // This is the correct method for copying object instances, it calls the default assignment operator generated 'behind the scenes' by the compiler
}

Quand main() revient, m_copy est détruit, ce qui appelle le destructeur std::vector. Celui-ci tente de supprimer la mémoire qui a déjà été libérée lorsque l'objet m a été détruit.

Ironiquement, j'utilisais en fait memcpy pour essayer de réaliser une "copie profonde". C'est là que réside la faute dans mon cas. Je pense qu'en utilisant l'opérateur d'affectation, tous les membres de message.options sont en fait copiés dans la "mémoire nouvellement allouée" alors que memcpy ne copierait que les membres qui ont été alloués au moment de la compilation (par exemple un membre uint32_t size). Voir memcpy ou memmove vont-ils causer des problèmes de copie de classes ? . Évidemment, cela s'applique également aux structs dont les membres sont typés de manière non fondamentale (comme c'est le cas ici).

Peut-être avez-vous également copié un std::vector de manière incorrecte et vu le même comportement, peut-être pas. Au final, c'est entièrement ma faute :).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X