Вебинар: Использование статических анализаторов кода при разработке безопасного ПО - 19.12
TrinityCore - бесплатный проект, распространяемый под лицензией GPL. Целью проекта является создание альтернативного программного обеспечения для эмулирования сервера многопользовательской игры World of Warcraft от Blizzard Entertainment. Основная цель проекта – обучающая. Проект ни в коем случаи не направлен на извлечение прибыли от использования. Исходный код, который написан на C и C++, является открытым, это значит, что он распространяется свободно, при этом на пользователя не накладывается никаких обязательств.
Мы проверили проект, используя анализатор PVS-Studio версии 4.54. Ошибок найдено немного и в основном они относятся к внешним библиотекам. Чтобы подробно проанализировать проект можно воспользоваться триальной версией PVS-Studio. Её будет достаточно, чтобы просмотреть все подозрительные участки кода. Здесь перечислим только некоторые фрагменты кода, на которых остановился взгляд:
1. Опечатка. Два раза используется 'other.y'.
inline Vector3int32& operator+=(const Vector3int32& other)
{
x += other.x;
y += other.y;
z += other.y;
return *this;
}
PVS-Studio: V537 Consider reviewing the correctness of 'y' item's usage. g3dlib vector3int32.h 77
2. Ошибка в макросе NEXT_CMP_VALUE.
static struct wordvalue doubles[] = {
{ "ch", (uchar*) "\014\031\057\057" },
{ "Ch", (uchar*) "\014\031\060\060" },
{ "CH", (uchar*) "\014\031\061\061" },
{ "c", (uchar*) "\005\012\021\021" },
{ "C", (uchar*) "\005\012\022\022" },
};
#define NEXT_CMP_VALUE(src, p, store, pass, value, len) \
while (1) \
{ \
...... \
for (i = 0; i < (int) sizeof(doubles); i++) \
{ \
const char * pattern = doubles[i].word; \
... \
} \
} \
...... \
}
PVS-Studio: V557 Array overrun is possible. The value of 'i' index could reach 39. libmysql ctype-czech.c 260
Корректный код должен выглядеть так:
for (i = 0; i < (int) sizeof(doubles) / sizeof(doubles[0]); i++)
3. Копирование только части строки.
class ACE_Name_Request
{
...
char *type_;
};
void
ACE_Name_Request::type (const char *c)
{
ACE_TRACE ("ACE_Name_Request::type");
ACE_OS::strsncpy (this->type_,
c,
sizeof this->type_);
}
PVS-Studio: V579 The strsncpy function receives the pointer and its size as arguments. It is possibly a mistake. Inspect the third argument. ace name_request_reply.cpp 251
Вместо длины строки вычисляется размер указателя.
4. Недоочищенный буфер.
ACE_INLINE int
ACE_Thread::disablecancel(struct cancel_state *old_state)
{
...
ACE_OS::memset (old_state,
0,
sizeof (old_state));
...
}
PVS-Studio: V579 The memset function receives the pointer and its size as arguments. It is possibly a mistake. Inspect the third argument. ace thread.inl 172
5. Ошибка при cравнении матриц.
bool Matrix4::operator==(const Matrix4& other) const {
if (memcmp(this, &other, sizeof(Matrix4) == 0)) {
return true;
}
...
}
PVS-Studio: V575 The 'memcmp' function processes '0' elements. Inspect the third argument. g3dlib matrix4.cpp 385
6. Всегда истинное условие.
enum enum_mysql_timestamp_type
str_to_datetime(....)
{
...
else if (str[0] != 'a' || str[0] != 'A')
...
}
PVS-Studio: V547 Expression 'str[0] != 'a' || str[0] != 'A'' is always true. Probably the '&&' operator should be used here. libmysql my_time.c 340
7. Не там поставленная скобка.
static my_bool socket_poll_read(my_socket sd, uint timeout)
{
int res;
...
if ((res = select((int) fd,
&readfds, NULL, &errorfds, &tm) <= 0))
{
DBUG_RETURN(res < 0 ? 0 : 1);
}
...
}
PVS-Studio: V593 Consider reviewing the expression of the 'A = B <= C' kind. The expression is calculated as following: 'A = (B <= C)'. libmysql viosocket.c 550
Корректный код должен выглядеть так:
if ((res= select((int) fd,
&readfds, NULL, &errorfds, &tm)) <= 0)
8. Неправильные проверки указатель на значение 0.
Достаточно много обнаруживается проверок, что указатель равен 0, уже после того, как указатель использовали. Первый пример:
bool OnCheck(Player* player, Unit* /*target*/)
{
bool checkArea =
player->GetAreaId() == AREA_ARGENT_TOURNAMENT_FIELDS ||
player->GetAreaId() == AREA_RING_OF_ASPIRANTS ||
player->GetAreaId() == AREA_RING_OF_ARGENT_VALIANTS ||
player->GetAreaId() == AREA_RING_OF_ALLIANCE_VALIANTS ||
player->GetAreaId() == AREA_RING_OF_HORDE_VALIANTS ||
player->GetAreaId() == AREA_RING_OF_CHAMPIONS;
return player && checkArea &&
player->duel && player->duel->isMounted;
}
PVS-Studio: V595 The 'player' pointer was utilized before it was verified against nullptr. Check lines: 310, 312. scripts achievement_scripts.cpp 310
Еще один пример:
CreatureAI* GetAI(Creature* creature) const
{
...
Item* item =
player->StoreNewItem(dest, ITEM_TEAR_OF_GODDESS, true);
if (item && player)
player->SendNewItem(item, 1, true, false, true);
...
}
PVS-Studio: V595 The 'player' pointer was utilized before it was verified against nullptr. Check lines: 224, 225. scripts hyjal.cpp 224
Вывод
С трудом верится, что этот проект разрабатывается энтузиастами, обучающимися программированию. Здесь перечислены не все замеченные недостатки. Но всё равно найденных ошибок слишком мало. Возможно, кто-то из разработчиков уже использовал PVS-Studio для проверки TrinityCore. Косвенным подтверждением является то, что больше всего ошибок находится с помощью диагностического правила V595. А эта диагностика появилась совсем недавно.
0