命令与征服3切换序列号_命令与征服游戏的代码:90年代的错误。 第一卷

命令与征服3切换序列号

image1.png

The American company Electronic Arts Inc (EA) has made the source code of the games Command & Conquer: Tibetan Dawn and Command & Conquer: Red Alert publicly available. This code should help the game community to develop mods and maps, create custom units, and customize the gameplay logic. We all now have a unique opportunity to plunge into the history of development, which is very different from the modern one. Back then, there was no StackOverflow site, convenient code editors, or powerful compilers. Moreover, at that time, there were no static analyzers, and the first thing the community will face is hundreds of errors in the code. This is what the PVS-Studio team will help you with by pointing out the erroneous places.

美国公司Electronic Arts Inc(EA)已公开发布了“命令与征服:藏族黎明”和“命令与征服:红色警报”游戏的源代码。 此代码应有助于游戏社区开发mod和地图,创建自定义单位并自定义游戏逻辑。 现在,我们所有人都有一个独特的机会,可以深入了解发展历史,这与现代发展历史截然不同。 那时,没有StackOverflow网站,方便的代码编辑器或强大的编译器。 而且,那时还没有静态分析器,而社区将要面对的第一件事就是代码中的数百个错误。 PVS-Studio团队会通过指出错误的地方来为您提供帮助。

介绍 (Introduction)

Command & Conquer is a series of computer games in the real-time strategy genre. The first game in the series was released in 1995. The Electronic Arts company acquired this game's development studio only in 1998.

Command&Conquer是实时策略类型的一系列计算机游戏。 该系列的第一款游戏于1995年发行。ElectronicArts公司仅在1998年收购了该游戏的开发工作室。

Since then, several games and many mods have been released. The source code of the games was posted together with the release of the Command & Conquer Remastered collection.

从那时起,已经发布了一些游戏和许多mod。 游戏的源代码与Command&Conquer Remastered系列的发布一起发布。

The PVS-Studio analyzer was used to find errors in the code. The tool is designed to detect errors and potential vulnerabilities in the source code of programs, written in C, C++, C#, and Java.

PVS-Studio分析仪用于查找代码中的错误。 该工具旨在检测用C,C ++,C#和Java编写的程序源代码中的错误和潜在漏洞。

Due to the large amount of problems found in the code, all error examples will be given in a series of two articles.

由于代码中存在大量问题,所有错误示例将在两篇系列文章中给出。

错别字和复制粘贴 (Typos and copy-paste)

V501 There are identical sub-expressions to the left and to the right of the '||' operator: dest == 0 || dest == 0 CONQUER.CPP 5576

V501在'||'的左侧和右侧有相同的子表达式 运算符:dest == 0 || 目标== 0 CONQUER.CPP 5576

void List_Copy(short const * source, int len, short * dest)
{
  if (dest == NULL || dest == NULL) {
    return;
  }
  ....
}

I'd like to start the review with the never ending copy-paste. The author hasn't checked the pointer for the source and checked the destination pointer twice, because they had copied the dest == NULLcheck and had forgotten to change the variable name.

我想从永无止境的复制粘贴开始审核。 作者没有检查源指针,也没有检查目标指针两次,因为他们复制了dest == NULL检查并且忘记了更改变量名。

V584 The 'Current' value is present on both sides of the '!=' operator. The expression is incorrect or it can be simplified. CREDITS.CPP 173

V584'!='运算符的两侧都存在'Current'值。 该表达式不正确或可以简化。 学分CPP 173

void CreditClass::AI(bool forced, HouseClass *player_ptr, bool logic_only)
{
  ....
  long adder = Credits - Current;
  adder = ABS(adder);
  adder >>= 5;
  adder = Bound(adder, 1L, 71+72);
  if (Current > Credits) adder = -adder;
  Current += adder;
  Countdown = 1;

  if (Current-adder != Current) {        // <=
    IsAudible = true;
    IsUp = (adder > 0);
  }
  ....
}

The analyzer found a meaningless comparison. I suppose there must have been something as follows:

分析仪发现无意义的比较。 我想一定有以下内容:

if (Current-adder != Credits)

but inattention has won.

但注意力不集中赢了。

The exact same code fragment was copied to another function:

完全相同的代码片段已复制到另一个函数:

  • V584 The 'Current' value is present on both sides of the '!=' operator. The expression is incorrect or it can be simplified. CREDITS.CPP 246

    V584'!='运算符的两侧都存在'Current'值。 该表达式不正确或可以简化。 学分246

V524 It is odd that the body of 'Mono_Y' function is fully equivalent to the body of 'Mono_X' function. MONOC.CPP 753

V524“ Mono_Y”功能的主体完全等同于“ Mono_X”功能的主体,这很奇怪。 MONOC.CPP 753

class MonoClass {
  ....
  int Get_X(void) const {return X;};
  int Get_Y(void) const {return Y;};
  ....
}

