Jump to content

Висячий указатель

(Перенаправлено со ссылки «Висячие» )
Висячий указатель

Висячие указатели и дикие указатели в компьютерном программировании — это указатели , которые не указывают на действительный объект соответствующего типа. Это частные случаи нарушений безопасности памяти . В более общем смысле, висячие ссылки и дикие ссылки — это ссылки , которые не преобразуются в допустимое место назначения.

Висячие указатели возникают во время разрушения объекта , когда объект, имеющий входящую ссылку, удаляется или освобождается без изменения значения указателя, так что указатель по-прежнему указывает на ячейку освобожденной памяти. Система может перераспределить ранее освобожденную память, и если затем программа разыменует висячий указатель (сейчас), непредсказуемому поведению это может привести к , поскольку теперь память может содержать совершенно другие данные. Если программа выполняет запись в память, на которую ссылается висячий указатель, это может привести к скрытому повреждению несвязанных данных, что приведет к тонким ошибкам , которые чрезвычайно трудно обнаружить. Если память была перераспределена другому процессу, то попытка разыменовать висячий указатель может привести к ошибкам сегментации (UNIX, Linux) или общим ошибкам защиты (Windows). Если программа имеет достаточные привилегии, позволяющие ей перезаписывать бухгалтерские данные, используемые распределителем памяти ядра, повреждение может привести к нестабильной работе системы. В в объектно-ориентированных языках со сборкой мусора висячие ссылки предотвращаются за счет уничтожения только недостижимых объектов, то есть у них нет входящих указателей; это обеспечивается либо трассировкой, либо подсчетом ссылок . Однако финализатор может создавать новые ссылки на объект, требуя воскрешения объекта , чтобы предотвратить висячую ссылку.

Дикие указатели, также называемые неинициализированными указателями, возникают, когда указатель используется до инициализации некоторого известного состояния, что возможно в некоторых языках программирования. Они демонстрируют такое же неустойчивое поведение, как и висячие указатели, хотя с меньшей вероятностью останутся незамеченными, поскольку многие компиляторы выдают предупреждение во время компиляции, если к объявленным переменным осуществляется доступ до их инициализации. [1]

Причина висячих указателей

[ редактировать ]

Во многих языках (например, языке программирования C ) явное удаление объекта из памяти или уничтожение кадра стека при возврате не приводит к изменению связанных указателей. Указатель по-прежнему указывает на то же место в памяти, хотя теперь это место можно использовать для других целей.

Простой пример показан ниже:

{
   char *dp = NULL;
   /* ... */
   {
       char c;
       dp = &c;
   } 
     /* c falls out of scope */
     /* dp is now a dangling pointer */
}

Если операционная система способна обнаруживать ссылки на нулевые указатели во время выполнения , решением вышеописанной проблемы является присвоение 0 (ноль) переменной dp непосредственно перед выходом из внутреннего блока. Другим решением было бы каким-то образом гарантировать, что dp не будет использоваться снова без дальнейшей инициализации.

Другим частым источником висячих указателей является беспорядочная комбинация malloc() и free() вызовы библиотеки: указатель становится висящим, когда блок памяти, на который он указывает, освобождается. Как и в предыдущем примере, один из способов избежать этого — обязательно сбросить указатель на ноль после освобождения его ссылки, как показано ниже.

#include <stdlib.h>

void func()
{
    char *dp = malloc(A_CONST);
    /* ... */
    free(dp);         /* dp now becomes a dangling pointer */
    dp = NULL;        /* dp is no longer dangling */
    /* ... */
}

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

int *func(void)
{
    int num = 1234;
    /* ... */
    return &num;
}

Попытки чтения указателя могут еще некоторое время возвращать правильное значение (1234) после вызова. func, но любые функции, вызванные после этого, могут перезаписать хранилище стека, выделенное для num с другими значениями, и указатель больше не будет работать правильно. Если указатель на num должен быть возвращен, num должна иметь область действия за пределами функции — ее можно объявить как static.

Ручное освобождение без висячей ссылки

[ редактировать ]

Антони Кречмар [ pl ] (1945–1996) создал полную систему управления объектами, свободную от феномена висячих ссылок. [2] Похожий подход был предложен Фишером и Лебланом. [3] под названием « Замки и ключи» .

