corruption de mémoire dans System.Move à cause du mode de 8087CW modifié (+ .png StretchBlt)

StackOverflow https://stackoverflow.com/questions/2584654

  •  24-09-2019
  •  | 
  •  

Question

J'ai un problème de corruption de mémoire étrange. Après de nombreuses heures de débogage et d'essayer, je pense avoir trouvé quelque chose.

Par exemple: Je fais une simple affectation de chaîne:

sTest := 'SET LOCK_TIMEOUT ';

Cependant, le résultat devient parfois:

sTest = 'SET LOCK'#0'TIMEOUT '

Ainsi, le _ remplacé par un obtient 0 octet.

Je l'ai vu ce qui se passe une fois (reproduction est délicate, en fonction du moment) dans la fonction System.Move, quand il utilise la pile FPU (FILD, FISTP) pour la copie de mémoire rapide (en cas de 9 jusqu'à 32 octets pour se déplacer ):

...
@@SmallMove: {9..32 Byte Move}
fild    qword ptr [eax+ecx] {Load Last 8}
fild    qword ptr [eax] {Load First 8}
cmp     ecx, 8
jle     @@Small16
fild    qword ptr [eax+8] {Load Second 8}
cmp     ecx, 16
jle     @@Small24
fild    qword ptr [eax+16] {Load Third 8}
fistp   qword ptr [edx+16] {Save Third 8}
...

Utilisation de la vue FPU et 2 vues de débogage de mémoire (Delphi -> Affichage -> Debug -> CPU -> Memory) Je l'ai vu une fois qui ne va pas ... ... ne pouvait pas reproduire mais ...

Ce matin, je lis quelque chose sur le mode 8087CW, et oui, si cela est changé en 27F $ Je reçois la corruption de la mémoire! Normalement, il est 133f $:

  

La différence entre 133f $ et 027F $ est que $ 027F met en place la FPU pour effectuer des calculs moins précis (limitant à doubler au lieu de Extended) et différent manipulation infiniti (qui a été utilisé pour de plus âgés de FPU, mais il est pas utilisé plus).

Bon, maintenant j'ai trouvé pourquoi mais pas quand

J'ai changé le travail de mon AsmProfiler avec une simple vérification (donc toutes les fonctions sont vérifié à entrer et sortir):

if Get8087CW = $27F then    //normally $1372?
  if MainThreadID = GetCurrentThreadId then  //only check mainthread
    DebugBreak;

I "profilés" quelques unités et dll et bingo (voir la pile):

Windows.StretchBlt(3372289943,0,0,514,345,4211154027,0,0,514,345,13369376)
pngimage.TPNGObject.DrawPartialTrans(4211154027,(0, 0, 514, 345, (0, 0), (514, 345)))
pngimage.TPNGObject.Draw($7FF62450,(0, 0, 514, 345, (0, 0), (514, 345)))
Graphics.TCanvas.StretchDraw((0, 0, 514, 345, (0, 0), (514, 345)),$7FECF3D0)
ExtCtrls.TImage.Paint
Controls.TGraphicControl.WMPaint((15, 4211154027, 0, 0))

Il se passe dans StretchBlt ...

Que faire maintenant? Est-ce un défaut de Windows, ou un bogue dans PNG (inclus dans le D2007)? Ou est la fonction System.Move pas infaillibles?

Remarque: tout simplement essayer de reproduire ne fonctionne pas:

  Set8087CW($27F);
  sSQL := 'SET LOCK_TIMEOUT ';

Il semble être plus exotique ... Mais DebugBreak sur « Get8087CW = $ 27F » je pourrais le reproduire sur une autre chaîne: FPU partie 1: partie FPU 1 FPU partie 2: partie FPU 2 FPU partie 3: partie FPU 3 FPU final: corrompu !: FPU final: corrompu

Note 2: Peut-être que la pile FPU doit être effacé dans le System.Move

Était-ce utile?

La solution

Je ne l'ai pas vu cette question particulière, mais certainement mouvement peut se foiré si le FPU est dans un mauvais état. Le pilote VPN de Cisco peut visser les choses horriblement, même si vous ne faites réseau tout ce qui touche.