int Mono_X(void)
{
  if (MonoClass::Is_Enabled()) {
    MonoClass *mono = MonoClass::Get_Current();
    if (!mono) {
      mono = new MonoClass();
      mono->View();
    }
    return(short)mono->Get_X();                  // <=
  }
  return(0);
}

int Mono_Y(void)
{
  if (MonoClass::Is_Enabled()) {
    MonoClass *mono = MonoClass::Get_Current();
    if (!mono) {
      mono = new MonoClass();
      mono->View();
    }
    return(short)mono->Get_X();                  // <= Get_Y() ?
  }
  return(0);
}

A larger piece of code that was copied with the consequences. You have to admit, that except using the analyzer, you won't be able notice that the Get_X function instead of Get_Ywas called from the Mono_Y function. The MonoClass class does have 2 functions that differ by one symbol. Most likely, we found a real error.

带有后果的更大的代码片段。 你不得不承认,但使用的分析仪,您将无法通知的Get_X函数,而不是Get_YMono_Y函数调用。 MonoClass类的确有2个函数,每个函数之间的符号不同。 我们很可能发现了一个真正的错误。

I found the identical piece of code below:

我在下面找到了相同的代码:

  • V524 It is odd that the body of 'Mono_Y' function is equivalent to the body of 'Mono_X' function. MONOC.CPP 1083

    V524“ Mono_Y”功能的主体与“ Mono_X”功能的主体等效是奇怪的。 MONOC.CPP 1083

数组错误 (Errors with arrays)

V557 Array overrun is possible. The '9' index is pointing beyond array bound. FOOT.CPP 232

V557阵列可能超限。 “ 9”索引指向数组边界之外。 脚CPP 232

#define  CONQUER_PATH_MAX 9 // Number of cells to look ahead for movement.

FacingType Path[CONQUER_PATH_MAX];

void FootClass::Debug_Dump(MonoClass *mono) const
{
  ....
  if (What_Am_I() != RTTI_AIRCRAFT) {
    mono->Set_Cursor(50, 3);
    mono->Printf("%s%s%s%s%s%s%s%s%s%s%s%s",
      Path_To_String(Path[0]),
      Path_To_String(Path[1]),
      Path_To_String(Path[2]),
      Path_To_String(Path[3]),
      Path_To_String(Path[4]),
      Path_To_String(Path[5]),
      Path_To_String(Path[6]),
      Path_To_String(Path[7]),
      Path_To_String(Path[8]),
      Path_To_String(Path[9]),
      Path_To_String(Path[10]),
      Path_To_String(Path[11]),
      Path_To_String(Path[12]));
    ....
  }
  ....
}

It seems that this is a debugging method, but the history doesn't record to what extent it could be detrimental for the developer's mental health. Here, the Path array consists of

看来这是一种调试方法,但是历史记录并未记录它可能在多大程度上损害开发人员的心理健康。 在这里, 路径数组由

9 (9)

elements, and all

元素,以及所有

13 (13)

of them are printed.

他们被打印。

In total, 4 memory accesses outside the array boundary:

总共,在数组边界之外有4个内存访问:

  • V557 Array overrun is possible. The '9' index is pointing beyond array bound. FOOT.CPP 232

    V557阵列可能超限。 “ 9”索引指向数组边界之外。 脚CPP 232
  • V557 Array overrun is possible. The '10' index is pointing beyond array bound. FOOT.CPP 233

    V557阵列可能超限。 '10'索引指向数组边界之外。 脚CPP 233
  • V557 Array overrun is possible. The '11' index is pointing beyond array bound. FOOT.CPP 234

    V557阵列可能超限。 '11'索引指向数组边界之外。 脚CPP 234
  • V557 Array overrun is possible. The '12' index is pointing beyond array bound. FOOT.CPP 235

    V557阵列可能超限。 '12'索引指向数组边界之外。 脚CPP 235

V557 Array underrun is possible. The value of '_SpillTable[index]' index could reach -1. COORD.CPP 149

V557阵列欠载是可能的。 “ _SpillTable [index]”索引的值可以达到-1。 第149章

typedef enum FacingType : char {
  ....
  FACING_COUNT,  // 8
  FACING_FIRST=0
} FacingType;

short const * Coord_Spillage_List(COORDINATE coord, int maxsize)
{
  static short const _MoveSpillage[(int)FACING_COUNT+1][5] = {
    ....
  };

  static char const _SpillTable[16] = {8,6,2,-1,0,7,1,-1,4,5,3,-1,-1,-1,-1,-1};

  ....
  return(&_MoveSpillage[_SpillTable[index]][0]);
  ....
}

At first glance, the example is complex, but it is easy to puzzle it out after a brief analysis.

乍一看,该示例很复杂,但是经过简要分析后,很容易将其迷惑。

The two-dimensional _MoveSpillage array is accessed by an index that is taken from the _SpillTable array. The array happens to contain negative values. Perhaps, access to data is organized according to a special formula and this is what the developer intended. Nonetheless, I'm not sure about that.

