CERT C++: MEM56-CPP

Не храните уже принадлежащее значение указателя в несвязанном смарт-указателе

Описание

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

Не храните уже принадлежащее значение указателя в несвязанном смарт-указателе.[1]

Реализация Polyspace

Эта проверка проверяет на Use of already-owned pointers

Примеры

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

Проблема

Эта проблема возникает, когда вы используете уже принадлежащий вам указатель в качестве аргумента:

  • Конструктор умных указателей. Например, в этом фрагменте кода, raw_ptr уже принадлежит s_ptr1 и используется для инициализации s_ptr2:

    char *raw_ptr = new char;
    std::shared_ptr<char> s_ptr1(raw_ptr);
    std::shared_ptr<char> s_ptr2(raw_ptr); //raw_ptr is already owned by s_ptr1

  • Операция сброса смарт-указателя. Например, в этом фрагменте кода сброс s_ptr2 заменяет raw_ptr2 с уже принадлежащими raw_ptr1:

    char *raw_ptr1 = new char;
    char *raw_ptr2 = new char;
    
    std::shared_ptr<char> s_ptr1(raw_ptr1);
    std::shared_ptr<char> s_ptr2(raw_ptr2);
    
    s_ptr2.reset(raw_ptr1); // s_ptr2 releases raw_ptr2 and owns already owned raw_ptr1

Polyspace® проверяет только типы смарт-указателей std::shared_ptr и std::unique_ptr и считает, что определяемые пользователем распределители и разделители имеют стандартное поведение выделения и удаления.

Указатель уже принадлежит смарт-указателю, если тип указателя не std::nullptr_t и либо:

  • Указатель использовался для инициализации смарт-указателя.

  • Указатель использовался как аргумент для смарт-указателя reset() функции представителя.

  • Указатель является возвратом значением интеллектуального указателя get() функции представителя.

  • Указатель является возвратом значением интеллектуального указателя operator-> функции представителя.

Риск

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

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

Использование std::make_shared чтобы создать смарт-указатель, а затем использовать конструкцию копирования для создания связанного смарт-указателя. Базовым значением указателя управляют оба смарт-указателя, и указанная память не освобождается до тех пор, пока не будут уничтожены все смарт-указатели.

Если вы не намерены позволять нескольким смарт-указателям управлять одним и тем же значением указателя, используйте std::make_unique чтобы создать std::unique_ptr умный указатель. A std::unique_ptr можно перемещать только, что отменяет владение базовым значением управляемого указателя.

Пример - Использование уже принадлежащего указателя
#include <memory>
#include <string>

struct Profile
{
    virtual ~Profile()=default;
};

struct Player : public Profile
{
    std::string name;
    std::int8_t rank;

    Player();
    Player(const std::string& name_, const std::int8_t& rank_) :
        name{ name_ }, rank{ rank_ } {}
};

void func(){

    Player * player = new Player("Richard Roll",1);
    std::shared_ptr<Player> player1(player);
    std::shared_ptr<Player> top_rank(player); //Non-compliant

}

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

Коррекция - Использование std::make_shared и скопируйте конструкцию, чтобы создать связанные интеллектуальные указатели
#include <memory>
#include <string>

struct Profile
{
    virtual ~Profile()=default;
};

struct Player : public Profile
{
    std::string name;
    std::int8_t rank;

    Player();
    Player(const std::string& name_, const std::int8_t& rank_) :
        name{ name_ }, rank{ rank_ } {}
};

void func2(){

    std::shared_ptr<Player> player1_shared =
        std::make_shared<Player>("Richard Roll",1);
    std::shared_ptr<Player> top_rank_shared(player1_shared); //Compliant

}

Одной из возможных коррекций является использование std::make_shared объявить player1_shared, а затем используйте конструкцию копирования для создания связанного интеллектуального указателя top_rank_shared. Базовое значение указателя не удаляется, пока не будут уничтожены все смарт-указатели.

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

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

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

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

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