Almost Perfect Libraries by Electronic Arts




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

Picture 1

Introduction

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.

Warning 1

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.

Warning 2

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:

  • V557 Array overrun is possible. The value of 'nFormatLength' index could reach 16. EASprintfOrdered.cpp 614
  • V557 Array overrun is possible. The value of 'nFormatLength' index could reach 16. EASprintfOrdered.cpp 977

Warning 3

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:

  • V560 A part of conditional expression is always true: (result >= 0). EASprintfOrdered.cpp 852
  • V560 A part of conditional expression is always true: (result >= 0). EASprintfOrdered.cpp 1215

Warning 4

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:

  • V1009 Check the array initialization. Only the first element is initialized explicitly. The rest elements are initialized with zeros. EASprintfOrdered.cpp 518
  • V1009 Check the array initialization. Only the first element is initialized explicitly. The rest elements are initialized with zeros. EASprintfOrdered.cpp 881

Warning 5

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.

Conclusion

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.



Use PVS-Studio to search for bugs in C, C++, C# and Java

We offer you to check your project code with PVS-Studio. Just one bug found in the project will show you the benefits of the static code analysis methodology better than a dozen of the articles.

goto PVS-Studio;


Bugs Found

Checked Projects
361
Collected Errors
13 417