二维_MoveSpillage数组由从_SpillTable数组获取的索引访问。 该数组恰好包含负值。 也许,对数据的访问是根据特殊公式进行组织的,这正是开发人员想要的。 但是,我不确定。

V512 A call of the 'sprintf' function will lead to overflow of the buffer '(char *) ptr'. SOUNDDLG.CPP 250

V512调用'sprintf'函数将导致缓冲区'(char *)ptr'溢出。 声音DLG.CPP 250

void SoundControlsClass::Process(void)
{
  ....
  void * ptr = new char [sizeof(100)];                                // <=

  if (ptr) {
    sprintf((char *)ptr, "%cTrack %d\t%d:%02d\t%s",                   // <=
      index, listbox.Count()+1, length / 60, length % 60, fullname);
    listbox.Add_Item((char const *)ptr);
  }
  ....
}

An attentive reader will wonder — why such a long string is saved in a buffer of 4 bytes? This is because the programmer thought that sizeof(100) would return something more (at least 100). However, the sizeof operator returns the size of the type and even never evaluates any expressions. The author should have just written the constant 100, or better yet, used named constants, or a different type for strings or a pointer.

细心的读者会怀疑-为什么这么长的字符串保存在4个字节的缓冲区中? 这是因为程序员认为sizeof(100)会返回更多值(至少100 )。 但是, sizeof运算符返回类型的大小,甚至从不计算任何表达式。 作者应该刚刚编写了常量100或更好的常量,使用命名常量,或者为字符串或指针使用了其他类型。

V512 A call of the 'memset' function will lead to underflow of the buffer 'Buffer'. KEYBOARD.CPP 96

V512调用'memset'函数将导致缓冲区'Buffer'下溢。 键盘CPP 96

unsigned short Buffer[256];

WWKeyboardClass::WWKeyboardClass(void)
{
  ....
  memset(Buffer, 0, 256);
  ....
}

A buffer is cleared by 256 bytes, although the full size of the original buffer is 256*sizeof(unsigned short). Oops… goofed this one.

尽管原始缓冲区的完整大小为256 * sizeof(unsigned short) ,但缓冲区会清除256个字节。 糟糕...把这个弄糟了。

It can be also fixed as follows:

也可以固定如下:

memset(Buffer, 0, sizeof(Buffer));

V557 Array overrun is possible. The 'QuantityB' function processes value '[0..86]'. Inspect the first argument. Check lines: 'HOUSE.H:928', 'CELL.CPP:2337'. HOUSE.H 928

V557阵列可能超限。 'QuantityB'函数处理值'[0..86]'。 检查第一个参数。 检查行:“ HOUSE.H:928”,“ CELL.CPP:2337”。 豪斯928

typedef enum StructType : char {
  STRUCT_NONE=-1,
  ....
  STRUCT_COUNT,                                       // <= 87
  STRUCT_FIRST=0
} StructType;

int BQuantity[STRUCT_COUNT-3];                        // <= [0..83]

int QuantityB(int index) {return(BQuantity[index]);}  // <= [0..86]

bool CellClass::Goodie_Check(FootClass * object)
{
  ....
  int bcount = 0;
  for( j=0; j < STRUCT_COUNT; j++) {
    bcount += hptr->QuantityB(j);                     // <= [0..86]
  }
  ....
}

There are a lot of global variables in the code and it is obvious that they are easy to get confused. The analyzer's warning about an array index out of bounds is issued at the point of accessing the BQuantity array by index. The array size is 84 elements. Algorithms for analyzing the data flow in the analyzer helped to find out that the index value comes from another function – Goodie_Check. There, a loop is executed with a final value of 86. Therefore, 12 bytes of "someone's" memory (3 int elements) are constantly being read in this place.

代码中有很多全局变量,很容易混淆。 在按索引访问BQuantity数组时,将发出分析器关于数组索引超出范围的警告。 数组大小为84个元素。 用于分析分析器中数据流的算法有助于发现索引值来自另一个函数– Goodie_Check 。 在那里执行循环,最终值为86 。 因此,在此位置不断读取12字节的“某人”的内存(3个int元素)。

V575 The 'memset' function processes '0' elements. Inspect the third argument. DLLInterface.cpp 1103

V575'memset'函数处理'0'元素。 检查第三个论点。 DLLInterface.cpp 1103

void* __cdecl memset(
  _Out_writes_bytes_all_(_Size) void*  _Dst,
  _In_                          int    _Val,
  _In_                          size_t _Size
);

extern "C" __declspec(dllexport) bool __cdecl CNC_Read_INI(....)
{
  ....
  memset(ini_buffer, _ini_buffer_size, 0);
  ....
}

In my opinion, I have repeatedly seen this error in modern projects. Programmers still confuse the 2nd and 3rd arguments of the memset function.

