CERT C: Rule STR31-C

Гарантируйте, что хранилище для строк имеет достаточное пространство для символьных данных и null terminator

Описание

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

Гарантируйте, что хранилище для строк имеет достаточное пространство для символьных данных и null terminator.[1]

Реализация Polyspace

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

  • Использование опасной стандартной функции.

  • Отсутствует null в строковые массивы.

  • Переполнение буфера из неверного спецификатора строкового формата.

  • Переполнение целевого буфера при манипуляции строками.

Примеры

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

Проблема

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

Опасная функцияУровень рискаБолее безопасная функция
getsПо своей сути опасно - Вы не можете контролировать длину входов с консоли.fgets
cinПо своей сути опасно - Вы не можете контролировать длину входов с консоли.Избегайте или предварительные вызовы cin с cin.width.
strcpyВозможно, опасно - если длина источника больше, чем адрес назначения, может произойти переполнение буфера.strncpy
stpcpyВозможно, опасно - если длина источника больше, чем адрес назначения, может произойти переполнение буфера.stpncpy
lstrcpy или StrCpyВозможно, опасно - если длина источника больше, чем адрес назначения, может произойти переполнение буфера.StringCbCopy, StringCchCopy, strncpy, strcpy_s, или strlcpy
strcatВозможно, опасно - если результат конкатенирования больше, чем адрес назначения, может произойти переполнение буфера.strncat, strlcat, или strcat_s
lstrcat или StrCatВозможно, опасно - если результат конкатенирования больше, чем адрес назначения, может произойти переполнение буфера.StringCbCat, StringCchCat, strncay, strcat_s, или strlcat
wcpcpyВозможно, опасно - если длина источника больше, чем адрес назначения, может произойти переполнение буфера.wcpncpy
wcscatВозможно, опасно - если результат конкатенирования больше, чем адрес назначения, может произойти переполнение буфера.wcsncat, wcslcat, или wcncat_s
wcscpyВозможно, опасно - если длина источника больше, чем адрес назначения, может произойти переполнение буфера.wcsncpy
sprintfВозможно, опасно - если выходная длина зависит от неизвестных длин или значений, может произойти переполнение буфера.snprintf
vsprintfВозможно, опасно - если выходная длина зависит от неизвестных длин или значений, может произойти переполнение буфера.vsnprintf
Риск

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

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

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

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

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

Пример - Использование sprintf
#include <stdio.h>
#include <string.h>
#include <iostream>

#define BUFF_SIZE 128


int dangerous_func(char *str)
{
    char dst[BUFF_SIZE];
    int r = 0;

    if (sprintf(dst, "%s", str) == 1)
    {
        r += 1;
        dst[BUFF_SIZE-1] = '\0';
    }
    
    return r;
}

Эта функция , взятая в качестве примера, использует sprintf чтобы скопировать строку str на dst. Однако, если str больше буфера, sprintf может вызвать переполнение буфера.

Коррекция - Использование snprintf с Buffer size

Одной из возможных коррекций является использование snprintf вместо этого задайте buffer size.

#include <stdio.h>
#include <string.h>
#include <iostream>

#define BUFF_SIZE 128


int dangerous_func(char *str)
{
    char dst[BUFF_SIZE];
    int r = 0;

    if (snprintf(dst, sizeof(dst), "%s", str) == 1)
    {
        r += 1;
        dst[BUFF_SIZE-1] = '\0';
    }
    
    return r;
}
Проблема

Missing null in string array происходит, когда в строке недостаточно пространства для завершения с использованием символа null '\0'.

Этот дефект применяется только к проектам в С.

Риск

Переполнение буфера может произойти, если вы скопируете строку в массив, не принимая неявный null terminator.

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

Если вы инициализируете символьный массив с литералом, избегайте задавать границы массива.

char three[]  = "THREE";
Компилятор автоматически выделяет пространство для нулевого терминатора строки. В предыдущем примере компилятор выделяет достаточное пространство для пяти символов и null terminator.

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

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

Пример - Размер массива слишком мал
void countdown(int i)
{
    static char one[5]   = "ONE";
    static char two[5]   = "TWO";
    static char three[5] = "THREE";
}

Область символьного массива three имеет размер 5 и 5 символов 'T', 'H', 'R', 'E', и 'E'. В конце нет места для символа null, потому что three всего пять байтов.

Коррекция - увеличение размера массива

Одной из возможных коррекций является изменение размера массива, позволяющее использовать пять символов плюс символ null.

void countdown(int i)
{
    static char one[5]   = "ONE";
    static char two[5]   = "TWO";
    static char three[6] = "THREE";
}
Коррекция - метод инициализации изменений

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

void countdown(int i)
{
    static char one[5]   = "ONE";
    static char two[5]   = "TWO";
    static char three[]  = "THREE";
}
Проблема

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

Риск

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

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

Используйте спецификатор формата, который совместим с buffer size памяти.

Пример - переполнение буфера памяти
#include <stdio.h>

void func (char *str[]) {
    char buf[32];
    sscanf(str[1], "%33c", buf);
}

В этом примере buf может содержать 32 char элементы. Поэтому спецификатор формата %33c вызывает переполнение буфера.

Коррекция - используйте меньшую точность в спецификаторе формата

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

#include <stdio.h>

void func (char *str[]) {
    char buf[32];
    sscanf(str[1], "%32c", buf);
}
Проблема

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

Например, при вызове функции sprintf(char* buffer, const char* format), вы используете постоянную строку format большего размера, чем buffer.

Риск

Переполнение буфера может привести к непредвиденному поведению, такому как повреждение памяти или остановка системы. Переполнение буфера также вводит риск инъекции кода.

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

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

  • Если вы используете sprintf для записи форматированных данных в строку используйте snprintf, _snprintf или sprintf_s вместо этого, чтобы применить управление длиной. Кроме того, используйте asprintf автоматическое выделение памяти, необходимой для целевого буфера.

  • Если вы используете vsprintf чтобы записать форматированные данные из списка аргументов переменной в строку, используйте vsnprintf или vsprintf_s вместо этого, чтобы применить управление длиной.

  • Если вы используете wcscpy для копирования широкой строки используйте wcsncpy, wcslcpy, или wcscpy_s вместо этого, чтобы применить управление длиной.

Другим возможным решением является увеличение buffer size.

Пример - переполнение буфера в sprintf Использовать
#include <stdio.h>

void func(void) {
    char buffer[20];
    char *fmt_string = "This is a very long string, it does not fit in the buffer";

    sprintf(buffer, fmt_string);
}

В этом примере buffer может содержать 20 char элементы, но fmt_string имеет больший размер.

Коррекция - Использование snprintf Вместо sprintf

Одной из возможных коррекций является использование snprintf функция для обеспечения контроля длины.

#include <stdio.h>

void func(void) {
    char buffer[20];
    char *fmt_string = "This is a very long string, it does not fit in the buffer";

    snprintf(buffer, 20, fmt_string);
}

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

Группа: Правило 07. Символы и строки (STR)
Введенный в R2019a

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

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

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