corrupción de memoria en System.Move debido al modo de 8087CW cambiada (png + StretchBlt)
-
24-09-2019 - |
Pregunta
Tengo extraño problema de corrupción de memoria. Después de muchas horas de depuración y tratando Creo que he encontrado algo.
Por ejemplo: hago un simple asignación de cadena:
sTest := 'SET LOCK_TIMEOUT ';
Sin embargo, el resultado veces se convierte en:
sTest = 'SET LOCK'#0'TIMEOUT '
Por lo tanto, el _ es reemplazado por un 0 bytes.
he visto que esto ocurra una vez (reproducción es complicado, depende de la sincronización) en la función System.Move, cuando se usa la pila FPU (Fild, FISTP) para la copia de memoria rápida (en el caso de 9 y 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}
...
Uso de la vista FPU y 2 vistas de depuración de memoria (Delphi -> Ver -> Depuración -> CPU -> Memoria) lo vi saliendo mal ... una vez ... no podía reproducirse sin embargo ...
Esta mañana he leído algo sobre el modo de 8087CW, y sí, si esto se convierte en $ 27F consigo daños en la memoria! Normalmente es $ 133f:
La diferencia entre $ 133f y $ 027F es que $ 027F conjuntos hasta la FPU para hacer cálculos menos precisos (limitando al doble en lugar de extendido) y gastos de envío diferente Infiniti (que fue utilizado para mayores FPU de, pero no se utiliza ningún más).
Bien, ahora he encontrado ¿Por qué pero no cuando
Me cambió el trabajo de mi AsmProfiler con una sencilla comprobación (por lo que todas las funciones son controlados en entrar y salir):
if Get8087CW = $27F then //normally $1372?
if MainThreadID = GetCurrentThreadId then //only check mainthread
DebugBreak;
"perfilado" y algunas unidades de DLL y bingo (ver pila):
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))
Por lo que está sucediendo en StretchBlt ...
¿Qué hacer ahora? ¿Es una falla de Windows o un error en PNG (incluido en D2007)? O es la función System.Move no a prueba de fallos?
Nota: , simplemente intentando reproducir no funciona:
Set8087CW($27F);
sSQL := 'SET LOCK_TIMEOUT ';
Parece que es más exótico ... Pero por DebugBreak en 'Get8087CW = $ 27F' que pueda reproducirla en una otra cadena: FPU parte 1: FPU parte 2: FPU parte 3: FPU final: corruptos !:
Nota 2:? Tal vez la pila FPU debe ser despejado en el System.Move
Solución
No he visto este tema en particular, sino que se mueven sin duda puede llegar en mal estado si el FPU está en mal estado. controlador VPN de Cisco puede enredar las cosas terriblemente, incluso si usted no está haciendo nada relacionado con la red.
http: //brianorr.blogspot .com / 2006/11 / Intel Pentium-d-punto flotante unit.html [roto]
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/ (los comentarios de Ritchie Annand)
En nuestro caso detectamos el conductor de carro VPN y de intercambio a cabo Mover y FillChar con las versiones de Delphi 7, reemplace IntToStr con una versión Pascal (Int64-versión utiliza la FPU), y, ya que estamos utilizando FastMM, desactivamos su costumbre rutinas de movimiento tamaño fijo también, ya que son más susceptibles que los System.Move.
Otros consejos
Podría ser un error en el controlador de vídeo que no conserva la palabra de control 8087 cuando se realiza la operación StretchBlt.
En el pasado he visto un comportamiento similar cuando se utilizan ciertos controladores de impresora. Ellos piensan que son dueños de la CW y 8087 están equivocados ...
Tenga en cuenta el valor por defecto del 8087 CW en Delphi parece $ 1372; para una explicación más detallada de los valores de CW, ver este artículo: . también explica una situación que Michael Justin describe cuando su 8087CW consiguió una manguera
- Jeroen
Sólo para su información (en caso de que alguno más tiene el mismo problema también): hicimos una actualización de nuestro software para un cliente, y la pantalla táctil completa encerrado cuando se inició nuestra aplicación! Windows fue completamente congelado! El PC ha tenido que reiniciar (la alimentación). Tomó un poco de tiempo para averiguar la causa de la congelación completa.
Afortunadamente, tenía una (sólo 1!) StackTrace de un AV en FastMove.LargeSSEMove. He desactivado el uso de la ESS en fastmove, y el problema se ha ido.
Por cierto:. Pantalla táctil tiene una CPU a través de Nehemías con un conjunto de chips S3
Así que no sólo se puede obtener corrupción de memoria cuando se utiliza el FPU, sino también una congelación completa!
Para aquellos que todavía está interesado en esto: Hay otra posible causa de problemas:
Delphi Rio todavía se envía con una versión ASM roto de Move
.
tuve el placer de correr en ese error hoy en día, por suerte tuve un caso de prueba reproducible. La cuestión es esta pieza 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