Повреждение памяти в System.Move из-за изменения режима 8087CW (png + stretchblt) - PullRequest
24 голосов
/ 06 апреля 2010

У меня странная проблема с повреждением памяти.После многих часов отладки и попыток, я думаю, что-то нашел.

Например: я делаю простое строковое присваивание:

sTest := 'SET LOCK_TIMEOUT ';

Однако результат иногда становится:

sTest = 'SET LOCK'#0'TIMEOUT '

Итак, _ заменяется на 0 байт.

Я видел, как это происходило один раз (воспроизведение сложно, зависит от синхронизации) в функции System.Move, когда она использует стек FPU (fild, fistp) для быстрого копирования памяти (в случае с 9 до 32байт для перемещения):

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

Использование представления FPU и двух представлений отладки памяти (Delphi -> View -> Debug -> CPU -> Memory) Я видел, что происходит не так ... один раз ...однако не удалось воспроизвести ...

Этим утром я прочитал кое-что о режиме 8087CW, и да, если это изменить на $ 27FI, получит повреждение памяти!Обычно это $ 133F:

Разница между $ 133F и $ 027F состоит в том, что $ 027F устанавливает FPU для выполнения менее точных вычислений (ограничение Double вместо Extended) и другой обработки бесконечности (который использовался для старых FPU, но больше не используется).

Хорошо, теперь я нашел почему , но не , когда !

Я изменил работу моего AsmProfiler с помощью простой проверки (чтобы все функции проверялись при входе и выходе):

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

Я "профилировал" некоторые единицы, а также библиотеки DLL и бинго(см. стек):

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

Так происходит в StretchBlt ...

Что теперь делать? Это ошибка Windows или ошибкав PNG (входит в D2007)?Или функция System.Move не является отказоустойчивой?

Примечание: просто попытка воспроизвести не работает:

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

Это кажется более экзотическим ...Но с помощью отладки на 'Get8087CW = $ 27F' я мог воспроизвести ее на другой строке: FPU часть 1: FPU part 1 FPU часть 2: FPU part 2 FPU часть 3: FPU part 3 FPU Final: повреждено !: FPU Final: corrupt!

Примечание 2: Может быть, стек FPU должен быть очищен в System.Move?

Ответы [ 4 ]

9 голосов
/ 06 апреля 2010

Я не видел этой конкретной проблемы, но Move может определенно испортиться, если FPU находится в плохом состоянии.Драйвер Cisco для VPN может привести к ужасным последствиям, даже если вы ничего не делаете с сетью.

http://brianorr.blogspot.com/2006/11/intel-pentium-d-floating-point-unit.html [сломано]

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/ (комментарии Ритчи Аннанд)

В нашем случае мы обнаруживаем глючный драйвер VPN и заменяем Move и FillChar версиями Delphi 7, заменяем IntToStr версией Pascal (используется Int64-версия)FPU), и, поскольку мы используем FastMM, мы также отключаем его пользовательские процедуры перемещения фиксированного размера, поскольку они даже более восприимчивы, чем System.Move.

3 голосов
/ 06 апреля 2010

Это может быть ошибка в вашем видеодрайвере, которая не сохраняет управляющее слово 8087 при выполнении операции StretchBlt.
В прошлом я видел подобное поведение при использовании определенных драйверов принтера. Они думают, что владеют 8087 CW и ошибаются ...

Обратите внимание, что значение по умолчанию для 8087 CW в Delphi составляет $ 1372; более подробное объяснение значений CW см. в этой статье : здесь также объясняется ситуация, описанная Майклом Джастином, когда его 8087CW надевали.

- Йерун

2 голосов
/ 07 июня 2011

Только для вашей информации (на случай, если у кого-то еще есть такая же проблема): мы выполнили обновление нашего программного обеспечения для клиента, и полный сенсорный экран заблокировался при запуске нашего приложения! Окна были полностью заморожены! ПК должен был быть перезагружен (выключен). Потребовалось некоторое время, чтобы выяснить причину полной заморозки.

К счастью, в FastMove.LargeSSEMove у нас была одна (всего 1!) Трассировка стека AV. Я отключил использование SSE в fastmove, и проблема исчезла.

Кстати: сенсорный экран оснащен процессором VIA Nehemiah с чипсетом S3.

Так что не только вы можете получить повреждения памяти при использовании FPU, но и полностью заморозить!

1 голос
/ 12 декабря 2018

Для тех, кто все еще заинтересован в этом: есть еще одна возможная причина проблем:

Delphi Rio все еще поставляется с сломанной версией ASM Move.

Я имел удовольствие бежатьв эту ошибку сегодня, к счастью, у меня был воспроизводимый контрольный пример.Проблема заключается в следующем коде:

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

Ссылки

...