我认为,我已经在现代项目中多次看到此错误。 程序员仍然混淆memset函数的第二个和第三个参数。

One more similar fragment:

还有一个类似的片段:

  • V575 The 'memset' function processes '0' elements. Inspect the third argument. DLLInterface.cpp 1404

    V575'memset'函数处理'0'元素。 检查第三个论点。 DLLInterface.cpp 1404

关于空指针 (About null pointers)

V522 Dereferencing of the null pointer 'list' might take place. DISPLAY.CPP 1062

V522可能会取消引用空指针“列表”。 第1062章

void DisplayClass::Get_Occupy_Dimensions(int & w, int & h, short const *list)
{
  ....
  if (!list) {
    /*
    ** Loop through all cell offsets, accumulating max & min x- & y-coords
    */
    while (*list != REFRESH_EOL) {
      ....
    }
    ....
  }
  ....
}

An explicit access to a null pointer looks very strange. This place looks like the one with a typo and there are a few other places worth checking out:

显式访问空指针看起来很奇怪。 这个地方看起来像是有错别字的地方,还有其他一些地方值得一看:

  • V522 Dereferencing of the null pointer 'list' might take place. DISPLAY.CPP 951

    V522可能会取消引用空指针“列表”。 DISPLAY.CPP 951
  • V522 Dereferencing of the null pointer 'unitsptr' might take place. QUEUE.CPP 2362

    V522可能会取消引用空指针“ unitsptr”。 队列2362
  • V522 Dereferencing of the null pointer 'unitsptr' might take place. QUEUE.CPP 2699

    V522可能会取消引用空指针“ unitsptr”。 队列2699

V595 The 'enemy' pointer was utilized before it was verified against nullptr. Check lines: 3689, 3695. TECHNO.CPP 3689

V595在针对nullptr进行验证之前,已使用“敌人”指针。 检查线:3689、3695。TECHNO.CPP 3689

void TechnoClass::Base_Is_Attacked(TechnoClass const *enemy)
{
  FootClass *defender[6];
  int value[6];
  int count = 0;
  int weakest = 0;
  int desired = enemy->Risk() * 2;
  int risktotal = 0;

  /*
  ** Humans have to deal with their own base is attacked problems.
  */
  if (!enemy || House->Is_Ally(enemy) || House->IsHuman) {
    return;
  }
  ....
}

The enemy pointer is dereferenced, and then checked to make sure that it is nonnull. It is still a vital problem, dare I say it, for every open source project. I'm sure that in projects with closed code the situation is about the same, unless, of course, PVS-Studio is used ;-)

取消对敌方指针的引用,然后检查以确保其为非空。 我敢说,对于每个开源项目,这仍然是一个至关重要的问题。 我确信在封闭代码的项目中情况大致相同,除非使用了PVS-Studio ;-)

转换不正确 (Incorrect casts)

V551 The code under this 'case' label is unreachable. The '4109' value of the 'char' type is not in the range [-128; 127]. WINDOWS.CPP 547

V551此“大小写”标签下的代码不可访问。 'char'类型的'4109'值不在[-128; 127]。 WINDOWS.CPP 547

#define VK_RETURN 0x0D

typedef enum {
  ....
  WWKEY_VK_BIT = 0x1000,
  ....
}

enum {
  ....
  KA_RETURN = VK_RETURN | WWKEY_VK_BIT,
  ....
}

void Window_Print(char const string[], ...)
{
  char c; // Current character.
  ....
  switch(c) {
    ....
    case KA_FORMFEED: // <= 12
        New_Window();
        break;
    case KA_RETURN:   // <= 4109
      Flush_Line();
      ScrollCounter++;
      WinCx = 0;
      WinCy++;
      break;
    ....
  }
  ....
}

This function handles the characters you enter. As you know, a 1-byte value is placed in the char type, and the number 4109 will never be there. So, this switch statement just contains an unreachable code branch.

此功能处理您输入的字符。 如您所知,在char类型中放置了一个1字节的值,并且数字4109将永远不会存在。 因此,此switch语句仅包含一个无法访问的代码分支。

Several such places were found:

找到了几个这样的地方:

  • V551 The code under this 'case' label is unreachable. The '4105' value of the 'char' type is not in the range [-128; 127]. WINDOWS.CPP 584

    V551此“大小写”标签下的代码不可访问。 'char'类型的'4105'值不在[-128; 127]。 WINDOWS.CPP 584
  • V551 The code under this 'case' label is unreachable. The '4123' value of the 'char' type is not in the range [-128; 127]. WINDOWS.CPP 628

    V551此“大小写”标签下的代码不可访问。 'char'类型的'4123'值不在[-128; 127]。 WINDOWS.CPP 628

V552 A bool type variable is being incremented: printedtext ++. Perhaps another variable should be incremented instead. ENDING.CPP 170

V552布尔类型变量正在增加:printedtext ++。 也许应该改为增加另一个变量。 结局CPP 170

