Разыменование пустого указателя как исправить
Перейти к содержимому

Разыменование пустого указателя как исправить

  • автор:

Динамический массив СИ

Дальше я заполняю его через scanf_s(«%s», temp, 49); order[1] = temp; и тд, но в итоге все елементы будут равны последнему значению. На free(order); выдаёт ошибку. Сама студия жалуется на разыменование пустого указателя order в этой строке order[i] = (char*)malloc(osize * sizeof(char)); Где ошибка?

Отслеживать
218k 15 15 золотых знаков 117 117 серебряных знаков 229 229 бронзовых знаков
задан 5 апр 2021 в 21:41
Vlad Ryazantsev Vlad Ryazantsev
3 3 3 бронзовых знака

1 ответ 1

Сортировка: Сброс на вариант по умолчанию

Очень интересное решение. Итак, у вас есть указатель на выделенную память (которую потом можно будет удалить через free ) order[1] .

Вы перезаписываете этот указатель

 order[1] = temp; 

и теперь он (как и все остальные, по окончании работы) указывает на память, выделенную в стеке массиву temp . Еще раз и медленно — все указатели указывают на одну и ту же память в стеке (к которой применять free нельзя). Т.е. все указывают на одну и ту же последнюю строку.

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

Еще — вам точно хватит места под вводимые слова? Все же в temp может поместиться 49-символьная строка, а в order[i] — только четырехсимвольная.

И еще — судя по вашим (char*) перед malloc вы все же компилируете в режиме C++, а не С — в С это приведение не требуется.

P.S. VC++ ни о каком разыменовании в указанном вами месте не говорит. Только о том, что после temp[50] вы точку с запятой забыли.

Разыменование пустого указателя как исправить

Указатели поддерживают ряд операций: присваивание, получение адреса указателя, получение значения по указателю, некоторые арифметические операции и операции сравнения.

Присваивание адреса

Указателю можно присвоить адрес объекта того же типа, либо значение другого указателя. Для получения адреса объекта используется операция & :

int a ; int *pa ; // указатель pa хранит адрес переменной a

При этом указатель и переменная должны иметь один и тот же тип, в данном случае это тип int.

Разыменование указателя

Операция разыменования указателя представляет выражение в виде *имя_указателя . Эта операция позволяет получить объект по адресу, который хранится в указателе.

#include int main() < int a ; int *pa ; // хранит адрес переменной a std::cout #include int main() < int a ; int b ; int *pa ; // указатель на переменную a int *pb ; // указатель на переменную b std::cout pa: address=0x56347ffc5c value=10 pb: address=0x56347ffc58 value=2 pa: address=0x56347ffc58 value=2 b value=125

Нулевые указатели

Нулевой указатель (null pointer) - это указатель, который не указывает ни на какой объект. Если мы не хотим, чтобы указатель указывал на какой-то конкретный адрес, то можно присвоить ему условное нулевое значение. Для определения нулевого указателя можно инициализировать указатель нулем или константой nullptr :

int *p1; int *p2<>;

Ссылки на указатели

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

#include int main() < int a ; int b ; int *p<>; // указатель int *&pRef

; // ссылка на указатель pRef = &a; // через ссылку указателю p присваивается адрес переменной a std::cout &:

int a ; int *pa ; std::cout >, >=, , ,==, !=. Операции сравнения применяются только к указателям одного типа. Для сравнения используются номера адресов:

#include int main() < int a ; int b ; int *pa ; int *pb ; if(pa > pb) std::cout

Консольный вывод в моем случае:

pa (0xa9da5ffdac) is greater than pb (0xa9da5ffda8)

Приведение типов

Иногда требуется присвоить указателю одного типа значение указателя другого типа. В этом случае следует выполнить операцию приведения типов с помощью операции (тип_указателя *) :

#include int main() < char c ; char *pc ; // указатель на символ int *pd <(int *)pc>; // указатель на int void *pv ; // указатель на void std::cout std::cout

Разыменование пустого указателя как исправить

PostgresPro

Inferno Solutions

Закладки на сайте
Проследить за страницей
Created 1996-2023 by Maxim Chirkov
Добавить, Поддержать, Вебмастеру

Обнаружение в коде дефекта «разыменование нулевого указателя»

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

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

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

#include class A < public: void bar() < std::cout >; int main() < A* a = 0; a->bar(); return 0; > 

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

#include class A < int x; public: void bar() < std::cout >; 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, так и в коммерческих проектах.

  • Анализ безопасности приложений
  • AppChecker
  • информационная безопасность

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

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