web-dev-qa-db-fra.com

Que signifie "liste à double chaînon corrompue"?

J'ai récemment eu l'erreur suivante de mon PHP:

WARNING: [pool www] child 42475 said into stderr: "*** glibc detected *** php-fpm: pool www: corrupted double-linked list: 0x00000000013fe680 ***"

Je ne suis pas très dérangé par ce problème et je ne suis pas très intéressé à le résoudre. Mais je suis très intéressé à comprendre ce que cette erreur signifie "liste corrompue corrompue", parce que je ne l'ai jamais vue auparavant. Je crois savoir ce qu'est une liste à double liaison, mais je n'ai pas réussi à produire un programme qui déclenche cette erreur.

Quelqu'un pourrait-il me fournir un court extrait de code dans lequel la glibc dit "liste à double liaison altérée" lorsque je la compile et l'exécute?

36
replay

J'ai trouvé la réponse à ma question moi-même :)

Donc, ce que je n'ai pas compris, c'est comment la glibc pourrait faire la différence entre un Segfault et une liste à double chaînon corrompue, car selon ma compréhension, ils devraient ressembler à la même chose, du point de vue de la glibc. Parce que si j'implémente une liste à double liaison dans mon programme, comment la glibc pourrait-elle savoir qu'il s'agit d'une liste à double liaison, au lieu de toute autre structure? Il ne peut probablement pas, alors c'est pourquoi j'ai été confus.

Maintenant, j'ai regardé malloc/malloc.c à l'intérieur du code de la glibc, et je vois ce qui suit:

1543 /* Take a chunk off a bin list */
1544 #define unlink(P, BK, FD) {                                            \
1545   FD = P->fd;                                                          \
1546   BK = P->bk;                                                          \
1547   if (__builtin_expect (FD->bk != P || BK->fd != P, 0))                \
1548     malloc_printerr (check_action, "corrupted double-linked list", P); \
1549   else {                                                               \
1550     FD->bk = BK;                                                       \
1551     BK->fd = FD;                                                       \

Alors maintenant, cela prend tout son sens. La raison pour laquelle glibc peut savoir qu'il s'agit d'une liste à double liaison est parce que la liste fait partie de la glibc elle-même. J'ai été dérouté parce que je pensais que glibc pouvait détecter que certaines programmations construisaient une liste à double liaison, ce que je ne comprendrais pas comment cela fonctionne. Mais si cette liste à double liaison dont il est question fait partie de la glibc elle-même, elle peut bien sûr savoir que c'est une liste à double liaison.

Je ne sais toujours pas ce qui a déclenché cette erreur. Mais au moins, je comprends la différence entre une liste à double lien corrompue et un Segfault, et comment la glibc peut savoir que cette structure est supposée être une liste à double lien :)

55
replay

Débordement de tas devrait être blâmé (mais pas toujours) pour corrupted double-linked list, malloc(): memory corruption, double free or corruption (!prev) - comme les avertissements de la glibc.

Il devrait être reproduit par le code suivant:

#include <vector>

using std::vector;


int main(int argc, const char *argv[])
{
    int *p = new int[3];
    vector<int> vec;
    vec.resize(100);
    p[6] = 1024;
    delete[] p;
    return 0;
}

si compilé avec g ++ (4.5.4):

$ ./heapoverflow
*** glibc detected *** ./heapoverflow: double free or corruption (!prev): 0x0000000001263030 ***
======= Backtrace: =========
/lib64/libc.so.6(+0x7af26)[0x7f853f5d3f26]
./heapoverflow[0x40138e]
./heapoverflow[0x400d9c]
./heapoverflow[0x400bd9]
./heapoverflow[0x400aa6]
./heapoverflow[0x400a26]
/lib64/libc.so.6(__libc_start_main+0xfd)[0x7f853f57b4bd]
./heapoverflow[0x4008f9]
======= Memory map: ========
00400000-00403000 r-xp 00000000 08:02 2150398851                         /data1/home/mckelvin/heapoverflow
00602000-00603000 r--p 00002000 08:02 2150398851                         /data1/home/mckelvin/heapoverflow
00603000-00604000 rw-p 00003000 08:02 2150398851                         /data1/home/mckelvin/heapoverflow
01263000-01284000 rw-p 00000000 00:00 0                                  [heap]
7f853f559000-7f853f6fa000 r-xp 00000000 09:01 201329536                  /lib64/libc-2.15.so
7f853f6fa000-7f853f8fa000 ---p 001a1000 09:01 201329536                  /lib64/libc-2.15.so
7f853f8fa000-7f853f8fe000 r--p 001a1000 09:01 201329536                  /lib64/libc-2.15.so
7f853f8fe000-7f853f900000 rw-p 001a5000 09:01 201329536                  /lib64/libc-2.15.so
7f853f900000-7f853f904000 rw-p 00000000 00:00 0
7f853f904000-7f853f919000 r-xp 00000000 09:01 74726670                   /usr/lib64/gcc/x86_64-pc-linux-gnu/4.8.1/libgcc_s.so.1
7f853f919000-7f853fb19000 ---p 00015000 09:01 74726670                   /usr/lib64/gcc/x86_64-pc-linux-gnu/4.8.1/libgcc_s.so.1
7f853fb19000-7f853fb1a000 r--p 00015000 09:01 74726670                   /usr/lib64/gcc/x86_64-pc-linux-gnu/4.8.1/libgcc_s.so.1
7f853fb1a000-7f853fb1b000 rw-p 00016000 09:01 74726670                   /usr/lib64/gcc/x86_64-pc-linux-gnu/4.8.1/libgcc_s.so.1
7f853fb1b000-7f853fc11000 r-xp 00000000 09:01 201329538                  /lib64/libm-2.15.so
7f853fc11000-7f853fe10000 ---p 000f6000 09:01 201329538                  /lib64/libm-2.15.so
7f853fe10000-7f853fe11000 r--p 000f5000 09:01 201329538                  /lib64/libm-2.15.so
7f853fe11000-7f853fe12000 rw-p 000f6000 09:01 201329538                  /lib64/libm-2.15.so
7f853fe12000-7f853fefc000 r-xp 00000000 09:01 74726678                   /usr/lib64/gcc/x86_64-pc-linux-gnu/4.8.1/libstdc++.so.6.0.18
7f853fefc000-7f85400fb000 ---p 000ea000 09:01 74726678                   /usr/lib64/gcc/x86_64-pc-linux-gnu/4.8.1/libstdc++.so.6.0.18
7f85400fb000-7f8540103000 r--p 000e9000 09:01 74726678                   /usr/lib64/gcc/x86_64-pc-linux-gnu/4.8.1/libstdc++.so.6.0.18
7f8540103000-7f8540105000 rw-p 000f1000 09:01 74726678                   /usr/lib64/gcc/x86_64-pc-linux-gnu/4.8.1/libstdc++.so.6.0.18
7f8540105000-7f854011a000 rw-p 00000000 00:00 0
7f854011a000-7f854013c000 r-xp 00000000 09:01 201328977                  /lib64/ld-2.15.so
7f854031c000-7f8540321000 rw-p 00000000 00:00 0
7f8540339000-7f854033b000 rw-p 00000000 00:00 0
7f854033b000-7f854033c000 r--p 00021000 09:01 201328977                  /lib64/ld-2.15.so
7f854033c000-7f854033d000 rw-p 00022000 09:01 201328977                  /lib64/ld-2.15.so
7f854033d000-7f854033e000 rw-p 00000000 00:00 0
7fff92922000-7fff92943000 rw-p 00000000 00:00 0                          [stack]
7fff929ff000-7fff92a00000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]
[1]    18379 abort      ./heapoverflow

