مكتبات الفنون الالكترونية بجودة عالية

تم لفت انتباهنا إلى مستودع الفنون الإلكترونية على جيثب. إنه صغير جدًا ومن بين 23 مشروعًا ، كنا مهتمين فقط بعدد قليل من مكتبات 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 ملفًا). مع مثل هذه المجلدات ، من الصعب التحدث عن جودة الكود ككل. فقط معدل 5 تحذيرات وتقرر لنفسك.

تحذير 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 يحتوي على أقصى مؤشر ممكن - 15 ، فستحدث زيادة تصل إلى 16 . كذلك في بيان التبديل ، من الممكن تجاوز حدود المصفوفة.

تم نسخ جزء الشفرة هذا إلى مكانين آخرين:

  • 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(); } 

لقد أصبح الكود أقصر كثيرًا ، الأمر الذي سهل إلى حد كبير فهم منطق التعبير الشرطي.

استنتاج


كما لاحظت ، يتم تكرار معظم التحذيرات المشبوهة في ثلاثة أماكن ، وداخل نفس الملف. رمز الازدواجية هو ممارسة التنمية سيئة للغاية ، كما يعقد دعم رمز. وإذا حدث خطأ في هذا الكود ، فإن استقرار البرنامج يتناقص بشكل حاد بسبب انتشار الأخطاء في جميع أنحاء الكود.

دعونا نأمل أن يتم نشر شيء آخر مثير للاهتمام وسنعود إلى هذا المستودع مرة أخرى :). في غضون ذلك ، أقترح أولئك الذين يرغبون في تنزيل PVS-Studio ومحاولة التحقق من مشاريعهم.



إذا كنت ترغب في مشاركة هذه المقالة مع جمهور يتحدث الإنجليزية ، فالرجاء استخدام الرابط الخاص بالترجمة: Svyatoslav Razmyslov. الكمال تقريبا المكتبات عن طريق الفنون الإلكترونية .

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


All Articles