http: //brianorr.blogspot .com / 2006/11 / Intel Pentium-d-virgule flottante-unit.html [cassé]

https: //web.archive .org / web / 20160601043520 / http: //www.dankohn.com/archives/343

http: //blog.excastle.com/2007/08/28/delphi-bug-of-the-day-fpu-stack-leak/ (commentaires par Ritchie Annand)

Dans notre cas, nous détectons le pilote VPN buggy et échanger sur Déplacer et FillChar avec les versions Delphi 7, remplacer IntToStr avec une version Pascal (Int64 version utilise le FPU), et, puisque nous utilisons FastMM, nous désactivons ses routines de déplacement de taille fixe aussi sur mesure, car ils sont encore plus sensibles que System.Move.

Autres conseils

Il est peut-être un bug dans votre pilote vidéo qui ne préserve pas le mot de commande 8087 quand il effectue l'opération StretchBlt.
Dans le passé, je l'ai vu un comportement similaire lorsque vous utilisez certains pilotes d'imprimante. Ils pensent qu'ils possèdent la 8087 CW et sont mal ...

Notez la valeur par défaut du 8087 CW dans Delphi semble 1372 $; pour une explication plus détaillée des valeurs CW, voir cet article . il explique aussi une situation que Michael Justin décrit quand son 8087CW se est lavé au jet

- jeroen

Pour votre information (au cas où un autre a même problème aussi): nous avons fait une mise à jour de notre logiciel pour un client, et l'écran tactile complet verrouillé lorsque notre application a commencé! Windows a été complètement gelé! Le PC a dû être redémarré (hors tension). Il a fallu un certain temps pour comprendre la cause du gel complet.

Heureusement, nous avons eu un (1 seul!) Stacktrace d'un AV FastMove.LargeSSEMove. J'ai désactivé l'utilisation de l'ESS dans fastmove, et le problème a disparu.

Soit dit en passant:. Écran tactile a un cpu Néhémie VIA avec un chipset S3

Ainsi, non seulement vous pouvez obtenir corruptions de mémoire lors de l'utilisation de la FPU, mais aussi un gel complet!

Pour ceux qui sont encore intéressés à ceci: Il y a encore une autre cause possible de problèmes:

Delphi Rio encore livré avec une version cassée ASM de Move.

J'ai eu le plaisir de courir dans ce bug aujourd'hui, heureusement assez j'ai eu un test reproductible. La question est ce morceau de code:

* ***** BEGIN LICENSE BLOCK *****
 *
 * The assembly function Move is licensed under the CodeGear license terms.
 *
 * The initial developer of the original code is Fastcode
 *
 * Portions created by the initial developer are Copyright (C) 2002-2004
 * the initial developer. All Rights Reserved.
 *
 * Contributor(s): John O'Harrow
 *
 * ***** END LICENSE BLOCK ***** *)

// ... some less interesting parts omitted ...

@@LargeMove:
        JNG     @@LargeDone {Count < 0}
        CMP     EAX, EDX
        JA      @@LargeForwardMove

        // the following overlap test is broken
        // when size>uint(destaddr), EDX underflows to $FFxxxxxx, in which case 
        // we jump to @LargeForwardMove even if a backward loop would be appropriate
        // this will effectively shred everything at EDX + size
        SUB     EDX, ECX              // when this underflows ...
        CMP     EAX, EDX              // ... we also get CF=1 here (EDX is usually < $FFxxxxxx)
        LEA     EDX, [EDX+ECX]        // (does not affect flags)
        JNA     @@LargeForwardMove    // ... CF=1 so let's jump into disaster!

        SUB     ECX, 8 {Backward Move}
        PUSH    ECX
        FILD    QWORD PTR [EAX+ECX] {Last 8}
        FILD    QWORD PTR [EAX] {First 8}
        ADD     ECX, EDX
        AND     ECX, -8 {8-Byte Align Writes}
        SUB     ECX, EDX

Références

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top