et si compilé avec clang ++ (6.0 (clang-600.0.56)):

$  ./heapoverflow
[1]    96277 segmentation fault  ./heapoverflow

Si vous pensiez avoir écrit un bogue comme celui-ci, voici quelques astuces pour le tracer.

Tout d’abord, compilez le code avec le drapeau de débogage (-g):

g++ -g foo.cpp

Et ensuite, lancez-le avec valgrind :

$ valgrind ./a.out
==12693== Memcheck, a memory error detector
==12693== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==12693== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==12693== Command: ./a.out
==12693==
==12693== Invalid write of size 4
==12693==    at 0x400A25: main (foo.cpp:11)
==12693==  Address 0x5a1c058 is 12 bytes after a block of size 12 alloc'd
==12693==    at 0x4C2B800: operator new[](unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-AMD64-linux.so)
==12693==    by 0x4009F6: main (foo.cpp:8)
==12693==
==12693==
==12693== HEAP SUMMARY:
==12693==     in use at exit: 0 bytes in 0 blocks
==12693==   total heap usage: 2 allocs, 2 frees, 412 bytes allocated
==12693==
==12693== All heap blocks were freed -- no leaks are possible
==12693==
==12693== For counts of detected and suppressed errors, rerun with: -v
==12693== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

Le bogue est situé dans == 12693 == à l'adresse 0x400A25: main (foo.cpp: 11)

21
McKelvin

Cela peut être dû à différentes raisons, ont dit d'autres candidats et je présenterai mon cas:

J'ai eu cette erreur en utilisant le multi-threading (std::pthread Et std::thread) Et l'erreur est survenue parce que j'ai oublié de verrouiller une variable que les multi-threads peuvent changer en même temps. cette erreur vient de manière aléatoire dans certaines exécutions, mais pas toutes parce que ... vous savez que l'accident entre les threads est aléatoire.

Cette variable dans mon cas était un global std::vector Auquel j’ai essayé de Push_back() quelque chose dans une fonction appelée par les threads .. et puis j’ai utilisé un std::mutex Sans jamais avoir cette erreur encore.

peut aider certains

3
ameerosein

Pour ceux qui recherchent des solutions ici, j'ai eu un problème similaire avec C++: malloc (): liste chaînée double smallbin corrompue:

Cela était dû à une fonction ne renvoyant pas une valeur comme elle était supposée.

std::vector<Object> generateStuff(std::vector<Object>& target> {
  std::vector<Object> returnValue;
  editStuff(target);
  // RETURN MISSING
}

Je ne sais pas pourquoi cela a été capable de compiler après tout. Il y avait probablement un avertissement à ce sujet.

2
lahjaton_j

J'ai rencontré cette erreur dans un code dans lequel quelqu'un appelait exit () dans un thread à peu près au même moment que main() retourné, de sorte que tous les constructeurs globaux/statiques étaient lancés simultanément dans deux threads distincts.

Cette erreur se manifeste également par double free or corruption, Ou par un segfault/sig11 dans exit() ou dans malloc_consolidate, Et probablement par d'autres. La pile d'appels pour le crash malloc_consolidate peut ressembler à:

#0  0xabcdabcd in malloc_consolidate () from /lib/libc.so.6
#1  0xabcdabcd in _int_free () from /lib/libc.so.6
#2  0xabcdabcd in operator delete (...)
#3  0xabcdabcd in operator delete[] (...)
(...)

Je ne pouvais pas l'obtenir pour montrer ce problème tout en courant sous Valgrind.

0
Brian Vandenberg