Причина диких указателей

[ редактировать ]

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

Чаще всего это происходит из-за пропуска инициализации, а не из-за ее пропуска. Большинство компиляторов способны об этом предупредить.

int f(int i)
{
    char *dp;    /* dp is a wild pointer */
    static char *scp;  /* scp is not a wild pointer:
                        * static variables are initialized to 0
                        * at start and retain their values from
                        * the last call afterwards.
                        * Using this feature may be considered bad
                        * style if not commented */
}

Дыры в безопасности, связанные со свисающими указателями

[ редактировать ]

Подобно ошибкам переполнения буфера , ошибки висячих/диких указателей часто становятся дырами в безопасности. Например, если указатель используется для вызова виртуальной функции , может быть вызван другой адрес (возможно, указывающий на код эксплойта) из-за vtable перезаписи указателя . Альтернативно, если указатель используется для записи в память, может быть повреждена другая структура данных. Даже если память читается только после того, как указатель становится висячим, это может привести к утечке информации (если интересные данные помещаются в следующую структуру, выделенную там) или к повышению привилегий (если теперь недействительная память используется в проверках безопасности). Когда висячий указатель используется после того, как он был освобожден без выделения для него нового фрагмента памяти, это становится известно как уязвимость «использования после освобождения». [4] Например CVE- , 2014-1776 — уязвимость использования после освобождения в Microsoft Internet Explorer 6–11. [5] используется атаками нулевого дня со стороны продвинутой постоянной угрозы . [6]

Как избежать ошибок висячего указателя

[ редактировать ]

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

#include <assert.h>
#include <stdlib.h>

/* Alternative version for 'free()' */
static void safefree(void **pp)
{
    /* in debug mode, abort if pp is NULL */
    assert(pp);
    /* free(NULL) works properly, so no check is required besides the assert in debug mode */
    free(*pp);                  /* deallocate chunk, note that free(NULL) is valid */
    *pp = NULL;                 /* reset original pointer */
}

int f(int i)
{
    char *p = NULL, *p2;
    p = malloc(1000);    /* get a chunk */
    p2 = p;              /* copy the pointer */
    /* use the chunk here */
    safefree((void **)&p);       /* safety freeing; does not affect p2 variable */
    safefree((void **)&p);       /* this second call won't fail as p is reset to NULL */
    char c = *p2;       /* p2 is still a dangling pointer, so this is undefined behavior. */
    return i + c;
}

Альтернативную версию можно использовать даже для того, чтобы гарантировать достоверность пустого указателя перед вызовом. malloc():

    safefree(&p);        /* i'm not sure if chunk has been released */
    p = malloc(1000);    /* allocate now */

Эти виды использования могут быть замаскированы с помощью #define директивы для создания полезных макросов (обычный из них — #define XFREE(ptr) safefree((void **)&(ptr))), создавая что-то вроде метаязыка, или может быть отдельно встроен в библиотеку инструментов. В любом случае программистам, использующим эту технику, следует использовать безопасные версии во всех случаях, когда free() будет использоваться; невыполнение этого требования снова приводит к проблеме. Кроме того, это решение ограничено рамками одной программы или проекта и должно быть надлежащим образом задокументировано.

Среди более структурированных решений популярным способом избежать висячих указателей в C++ является использование интеллектуальных указателей . Интеллектуальный указатель обычно использует подсчет ссылок для восстановления объектов. Некоторые другие методы включают метод надгробий и метод замков и ключей . [3]

Другой подход — использовать сборщик мусора Boehm , консервативный сборщик мусора , который заменяет стандартные функции выделения памяти в C и C++ сборщиком мусора. Этот подход полностью устраняет ошибки висячего указателя за счет отключения освобождения и освобождения объектов путем сборки мусора.

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

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

Обнаружение висячего указателя

[ редактировать ]

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

Некоторые отладчики автоматически перезаписывают и уничтожают освобожденные данные, обычно по определенному шаблону, например 0xDEADBEEF (Например, отладчик Microsoft Visual C/C++ использует 0xCC, 0xCD или 0xDD в зависимости от того, что было освобождено [7] ). Обычно это предотвращает повторное использование данных, делая их бесполезными, а также очень заметными (шаблон служит для того, чтобы показать программисту, что память уже освобождена).

