CERT C++: MEM50-CPP

Не получайте доступ к свободной памяти

Описание

Определение правила

Не получайте доступ к свободной памяти.[1]

Реализация Polyspace

Эта проверка проверяет на наличие следующих проблем:

  • Доступ к указателям вне границ.

  • Отмена присвоения предварительно освобожденного указателя.

  • Использование ранее освобожденного указателя.

Примеры

расширить все

Проблема

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

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

Риск

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

Зафиксировать

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

  • Верхняя граница цикла слишком велика.

  • Вы использовали арифметику указателя для продвижения указателя с неправильным значением шага указателя.

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

Часто детали результата показывают последовательность событий, которые привели к дефекту. Вы можете реализовать исправление на любом событии в последовательности. Если сведения о результате не отображают историю событий, можно отследить их с помощью опций правого щелчка в исходном коде и просмотреть предыдущие связанные события. Смотрите также Результаты интерпретации Bug Finder в интерфейсе пользователя Polyspace Desktop.

См. примеры исправлений ниже.

Если вы не хотите устранять проблему, добавьте комментарии к своему результату или коду, чтобы избежать другой проверки. Смотрите Адрес Результаты Polyspace через исправления ошибок или обоснования.

Пример - Доступ к указателю за пределами ошибки
int* Initialize(void)
{
 int arr[10];
 int *ptr=arr;

 for (int i=0; i<=9;i++)
   {
    ptr++;
    *ptr=i;
    /* Defect: ptr out of bounds for i=9 */
   }

 return(arr);
}

ptr присваивается адрес arr который указывает на блок памяти размером 10*sizeof(int). В for-цикл, ptr увеличивается в 10 раз. В последней итерации цикла ptr точки вне назначенного ему блока памяти. Поэтому его нельзя отрешать.

Коррекция - Контрольный указатель остается в границах

Одна из возможных коррекций состоит в том, чтобы изменить порядок шага и дереференции ptr.

int* Initialize(void)
{
 int arr[10];
 int *ptr=arr;

 for (int i=0; i<=9;i++)
     {
      /* Fix: Dereference pointer before increment */
      *ptr=i;
      ptr++;
     }

 return(arr);
}

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

Проблема

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

Риск

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

Зафиксировать

Исправление зависит от первопричины дефекта. Посмотрите, намеревались ли вы выделить блок памяти указателю между первой деаллокацией и второй. В противном случае удалите вторую free оператор.

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

Пример - Отмена присвоения ранее отключенной ошибки указателя
#include <stdlib.h>

void allocate_and_free(void)
{

    int* pi = (int*)malloc(sizeof(int));
    if (pi == NULL) return;

    *pi = 2;
    free(pi);
    free (pi);       
    /* Defect: pi has already been freed */
}

Первый free оператор освобождает блок памяти, который pi относится к. Вторая free оператор по pi освобождает блок памяти, который уже был освобожден.

Коррекция - Удаление повторяющейся отмены присвоения

Одной из возможных коррекций является удаление второго free оператор.

#include <stdlib.h>

void allocate_and_free(void)
{

    int* pi = (int*)malloc(sizeof(int));
    if (pi == NULL) return;

    *pi = 2;
    free(pi);
    /* Fix: remove second deallocation */
 }
Проблема

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

Риск

Когда указателю выделяется динамическая память с malloc, calloc или realloc, это указывает на место памяти на куче. Когда вы используете free функция на этом указателе, связанный блок памяти освобождается для перераспределения. Попытка получить доступ к этому блоку памяти может привести к непредсказуемому поведению или даже отказу сегментации.

Зафиксировать

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

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

Пример - Использование ранее освобожденной ошибки указателя
#include <stdlib.h>
#include <stdio.h>
 int increment_content_of_address(int base_val, int shift)
   { 
    int j;
    int* pi = (int*)malloc(sizeof(int));
    if (pi == NULL) return 0;

    *pi = base_val;
    free(pi);

    j = *pi + shift;
    /* Defect: Reading a freed pointer */
 
    return j;
   }

The free оператор освобождает блок памяти, который pi относится к. Поэтому разыменование pi после free недопустимый оператор.

Коррекция - Свободный указатель после использования

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

#include <stdlib.h>

int increment_content_of_address(int base_val, int shift)
{
    int j;
    int* pi = (int*)malloc(sizeof(int));
    if (pi == NULL) return 0;

    *pi = base_val;

    j = *pi + shift;
    *pi = 0;

    /* Fix: The pointer is freed after its last use */
    free(pi);               
    return j;
}

Проверяйте информацию

Группа: 06. Управление памятью (MEM)
Введенный в R2019a

[1] Это программное обеспечение было создано MathWorks, включающее фрагменты: «Сайт SEI CERT-C», © 2017 Университет Карнеги Меллон, Веб-сайт SEI CERT-C + + © 2017 Университет Карнеги Меллон, "Стандарт кодирования SEI CERT C - Правила разработки безопасных, Надежные и безопасные системы - 2016 Edition ", © 2016 Университет Карнеги Меллон, и "Стандарт кодирования SEI CERT C++ - Правила разработки безопасных, Надежные и безопасные системы в C++ - 2016 Edition "© 2016 Университет Карнеги Меллон, с специального разрешения от его Института программной инженерии.

ЛЮБОЙ МАТЕРИАЛ УНИВЕРСИТЕТА КАРНЕГИ МЕЛЛОН И/ИЛИ ЕГО ИНЖЕНЕРНОГО ИНСТИТУТА ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ, СОДЕРЖАЩИЙСЯ В НАСТОЯЩЕМ ДОКУМЕНТЕ, ПОСТАВЛЯЕТСЯ НА БАЗИСЕ «КАК ЕСТЬ». УНИВЕРСИТЕТ КАРНЕГИ МЕЛЛОН НЕ ДАЕТ НИКАКИХ ГАРАНТИЙ, ВЫРАЖЕННЫХ ИЛИ ПОДРАЗУМЕВАЕМЫХ, В ОТНОШЕНИИ ЛЮБОГО ВОПРОСА, ВКЛЮЧАЯ, НО НЕ ОГРАНИЧИВАЯСЬ, ГАРАНТИЮ ПРИГОДНОСТИ ДЛЯ ЦЕЛЕЙ ИЛИ КОММЕРЧЕСКОЙ ВЫГОДЫ, ИСКЛЮЧИТЕЛЬНОСТИ, ИЛИ УНИВЕРСИТЕТ КАРНЕГИ МЕЛЛОН НЕ ДАЕТ НИКАКИХ ГАРАНТИЙ В ОТНОШЕНИИ СВОБОДЫ ОТ ПАТЕНТА, ТОВАРНОГО ЗНАКА ИЛИ НАРУШЕНИЯ АВТОРСКИХ ПРАВ.

Это программное обеспечение и связанная с ним документация не были рассмотрены и не одобрены Университетом Карнеги-Меллон или его Институтом программной инженерии.