Webinar: Evaluation - 05.12
Our attention was recently attracted by the Electronic Arts repository on GitHub. It's tiny, and of the twenty-three projects available there, only a few C++ libraries seemed interesting: EASTL, EAStdC, EABase, EAThread, EATest, EAMain, and EAAssert. The projects themselves are tiny too (about 10 files each), so bugs were found only in the "largest" project of 20 files :D But we did find them, and they do look interesting! As I was writing this post, we were also having a lively discussion of EA games and the company's policy :D.
Electronic Arts (EA) is an American video game company. It has a small repository on GitHub and a few C++ projects, namely C++ libraries: EASTL, EAStdC, EABase, EAThread, EATest, EAMain, and EAAssert. They are tiny, and the PVS-Studio analyzer managed to find any bugs at all only in the "largest" project, EAStdC (20 files). With sizes like that, you can't reliably judge the overall code quality, so just take a look at the following five warnings and decide for yourselves.
V524 It is odd that the body of '>>' function is fully equivalent to the body of '<<' function. 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;}
....
}
When overloading the shift operators, the programmer made a typo in one of them by writing << instead of >>. This looks very much like a copy-paste mistake.
V557 Array overrun is possible. The value of 'nFormatLength' index could reach 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;
....
}
The spans[spanIndex].mFormat array consists of 16 elements, so the last valid element's index is 15. In its current form, the OVprintfCore function ends up incrementing the index of nFormatLength to 16 if it has the highest possible index, i.e. 15. After that, an array-out-of-bounds error will occur in the switch statement.
This fragment was copied two more times:
V560 A part of conditional expression is always true: (result >= 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;
}
....
}
The result >= 0 condition is always true as the result variable is not changed anywhere in the loop. The code doesn't look right at all, and there must be some mistake in it.
This fragment was copied two more times:
V1009 Check the array initialization. Only the first element is initialized explicitly. The rest elements are initialized with zeros. EASprintfOrdered.cpp 151
static int OVprintfCore(....)
{
....
int spanArgOrder[kArgCapacity] = { -1 };
....
}
This is not necessarily a bug, but the authors should be warned that only the first element of the spanArgOrder array is initialized to -1, while all the rest will be set to 0.
This fragment was copied two more times:
V728 An excessive check can be simplified. The '(A && !B) || (!A && B)' expression is equivalent to the 'bool(A) != bool(B)' expression. int128.h 1242
inline void int128_t::Modulus(....) const
{
....
bool bDividendNegative = false;
bool bDivisorNegative = false;
....
if( (bDividendNegative && !bDivisorNegative)
|| (!bDividendNegative && bDivisorNegative))
{
quotient.Negate();
}
....
}
I formatted this example for clarity, but in its original form, this condition is very long and difficult to read. But we can make it much better by simplifying the conditional expression, as the analyzer suggests:
if( bDividendNegative != bDivisorNegative)
{
quotient.Negate();
}
The code is much shorter now, which makes the condition's logic much easier to grasp.
As you may have noticed, most of the warnings have two more duplicates, and all those duplicates are found in the same file. Code duplication is a very bad anti-pattern since it complicates program maintenance a lot. When bugs creep into such code, the program's stability drops drastically because they spread all over the code.
Hopefully, EA will upload some other interesting projects and we'll visit their repository once again :). Meanwhile, welcome to download PVS-Studio and try it on your own projects.
0