فساد الذاكرة في النظام.
-
24-09-2019 - |
سؤال
لدي مشكلة غريبة في ذاكرة. بعد ساعات طويلة تصحيح الأخطاء والمحاولة أعتقد أنني وجدت شيئًا.
على سبيل المثال: أقوم بتخصيص سلسلة بسيطة:
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 و 2 طرق عرض تصحيح للذاكرة (Delphi -> View -> Debug -> CPU -> الذاكرة) رأيت أنه يحدث خطأ ... بمجرد ... لم أستطع إعادة إنتاج ...
قرأت هذا الصباح شيئًا عن وضع 8087CW ، ونعم ، إذا تم تغيير هذا إلى 27 دولارًا أمريكيًا ، احصل على فساد الذاكرة! عادة ما يكون 133 دولارًا أمريكيًا:
الفرق بين 133F و 027F $ هو أن 027F $ يقوم بإعداد FPU للقيام بحسابات أقل دقة (الحد من مضاعفة في وضع ممتد) ومعالجة Infiniti المختلفة (التي تم استخدامها في FPU الأقدم ، ولكن لم يتم استخدامها بعد الآن).
حسنًا ، لقد وجدت الآن لماذا لكن لا متى!
لقد غيرت عمل بلدي Asmprofiler مع فحص بسيط (بحيث يتم فحص جميع الوظائف عند الإدخال والمغادرة):
if Get8087CW = $27F then //normally $1372?
if MainThreadID = GetCurrentThreadId then //only check mainthread
DebugBreak;
لقد "تمتعت" ببعض الوحدات و DLL's و Bingo (انظر المكدس):
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 الجزء 2:
FPU الجزء 3:
FPU النهائي: فاسد!:
ملاحظة 2: ربما يجب مسح مكدس FPU في النظام.
المحلول
لم أر هذه القضية بالذات ، لكن Move يمكن أن تفسد بالتأكيد إذا كانت FPU في حالة سيئة. يمكن لبرنامج VPN الخاص بـ Cisco أن يفسد الأمور بشكل فظيع ، حتى لو كنت لا تفعل أي شيء يتعلق بالشبكة.
http://brianorr.blogspot.com/2006/11/intel-pentium-d-floating-point-unit.html مكسور
https://web.archive.org/web/2016001043520/http://www.dankohn.com/archives/343
http://blog.excastle.com/2007/08/28/delphi-bug-of-the-day-fpu-stack-leak/ (تعليقات ريتشي أناند)
في حالتنا ، نكتشف برنامج تشغيل VPN عربات التي تجرها الدواب وتبادل الحركة وملاء تشاردو مع إصدارات Delphi 7 ، واستبدل inttoSt تحرك الحجم أيضًا ، لأنها أكثر عرضة من System.move.
نصائح أخرى
قد يكون خطأ في برنامج تشغيل الفيديو الخاص بك لا يحافظ على كلمة التحكم 8087 عند تنفيذ عملية StretchBlt.
في الماضي ، رأيت سلوكًا مشابهًا عند استخدام برامج تشغيل طابعة معينة. يعتقدون أنهم يمتلكون 8087 CW وهم مخطئون ...
لاحظ أن القيمة الافتراضية لـ 8087 CW في Delphi تبدو 1372 دولارًا ؛ للحصول على شرح أكثر تفصيلاً لقيم CW ، انظر هذه المقالة: هذا ما يفسر أيضًا الموقف الذي وصفه مايكل جوستين عندما حصل 8087cw.
-جيرون
فقط للحصول على معلوماتك (في حالة وجود نفس المشكلة أيضًا): قمنا بترقية لبرنامجنا للعميل ، والشاشة التي تعمل باللمس الكاملة عند بدء تطبيقنا! تم تجميد Windows بالكامل! كان لا بد من إعادة تشغيل الكمبيوتر (إيقاف الطاقة). استغرق الأمر بعض الوقت لمعرفة سبب التجميد الكامل.
لحسن الحظ كان لدينا واحد (فقط 1!) stacktrace من AV في fastmove.largessemove. لقد عطلت استخدام SSE في Fastmove ، وذهبت المشكلة.
بالمناسبة: لدى الشاشة التي تعمل باللمس وحدة المعالجة المركزية في Nehemiah مع مجموعة شرائح S3.
لذلك ليس فقط يمكنك الحصول على فساد الذاكرة عند استخدام FPU ، ولكن أيضًا تجميد كامل!
بالنسبة لأولئك الذين ما زالوا مهتمين بهذا: هناك سبب محتمل آخر للمشاكل:
لا يزال 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