|
Модераторы: xvr |
|
Alticor |
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Новичок Профиль Группа: Участник Сообщений: 6 Регистрация: 15.12.2015 Репутация: нет Всего: нет |
Заключительная статья про проверку ядра FreeBSD с помощью статического анализатора PVS-Studio Строки V541 It is dangerous to print the string 'buffer' into itself. ata-highpoint.c 102
Здесь формируют некую строку в буфере. Потом хотят получить новую строку, сохранив предыдущее значение строки, и добавить к ней ещё два слова. Вроде всё просто. Для объяснения, почему здесь будет получен неожиданный результат, я процитирую простой и понятный пример из документации к этой диагностике:
В результате работы этого кода хочется получить строку:
Но на практике в буфере будет сформирована строка:
В других ситуациях аналогичный код может привести не только к выводу некорректного текста, но и к аварийному завершению программы. Код может быть исправлен, если использовать для сохранения результата новый буфер. Корректный вариант:
V512 A call of the 'strcpy' function will lead to overflow of the buffer 'p->vendor'. aacraid_cam.c 571
Все три строки здесь заполняются неверно. В массивах нет места для null-терминального символа, из-за чего могут возникать серьёзные проблемы при дальнейшей работе с такими строками. В случае с "p->vendor" и "p->product" можно убрать один пробел. Тогда поместится терминальный ноль, который функция strcpy() добавляет в конец строки. А вот для "p->revision" совсем нет места для символа конца строки, поэтому надо увеличить значение SID_REVISION_SIZE хотя бы на единицу. Мне, конечно, сложно судить об этом коде. Возможно, терминальный ноль и не нужен, и всё рассчитано на определенный размер буфера. Тогда неверно выбрана функция strcpy(). В этом случае следовало написать как-то так:
V583 The '?:' operator, regardless of its conditional expression, always returns one and the same value: td->td_name. subr_turnstile.c 1029
Подозрительное место. Несмотря на проверку "td->td_name[0] != '\0'", эту строку всё равно выводят на печать. Все такие места:
Операции с памятью В этом разделе я расскажу о неправильном использовании следующих функций:
Функция bzero() заполняет нулями 'len' байт по указателю 'b'.
Функция copyout() копирует 'len' байт из 'kaddr' в 'uaddr'. V579 The bzero function receives the pointer and its size as arguments. It is possibly a mistake. Inspect the second argument. osapi.c 316
Чтобы обнулить структуру, в функцию bzero() надо передать указатель на структуру и размер обнуляемой памяти в байтах, но тут в функцию передают размер указателя, а не размер структуры. Правильный вариант должен выглядеть так:
V579 The bzero function receives the pointer and its size as arguments. It is possibly a mistake. Inspect the second argument. acpi_package.c 83
В этом примере похожая ситуация: в функцию 'bzero' опять передали размер указателя, а не объекта. Правильный вариант должен выглядеть так:
V579 The copyout function receives the pointer and its size as arguments. It is possibly a mistake. Inspect the third argument. if_nxge.c 1498
В данном примере копируют память из 'data' в 'ifreqp->ifr_data', при этом размер копируемой памяти равен sizeof(data), т.е. 4 или 8 байт в зависимости от разрядности архитектуры. Указатели V557 Array overrun is possible. The '2' index is pointing beyond array bound. if_spppsubr.c 4348
Размер типа 'u_char' - 1 байт в 32-х и 64-х битном приложениях, а размер типа 'u_long' - 4 байта в 32-х битном приложении и 8 байт в 64-х битном приложении. Тогда в 32-х битном приложении при выполнении операции "u_long* ch = (u_long *)sp->myauth.challenge" массив 'ch' будет состоять из 4-х элементов по 4 байта. А в 64-х битном приложении массив 'ch' будет состоять из 2-х элементов по 8 байт. Следовательно, если мы соберём 64-битное ядро, то при обращение к ch[2] и ch[3] происходит выход за границы массива. V503 This is a nonsensical comparison: pointer >= 0. geom_vinum_plex.c 173
Очень интересное место удалось найти с помощью 503-й диагностики. Нет практического смысла проверять, что значение указателя больше или равно 0. Скорее всего, здесь забыли разыменовать указатель "sdno", чтобы сравнить хранимое там значение. Ещё два сравнения указателя с нулём:
V522 Dereferencing of the null pointer 'sc' might take place. mrsas.c 4027
Если указатель "sc" нулевой, то выполняется выход из функции. Но тут непонятно, зачем пытаться выполнить разыменование такого указателя "sc->mrsas_dev". Список странных мест:
V713 The pointer m was utilized in the logical expression before it was verified against nullptr in the same logical expression. ip_fastfwd.c 245
Проверка "m == NULL" стоит в неправильном месте. Сначала надо выполнить проверку указателя, а только потом только вызывать функцию pfil_run_hooks(). Циклы V621 Consider inspecting the 'for' operator. It's possible that the loop will be executed incorrectly or won't be executed at all. if_ae.c 1663
В исходном коде FreeBSD нашёлся такой интересный и неправильный цикл. Неизвестно зачем, но тут делается декремент счётчика цикла, вместо того, чтобы делать инкремент. Получается, что цикл может выполняться гораздо больше, чем значение AE_IDLE_TIMEOUT, пока не выполнится оператор 'break'. Если цикл вовремя не будет остановлен, то произойдёт переполнение знаковой переменной 'i'. Переполнение знаковой переменной является ничем иным, как неопределённым поведением программы. Причем это не абстрактная теоретическая опасность, а вполне реальная. Недавно мой коллега писал статью на эту тему: "Undefined behavior ближе, чем вы думаете". Ещё интересный момент. Точно такая же ошибка была обнаружена мной в коде операционной системы Haiku (см. раздел "Предупреждения #17, #18"). Не знаю, кто у кого позаимствовал файл "if_ae.c", но ошибка явно размножается копированием . V535 The variable 'i' is being used for this loop and for the outer loop. Check lines: 182, 183. mfi_tbolt.c 183
Этот небольшой код скорее всего используется для создания задержки, только суммарно тут выполняется 10000 итераций, а не 10*10000, тогда зачем использовать два цикла? Я специально привел этот пример, т.к. он является наиболее наглядным, когда использование одной переменной во внешних и вложенных циклах приводит к неожиданным результатам. V535 The variable 'i' is being used for this loop and for the outer loop. Check lines: 197, 208. linux_vdso.c 208
А это слишком сложный пример, чтобы понять, правильно ли выполняется код. Но по предыдущему примеру можно сделать вывод, что тут возможно тоже выполняется неверное количество итераций. V547 Expression 'j >= 0' is always true. Unsigned type value is always >= 0. safe.c 1596
В этой функции присутствуют два опасных цикла. Т.к. переменная 'j' (счётчики циклов) имеет беззнаковый тип, то проверка "j >= 0" всегда истинна и циклы являются "вечными". Другая проблема заключается в том, что из этого счётчика постоянно вычитаются значения, следовательно, если будет попытка преодолеть нулевое значение, то переменная 'j' примет максимальное значение типа. V711 It is dangerous to create a local variable within a loop with a same name as a variable controlling this loop. powernow.c 733
В теле цикла обнаружено объявление переменной, совпадающей с переменной, используемой для контроля цикла. У меня есть подозрение, что из-за создания локального указателя с таким же именем 'pst', значение внешнего указателя с именем 'pst' не изменяется. Возможно, в условии цикла do....while() всегда проверяется одно и тоже значение "pst->cupid". Разработчикам необходимо перепроверить это место и обязательно дать разные имена переменным. Разное V569 Truncation of constant value -96. The value range of unsigned char type: [0, 255]. if_rsu.c 1516
Очень подозрительно, что беззнаковой переменной "rxs.nf" присваивается отрицательное значение '-96'. В итоге переменная будет иметь значение '160'. V729 Function body contains the 'done' label that is not used by any 'goto' statements. zfs_acl.c 2023
В коде встречаются функции, которые содержат метки, но при этом вызов оператора 'goto' для этих меток отсутствует. Например, в данном фрагменте кода метка 'top' используется, а вот 'done' нигде не используется. Возможно, переход на метку забыли добавить или со временем удалили, а метку случайно оставили. V646 Consider inspecting the application's logic. It's possible that 'else' keyword is missing. mac_process.c 352
Напоследок хочу рассказать про подозрительное форматирование, с которым уже столкнулся в самом начале проверки проекта. Здесь код оформлен таким образом, что отсутствие ключевого слова 'else' выглядит подозрительным. V705 It is possible that 'else' block was forgotten or commented out, thus altering the program's operation logics. scsi_da.c 3231
В этом коде ещё нет ошибки, но когда-нибудь она точно появится. Оставляя такой большой комментарий перед 'else', можно случайно забыть, что где-то там было это ключевое слово, и неосознанно внести ошибочную правку в код в будущем. Заключение Проект FreeBSD проверялся специальной версией PVS-Studio, которая показала отличный результат! Весь полученный материал невозможно было уместить в одной этой статье. Тем не менее, команда разработчиков FreeBSD получила весь список предупреждений анализатора, на которые стоит обратить внимание. Предлагаю всем желающим попробовать PVS-Studio на своих проектах. Анализатор работает в среде Windows. Для использования анализатора в разработке проектов для Linux/FreeBSD у нас нет публичной версии. Но мы можем обсудить возможные варианты заключения контракта по адаптации PVS-Studio для ваших проектов и задач. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||
_zorn_ |
|
|||
Эксперт Профиль Группа: Завсегдатай Сообщений: 1077 Регистрация: 21.8.2007 Репутация: нет Всего: 12 |
Фряшные, они настолько консервативные... что умирают... а жаль...
|
|||
|
||||
feodorv |
|
|||
Эксперт Профиль Группа: Комодератор Сообщений: 2214 Регистрация: 30.7.2011 Репутация: 1 Всего: 45 |
Всегда с удовольствием читаю про найденные Вашим анализатором ошибки и огрехи. И всё же возникают замечания.
Это точно правильный вариант??? sizeof(void)??? На мой взгляд, поспешный вывод. Обратите внимание на &m при вызове pfil_run_hooks. То есть, быть может, при входе в функцию ip_tryforward переменная m однозначно не NULL, но после вызова функции pfil_run_hooks может стать NULL'ом, и тогда нужно делать goto drop. Честно говоря, нужно покопаться в коде, чтобы понять, есть здесь ошибка или нет. К сожалению, не приведено объявление переменной data, поэтому судить об ошибке сложно (и это если не обращать внимания на то, что *data затирается при вызове copyout). Это сообщение отредактировал(а) feodorv - 20.2.2016, 18:43 -------------------- Напильник, велосипед, грабли и костыли - основные инструменты программиста... |
|||
|
||||
borisbn |
|
|||
Эксперт Профиль Группа: Завсегдатай Сообщений: 4875 Регистрация: 6.2.2010 Где: Ростов-на-Дону Репутация: нет Всего: 135 |
Там ещё m->m_pkthdr.rcvif есть, так что должно упасть, если m==NULL. Если бы этого не было, то - да, анализатор мог и ошибиться. -------------------- Женщины отличаются от программистов тем, что у них чары состоят из стрингов |
|||
|
||||
feodorv |
|
|||
Эксперт Профиль Группа: Комодератор Сообщений: 2214 Регистрация: 30.7.2011 Репутация: 1 Всего: 45 |
Совершено верно, почему и есть предположение, что
-------------------- Напильник, велосипед, грабли и костыли - основные инструменты программиста... |
|||
|
||||
Правила форума "С/С++: Программирование под Unix/Linux" | |
|
Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, xvr. |
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей) | |
0 Пользователей: | |
« Предыдущая тема | C/C++: Программирование под Unix/Linux | Следующая тема » |
|
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности Powered by Invision Power Board(R) 1.3 © 2003 IPS, Inc. |