void Nod_Ending(void)
{
  ....
  bool printedtext = false;
  while (!done) {
    if (!printedtext && !Is_Sample_Playing(kanefinl)) {
      printedtext++;
      Alloc_Object(....);
      mouseshown = true;
      Show_Mouse();
    }
    ....
  }
  ....
}

In this code fragment, the analyzer found the application of the increment operation to a variable of the bool type. This is correct code from the point of view of the language, but it looks very strange now. This operation is also marked as deprecated, starting from the C++17 standard.

在此代码段中,分析器发现了增量操作对布尔类型变量的应用。 从语言的角度来看,这是正确的代码,但是现在看起来很奇怪。 从C ++ 17标准开始,此操作也被标记为已弃用。

In total, 2 such places were detected:

总共检测到2个这样的地方:

  • V552 A bool type variable is being incremented: done ++. Perhaps another variable should be incremented instead. ENDING.CPP 187

    V552布尔类型变量正在递增:完成++。 也许应该改为增加另一个变量。 ENDP.CPP 187

V556 The values of different enum types are compared. Types: ImpactType, ResultType. AIRCRAFT.CPP 742

V556比较不同枚举类型的值。 类型:ImpactType,ResultType。 飞机CPP 742

ImpactType FlyClass::Physics(COORDINATE & coord, DirType facing);

typedef enum ImpactType : unsigned char {             // <=
  IMPACT_NONE,
  IMPACT_NORMAL,
  IMPACT_EDGE
} ImpactType;

typedef enum ResultType : unsigned char {             // <=
  RESULT_NONE,
  ....
} ResultType;

void AircraftClass::AI(void)
{
  ....
  if (Physics(Coord, PrimaryFacing) != RESULT_NONE) { // <=
    Mark();
  }
  ....
}

The programmer tied some logic to comparing the values of different enumerations. Technically, this works because numerical representations are compared. But such code often leads to logical errors. It is worth tweaking the code (of course, if this project is to be supported).

程序员使用一些逻辑来比较不同枚举的值。 从技术上讲,这是有效的,因为可以比较数字表示形式。 但是这样的代码通常会导致逻辑错误。 值得调整代码(当然,如果要支持此项目)。

The entire list of warnings for this diagnostic looks like this:

此诊断的警告的整个列表如下所示:

  • V556 The values of different enum types are compared: SoundEffectName[voc].Where == IN_JUV. DLLInterface.cpp 402

    V556比较不同枚举类型的值:SoundEffectName [voc]。其中== IN_JUV。 DLLInterface.cpp 402
  • V556 The values of different enum types are compared: SoundEffectName[voc].Where == IN_VAR. DLLInterface.cpp 405

    V556比较不同枚举类型的值:SoundEffectName [voc]。其中== IN_VAR。 DLLInterface.cpp 405
  • V556 The values of different enum types are compared: Map.Theater == CNC_THEATER_DESERT. Types: TheaterType, CnCTheaterType. DLLInterface.cpp 2805

    V556比较了不同枚举类型的值:Map.Theater == CNC_THEATER_DESERT。 类型:TheaterType,CnCTheaterType。 DLLInterface.cpp 2805
  • V556 The values of different enum types are compared. Types: ImpactType, ResultType. AIRCRAFT.CPP 4269

    V556比较不同枚举类型的值。 类型:ImpactType,ResultType。 飞机CPP 4269
  • V556 The values of different enum types are compared: SoundEffectName[voc].Where == IN_VAR. DLLInterface.cpp 429

    V556比较不同枚举类型的值:SoundEffectName [voc]。其中== IN_VAR。 DLLInterface.cpp 429

V716 Suspicious type conversion in assign expression: 'HRESULT = BOOL'. GBUFFER.H 780

V716指定表达式中的可疑类型转换:“ HRESULT = BOOL”。 GBUFFER.H 780

BOOL __cdecl Linear_Blit_To_Linear(...);

inline HRESULT GraphicViewPortClass::Blit(....)
{
  HRESULT return_code=0;
  ....
  return_code=(Linear_Blit_To_Linear(this, &dest, x_pixel, y_pixel
                      , dx_pixel, dy_pixel
                      , pixel_width, pixel_height, trans));
  ....

  return ( return_code );
}

This is a very old problem that is still relevant today. There are special macros for working with the HRESULT type. Casting to BOOL and visa versa isn't used for this type. These two data types are extremely similar to each other from the point of view of the language, but logically they are still incompatible. The implicit type casting operation that exists in the code doesn't make sense.

这是一个非常老的问题,今天仍然存在。 有一些特殊的宏可用于HRESULT类型。 这种类型不使用强制转换为BOOL,反之亦然。 从语言的角度来看,这两种数据类型极为相似,但从逻辑上讲它们仍然不兼容。 代码中存在的隐式类型转换操作没有意义。

This and a few other places would be worth refactoring:

