Corrupção de memória em System.Move devido à alteração do modo 8087CW (png + stretchblt)

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

  •  24-09-2019
  •  | 
  •  

Pergunta

Eu tenho um estranho problema de corrupção de memória.Depois de muitas horas depurando e tentando, acho que encontrei algo.

Por exemplo:Eu faço uma atribuição de string simples:

sTest := 'SET LOCK_TIMEOUT ';

No entanto, o resultado às vezes se torna:

sTest = 'SET LOCK'#0'TIMEOUT '

Então, o _ é substituído por um byte 0.

Já vi isso acontecer uma vez (a reprodução é complicada, depende do tempo) na função System.Move, quando ela usa a pilha FPU (fild, fistp) para cópia rápida da memória (no caso de 9 a 32 bytes para mover):

...
@@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}
...

Usando a visualização FPU e 2 visualizações de depuração de memória (Delphi -> View -> Debug -> CPU -> Memory) vi que estava dando errado ...uma vez...não consegui reproduzir no entanto...

Esta manhã li algo sobre o modo 8087CW e, sim, se isso for alterado para US$ 27F, recebo corrupção de memória!Normalmente custa $ 133F:

A diferença entre $ 133F e $ 027F é que $ 027F configura o FPU para fazer cálculos menos precisos (limitando-se a Double em vez de Extended) e manuseio diferente do Infiniti (que era usado para FPUs mais antigos, mas não é mais usado).

Ok, agora eu encontrei por que mas não quando!

mudei o funcionamento do meu AsmProfiler com uma verificação simples (para que todas as funções sejam verificadas ao entrar e sair):

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

Eu "criei o perfil" de algumas unidades e dll's e bingo (veja a pilha):

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))

Então está acontecendo no StretchBlt ...

O que fazer agora? É uma falha do Windows ou um bug no PNG (incluído no D2007)?Ou a função System.Move não é à prova de falhas?

Observação: simplesmente tentar reproduzir não funciona:

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

Parece ser mais exótico...Mas por debugbreak em 'Get8087CW = $27F' eu poderia reproduzi-lo em outra string:FPU parte 1:FPU part 1FPU parte 2:FPU part 2FPU parte 3:FPU part 3Final da FPU:corrupto!:FPU Final: corrupt!

Nota 2: Talvez a pilha FPU deva ser limpa no System.Move?

Foi útil?

Solução

Não vi esse problema em particular, mas o movimento pode definitivamente ficar confuso se a FPU estiver em um estado ruim. O driver VPN da Cisco pode estragar tudo, mesmo que você não esteja fazendo nada relacionado à rede.

http://brianorr.blogspot.com/2006/11/Intel-Pentium-D-floating-point-unit.html quebrado

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/ (Comentários de Ritchie Annand)

No nosso caso, detectamos o driver VPN de buggy e trocamos de movimento e preenchem com as versões Delphi 7, substituímos o InttoStr por uma versão Pascal (o Int64-Version usa a FPU) e, como estamos usando o Fastmm, desativamos seu personalizado fixo personalizado O tamanho de mover rotinas também, já que são ainda mais suscetíveis que o sistema.

Outras dicas

Pode ser um bug no seu driver de vídeo que não preserva a palavra de controle 8087 quando executa a operação de alongamento.
No passado, vi comportamentos semelhantes ao usar certos drivers de impressora. Eles acham que possuem o 8087 CW e estão errados ...

Observe o valor padrão do 8087 CW em Delphi parece US $ 1372; Para uma explicação mais detalhada dos valores da CW, consulte Este artigo: Isso também explica uma situação que Michael Justin descreveu quando seu 8087CW foi cortado.

--Jeroen

Apenas para suas informações (caso alguém também tenha o mesmo problema): fizemos uma atualização do nosso software para um cliente e a tela sensível ao toque completa travada quando nosso aplicativo foi iniciado! O Windows estava completamente congelado! O PC teve que ser reiniciado (desligado). Demorou algum tempo para descobrir a causa do congelamento completo.

Felizmente, tivemos um (apenas 1!) Stacktrace de um AV em fastmove.Largessemove. Desativei o uso do SSE em FastMove, e o problema se foi.

A propósito: a tela sensível ao toque tem uma CPU via Nehemiah com um chipset S3.

Portanto, você não apenas pode obter corrupções de memória ao usar a FPU, mas também um congelamento completo!

Para quem ainda está interessado nisso:Há ainda outra causa possível de problemas:

Delphi Rio ainda vem com uma versão ASM quebrada do Move.

Tive o prazer de encontrar esse bug hoje, por sorte tive um caso de teste reproduzível.O problema é este pedaço de código:

* ***** 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

Referências

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top