Отново проверявам HTTP сървъра на Apache

Проектът Apache HTTP Server продължава да се развива. Анализаторът PVS-Studio не изостава и става все по-мощен с всяка нова версия. Да видим какви резултати ще покаже този път тестът.

http

Apache HTTP Server е междуплатформен проект с отворен код. Проектът се състои от множество модули. Ядрото на HTTP сървъра е разработено изцяло на език C от екипа на Apache Software Foundation Други компоненти на сървъра са разработени от различни разработчици на трети страни, които поддържат инициативата с отворен код.

Ранните версии на проекта Apache HTTP Server бяха тествани с Coverity. Проверената версия не съдържа никакви следи от проверка от други анализатори. Качеството на кода на проекта е на високо ниво. Въпреки това анализаторът на PVS-Studio успя да открие няколко интересни грешки.

Проектът вече беше тестван през 2011 г. Можете да прочетете за откритите по това време грешки в статията „Лев Толстой и статичен анализ на кода“.

За проверка беше използван анализаторът PVS-Studio версия 6.08.

Неправилна проверка дали низът е празен

V528 Странно е, че указателят към типа 'char' се сравнява със стойността '\0'. Вероятно има предвид: ** ctx->re_source == '\0'. util_expr_eval.c 199

Когато работите с указатели, понякога има объркване между указател и стойностите, към които той сочи. Примерът показва, че при проверка на третия подизраз в условието са забравили да дереферират указателя. Разработчикът предполага, че проверява дали низът е празен или не. За да направите това, трябва да сравните първия символ на низа с терминална нула. Но това, което всъщност се случва, е сравнение на указателя с нулев знак. След коригиране на израза става очевидно, че кодът трябва да бъде допълнен и с проверка за съществуването на указател къмлиния.

Анализаторът вече е открил тази грешка в проекта, както се вижда от записа на страницата с примери за грешки, открити от диагностиката на V528. Тъй като грешката все още присъства в проекта, тя трябва да бъде спомената отново. За да го коригирате, трябва да промените кода, както следва:

Увеличаване на указател вместо стойност

V532 Помислете за проверка на оператора на шаблона '*pointer++'. Вероятно има предвид: „(*указател)++“. iconv_uc.c 114

Кодът използва операцията за деименуване на указателя. Получената стойност не се използва. Съдейки по функционалния код, беше необходимо да се работи със стойността на показалеца. И така, за да поправите грешката, трябва да увеличите приоритета на операцията за деименуване, като използвате скоби: (*res) ++ ;

Неправилно презаписване на паролата

V597 Компилаторът може да изтрие извикването на функцията 'memset', което се използва за изчистване на буфера 'buf'. Функцията RtlSecureZeroMemory() трябва да се използва за изтриване на личните данни. passwd_common.c 165

Задължението на всяка програма, която работи с поверителни данни, да презаписва пароли и друга важна информация в паметта след приключване на работата с нея. В този фрагмент разработчиците се опитват да нулират буфера, използван за съхраняване на паролата. Методът, който избраха, беше да изпълнят тази задача. Въпреки това, за да изпълнява функцията memset правилно своята задача, буферът трябва да се използва в последващ код. Ако паметта се запълни с нули и след това не се използва, компилаторът има право да премахне функцията memset при изграждането на кода. В резултат на това информацията, която трябва да бъде унищожена, остава в паметта. Не е известно какво ще се случи след това с този регион на паметта и къде ще отиде тази неунищожена информация. За да почистите паметта, трябва да използвате специализирани функции, като напрRtlSecureZeroMemory() или memset_s().

Бих искал да отбележа, че това са може би най-сериозните дефекти, открити за приложение като Apache HTTP Server!

Още няколко съобщения, открити от тази диагностика:

  • V597 Компилаторът може да изтрие извикването на функцията 'memset', което се използва за изчистване на буфер 'x'. Функцията RtlSecureZeroMemory() трябва да се използва за изтриване на личните данни. apr_md4.c 362
  • V597 Компилаторът може да изтрие извикването на функцията 'memset', което се използва за изчистване на буфера 'tmpbuf'. Функцията RtlSecureZeroMemory() трябва да се използва за изтриване на личните данни. apr_md5.c 436
  • V597 Компилаторът може да изтрие извикването на функцията 'memset', което се използва за изчистване на 'final' буфер. Функцията RtlSecureZeroMemory() трябва да се използва за изтриване на личните данни. apr_md5.c 662

неинициализирана променлива

V614 Използван потенциално неинициализиран указател 'wch'. начало.c 58