这个地方和其他一些地方值得重构:

  • V716 Suspicious type conversion in assign expression: 'HRESULT = BOOL'. GBUFFER.H 817

    V716指定表达式中的可疑类型转换:“ HRESULT = BOOL”。 GBUFFER.H 817
  • V716 Suspicious type conversion in assign expression: 'HRESULT = BOOL'. GBUFFER.H 857

    V716指定表达式中的可疑类型转换:“ HRESULT = BOOL”。 GBUFFER.H 857
  • V716 Suspicious type conversion in assign expression: 'HRESULT = BOOL'. GBUFFER.H 773

    V716指定表达式中的可疑类型转换:“ HRESULT = BOOL”。 GBUFFER.H 773
  • V716 Suspicious type conversion in assign expression: 'HRESULT = BOOL'. GBUFFER.H 810

    V716指定表达式中的可疑类型转换:“ HRESULT = BOOL”。 GBUFFER.H 810
  • V716 Suspicious type conversion in assign expression: 'HRESULT = BOOL'. GBUFFER.H 850

    V716指定表达式中的可疑类型转换:“ HRESULT = BOOL”。 GBUFFER.H 850

V610 Undefined behavior. Check the shift operator '<<'. The left operand '(~0)' is negative. MP.CPP 2410

V610未定义的行为。 检查移位运算符“ <<”。 左操作数'(〜0)'为负。 MP.CPP 2410

void XMP_Randomize(digit * result, Straw & rng, int total_bits, int precision)
{
  ....
  ((unsigned char *)result)[nbytes-1] &=
    (unsigned char)(~((~0) << (total_bits % 8)));
  ....
}

Here, the negative number is shifted to the left, which is undefined behavior. A negative number is obtained from zero when using the inversion operator. Since the result of the operation is placed in the int type, the compiler uses it to store the value, whereas it is of the signed type.

在此,负数向左移动,这是未定义的行为。 使用反转运算符时,从零获得负数。 由于运算结果放置在int类型中,因此编译器使用它来存储值,而它是有符号类型。

In 2020, the compiler already finds this error as well:

在2020年,编译器也已经发现此错误:

Warning C26453: Arithmetic overflow: Left shift of a negative signed number is undefined behavior. 警告C26453:算术溢出:负号左移是未定义的行为。

But compilers are not full-fledged static analyzers, because they solve other problems. So here is another example of undefined behavior that is only detected by PVS-Studio:

但是编译器不是成熟的静态分析器,因为它们可以解决其他问题。 因此,这是仅由PVS-Studio检测到的未定义行为的另一个示例:

V610 Undefined behavior. Check the shift operator '<<'. The right operand ('(32 — bits_to_shift)' = [1..32]) is greater than or equal to the length in bits of the promoted left operand. MP.CPP 659

V610未定义的行为。 检查移位运算符“ <<”。 右操作数('(32-bits_to_shift)'= [1..32])大于或等于提升后的左操作数的位长度。 MP.CPP 659

#define UNITSIZE 32

void XMP_Shift_Right_Bits(digit * number, int bits, int precision)
{
  ....
  int digits_to_shift = bits / UNITSIZE;
  int bits_to_shift = bits % UNITSIZE;

  int index;
  for (index = digits_to_shift; index < (precision-1); index++) {
    *number = (*(number + digits_to_shift) >> bits_to_shift) |
      (*(number + (digits_to_shift + 1)) << (UNITSIZE - bits_to_shift));
    number++;
  }
  ....
}

The analyzer has found an unusual situation. The 32-bit number can be potentially shifted to the right for the number of bits, exceeding the available number. Here's how it works:

分析仪发现异常情况。 32位数字可能会向右移动位数,超过了可用数字。 运作方式如下:

int bits_to_shift = bits % UNITSIZE;

The UNITIZE constant has the value 32:

UNITIZE常数的值为32

int bits_to_shift = bits % 32;

Thus, the value of the bits_to_shift variable will be zero for all bits values that are multiples of 32.

因此,对于32的倍数的所有值, bits_to_shift变量的值将为零。

Therefore, in this code fragment:

因此,在此代码片段中:

.... << (UNITSIZE - bits_to_shift) ....

32 digits will be shifted if 0 is subtracted from the constant 32.

如果从常数32减去0,则32位将被移位。

List of all PVS-Studio warnings about shifts with undefined behavior:

