Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате |
Форум программистов > C/C++: Программирование под Unix/Linux > Проверка ядра FreeBSD. Часть 2 |
Автор: Alticor 17.2.2016, 21:40 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Заключительная статья про проверку ядра FreeBSD с помощью статического анализатора PVS-Studio Строки ![]() http://www.viva64.com/ru/d/0130/ It is dangerous to print the string 'buffer' into itself. ata-highpoint.c 102
Здесь формируют некую строку в буфере. Потом хотят получить новую строку, сохранив предыдущее значение строки, и добавить к ней ещё два слова. Вроде всё просто. Для объяснения, почему здесь будет получен неожиданный результат, я процитирую простой и понятный пример из документации к этой диагностике:
В результате работы этого кода хочется получить строку:
Но на практике в буфере будет сформирована строка:
В других ситуациях аналогичный код может привести не только к выводу некорректного текста, но и к аварийному завершению программы. Код может быть исправлен, если использовать для сохранения результата новый буфер. Корректный вариант:
http://www.viva64.com/ru/d/0101/ A call of the 'strcpy' function will lead to overflow of the buffer 'p->vendor'. aacraid_cam.c 571
Все три строки здесь заполняются неверно. В массивах нет места для http://www.viva64.com/ru/t/0088/, из-за чего могут возникать серьёзные проблемы при дальнейшей работе с такими строками. В случае с "p->vendor" и "p->product" можно убрать один пробел. Тогда поместится терминальный ноль, который функция strcpy() добавляет в конец строки. А вот для "p->revision" совсем нет места для символа конца строки, поэтому надо увеличить значение SID_REVISION_SIZE хотя бы на единицу. Мне, конечно, сложно судить об этом коде. Возможно, терминальный ноль и не нужен, и всё рассчитано на определенный размер буфера. Тогда неверно выбрана функция strcpy(). В этом случае следовало написать как-то так:
http://www.viva64.com/ru/d/0186/ 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'. http://www.viva64.com/ru/d/0181/ The bzero function receives the pointer and its size as arguments. It is possibly a mistake. Inspect the second argument. osapi.c 316
Чтобы обнулить структуру, в функцию bzero() надо передать указатель на структуру и размер обнуляемой памяти в байтах, но тут в функцию передают размер указателя, а не размер структуры. Правильный вариант должен выглядеть так:
http://www.viva64.com/ru/d/0181/ 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' опять передали размер указателя, а не объекта. Правильный вариант должен выглядеть так:
http://www.viva64.com/ru/d/0181/ 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 байт в зависимости от разрядности архитектуры. Указатели ![]() http://www.viva64.com/ru/d/0148/ 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] происходит выход за границы массива. http://www.viva64.com/ru/d/0092/ This is a nonsensical comparison: pointer >= 0. geom_vinum_plex.c 173
Очень интересное место удалось найти с помощью 503-й диагностики. Нет практического смысла проверять, что значение указателя больше или равно 0. Скорее всего, здесь забыли разыменовать указатель "sdno", чтобы сравнить хранимое там значение. Ещё два сравнения указателя с нулём:
http://www.viva64.com/ru/d/0111/ Dereferencing of the null pointer 'sc' might take place. mrsas.c 4027
Если указатель "sc" нулевой, то выполняется выход из функции. Но тут непонятно, зачем пытаться выполнить разыменование такого указателя "sc->mrsas_dev". Список странных мест:
http://www.viva64.com/ru/d/0354/ 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(). Циклы ![]() http://www.viva64.com/ru/d/0238/ 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'. Переполнение знаковой переменной является ничем иным, как неопределённым поведением программы. Причем это не абстрактная теоретическая опасность, а вполне реальная. Недавно мой коллега писал статью на эту тему: "http://www.viva64.com/ru/b/0374/". Ещё интересный момент. Точно такая же ошибка была http://www.viva64.com/ru/b/0317/ мной в коде операционной системы Haiku (см. раздел "Предупреждения #17, #18"). Не знаю, кто у кого позаимствовал файл "if_ae.c", но ошибка явно размножается копированием ![]() http://www.viva64.com/ru/d/0124/ 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, тогда зачем использовать два цикла? Я специально привел этот пример, т.к. он является наиболее наглядным, когда использование одной переменной во внешних и вложенных циклах приводит к неожиданным результатам. http://www.viva64.com/ru/d/0124/ The variable 'i' is being used for this loop and for the outer loop. Check lines: 197, 208. linux_vdso.c 208
А это слишком сложный пример, чтобы понять, правильно ли выполняется код. Но по предыдущему примеру можно сделать вывод, что тут возможно тоже выполняется неверное количество итераций. http://www.viva64.com/ru/d/0137/ Expression 'j >= 0' is always true. Unsigned type value is always >= 0. safe.c 1596
В этой функции присутствуют два опасных цикла. Т.к. переменная 'j' (счётчики циклов) имеет беззнаковый тип, то проверка "j >= 0" всегда истинна и циклы являются "вечными". Другая проблема заключается в том, что из этого счётчика постоянно вычитаются значения, следовательно, если будет попытка преодолеть нулевое значение, то переменная 'j' примет максимальное значение типа. http://www.viva64.com/ru/d/0352/ 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". Разработчикам необходимо перепроверить это место и обязательно дать разные имена переменным. Разное http://www.viva64.com/ru/d/0164/ Truncation of constant value -96. The value range of unsigned char type: [0, 255]. if_rsu.c 1516
Очень подозрительно, что беззнаковой переменной "rxs.nf" присваивается отрицательное значение '-96'. В итоге переменная будет иметь значение '160'. http://www.viva64.com/ru/d/0376/ Function body contains the 'done' label that is not used by any 'goto' statements. zfs_acl.c 2023
В коде встречаются функции, которые содержат метки, но при этом вызов оператора 'goto' для этих меток отсутствует. Например, в данном фрагменте кода метка 'top' используется, а вот 'done' нигде не используется. Возможно, переход на метку забыли добавить или со временем удалили, а метку случайно оставили. http://www.viva64.com/ru/d/0265/ Consider inspecting the application's logic. It's possible that 'else' keyword is missing. mac_process.c 352
Напоследок хочу рассказать про подозрительное форматирование, с которым уже столкнулся в самом начале проверки проекта. Здесь код оформлен таким образом, что отсутствие ключевого слова 'else' выглядит подозрительным. http://www.viva64.com/ru/d/0344/ 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 получила весь список предупреждений анализатора, на которые стоит обратить внимание. Предлагаю всем желающим попробовать http://www.viva64.com/ru/pvs-studio/ на своих проектах. Анализатор работает в среде Windows. Для использования анализатора в разработке проектов для Linux/FreeBSD у нас нет публичной версии. Но мы можем обсудить возможные варианты заключения контракта по адаптации PVS-Studio для ваших проектов и задач. |
Автор: _zorn_ 19.2.2016, 16:04 |
Фряшные, они настолько консервативные... что умирают... а жаль... |
Автор: borisbn 22.2.2016, 09:10 |
Там ещё m->m_pkthdr.rcvif есть, так что должно упасть, если m==NULL. Если бы этого не было, то - да, анализатор мог и ошибиться. |
Автор: feodorv 22.2.2016, 20:47 |
Совершено верно, почему и есть предположение, что. |