CERT C++: ENV34-C

Не храните указатели, возвращенные определенными функциями

Описание

Управляйте определением

Не храните указатели, возвращенные определенными функциями. [1]

Примеры

развернуть все

Описание

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

  1. Вы указываете на буфер, возвращенный в неповторно используемую стандартную функцию, такую как getenv или setlocale.

    user = getenv("USER");
  2. Вы вызываете ту неповторно используемую стандартную функцию снова.

    user2 = getenv("USER2");
  3. Вы используете или разыменовываете указатель от первого шага, ожидая, что буфер останется неизмененными начиная с того шага. Тем временем вызов на втором шаге изменил буфер.

    Например:

    var=*user;

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

char* func() {
     user=getenv("USER");
     .
     .
     return user;
}

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

Риск

Стандарт C позволяет неповторно используемым функциям, таким как getenv возвращать указатель на статический буфер. Поскольку буфер статичен, второй вызов getenv изменяет буфер. Если вы продолжаете использовать указатель, возвращенный в первый вызов мимо второго вызова, вы видите неожиданные результаты. Буфер, на который это не указывает больше, имеет значения от первого вызова.

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

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

Фиксация

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

Пример - возвращается из getenv, используемого после второго вызова getenv

#include <stdlib.h>
#include <string.h>

int func()
{
    int result = 0;

    char *home = getenv("HOME");   /* First call */ 
    if (home != NULL) {
        char *user = NULL;
        char *user_name_from_home = strrchr(home, '/');
 
        if (user_name_from_home != NULL) {
            user = getenv("USER");   /* Second call */
            if ((user != NULL) &&
                (strcmp(user, user_name_from_home) == 0)) 
            {
                result = 1;
            }
        }
    }
    return result;
}

В этом примере указатель user_name_from_home выведен от указателя home. home указывает на буфер, возвращенный от первого вызова до getenv. Поэтому user_name_from_home указывает на местоположение в том же буфере.

После второго вызова getenv изменяется буфер. Если вы продолжаете использовать user_name_from_home, можно получить неожиданные результаты.

Исправление — делает копию буфера перед вторым вызовом

Если вы хотите получить доступ к буферу от первого вызова до getenv мимо второго вызова, сделайте копию буфера после первого вызова. Одно возможное исправление должно использовать функцию strdup, чтобы сделать копию.

#include <stdlib.h>
#include <string.h>

int func()
{
    int result = 0;

    char *home = getenv("HOME");    
    if (home != NULL) {
        char *user = NULL;
        char *user_name_from_home = strrchr(home, '/'); 
        if (user_name_from_home != NULL) {
            /* Make copy before second call */
            char *saved_user_name_from_home = strdup(user_name_from_home); 
            if (saved_user_name_from_home != NULL) {
                user = getenv("USER");  
                if ((user != NULL) &&
                    (strcmp(user, saved_user_name_from_home) == 0)) 
                {
                    result = 1;
                }
                free(saved_user_name_from_home);
            }
        }
    }
    return result;
}

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

Группа: 49. Разное (MSC)

Введенный в R2019a


[1]  Это программное обеспечение было создано MathWorks, включающим фрагменты: “Веб-сайт SEI CERT-C”, © 2017 Carnegie Mellon University, веб-сайт SEI CERT-C © 2017 Carnegie Mellon University”, CERT SEI C Кодирование Стандарта – Правил для Разработки безопасных, Надежных и Защищенных систем – 2 016 Выпусков”, © 2016 Carnegie Mellon University, and “CERT SEI Стандарт Кодирования C++ – Правил для Разработки безопасных, Надежных и Защищенных систем на C++ – 2 016 Выпусков” © 2016 Carnegie Mellon University, со специальным разрешением от его Института программной инженерии.

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

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