انجذب انتباهنا مؤخرًا إلى مستودع الفنون الإلكترونية على جيثب. إنه صغير جدًا ، ومن بين المشاريع الثلاثة والعشرين المتاحة هناك ، بدا عددًا قليلاً من مكتبات C ++ مثيرة للاهتمام: EASTL و EAStdC و EABase و EAThread و EATest و EAMain و EAAssert. المشاريع نفسها صغيرة جدًا (حوالي 10 ملفات لكل منها) ، لذلك تم العثور على الأخطاء فقط في المشروع "الأكبر" المكون من 20 ملفًا: D لكننا وجدناها ويبدو أنها مثيرة للاهتمام! بينما كنت أكتب هذا المنشور ، كنا نواجه أيضًا مناقشة حية لألعاب EA وسياسة الشركة: D
مقدمة
Electronic Arts (EA) هي شركة ألعاب فيديو أمريكية. يحتوي على
مستودع صغير على GitHub وعدد قليل من مشاريع C ++ ، وهي مكتبات C ++: EASTL و EAStdC و EABase و EAThread و EATest و EAMain و EAAssert. إنها صغيرة جدًا ، وقد تمكن محلل
PVS-Studio من العثور على أي أخطاء على الإطلاق في المشروع "الأكبر" ،
EAStdC (20 ملفًا). باستخدام أحجام كهذه ،
لا يمكنك الحكم على جودة الشفرة العامة
بشكل موثوق ، لذلك ألقِ نظرة على التحذيرات الخمسة التالية واتخذ قرارًا بأنفسك.
تحذير 1
V524 من الغريب أن يكون جسم الدالة ">>" مكافئًا تمامًا لجسم وظيفة "<<". EAFixedPoint.h 287
template <class T, int upShiftInt, int downShiftInt, int upMulInt, int downDivInt> struct FPTemplate { .... FPTemplate operator<<(int numBits) const { return value << numBits; } FPTemplate operator>>(int numBits) const { return value << numBits; } FPTemplate& operator<<=(int numBits) { value <<= numBits; return *this;} FPTemplate& operator>>=(int numBits) { value >>= numBits; return *this;} .... }
عند التحميل الزائد لمشغلي النوبات ، قام المبرمج بعمل خطأ مطبعي في أحدهم بكتابة << بدلاً من >>. هذا يشبه إلى حد كبير خطأ نسخ لصق.
تحذير 2
V557 تجاوز الصفيف هو ممكن. يمكن أن تصل قيمة مؤشر 'nFormatLength' إلى 16. EASprintfOrdered.cpp 246
static const int kSpanFormatCapacity = 16; struct Span8 { .... char mFormat[kSpanFormatCapacity]; .... }; static int OVprintfCore(....) { .... EA_ASSERT(nFormatLength < kSpanFormatCapacity); if(nFormatLength < kSpanFormatCapacity) spans[spanIndex].mFormat[nFormatLength++] = *p;
تتكون
الصفوف الممتدة [spanIndex] .mFormat من
16 عنصرًا ، بحيث يكون فهرس العنصر
الصالح الأخير هو
15 . في شكلها الحالي ، تنتهي الدالة
OVprintfCore بزيادة مؤشر
nFormatLength إلى
16 إذا كان به أعلى مؤشر ممكن ، أي
15 . بعد ذلك ، سيحدث خطأ صفيف خارج الحدود في بيان
التبديل .
تم نسخ هذه القطعة مرتين أخريين:
- V557 تجاوز الصفيف هو ممكن. يمكن أن تصل قيمة مؤشر 'nFormatLength' إلى 16. EASprintfOrdered.cpp 614
- V557 تجاوز الصفيف هو ممكن. يمكن أن تصل قيمة مؤشر "nFormatLength" إلى 16. EASprintfOrdered.cpp 977
تحذير 3
V560 جزء من التعبير الشرطي صحيح دائمًا: (النتيجة> = 0). EASprintfOrdered.cpp 489
static int OVprintfCore(....) { .... for(result = 1; (result >= 0) && (p < pEnd); ++p) { if(pWriteFunction8(p, 1, pWriteFunctionContext8, kWFSIntermediate) < 0) return -1; nWriteCountSum += result; } .... }
تكون
النتيجة> = 0 شرطًا صحيحًا دائمًا حيث لا يتم تغيير متغير
النتيجة في أي مكان في الحلقة. لا يبدو الرمز صحيحًا على الإطلاق ، ويجب أن يكون هناك خطأ ما فيه.
تم نسخ هذه القطعة مرتين أخريين:
- V560 جزء من التعبير الشرطي صحيح دائمًا: (النتيجة> = 0). EASprintfOrdered.cpp 852
- V560 جزء من التعبير الشرطي صحيح دائمًا: (النتيجة> = 0). EASprintfOrdered.cpp 1215
تحذير 4
V1009 تحقق من تهيئة الصفيف. تتم تهيئة العنصر الأول فقط بشكل صريح. تتم تهيئة العناصر الباقية مع الأصفار. EASprintfOrdered.cpp 151
static int OVprintfCore(....) { .... int spanArgOrder[kArgCapacity] = { -1 }; .... }
هذا ليس بالضرورة خطأ ، لكن يجب تحذير المؤلفين من أنه تتم تهيئة العنصر الأول فقط من مجموعة
spanArgOrder إلى
-1 ، بينما سيتم تعيين الباقي على 0.
تم نسخ هذه القطعة مرتين أخريين:
- V1009 تحقق من تهيئة الصفيف. تتم تهيئة العنصر الأول فقط بشكل صريح. تتم تهيئة العناصر الباقية مع الأصفار. EASprintfOrdered.cpp 518
- V1009 تحقق من تهيئة الصفيف. تتم تهيئة العنصر الأول فقط بشكل صريح. تتم تهيئة العناصر الباقية مع الأصفار. EASprintfOrdered.cpp 881
تحذير 5
V728 يمكن تبسيط عملية التحقق المفرطة. The '(A &&! B) || تعبير (! A && B) 'يعادل التعبير' bool (A)! = Bool (B) '. int128.h 1242
inline void int128_t::Modulus(....) const { .... bool bDividendNegative = false; bool bDivisorNegative = false; .... if( (bDividendNegative && !bDivisorNegative) || (!bDividendNegative && bDivisorNegative)) { quotient.Negate(); } .... }
لقد قمت بتنسيق هذا المثال من أجل الوضوح ، ولكن في شكله الأصلي ، هذا الشرط طويل جدًا ويصعب قراءته. ولكن يمكننا أن نجعلها أفضل بكثير من خلال تبسيط التعبير الشرطي ، كما يشير المحلل:
if( bDividendNegative != bDivisorNegative) { quotient.Negate(); }
الرمز الآن أقصر بكثير ، مما يجعل فهم منطق الشرط أسهل بكثير.
استنتاج
كما لاحظت ، فإن معظم التحذيرات تحتوي على نسختين أخريين ، وكل تلك التكرارات موجودة في نفس الملف. يعد ازدواج التعليمات البرمجية نمطًا سيئًا جدًا لمكافحة النمط لأنه يعقد صيانة البرنامج كثيرًا. عندما تتسلل الأخطاء إلى مثل هذا الكود ، ينخفض استقرار البرنامج بشكل كبير لأنها تنتشر في جميع أنحاء الكود.
نأمل أن تقوم EA بتحميل بعض المشروعات الأخرى المثيرة للاهتمام وسنقوم بزيارة مستودعها مرة أخرى :). في هذه الأثناء ، مرحبًا بتنزيل
PVS-Studio وجربه على مشاريعك الخاصة.