Такие инструменты, как Polyspace , TotalView , Valgrind , Mudflap, [8] AddressSanitizer , или инструменты на базе LLVM [9] также может использоваться для обнаружения использования висячих указателей.

Другие инструменты ( SoftBound , Insure++ и CheckPointer ) инструментируют исходный код для сбора и отслеживания допустимых значений указателей («метаданных») и проверки достоверности каждого доступа к указателю по метаданным.

Другая стратегия, при подозрении на небольшой набор классов, состоит в том, чтобы временно сделать все их функции-члены виртуальными : после того, как экземпляр класса был уничтожен/освобожден, его указатель на таблицу виртуальных методов устанавливается в значение NULL, и любой вызов функции-члена приведет к сбою программы и отобразит виновный код в отладчике.

См. также

[ редактировать ]
  1. ^ «Параметры предупреждения — использование коллекции компиляторов GNU (GCC)» .
  2. ^ Джанна Чиони, Антони Кречмар, Запрограммированное освобождение без висящей ссылки , Письма об обработке информации , т. 18, 1984 , стр. 179–185
  3. ^ Jump up to: а б К. Н. Фишер, Р. Дж. Леблан, Реализация диагностики во время выполнения в Паскале , Транзакции IEEE по программной инженерии , 6 (4): 313–319, 1980.
  4. ^ Далчи, Эрик; анонимный автор; Группа контента CWE (11 мая 2012 г.). «CWE-416: Использовать после освобождения» . Перечень распространенных слабостей . Корпорация Митра . Проверено 28 апреля 2014 г. {{cite web}}: |author2= имеет общее имя ( справка )
  5. ^ «CVE-2014-1776» . Распространенные уязвимости и риски (CVE) . 29 января 2014 г. Архивировано из оригинала 30 апреля 2017 г. Проверено 16 мая 2017 г.
  6. ^ Чен, Сяобо; Кэселден, Дэн; Скотт, Майк (26 апреля 2014 г.). «Новый эксплойт нулевого дня, нацеленный на Internet Explorer версий с 9 по 11, обнаружен в целевых атаках» . Блог FireEye . Огненный Глаз . Проверено 28 апреля 2014 г.
  7. ^ Шаблоны заполнения памяти Visual C++ 6.0
  8. ^ Отладка указателя брызговика
  9. ^ Дхурджати Д. и Адве В. Эффективное обнаружение всех случаев использования висячих указателей на производственных серверах
Arc.Ask3.Ru: конец переведенного документа.
Arc.Ask3.Ru
Номер скриншота №: a1d58621c0610ce985f981227c7c468f__1717446180
URL1:https://arc.ask3.ru/arc/aa/a1/8f/a1d58621c0610ce985f981227c7c468f.html
Заголовок, (Title) документа по адресу, URL1:
Dangling pointer - Wikipedia
Данный printscreen веб страницы (снимок веб страницы, скриншот веб страницы), визуально-программная копия документа расположенного по адресу URL1 и сохраненная в файл, имеет: квалифицированную, усовершенствованную (подтверждены: метки времени, валидность сертификата), открепленную ЭЦП (приложена к данному файлу), что может быть использовано для подтверждения содержания и факта существования документа в этот момент времени. Права на данный скриншот принадлежат администрации Ask3.ru, использование в качестве доказательства только с письменного разрешения правообладателя скриншота. Администрация Ask3.ru не несет ответственности за информацию размещенную на данном скриншоте. Права на прочие зарегистрированные элементы любого права, изображенные на снимках принадлежат их владельцам. Качество перевода предоставляется как есть. Любые претензии, иски не могут быть предъявлены. Если вы не согласны с любым пунктом перечисленным выше, вы не можете использовать данный сайт и информация размещенную на нем (сайте/странице), немедленно покиньте данный сайт. В случае нарушения любого пункта перечисленного выше, штраф 55! (Пятьдесят пять факториал, Денежную единицу (имеющую самостоятельную стоимость) можете выбрать самостоятельно, выплаичвается товарами в течение 7 дней с момента нарушения.)