CERT C: Rec. MEM05-C

Избегайте больших выделений стека

Описание

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

Избегайте больших выделений стека.[1]

Реализация Polyspace

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

  • Прямой или косвенный вызов функции к себе.

  • Массив переменной длины с непозитивным размером.

  • Испорченный размер массива переменной длины.

Примеры

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

Проблема

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

Риск

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

Пример - Прямая и косвенная рекурсия
void foo1( void ) {     /* Non-compliant */
          /*- Indirect recursion foo1->foo2->foo1... */
    foo2();
    foo1();   /* Non-compliant - Direct recursion */
}

void foo2( void ) {/*Noncompliant*/
         /* Indirect Recursion - Foo2->foo1->foo2*/
    foo1();
}

В этом примере правило нарушается из-за:

  • Прямая рекурсия foo1foo1.

  • Косвенная рекурсия foo1foo2foo1.

  • Косвенная рекурсия foo2foo1foo2.

Проблема

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

Риск

Если размер массива переменной длины равен нулю или отрицательному, может произойти неожиданное поведение, такое как переполнение стека.

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

Когда вы объявляете массив переменной длины как локальную переменную в функции:

  • Если вы используете параметр функции в качестве размера массива, проверяйте, что параметр положительный.

  • Если вы используете результат расчета параметра функции в качестве размера массива, проверяйте, что результат положительный.

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

Пример - Непозитивный Размер Массива
int input(void);

void add_scalar(int n, int m) {
    int r=0;
    int arr[m][n];
    for (int i=0; i<m; i++) {
        for (int j=0; j<n; j++) {
            arr[i][j] = input();
            r += arr[i][j];
        }
    }
}

void main() {
    add_scalar(2,2);
    add_scalar(-1,2);
    add_scalar(2,0);
}

В этом примере второй и третий вызовы add_scalar привести к отрицательному и нулевому размеру arr.

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

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

Проблема

Испорченный размер массива переменной длины обнаруживает массивы переменной длины (VLA), размер которых получен из небезопасного источника.

Риск

Если атакующий изменил размер вашего VLA на неожиданное значение, это может привести к сбою или неожиданному поведению вашей программы.

Если размер не положителен, поведение VLA не определено. Ваша программа работает не так, как ожидалось.

Если размер не ограничен, VLA может вызвать истощение памяти или переполнение стека.

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

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

Пример - Входной параметр, используемый как размер VLA
#include<stdio.h>
#inclule<stdlib.h>
#define LIM 40

long squaredSum(int size) {

	int tabvla[size];
	long res = 0;
	for (int i=0 ; i<LIM-1 ; ++i) {
		tabvla[i] = i*i;
		res += tabvla[i];
	}
	return res;
}
int main(){
	int size;
	scanf("%d",&size);
	//...
	long result = squaredSum(size);
	//...
	return 0;
}

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

Коррекция - Проверяйте размер VLA

Одной из возможных коррекций является проверка переменной size перед созданием массива переменной длины. Этот пример проверяет, больше ли размер 10 и меньше 100, перед созданием VLA

enum {
    SIZE10  =  10,
    SIZE100 = 100,
    SIZE128 = 128
};

int taintedvlasize(int size) {
    int res = 0;
    if (size>SIZE10 && size<SIZE100) {
        int tabvla[size]; 
        for (int i=0 ; i<SIZE10 ; ++i) {
            tabvla[i] = i*i;
            res += tabvla[i];
        }
    }
    return res;
}

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

Группа: Рек. 08. Управление памятью (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 Университет Карнеги Меллон, с специального разрешения от его Института программной инженерии.

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

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