Dump de base “inexplicable”

J’ai vu beaucoup de décharges de base dans ma vie, mais celle-ci me laisse perplexe.

Le contexte:

  • programme multi-thread Linux / x86_64 exécuté sur un cluster de processeurs AMD Barcelona
  • le code qui plante est beaucoup exécuté
  • l’exécution de 1000 instances du programme (l’exact même binary optimisé) sous charge produit 1-2 accidents par heure
  • les accidents se produisent sur des machines différentes (mais les machines elles-mêmes sont assez identiques)
  • les plantage se ressemblent tous (même adresse exacte, même stack d’appel)

Voici les détails du crash:

Program terminated with signal 11, Segmentation fault. #0 0x00000000017bd9fd in Foo() (gdb) x/i $pc => 0x17bd9fd : rex.RB orb $0x8d,(%r15) (gdb) x/6i $pc-12 0x17bd9f1 : mov (%rbx),%eax 0x17bd9f3 : mov %rbx,%rdi 0x17bd9f6 : callq *0x70(%rax) 0x17bd9f9 : cmp %eax,%r12d 0x17bd9fc : mov %eax,-0x80(%rbp) 0x17bd9ff : jge 0x17bd97e  

Vous remarquerez que le plantage s’est produit au milieu d’une instruction à 0x17bd9fc , qui est revenu d’un appel à 0x17bd9f6 à une fonction virtuelle.

Lorsque j’examine la table virtuelle, je constate qu’elle n’est en aucun cas corrompue:

 (gdb) x/a $rbx 0x2ab094951f80: 0x3f8c550  (gdb) x/a 0x3f8c550+0x70 0x3f8c5c0 : 0x2d3d7b0  

et qu’il pointe vers cette fonction sortingviale (comme prévu en regardant la source):

 (gdb) disas 0x2d3d7b0 Dump of assembler code for function _ZN4Foo13GetEv: 0x0000000002d3d7b0 : push %rbp 0x0000000002d3d7b1 : mov 0x70(%rdi),%eax 0x0000000002d3d7b4 : mov %rsp,%rbp 0x0000000002d3d7b7 : leaveq 0x0000000002d3d7b8 : retq End of assembler dump. 

De plus, lorsque je regarde l’adresse de retour que Foo1::Get() devrait avoir renvoyée à:

 (gdb) x/a $rsp-8 0x2afa55602048: 0x17bd9f9  

Je vois que cela pointe vers la bonne instruction, donc c’est comme si lors du retour de Foo1::Get() , certains gremlin venaient et augmentaient de %rip de 4.

Des explications plausibles?

Aussi improbable que cela puisse paraître, il semble que nous ayons rencontré un bogue de CPU réel.

http://support.amd.com/us/Processor_TechDocs/41322_10h_Rev_Gd.pdf a erratum # 721:

Processeur 721 peut mettre à jour de manière incorrecte pointeur de stack

La description

 Under a highly specific and detailed set of internal timing conditions, the processor may incorrectly update the stack pointer after a long series of push and/or near-call instructions, or a long series of pop and/or near-return instructions. The processor must be in 64-bit mode for this erratum to occur. 

Effet potentiel sur le système

 The stack pointer value jumps by a value of approximately 1024, either in the positive or negative direction. This incorrect stack pointer causes unpredictable program or system behavior, usually observed as a program exception or crash (for example, a #GP or #UD). 

J’ai déjà vu un “opcode illégal” tomber en panne au beau milieu d’une instruction. Je travaillais sur un port Linux. En bref, Linux se soustrait au pointeur d’instruction pour redémarrer un appel système, et dans mon cas, cela se produisait deux fois (si deux signaux arrivaient en même temps).

C’est donc un coupable possible: le kernel sortingpote votre pointeur d’instruction. Il peut y avoir une autre cause dans votre cas.

Gardez à l’esprit que parfois, le processeur comprendra les données qu’il traite en tant qu’instruction, même si elles ne sont pas censées le faire. Ainsi, le processeur peut avoir exécuté “l’instruction” à 0x17bd9fa, puis déplacé vers 0x17bd9fd, puis généré une exception d’opcode illégale. (Je viens juste d’append ce numéro, mais expérimenter un désassembleur peut vous montrer où le processeur peut avoir “entré” le stream d’instructions.)

Bon débogage!