有关具有未定义行为的班次的所有PVS-Studio警告列表:

  • V610 Undefined behavior. Check the shift operator '<<'. The left operand '(~0)' is negative. TARGET.H 66

    V610未定义的行为。 检查移位运算符“ <<”。 左操作数'(〜0)'为负。 目标66
  • V610 Undefined behavior. Check the shift operator '<<'. The left operand '(((- 24) * 256) / 24)' is negative. ANIM.CPP 160

    V610未定义的行为。 检查移位运算符“ <<”。 左操作数'((((-24)* 256)/ 24)'为负。 动漫CPP 160
  • V610 Undefined behavior. Check the shift operator '<<'. The left operand '(((- 12) * 256) / 24)' is negative. BUILDING.CPP 4037

    V610未定义的行为。 检查移位运算符“ <<”。 左操作数'((((-12)* 256)/ 24)'为负。 建筑.CPP 4037
  • V610 Undefined behavior. Check the shift operator '<<'. The left operand '(((- 21) * 256) / 24)' is negative. DRIVE.CPP 2160

    V610未定义的行为。 检查移位运算符“ <<”。 左操作数'((((-21)* 256)/ 24)'为负。 DRIVE.CPP 2160
  • V610 Undefined behavior. Check the shift operator '<<'. The left operand '(((- 21) * 256) / 24)' is negative. DRIVE.CPP 2161

    V610未定义的行为。 检查移位运算符“ <<”。 左操作数'((((-21)* 256)/ 24)'为负。 DRIVE.CPP 2161
  • V610 Undefined behavior. Check the shift operator '<<'. The left operand '(((- 20) * 256) / 24)' is negative. DRIVE.CPP 2162

    V610未定义的行为。 检查移位运算符“ <<”。 左操作数'((((-20)* 256)/ 24)'为负。 DRIVE.CPP 2162
  • V610 Undefined behavior. Check the shift operator '<<'. The left operand '(((- 20) * 256) / 24)' is negative. DRIVE.CPP 2163

    V610未定义的行为。 检查移位运算符“ <<”。 左操作数'((((-20)* 256)/ 24)'为负。 DRIVE.CPP 2163
  • V610 Undefined behavior. Check the shift operator '<<'. The left operand '(((- 18) * 256) / 24)' is negative. DRIVE.CPP 2164

    V610未定义的行为。 检查移位运算符“ <<”。 左操作数'((((-18)* 256)/ 24)'为负。 DRIVE.CPP 2164
  • V610 Undefined behavior. Check the shift operator '<<'. The left operand '(((- 18) * 256) / 24)' is negative. DRIVE.CPP 2165

    V610未定义的行为。 检查移位运算符“ <<”。 左操作数'((((-18)* 256)/ 24)'为负。 DRIVE.CPP 2165
  • V610 Undefined behavior. Check the shift operator '<<'. The left operand '(((- 17) * 256) / 24)' is negative. DRIVE.CPP 2166

    V610未定义的行为。 检查移位运算符“ <<”。 左操作数'((((-17)* 256)/ 24)'为负。 DRIVE.CPP 2166
  • V610 Undefined behavior. Check the shift operator '<<'. The left operand '(((- 16) * 256) / 24)' is negative. DRIVE.CPP 2167

    V610未定义的行为。 检查移位运算符“ <<”。 左操作数'((((-16)* 256)/ 24)'为负。 DRIVE.CPP 2167
  • V610 Undefined behavior. Check the shift operator '<<'. The left operand '(((- 15) * 256) / 24)' is negative. DRIVE.CPP 2168

    V610未定义的行为。 检查移位运算符“ <<”。 左操作数'((((-15)* 256)/ 24)'为负。 DRIVE.CPP 2168
  • V610 Undefined behavior. Check the shift operator '<<'. The left operand '(((- 14) * 256) / 24)' is negative. DRIVE.CPP 2169

    V610未定义的行为。 检查移位运算符“ <<”。 左操作数'((((-14)* 256)/ 24)'为负。 DRIVE.CPP 2169
  • V610 Undefined behavior. Check the shift operator '<<'. The left operand '(((- 13) * 256) / 24)' is negative. DRIVE.CPP 2170

    V610未定义的行为。 检查移位运算符“ <<”。 左操作数'((((-13)* 256)/ 24)'为负。 DRIVE.CPP 2170
  • V610 Undefined behavior. Check the shift operator '<<'. The left operand '(((- 12) * 256) / 24)' is negative. DRIVE.CPP 2171

    V610未定义的行为。 检查移位运算符“ <<”。 左操作数'((((-12)* 256)/ 24)'为负。 DRIVE.CPP 2171
  • V610 Undefined behavior. Check the shift operator '<<'. The left operand '(((- 11) * 256) / 24)' is negative. DRIVE.CPP 2172

    V610未定义的行为。 检查移位运算符“ <<”。 左操作数'((((-11)* 256)/ 24)'为负。 DRIVE.CPP 2172
  • V610 Undefined behavior. Check the shift operator '<<'. The left operand '(((- 10) * 256) / 24)' is negative. DRIVE.CPP 2173

    V610未定义的行为。 检查移位运算符“ <<”。 左操作数'((((-10)* 256)/ 24)'为负。 DRIVE.CPP 2173
  • V610 Undefined behavior. Check the shift operator '<<'. The left operand '(((- 9) * 256) / 24)' is negative. DRIVE.CPP 2174

    V610未定义的行为。 检查移位运算符“ <<”。 左操作数'((((-9)* 256)/ 24)'为负。 DRIVE.CPP 2174
  • V610 Undefined behavior. Check the shift operator '<<'. The left operand '(((- 8) * 256) / 24)' is negative. DRIVE.CPP 2175

    V610未定义的行为。 检查移位运算符“ <<”。 左操作数'((((-8)* 256)/ 24)'为负。 DRIVE.CPP 2175
  • V610 Undefined behavior. Check the shift operator '<<'. The left operand '(((- 7) * 256) / 24)' is negative. DRIVE.CPP 2176

    V610未定义的行为。 检查移位运算符“ <<”。 左操作数'((((-7)* 256)/ 24)'为负。 DRIVE.CPP 2176
  • V610 Undefined behavior. Check the shift operator '<<'. The left operand '(((- 6) * 256) / 24)' is negative. DRIVE.CPP 2177

    V610未定义的行为。 检查移位运算符“ <<”。 左操作数'((((-6)* 256)/ 24)'为负。 DRIVE.CPP 2177
  • V610 Undefined behavior. Check the shift operator '<<'. The left operand '(((- 5) * 256) / 24)' is negative. DRIVE.CPP 2178

    V610未定义的行为。 检查移位运算符“ <<”。 左操作数'((((-5)* 256)/ 24)'为负。 DRIVE.CPP 2178
  • V610 Undefined behavior. Check the shift operator '<<'. The left operand '(((- 4) * 256) / 24)' is negative. DRIVE.CPP 2179

    V610未定义的行为。 检查移位运算符“ <<”。 左操作数'((((-4)* 256)/ 24)'为负。 DRIVE.CPP 2179
  • V610 Undefined behavior. Check the shift operator '<<'. The left operand '(((- 3) * 256) / 24)' is negative. DRIVE.CPP 2180

    V610未定义的行为。 检查移位运算符“ <<”。 左操作数'((((-3)* 256)/ 24)'为负。 DRIVE.CPP 2180
  • V610 Undefined behavior. Check the shift operator '<<'. The left operand '(((- 2) * 256) / 24)' is negative. DRIVE.CPP 2181

    V610未定义的行为。 检查移位运算符“ <<”。 左操作数'((((-2)* 256)/ 24)'为负。 DRIVE.CPP 2181
  • V610 Undefined behavior. Check the shift operator '<<'. The left operand '(((- 1) * 256) / 24)' is negative. DRIVE.CPP 2182

    V610未定义的行为。 检查移位运算符“ <<”。 左操作数'((((-1)* 256)/ 24)'为负。 DRIVE.CPP 2182
  • V610 Undefined behavior. Check the shift operator '<<'. The left operand '(((- 5) * 256) / 24)' is negative. INFANTRY.CPP 2730

    V610未定义的行为。 检查移位运算符“ <<”。 左操作数'((((-5)* 256)/ 24)'为负。 婴幼儿CPP 2730
  • V610 Undefined behavior. Check the shift operator '>>'. The right operand ('(32 — bits_to_shift)' = [1..32]) is greater than or equal to the length in bits of the promoted left operand. MP.CPP 743

    V610未定义的行为。 检查移位运算符“ >>”。 右操作数('(32-bits_to_shift)'= [1..32])大于或等于提升后的左操作数的位长度。 MP.CPP 743
  • V610 Undefined behavior. Check the shift operator '<<'. The left operand '(~0)' is negative. RANDOM.CPP 102

    V610未定义的行为。 检查移位运算符“ <<”。 左操作数'(〜0)'为负。 随机CPP 102
  • V610 Undefined behavior. Check the shift operator '<<'. The left operand '(~0L)' is negative. RANDOM.CPP 164

    V610未定义的行为。 检查移位运算符“ <<”。 左操作数'(〜0L)'为负。 随机CPP 164

结论 (Conclusion)

Let's hope that modern projects of Electronic Arts are of better quality. If not, we invite you to our site to download and try PVS-Studio on all projects.

我们希望电子艺术的现代项目质量更高。 如果没有,我们邀请您访问我们的网站以下载并尝试在所有项目上使用PVS-Studio。

Someone may object that cool successful games used to be made with this quality, and we can partially agree with this. On the other hand, we mustn't forget that competition in the development of programs and games has grown many times over the years. Expenses on development, support, and advertising have also increased. Consequently, fixing errors at later stages of development can lead to significant financial and reputational losses.

有人可能会反对曾经以这种品质制作成功的出色游戏,我们可以部分同意。 另一方面,我们不要忘记,多年来程序和游戏开发方面的竞争已经增长了很多倍。 开发,支持和广告费用也有所增加。 因此,在开发的后期阶段修复错误可能会导致重大的财务和声誉损失。

Follow our blog and don't miss the 2nd part of the review of this games series.

请关注我们的博客,不要错过本游戏系列评论的第二部分。

翻译自: https://habr.com/en/company/pvs-studio/blog/507164/

命令与征服3切换序列号

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值