Как PVS-Studio защищает от поспешных правок кода, пример N3

от автора

Blender, PVS-Studio, Example 3
Продолжаем серию маленьких заметок про то, как анализатор PVS-Studio может быстро находить новые ошибки в коде. При условии, конечно, что он регулярно используется :). Итак, перед нами очередной баг в проекте Blender.

Ради развлечения я мониторю проект Blender. Каждый день мне приходит отчёт PVS-Studio с предупреждениями, относящимися к новому коду. Иногда какая-то ошибка мне нравится, и я пишу про неё. Именно этим я занимаюсь и сейчас :).

На предыдущие статьи я ссылки давать не буду, так как они однотипны. Этими статьями я демонстрирую, что если регулярно использовать статический анализатор, то многие баги можно обнаружить очень быстро. А чем раньше ошибка найдена, тем дешевле её исправление.

В этот раз моё внимание привлекло два предупреждения PVS-Studio, указывающих на одну строчку кода:

  • [CWE-480] V616: The ‘OB_MODE_OBJECT’ named constant with the value of 0 is used in the bitwise operation. transform_snap_object.c 480
  • [CWE-571] V560: A part of conditional expression is always true: !(base->object->mode & OB_MODE_OBJECT). transform_snap_object.c 480

Это нормально. Одна аномалия в коде может быть подозрительна с точки зрения разных диагностических правил. Перед нами как раз такой случай:

if (is_object_active && !(base->object->mode & OB_MODE_OBJECT)) {

Если вы прочитали сообщения анализатора, то, думаю, уже догадались, в чём дело. Однако если просто смотреть на этот код, то он кажется совершенно нормальным. Скажем так, эта строчка может не вызвать ни у кого подозрения в процессе обзора кода.

Чтобы понять, что код неверен, нужно посмотреть, как объявлена именованная константа в перечислении eObjectMode:

typedef enum eObjectMode {   OB_MODE_OBJECT = 0,   OB_MODE_EDIT = 1 << 0,   OB_MODE_SCULPT = 1 << 1,   OB_MODE_VERTEX_PAINT = 1 << 2,   OB_MODE_WEIGHT_PAINT = 1 << 3,   OB_MODE_TEXTURE_PAINT = 1 << 4,   OB_MODE_PARTICLE_EDIT = 1 << 5,   OB_MODE_POSE = 1 << 6,   OB_MODE_EDIT_GPENCIL = 1 << 7,   OB_MODE_PAINT_GPENCIL = 1 << 8,   OB_MODE_SCULPT_GPENCIL = 1 << 9,   OB_MODE_WEIGHT_GPENCIL = 1 << 10,   OB_MODE_VERTEX_GPENCIL = 1 << 11, } eObjectMode;

Итак, константа OB_MODE_OBJECT равна нулю! Ещё раз взглянем на условие:

if (is_object_active && !(base->object->mode & OB_MODE_OBJECT)) {

Получается, что результат операции «побитовый И» (&) всегда равен 0. Про это первое сообщение анализатора.

Применим к 0 оператор «!», и получается, что перед нами выражение:

if (is_object_active && true) {

Про то, что часть выражения всегда истинна, нам говорит уже второе предупреждение PVS-Studio.

Скорее всего, правильным вариантом будет:

if (is_object_active && base->object->mode != OB_MODE_OBJECT) {

Впрочем, я не уверен, так как не разбираюсь в исходном коде Blender. Задача анализатора – указать на ошибку, а что с ней делать, должен решать уже разработчик проекта.

Спасибо за внимание и подписывайтесь на мой Twitter: @Code_Analysis.

Дополнительные ссылки:

  1. Бесплатные варианты лицензирования PVS-Studio.
  2. Как внедрить статический анализатор кода в legacy проект и не демотивировать команду.
  3. Развитие инструментария С++ программистов: статические анализаторы кода.

Если хотите поделиться этой статьей с англоязычной аудиторией, то прошу использовать ссылку на перевод: Andrey Karpov. How PVS-Studio prevents rash code changes, example N3.


ссылка на оригинал статьи https://habr.com/ru/company/pvs-studio/blog/651939/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *