Этой статьей мы открываем серию публикаций, посвященных обнаружению ошибок и уязвимостей в open-source проектах с помощью статического анализатора кода AppChecker. В рамках этой серии будут рассмотрены наиболее часто встречающиеся дефекты в программном коде, которые могут привести к серьезным уязвимостям. Сегодня мы остановимся на дефекте типа «разыменование нулевого указателя».

Разыменование нулевого указателя (CWE-476) представляет собой дефект, когда программа обращается по некорректному указателю к какому-то участку памяти. Такое обращение ведет к неопределенному поведению программы, что приводит в большинстве случаев к аварийному завершению программы.

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

#include <iostream>
class A {
        public:
            void bar() {
                std::cout << "Test!\n";
            }
};

int main() {
    A* a = 0;
    a->bar();
    return 0;
}

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

#include <iostream>
class A {
        int x;
        public:
            void bar() {
                std::cout << x << "Test!\n";
            }
};

int main() {
    A* a = 0;
    a->bar();
    return 0;
}

Почему же в одном случае программа отработает нормально, а в другом нет? Дело в том, что во втором случае вызываемый метод обращается к одному из полей нулевого объекта, что приведет к считыванию информации из непредсказуемой области адресного пространства. В первом же случае в методе нет обращения к полям объекта, поэтому программа скорее всего завершится корректно.

Рассмотрим следующий фрагмент кода на C++:

if( !pColl )
   pColl->SetNextTxtFmtColl( *pDoc->GetTxtCollFromPool( nNxt ));

Нетрудно заметить, что если pColl == NULL, выполнится тело этого условного оператора. Однако в теле оператора происходит разыменование указателя pColl, что вероятно приведет к краху программы.

Обычно такие дефекты возникают из-за невнимательности разработчика. Чаще всего блоки такого типа применяются в коде для обработки ошибок. Для выявления таких дефектов можно применить различные методы статического анализа, например, сигнатурный анализа или symbolic execution. В первом случае пишется сигнатура, которая ищет в абстрактном синтаксическом дереве (AST) узел типа «условный оператор», в условии которого есть выражение вида! а, a==0 и пр., а в теле оператора есть обращение к этому объекту или разыменование этого указателя. После этого необходимо отфильтровать ложные срабатывания, например, перед разыменованием этой переменной может присвоиться значение:

if(!a) {
  a = new A();
  a->bar();
}

Выражение в условии может быть нетривиальным.

Во втором случае во время работы анализатор «следит», какие значения могут иметь переменные. После обработки условия if (!a) анализатор понимает, что в теле условного оператора переменная a равна нулю. Соответственно, ее разыменование можно считать ошибкой.

Приведенный фрагмент кода взят из популярного свободного пакета офисных приложений Apache OpenOffice версии 4.1.2. Дефект в коде был обнаружен при помощи статического анализатора программного кода AppChecker. Разработчики были уведомлены об этом дефекте, и выпустили патч, в котором этот дефект был исправлен ).

Рассмотрим аналогичный дефект, обнаруженный в Oracle MySQL Server 5.7.10:

bool sp_check_name(LEX_STRING *ident)
{
  if (!ident || !ident->str || !ident->str[0] ||
      ident->str[ident->length-1] == ' ')
  {
    my_error(ER_SP_WRONG_NAME, MYF(0), ident->str);
    return true;
  }
..
}

В этом примере если ident равен 0, то условие будет истинным и выполнится строка:

my_error(ER_SP_WRONG_NAME, MYF(0), ident->str);

что приведет к разыменованию нулевого указателя. По всей видимости разработчик в процессе написания этого фрагмента кода, в котором ловятся ошибки, просто не учел, что такая ситуация может возникнуть. Правильным решением было бы сделать отдельный обработчик ошибок в случае, когда ident=0.

Нетрудно догадаться, что разыменование нулевого указателя – это дефект, не зависящий от языка программирования. Предыдущие два примера демонстрировали код на языке C++, однако с помощью статического анализатора AppChecker можно находить подобные проблемы в проектах на языках Java и PHP. Приведем соответствующие примеры.

Рассмотрим фрагмент кода системы управления и централизации информации о строительстве BIM Server версии bimserver 1.4.0-FINAL-2015-11-04, написанной на языке Java:

if (requestUri.equals("") || requestUri.equals("/") || requestUri == null) {
     requestUri = "/index.html";
}

В данном примере сначала идет обращение к переменной requestUri и только после этого происходит проверка на нулевой указатель. Для того чтобы избежать этого дефекта, достаточно было просто поменять очередность выполнения этих действий.

Теперь рассмотрим фрагмент кода популярной коллекции веб-приложений phabricator, написанной на php:

if (!$device) {
    throw new Exception(
      pht(
        'Invalid device name ("%s"). There is no device with this name.',
        $device->getName()));
}

В данном случае условие выполняется только если $device = NULL, однако затем происходит обращение к $device->getName(), что приведет к fatal error.

Подобные дефекты могут оставаться незамеченными очень долго, но в какой-то момент условие выполнится, что приведет к краху программы. Несмотря на простоту и кажущуюся банальность такого рода дефектов, они встречаются достаточно часто, как в open-source, так и в коммерческих проектах.

Update:

Ссылка на бесплатную версию AppChecker: https://file.cnpo.ru/index.php/s/o1cLkNrUX4plHMV
Поделиться с друзьями
-->

Комментарии (91)


  1. AllexIn
    10.01.2017 12:23
    +11

    Разжевывание совсем уж для детей.


    1. npoechelon
      10.01.2017 13:16

      Рассмотренные проекты явно не детьми разрабатываются, а ошибки такие допускаются…


      1. andreymal
        10.01.2017 13:27
        +10

        Но не потому что не знают, что это такое, а по невнимательности.

        В том числе поэтому языки, не имеющие NULL, рулят)


        1. npoechelon
          10.01.2017 13:43
          -6

          так поэтому мы разрабатываем наше решение AppChecker. На нашем сайте можно заполнить форму и получить лайт версию.


        1. aragaer
          10.01.2017 14:29
          +1

          Никогда не понимал до конца вот это вот «в языке есть NULL, поэтому это плохо» — NULL это просто ноль. Или какое-то другое значение. Почему если есть возможность присводить какое-то значение в какую-то переменную, то сразу язык от этого портится?


          1. andreymal
            10.01.2017 14:34
            +3

            NULL — это не просто ноль, а нулевой указатель. И плохо это как раз тем, что к нему можно случайно обратиться и программа случайно упадёт (или не упадёт, что ещё хуже, потому что ошибку никто не заметит, а потом она вылезет лет через двадцать на ближайшей к вам атомной электростанции :). И эти обращения в мейнстримовых языках не проверяются во время компиляции, поэтому над ними приходится городить костыли в виде этих самых статических анализаторов


            1. aragaer
              10.01.2017 14:47

              С тем же успехом можно разыменовать любой другой указатель (например мой любимый — 5). Указатель это число, некоторый адрес в памяти. Он может принимать очень много значений и очень немногие из них будут соответствовать расположенным в этой памяти объектам нужного типа. NULL это только одно из некорректных значений. Просто оно очень удобно для обозначения «на самом деле тут ничего нет». Все равно, это лучше, чем если бы был указатель на объект, который уже удален, но зато не NULL.


              1. andreymal
                10.01.2017 14:50

                разыменовать любой другой указатель

                Ответ почти аналогичный: в том числе поэтому языки с указателями это плохо)


                1. sand14
                  10.01.2017 15:04

                  Интересно, что в статически типизированных языках без использования unsafe-кода нельзя в ссылочную переменную записать недействительное значение указателя, кроме нулевого.
                  Чтобы обойти статические проверки компилятора, даже придумали для нуля ключевое слово null.
                  Интересно, можно ли было обойтись без null.


                  1. andreymal
                    10.01.2017 15:17
                    +6

                    обойтись без null

                    let obj: Option<MyClass> = get_some_object_if_exists();
                    match obj {
                      Some(x) => { println!("Имя объекта: {}", x.getName()) },
                      None => { println!("Объекта нету") }
                    }

                    Без явной проверки на наличие объекта обратиться к нему в Rust никак нельзя, obj.getName() не скомпилируется, только или match, или if let, или obj.unwrap().getName(), если есть уверенность, что объект действительно есть (а если его таки не окажется, то на unwrap программа упадёт с внятным сообщением об ошибке, а не с неопределённым поведением как в C/C++)


                    А просто let obj: MyClass = чтототам будет содержать рабочий объект гарантированно и в таком случае будет можно obj.getName()


                    1. Barafu
                      11.01.2017 03:30
                      -3

                      Смотрите шире: просто не надо писать на С++ то, что можно писать не на С++. Я Rust не знаю, но приведённый пример вызывает уныние: это что, при каждом обращении к свойству объекта необходимо вручную писать код, что делать, если объекта нет? Извините, но это просто слишком. Если писать код не для спутника или ядерной ракеты, то это просто безумный расход труда на перестраховки.


                      1. staticlab
                        11.01.2017 09:21

                        А если бы это был C++?


                        MyClass* obj = get_some_object_if_exists();
                        if (obj != nullptr) {
                            std::cout << "Имя объекта: " << obj->getName() << '\n';
                        } else {
                            std::cout << "Объекта нету\n";
                        }
                        free_some_object(obj);

                        это что, при каждом обращении к свойству объекта необходимо вручную писать код, что делать, если объекта нет? Извините, но это просто слишком. Если писать код не для спутника или ядерной ракеты, то это просто безумный расход труда на перестраховки.


                      1. andreymal
                        11.01.2017 10:11
                        +3

                        Зачем при каждом-то


                        // fn get_some_object_if_exists() -> Option<MyClass> { ... }
                        
                        let obj: MyClass = match get_some_object_if_exists() {
                          Some(x) => { x },
                          None => { println!("Объекта нету"); return; }
                        };
                        // Здесь объект уже абсолютно точно есть
                        println!("{}", obj.getName());
                        println!("{}", obj.getDate());
                        println!("{}", obj.getStatus());
                        println!("{}", obj.getЧтоТоТам());
                        // ...

                        Или, как вариант


                        if let Some(obj) = get_some_object_if_exists() {
                          println!("{}", obj.getName());
                          println!("{}", obj.getЧтоТоТам());
                          // ...
                        } else {
                          println!("Объекта нету");
                        }

                        Капитан Очевидность замечает, что если объекта нет, то и обращаться к нему нельзя. Причём вы ДОЛЖНЫ проверить, есть объект или нет, вообще на ЛЮБОМ языке программирования, ОСОБЕННО на C/C++ :)


            1. Sixshaman
              10.01.2017 15:53

              Так ведь наоборот всё, гораздо лучше, если приложение падает там где должно, разве нет?

              Простейший пример: функция загрузки какого-либо объекта из файла, если файла нет или он повреждён — возвращается NULL. Естественная абстракция.

              Без NULL необходимо вводить специальный экземпляр «невалидного объекта», на который после вызова функции следует проверять возвращённое значение. Проверка такая же, только теперь уже анализатор под это не напишешь. Более того, приложение начнёт работать как ни в чём не бывало, если эту проверку пропустить. В итоге этот-то баг как раз и просочится на электростанцию и вылезет через 20 лет, а не будет обнаружен на тестировании.

              Разве не так всё?


              1. andreymal
                10.01.2017 16:03
                +1

                Так с NULL оно может или падать, или не падать — как повезёт. На тестировании не упадёт, а на атомной станции через 20 лет сложится такая сильно специфическая ситуация, что оно возьмёт да упадёт, кто знает. Или наоборот: на тестировании будет падать и на это будет расчёт (хотя зачем так вообще делать?), а на станции возьмёт и не упадёт.


                Языки, в которых эту самую проверку пропустить нельзя, рулят) Специальный экземпляр «невалидного объекта» должен быть таким, чтобы его нельзя было не проверить. Тот пример на Rust, что я кинул выше, подходит и для этого случая с файлом. Анализ там проходит прямо в процессе компиляции и никаких проблем нет и быть не может)


                Кстати, за это я не люблю активно пиарившийся тут Go: там проверку пропустить можно (if err != nil, вот это вот всё), и это считается нормой. В Rust пропустить проверку нельзя — не скомпилируется.


            1. ainoneko
              11.01.2017 22:25

              NULL — это не просто ноль, а нулевой указатель.
              А разве не nullptr, как теперь рекомендуется?


              1. andreymal
                11.01.2017 22:28

                Может и nullptr, в контексте данной ветки всё равно не суть)
                Хотя гугл намекает, что в C такого нет


          1. sand14
            10.01.2017 15:01

            Потому что NULL это ноль не в абстрактной целочисленной переменной, а в переменной, которая трактуется как указатель на объект (или на на ту же целочисленную переменную).

            Но принята «конвенция», что в памяти по адресу ноль не может быть объекта/переменной.

            Именно поэтому разыменование нулевого указателя (или обращение с полям/методам переменной ссылочного типа) приводит в лучшем случае Null Reference/Pointer Exception, в худшем — к Undefined Behaviour.


            1. aragaer
              10.01.2017 15:22

              Интересно, никогда раньше не слышал о такой «конвенции». Гугл ничего явного не подсказал, или я что-то не то искал. Зато я узнал, что в Линуксе нулевая страница адресного пространства просто всегда явно маппится без прав доступа, поэтому происходит segfault.

              В «особых случаях» нулевой адрес является вполне себе валидной штукой — примеры опять же можно найти в гугле.

              Под рукой нет Кернигана-Ритчи, но насколько я помню, разыменование указателя, который не содержит адрес объекта того типа, на который ссылается этот указатель, это Undefined Behavior. Ни о каком особом статусе нулевого адреса я не помню. Может просто невнимательно читал.

              Все эти фокусы с указателями, разыменовываниями и прочим могут выглядеть как игра с огнем или чем-то более опасным, но если просто оперировать памятью, структурами и массивами, то оно все выглядит вполне естественным. Выделил память, передал в функцию адрес этой памяти, записал по нужному смещению нужное значение. Никакой магии, все на виду.


              1. aragaer
                10.01.2017 16:23

                А вот нашел C99. Там все написано:

                An integer constant expression with the value 0, or such an expression cast to type
                void *, is called a null pointer constant. If a null pointer constant is converted to a pointer type, the resulting pointer, called a null pointer, is guaranteed to compare unequal to a pointer to any object or function.


                Теперь я буду знать, что у нулевого адреса и нулевого указателя действительно особый статус, определенный стандартом.


                1. mayorovp
                  10.01.2017 16:36
                  +1

                  Не совсем так. В стандарте нет требования, чтобы нулевой указатель указывал на нулевой адрес. На нестандартных архитектурах или компиляторах он может указывать, к примеру, на 0xffffffff.


                  1. aragaer
                    10.01.2017 18:26
                    -3

                    В стандарте есть требование, что константа 0 это нулевой указатель. Про адрес тут речи нет. Кроме того, есть требование, что любые два нулевых указателя равны. Значит никаких других нулевых указателей быть не должно. 0xffffffff это не нулевой указатель. Просто особый невалидный.


                    1. mayorovp
                      10.01.2017 21:39
                      +1

                      В стандарте нет требования, чтобы нулевые указатели в понимании разных компиляторов совпадали. Если на некоторой платформе все нулевые указатели равны 0xffffffff — это не будет нарушением стандарта.


                      1. aragaer
                        10.01.2017 22:56

                        Если при этом выполняется условие «константа 0 является нулевым указателем» — вопросов нет. Но выглядит это странно.


                        1. Mingun
                          13.01.2017 21:51

                          Так в станарте не сказано, что битовое представление константы 0 и нулевого указателя должно совпадать. Это в коде вы пишите


                          int xxx  = 0
                          void* ptr = (void*)xxx;

                          а машина выполняет


                          if xxx == 0 {
                            ptr = 0xFFFFFFFF;
                          }

                          Никаких противоречий, хоть и непривычно.


                          1. mayorovp
                            13.01.2017 21:56

                            Нет, не так. (void*)xxx будет указателем на нулевой адрес. К нулевому указателю приводится только константа 0.


                            1. Mingun
                              13.01.2017 22:16

                              Ну в общем да. Изначально я писал пример с приводом (void*)0, но потом мне показалось нелогичным, что в "точке зрения компилятора" используется какой-то необъявленный xxx и я решил его явно указать. Исправлять потом было поздно :). Но логика, я думаю, понятна


                1. sand14
                  10.01.2017 17:15

                  Верно, особый статус, т.к. понятие указателя несет определенную семантику, в отличие от «просто» числа
                  И выше вам верно отметили, что NULL это необязательно 0, может быть и "-1" (все биты единичные)
                  Даже в WinAPI, функции, возвращающие хендлы (те же указатели), в качестве невалидного указателя, через одну возвращают то 0, то -1.


          1. Nakosika
            11.01.2017 13:07

            Сложность возрастает. Нужно писать больше if для одного и того же, потому что внезапно значение вместо юзернейм пришёл нул. Особенную пикантность добавляет то, что в типизированных языках обычно нет разделение на тип который может принимать нул и тип который не может. Приходится пилить костыли, как это сделано в восьмой яве — Optional. Но эти костыли на то и костыли что спасают только от части проблем.


        1. ArRnorets
          11.01.2017 23:26

          -Рассмотренные проекты явно не детьми разрабатываются, а ошибки такие допускаются…
          -Но не потому что не знают, что это такое, а по невнимательности.

          Причина не имеет значения, ключевое слово «допускаются». А главное, попробуйте потом объяснить, что «я просто просмотрел» персоналу ближайшей атомной электростанции :)
          С утверждением о языках, не имеющих NULL,  категорически согласен :)


    1. Andrey2008
      10.01.2017 14:42
      +6

      Кстати, предлагаю свою статью, для тек хочет пофилософствовать о более интересном и экзотическом случае. Ведь иногда бывает, что разыменование вроде как есть и вроде как его нет :)


  1. mayorovp
    10.01.2017 13:49
    +5

    Поддерживаемые спецификации языков программирования

    ANSI С/С90/С99/С11;
    C++98/C++2003/С++11/С++14;
    Java 6/7 Language Specification;
    PHP 4/5;
    С# 5.0.

    В общем, кроме Си/C++ никакие языки толком не поддерживаются.


    1. kishchenko
      10.01.2017 14:05
      +6

      С# 5.0

      Абсолютно не актуально, ибо на дворе уже 2017 с C# 7.0


      1. mayorovp
        10.01.2017 14:14
        +3

        Также как Java 8 и PHP 7 (если уже новых версий не выпустили, я там не слежу особо). Собственно, потому я и выделил эти версии.


        1. npoechelon
          10.01.2017 14:26
          +3

          Java 8 — уже поддерживаем, новые PHP и C# — в процессе доработки.


          1. mayorovp
            10.01.2017 14:29
            +2

            Я цитировал страницу продукта по вашей же ссылке.


      1. quzor
        10.01.2017 14:37
        -8

        Можно подумать PVS-Studio с первых дней поддерживал C# и все новомодные фишки языков. Пройдет чуть больше времени, разработчики подтянут свой проект, вот тогда и посмотрим.


        1. mayorovp
          10.01.2017 14:44
          +4

          Но они и такой поддержки не заявляли. А тут поддержка как бы есть — но на самом деле ее нет.


          Кстати, а при чем тут вообще PVS-Studio?


          1. quzor
            10.01.2017 14:51
            -2

            Потому, что наш уважаемый kishchenko — сотрудник Сипровер.
            Который, конечно же не оставил без внимания этот нюанс.


            1. n0mo
              10.01.2017 15:01
              +4

              «Долой PVS-Studio! Да здравствует конкуренция на Хабре.»


            1. n0mo
              10.01.2017 15:07
              +3

              Процитировав Ваш комментарий, я хотел сказать, что Вы же вроде за конкуренцию на Хабре…


      1. abyrkov
        11.01.2017 13:00
        -1

        unsafe убрали?


  1. Andrey2008
    10.01.2017 14:24
    +5

    Кстати, интересный пример двойственности мира. Описывая подобные ошибки в наших статьях, мы обычно используем для кода вида:

    if( !pColl )
       pColl->SetNextTxtFmtColl( *pDoc->GetTxtCollFromPool( nNxt ));
    


    Термин опечатка. Ведь в показанных примерах явно «дрогнула рука». Впрочем, это не значит, что это не серьезная ошибка. И обнаруживаем мы таких ошибок действительно много (примеры найденных ошибок в открытых проектах: V522, V595, V713). А двойственность в том, что эту ошибку можно назвать и уязвимостью. Думается мне, рано или поздно мы выпустим PVS-Studio для поиска уязвимостей, вообще ничего в нём не меняя, кроме названия диагностик и документации. :)


  1. quzor
    10.01.2017 14:26
    +2

    Долой PVS-Studio! Да здравствует конкуренция на Хабре.


    1. mayorovp
      10.01.2017 14:27
      +6

      Чему вы так радуетесь? Вы их прайс-то видели?


      1. quzor
        10.01.2017 14:46
        -4

        А вы прайс PVS-Studio видели? Нет? Вот и я о том же…


      1. npoechelon
        10.01.2017 15:16
        +1

        Прайс? Сейчас можно скачать лайт-версию (без ограничений по времени использования) бесплатно: https://file.cnpo.ru/index.php/s/o1cLkNrUX4plHMV


        1. staticlab
          10.01.2017 15:17
          +1

          И чем лайт отличается от фулл?


          1. npoechelon
            11.01.2017 13:21
            +1

            Лайт-версия — это полнофункциональный AppChecker с зафиксированной базой правил поиска дефектов на август 2016 года


        1. npoechelon
          10.01.2017 15:22
          -1

          Кстати, если кто-нибудь из уважаемых критиков испытает продукт в деле и сделает обзор, подарим наш фирменный настенный календарь)


          1. pavel_pimenov
            11.01.2017 08:48
            +2

            Для windows опишите подробнее по шагам как проверить .sln
            который корректно собирается в Visual C++


            1. facet
              11.01.2017 20:36
              +1

              У нас есть инструкция по конфигурированию проектов C/C++. Её можно скачать прямо из AppChecker — при создании проекта перейдите по ссылке «Перейти на страницу загрузки утилит и инструкции по конфигурированию» -> «Настройка парсера C/C++», там же можно скачать и утилиту для конфигурирования.




              Шаги такие:

              • запустить утилиту CppConfMonitor.exe
              • пересобрать проект в Visual Studio
              • остановить утилиту, введя «s» в ее консоли
              • утилита сформирует архив, который нужно загрузить в AppChecker


        1. mayorovp
          10.01.2017 15:40
          +1

          Объясняю при чем тут прайс. После выхода версии PVS-Studio под Linux а также появления бесплатной лиценции со своеобразными, но все же выполнимыми, условиями, у нас осталась всего одна причина ненавидеть PVS-Studio — их секретный прайс.


          И когда человек пришел сюда и написал в комментариях: "Долой PVS-Studio! Да здравствует конкуренция на Хабре." — мне и стало интересно, видел ли он ваш прайс. Я вот, к слову, его не нашел.


          1. quzor
            10.01.2017 15:44
            -3

            То то мы и наблюдаем как их бесплатной версией активно пользуются на гитхабе
            Если серьезно, то это больше плевок в лицо, нежели нормальная бесплатная версия.


            1. kishchenko
              10.01.2017 17:35
              +7

              Если правильно пользоваться поиском на гитхабе, то процент активного использования PVS-Studio Free резко возрастает.


              1. kishchenko
                10.01.2017 17:49
                +2

                Некоторые настройки поиска при переходе по ссылке не сохраняются, поэтому руками нужно переключить фильтр с «Best Match» на «Recently indexed», либо выбрать язык (C#/C++) из списка.

                Результат: 2 351 упоминание в коде.


                1. mayorovp
                  10.01.2017 21:42

                  Простите, но где это выбирать и переключать?


                1. mayorovp
                  11.01.2017 09:38
                  +2

                  UPD: там не "настройки не сохраняются", а нельзя выполнить поиск по всем открытым репозиториям по прямой ссылке. Надо обязательно либо вводить запрос вручную, либо после перехода по ссылке поменять любую настройку. И еще надо быть залогиненым — анонимам искать по всем репозиториям нельзя.


              1. mayorovp
                11.01.2017 09:34

                (удалено)


        1. 4144
          11.01.2017 13:03
          +1

          У вас есть лайт версия под linux?


          1. npoechelon
            11.01.2017 13:05
            -2

            Лайт-версия AppChecker только под Windows, коммерческую можем собрать и под Linux, если такая потребность будет у наших заказчиков


          1. facet
            11.01.2017 20:38

            Есть и под Linux (Ubuntu 14.04), так же есть и в виде ВМ (внутри которой тоже убунта).


            1. phprus
              11.01.2017 20:55

              Именно лайт версия? А ее можно как-то скачать?


  1. staticlab
    10.01.2017 15:06
    +7

    А почему AppChecker предлагает загружать архив с исходниками? А как же интеграция с системой контроля версий, CI? На дворе 2017 год!


    Ещё и написали о "патенте", коим оказалось всего лишь свидетельство о регистрации ПО.


    1. npoechelon
      10.01.2017 15:59
      +1

      Интеграция с СУВ (git, subversion, mercurial) уже есть. Поддержка CI (Jenkins, TeamCity) также есть, но в альфа-версии, релиз планируем в феврале. Так же имеется консольный клиент, с помощью него можно в автоматическом режиме производить анализ и встраивать его в свою систему сборки.

      Ряд алгоритмов, используемых в AppChecker, покрывается следующим патентом:
      https://npo-echelon.ru/upload/iblock/7d0/7d01385cccfddcb4eb4c925db3fc3b7a.png


      1. VolCh
        11.01.2017 13:02

        Для оборонки вроде были характерны нейтральные названия вроде средмаша или вообще маскировка типа тракторного завода или вагоностроительного.


  1. mtp
    10.01.2017 21:44
    +1

    ЗАО «НПО „Эшелон“» Ничего себе! Название — прямо привет из оборонки 70-х. Ну, разве что «ЗАО» несколько выбивается из ассоциативного ряда.

    Рад за отечественную разработку, честно.


    1. monah_tuk
      11.01.2017 11:25

      В современных реалиях ЗАО только дополняет старый термин ;-)


      ЗЫ только, вроде, с 2014 года классификация поменялась ЗАО -> АО, ОАО — ПАО.


  1. asasasdd
    11.01.2017 12:55
    +7

    Результат разработки AppChecker довольно предсказуем.
    Компания Эшелон специализируется на проведении работ в системах сертификации ФСТЭК, МО, ФСБ. Для проведения этих работ много лет назад разработано средство анализа АК-ВС, осуществляющее ряд исследований по руководящему документу ФСТЭК по контролю наличия недекларированных возможностей. Те, кто с этим средством имел несчастье соприкасаться, знаком с его качеством реализации и функционирования.
    В прошлом году был утверждён ГОСТ, разработанный компанией Эшелон, определяющий процесс безопасной разработки ПО. ГОСТ будет обязателен для применения компаниями, которые работают на рынке сертифицируемых решений. Это светлое будущее различных систем сертификации, в котором декларируется необходимость поиска программных дефектов (уязвимостей). Вот для этого и разрабатывается AppChecker. Ни разработчикам, ни потребителям, ни исследователям, ни экспертам не интересно качество реализации и функционирования средства анализа, всем нужна заветная бумажка — сертификат. Есть бумажка — ПО надёжно и безопасно.


    1. npoechelon
      11.01.2017 12:57
      -4

      Александр, хорошая попытка вскрыть международный заговор строителей систем защиты информации!))

      По поводу качества первой версии нашего продукта АК-ВС: качество – характеристика относительная и, если решили о ней поговорить, то давайте сравнивать с другими аналогичными решениями, а потом уже выплескивать эмоции. Продукт, как вам известно, не единственный на рынке, также никто не мешает вам разработать собственный инструмент анализа.

      Правда, проводить тестирование лучше уже АК-ВС 2, который уже пару лет как представлен на рынке. Если у вас появятся конкретные предложения по улучшению нашего продукта, будем очень признательны.

      Спасибо, что вспомнили ГОСТ Р 56939-2016. В его разработке приняло участие 105 компаний и было учтено более 300 замечаний (в том числе и от вашей). Нам о планах сделать его обязательным ничего неизвестно, но мы бы порадовались, так как вложили в ГОСТ массу интеллектуальных усилий: обратите внимание, что он не является переводом западного, а разработан специально под наши реалии. Если его читать внимательно, то можно заметить, что помимо статического анализа кода, реализованного в AppChecker, предусматривается и динамический анализ, и fuzzy-тестирование, и тестирование на проникновение и многие другие меры.

      Александр, предлагаем вам, как эксперту скачать AppChecker и попробовать его в деле, и поделиться впечатлением на страницах Хабра, а мы вам подарим наш легендарный перекидной календарь!


      1. pavel_pimenov
        11.01.2017 14:09
        +6

        Подпишите инсталлятор и исполняемые модули AppChecker сертификатом
        не солидно выглядит такое распространение софта от фирмы с профилем по ИБ.

        Не эксперт, но заинтригован… что в вашем календаре легендарного — он очень старый… за какой год?


      1. asasasdd
        11.01.2017 22:55
        +2

        npoechelon, нет никакого заговора, но есть безразличие и безответственность.
        Вас оправдывает то, что аналогичные решения написаны такими же безразличными к результату разработчиками? Да и какие это решения, Аист-С? Все, кто работает в отрасли сертификации, прекрасно знает как работает АК-ВС и что большая удача найти хоть какие-то исходники, на которых он отработает без единого сбоя.

        АК-ВС за последние годы стал стандартом де-факто в отрасли сертификации, новые лаборатории покупают у вас продукт, отправляют в ваш учебный центр своих сотрудников, которые после прохождения обучения начинают считают, что раз средство разрешено к применению ФСТЭКом, то это автоматически решает все проблемы. Старый добрый принцип — есть бумажка, значит всё защищено и надёжно. В итоге у среднего специалиста в отрасли сертификации долгие годы было убеждение, что надо просто запустить АК-ВС, скормить ему исходники, сгенерировать протоколы и отправить их для проведения экспертизы, где в их содержимое редко кто будет вчитываться на предмет корректности проведённых работы. Вот и вся работа, вся сертификация на НДВ.

        АК-ВС 2 пару лет представлен на рынке, но при этом работает также некорректно (но чуть лучше, чем АК-ВС). При этом, по старой доброй традиции, это никому не мешает: ни вам — его продавать, ни регуляторам — его разрешать к применению, ни лабораториям — его непосредственно применять.

        Первая редакция ГОСТ представляла собой РД НДВ в новой обёртке. Утверждённая редакция является просто учётом тех замечаний, о которых вы упомянули. Если я правильно помню (это легко уточнить), то те виды исследований, которые вы перечислили, появились именно по результатам устранения замечаний.

        npoechelon, я неоднократно пробовал в деле и АК-ВС, и АК-ВС 2. Вы думаете, что у меня ещё осталась надежда, что у вашей компании есть мотивация делать качественные продукты? Единственная ваша цель — не допустить распространения в отрасли сертификации PVS-Studio и средств анализа путём формального выполнения требования использования статического анализа. В качестве вы не заинтересованы.


        1. facet
          12.01.2017 18:05

          Уважаемый, asasasdd!

          По вашему тексту можно изучать методы геббельсовской пропаганды. Здесь есть и явная ложь («появились именно по результатам устранения замечаний»), и попытки манипулировать («Все, кто работает… прекрасно знает», «Старый добрый принцип…»), и отсутствие конкретики («работает также некорректно, но чуть лучше»), и попытки сталкивать с третьей стороной («ваша цель — не допустить распространения в отрасли сертификации…»), ну и, конечно, перевод на параллельные темы и масса эмоций (в основном обиды). Короче, характерно выделяющийся профиль.

          Так как дискуссия перестает быть конструктивной, этот ответ вам будет заключительным.

          Теперь по пунктам.

          Относительно ГОСТ Р 56939-2016, который вы связали с обсуждаемой темой.

          По поводу исследований, которые с ваших слов появились после замечаний, вот первый вариант стандарта, в котором они все есть, так как были изначально: http://docs.cntd.ru/document/1200113247
          Cмотреть нужно 13-16 стр. asasasdd, ЛГАТЬ – это очень плохо.

          Ликбез. Разумеется, ГОСТ изначально не был копией РД НДВ и не задумывался таковым. Извините, ну уж даже не знать, что упоминаемый статический анализ в РД и в ГОСТ – это абсолютно разные понятия?! Да уж.
          Идея РД НДВ в декомпозиционном подходе к разбору структуры текста программы (там вопросы безопасности косвенно возникают лишь тогда, когда речь идет о защите гостайны уровня аж СС!). Это поймет любой начинающий программист.
          ГОСТ Р 56939-2016 – это (для справки) организационный стандарт, который в том числе включает в себя совокупность мер, которые может обоснованно принять разработчик ПО с целью повышения безопасности программного изделия в рамках именно его жизненного цикла! Здесь, пардон, преследуются другие цели: разработчики стремятся минимизировать риски появления ошибок безопасности, уязвимостей, угроз (связанных их наличием) и т.п. Иные и задачи, и предметная область, и методическая база.

          Относительно АК-ВС, который вы подтянули к обсуждаемой теме:

          Есть старый анекдот:
          — Таки мне Каррузо не понравился: каатавит, гнусавит, в ноты не попадает…
          — Вы были на концерте Карузо?
          — Нет, мне дгуг по телефону напел.

          asasasdd, вы легально используете АК-ВС 2.0 на своем месте работы, и у вас есть конкретные замечания по целевой работе этого продукта? Вы о них сообщили в техподдержку? Разработчики не отреагировали? Номера ваших обращений в техподдержку? Все, как в анекдоте.

          Мы признательны всем за конструктивные конкретные замечания и предложения по улучшению продукта, чему и посвящен данный пост.


  1. vovochkin
    11.01.2017 16:38
    +6

    Спасибо за еще один продукт синтаксического анализа! Я опробовал его, кратко поделюсь впечатлениями.
    User Interface: красиво:) Запускаешь серверную часть, идешь в браузер — создаешь проект. И тут — неудобство:

    Для С/C++ проектов требуется дополнительная конфигурация.
    Я понимаю, почему это сделано, но запускать утилиту, которая мониторит системные вызовы — не всегда удобно.
    На большом проекте, использующем Qt и сторонние библиотеки, утилита сдохла. bad_alloc и падение без каких-либо полезных результатов. Уже минус, т.к. проверять планировал именно его.
    Открыт древний и большой проект, не использующий ничего и написанный на Си с классами:)
    Продукт нашел всего 4 дефекта трех типов, которые, признаюсь, действительно выглядят странно:
    • присвоение переменной самой себе
      tmp = tmp;
    • всегда истинное выражение
      if ( arr[i]!='+' || arr[i]!=' ' )
    • неконтролируемая рекурсия
      ISuperStringStream& MySuperStringStream::operator<<(signed char v)
      {
      	return operator<<((char)v) ;
      }


    При этом параллельно работал CppCheck, который одних только ошибок нашел 37 штук, варнинги и прочие стилистические сообщения я отключил. При этом в коде есть вещи, которые явно указывают на ошибку:
    void nps::MyClassEvent::getTextData(MyElement& el) const 
    {
    	el.doSmth();
    	MyAttributes* attr = new MyAttributes() ;
    	attr->insert("t0") ;
    }

    У меня есть подозрение, что AppChecker анализирует не все файлы и файл с проектом сформировался криво, но как это быстро проверить и исправить, я не знаю.
    Возможно, я что-то делаю не так, однако для быстрой проверки CppCheck и прочие Clang Static Analyzer'ы лично мне подходят удобнее.
    Я допускаю, что ваш продукт можно правильно настроить и он будет работать превосходно, но, например, ручное формирование и загрузка специально сохраненного файла — как-то это долго и лениво.


    1. Andrey2008
      11.01.2017 16:54
      +7

      Ради интереса, прошу ещё попробовать PVS-Studio. Выдам ключ на неделю (напишите мне на karpov [@] viva64.com), если демонстрационной версии покажется мало. И прошу написать о результатах.


      1. vovochkin
        11.01.2017 21:49
        +13

        Попробовал и понял, что сравнение в пользу PVS-Studio.
        Проверял я на standalone-приложении. Отпишу кратко впечатления от первого запуска. При первом запуске увидел отличную

        картинку:


        1. Andrey2008
          11.01.2017 22:10
          +1

          Большое спасибо за эксперимент и подробный комментарий. Попробуем понять, что там не так с Settings.xml. По этому вопросу, возможно, кто-то из моих коллег напишет Вам личное письмо и уточнит детали.

          Касательно ложного срабатывания. В PVS-Studio есть множество механизмов для их подавления. В данном случае, лучше всего написать комментарий:

          //-V:LOG_ERROR:V672

          Комментарий рационально написать рядом с объявлением макроса. Тогда комментарий будет воздействовать на весь код, где используется этот макрос. Есть и другие способы. Подробности можно посмотреть в документации.

          Ещё раз спасибо.

          P.S. Да, если кому-то интересны графики и т.п., напоминаю, что PVS-Studio умеет интегрироваться с SonarQube.


          1. staticlab
            11.01.2017 22:55
            +1

            Андрей, а у вас нет какого-нибудь не очень большого забагованного проекта для экспериментов? Хочу на выходных попробовать сравнить PVS-Studio, AppChecker, Clang, CppCheck и может быть что-нибудь ещё доступное.


            1. Andrey2008
              11.01.2017 23:28
              +2

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

              На вскидку, предлагаю взять проект WinMerge. Но не потому, что он очень забагован :). Просто проект небольшой и должен относительно легко проверяться (правда не знаю, что делать с Clang). Мы про него уже пару раз писали (в 2010 и 2012 году). Но только надо брать старую версию проекта (скажем конца 2009 года), когда до него ещё не добрался ни PVS-Studio, ни Cppcheck.

              Что ещё… Вот есть такой проект Torque2D. Я его пару месяцев назад быстренько проверил, увидел, что есть ошибки, но не добрался до полноценной проверки и написания статьи. Можно на нём попробовать.


            1. asasasdd
              12.01.2017 00:01
              +1

              1. Andrey2008
                12.01.2017 00:17
                +2

                Интересно, а там есть где-нибудь ошибки вида 1, 2, 3? Почему-то в синтетических тестах и коллекциях их обычно нет. Видимо считаются, что такие ошибки не делают. А они есть. :)


        1. staticlab
          11.01.2017 22:50

          Странно, что у вас cppcheck ничего не нашёл.


          $ cppcheck bugs.cpp
          [bugs.cpp:21]: (error) Uninitialized variable: a

          Clang так же остался недоволен:


          $ g++ -Wall -Wextra -pedantic -Werror -O3 -c bugs.cpp
          bugs.cpp:21:5: error: variable 'a' is uninitialized when used here [-Werror,-Wuninitialized]
              a->bar();
              ^
          bugs.cpp:20:9: note: initialize the variable 'a' to silence this warning
              A* a;
                  ^
                   = NULL
          1 error generated.


          1. vovochkin
            12.01.2017 07:46
            +3

            Вы правы, CppCheck всё нашел. Было уже поздно и я проверял файл, в котором этих ошибок еще не было:( В этом плюс AppChecker'а и PVS-Studio: там что ты компилируешь, то и проверяешь.
            Тем не менее, AppChecker ничего подозрительного не нашел в этом коде.


        1. facet
          12.01.2017 14:28

          Да, Вы правы, использовать standalone-приложение конечно приятнее. Но для этого необходимо, чтобы оно было на той же машине, на которой производится сборка проекта, а это не всегда удобно и даже не всегда реально — из-за различий операционных систем и даже архитектур процессора. Особенно, если аудит кода проводит не разработчик, а специалист по QA, ИБ и т.п.


          1. mayorovp
            12.01.2017 14:32
            +2

            Вот только PVS-Studio умеет запускаться по-разному, а AppChecker — только через одно место.


            1. EvgeniyRyzhkov
              12.01.2017 14:33
              +5

              PVS-Studio тоже через одно место умеет!

              (не сдержался)


          1. vovochkin
            12.01.2017 14:59
            +4

            А как аудит кода проводят вышеозначенные специалисты?
            Насколько я понимаю, им тоже придется запустить сборку проекта, чтобы гарантировать соответствие исходников и бинарных файлов. Или там другие продукты используются?


            1. staticlab
              12.01.2017 15:28

              По идее же, если отчёт был получен с сервера CI, то будет известен коммит, для которого выполнялся анализ. Этот коммит аналитик у себя и зачекаутит.


              1. vovochkin
                12.01.2017 15:35

                Поддержка CI (Jenkins, TeamCity) также есть, но в альфа-версии

                Я не заметил этот комментарий от разработчиков, а сейчас AppChecker на такое не очень способен (по крайней мере, доступная для скачивания версия).


    1. facet
      12.01.2017 14:27

      Спасибо за предоставленный отзыв! Как с вами можно напрямую связаться, чтобы получить более подробную информацию для разрешения возникших сложностей?

      Относительно результатов тестов, то похоже действительно проанализировались не все исходники, поскольку те дефекты, которые нашел pvs и о которых Вы пишете в своем комментарии, AppChecker также обнаруживает)

      В частности:
      V570 The 'Parabola' variable is assigned to itself. SomeFileName.cpp 287

      Parabola      =Parabola ;
      

      Это тот же тип дефекта, что и нашелся у Вас:
      присвоение переменной самой себе
      tmp = tmp;
      

      Относительно V522 Dereferencing of the null pointer 'SetterGetter' might take place. OtherFileName.cpp 305
      if ( SetterGetter==0 )
      	{
      		SetterGetter->set(false) ;
      	}
      


      об этом типе дефекта собственно сама наша статья) было бы подозрительно, если бы мы писали о таком дефекте и при этом его не обнаруживали)


      1. vovochkin
        12.01.2017 14:57
        +2

        Да, я сам удивился, что дефект не найден — и поэтому решил сделать специальный пример. Удалось ли проверить этот тест из комментария выше?
        Я работаю из под QtCreator, pro-файл имеет

        следующий вид:
        TEMPLATE = app
        CONFIG += console
        CONFIG -= app_bundle
        CONFIG -= qt
        
        SOURCES += main.cpp