Функцията подготвя информация за преобразуване на низ от W >args съдържа отрицателна стойност, което означава, че броят знаци в низа е неизвестен и те трябва да бъдат преброени.

В момента функцията се използва само веднъж в проекта със стойност на args равна на -1. Следователно тази грешка ще остане незабелязана дълго време, докато не се наложи да се използва функция с положителна стойност на аргумента. Не мога да знам какво е планирано да се направи в тази функция в такава ситуация. Най-малкото е странно да се види стойност като параметър, който в крайна сметка се връща в резултат на работата си. Докато наличието на условен оператор в началото на функцията, при положителна стойност на args, напълно обезсмисля нейното изпълнение.

подозрително изражение

V694 Условието ((s + 1) != ((void *) 0)) еfalse само ако има препълване на показалеца, което така или иначе е недефинирано поведение. mod_mime.c 531

Много подозрително състояние. Първият израз в условието може да бъде фалшив само ако възникне препълване, когато указателят се добави към един. Ако възникне препълване на указател, възниква недефинирано поведение, така че този код така или иначе е неправилен.

Невалидна проверка на HRESULT

V545 Такъв условен израз на оператора „if“ е неправилен за стойността на типа HRESULT „SHGetMalloc(& pMalloc)“. Вместо това трябва да се използва макросът SUCCEEDED или FAILED. apachemonitor.c 915

SHGetMalloc е системна функция и връща резултат от тип HRESULT. HRESULT 32-битова стойност, логически състояща се от три полета. Не може да се използва като bool стойност. За да обработите такива стойности, трябва да използвате макроса SUCCEEDED.

Допълнителна операция?

V560 Част от условния израз винаги е вярна: (rv == 0). config.c 2029

Анализаторът откри допълнителна проверка в условието. На пръв поглед това е просто излишен код. При по-внимателно разглеждане става ясно, че такава стойност за променливата rv не би позволила достигане до входа на цикъла. Освен това е странно защо да се използва стойността на променливата, останала след изпълнението на предишните операции, ако тя не се използва никъде другаде в тялото на цикъла.

Логично е да се предположи, че преди условието е било необходимо да се използва и функцията rv = apr_dir_open(. ); и тогава проверката на резултата от rv има смисъл. Разбира се, може и да греша и това е само допълнителна проверка, но според мен кодовият фрагмент трябва да се провери. Може би разработчиците ще видят, че грешка наистина се е промъкнала в кода на цикъла и операциите, обработени в него, и ще могат да го поправят навреме.

Още няколко подобни грешки:

    V560 Част от условния израз винаги е верен: status == 0. mod_ >Предупреждение:

V571 Периодична проверка. Условието 'ldc->ChaseReferrals == 1' вече беше потвърдено в ред 399. util_ldap.c 400

Този пример показва излишно условие. Не е необходимо да се проверява един и същ израз във вътрешния и външния условен оператор, тъй като условието за въвеждане на вътрешния оператор е да премине проверките на външния. В този случай целият код в операторите изисква всички изрази и в двата оператора if да бъдат проверени. Следователно е по-логично да премахнете външния условен оператор. И за да запазите последователността на проверките, предлагам леко да промените израза на вътрешния условен оператор.

Невалидна директива

V665 Възможно е използването на '#pragma warning(по подразбиране: X)' да е неправилно в този контекст. Вместо това трябва да се използва '#pragma warning(push/pop)'. Проверете редове: 38, 40. apr_getpass.c 40

В горния кодов фрагмент, вместо да върнат предишната стойност на директивата, разработчиците използват стойността по подразбиране за директивата. Това е грешен подход. В такива случаи трябва да действате по различен начин: запомнете предишната стойност с помощта на директивата #pragma warning(push) и я върнете с #pragma warning(pop). Коригиран код:

Заключение

Откритите недостатъци показват, че грешките могат да останат дори в най-качествените и добре тествани проекти. Статичният анализ изисква редовен подход. Еднократна проверка не е достатъчна. Без значение колко опитен е програмистът, той все още може да направи печатни и други грешки. Анализаторът на PVS-Studio ще ви позволи да идентифицирате грешки и подозрителни места, преди те да повлияят на окончателното издание. Предлагам ви да изтеглите иопитайте сами анализатора на PVS-Studio.

проверявам

Намерете грешки във вашия C, C++, C# и Java код

Предлагаме да опитате да проверите кода на вашия проект с помощта на анализатора на код PVS-Studio. Една грешка, намерена в него, ще ви каже повече за предимствата на методологията за анализ на статичен код, отколкото дузина статии.