الكمال تقريبا المكتبات عن طريق الفنون الإلكترونية

انجذب انتباهنا مؤخرًا إلى مستودع الفنون الإلكترونية على جيثب. إنه صغير جدًا ، ومن بين المشاريع الثلاثة والعشرين المتاحة هناك ، بدا عددًا قليلاً من مكتبات C ++ مثيرة للاهتمام: EASTL و EAStdC و ​​EABase و EAThread و EATest و EAMain و EAAssert. المشاريع نفسها صغيرة جدًا (حوالي 10 ملفات لكل منها) ، لذلك تم العثور على الأخطاء فقط في المشروع "الأكبر" المكون من 20 ملفًا: D لكننا وجدناها ويبدو أنها مثيرة للاهتمام! بينما كنت أكتب هذا المنشور ، كنا نواجه أيضًا مناقشة حية لألعاب EA وسياسة الشركة: D

الصورة 1


مقدمة


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; // <= else return -1; switch(*p) { case 'b': case 'd': case 'i': case 'u': case 'o': case 'x': case 'X': case 'g': case 'G': case 'e': case 'E': case 'f': case 'F': case 'a': case 'A': case 'p': case 'c': case 'C': case 's': case 'S': case 'n': { // Finalize the current span. spans[spanIndex].mpEnd = p + 1; spans[spanIndex].mFormat[nFormatLength] = 0; // <= spans[spanIndex].mFormatChar = *p; if(++spanIndex == kSpanCapacity) break; .... } 

تتكون الصفوف الممتدة [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 وجربه على مشاريعك الخاصة.

Source: https://habr.com/ru/post/ar461677/


All Articles