CERT C: Rule EXP47-C

Не вызывайте va_arg с аргументом неправильного типа

Описание

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

Не вызывайте va_arg с аргументом неправильного типа.[1]

Реализация Polyspace

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

  • В va_arg передан неправильный тип данных.

  • Слишком много va_arg вызовов для текущего списка аргументов.

Примеры

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

Проблема

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

Например, вы передаете unsigned char аргумент в виде вариадной функции func. Из-за повышения аргумента по умолчанию аргумент повышается до int. Когда вы используете va_arg вызов, который читает unsigned char аргумент, происходит несоответствие типа.

void func (int n, ...) {
   //...   
   va_list args;
   va_arg(args, unsigned char);
   //...   
}

void main(void) {
   unsigned char c;
   func(1,c);
}

Риск

В вариадной функции (функция с переменным количеством аргументов) вы используете va_arg чтобы считать каждый аргумент из списка аргументов переменной (va_list). The va_arg использование не гарантирует, что действительно существует аргумент для чтения или что тип данных аргумента соответствует типу данных в va_arg вызов. Вы должны убедиться, что оба условия верны.

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

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

Убедитесь, что тип данных аргумента, переданного вариадной функции, совпадает с типом данных в va_arg вызов.

Аргументы вариативной функции подвергаются продвижению аргументов по умолчанию. Типы данных аргументов вариадной функции не могут быть определены из прототипа. Аргументы таких функций передаются по умолчанию (см. разделы 6.5.2.2 и 7.15.1.1 в C99 стандарте). Целочисленные аргументы проходят целочисленное повышение и аргументы типа float повышены до double. Для целочисленных аргументов, если тип данных может быть представлен int, для образца, char или short, он повышается до int. В противном случае он повышается до unsigned int. Все другие аргументы не проходят продвижение.

Чтобы избежать неопределенного и определяемого реализацией поведения, минимизируйте использование вариадных функций. Используйте шашки для MISRA C:2012 Rule 17.1 или MISRA C++:2008 Rule 8-4-1 для обнаружения использования вариадных функций.

Пример - char Используется в качестве типа аргумента функции и va_arg аргумент
#include <stdarg.h>
#include <stdio.h>

unsigned char func(size_t count, ...) {
    va_list ap;
    unsigned char result = 0;
    va_start(ap, count);
    if (count > 0) {
        result = va_arg(ap, unsigned char);
    }
    va_end(ap);
    return result;
}

void func_caller(void) {
    unsigned char c = 0x12;
    (void)func(1, c);
}

В этом примере func принимает unsigned char аргумент, который по умолчанию продвигается в int. Тип данных в va_arg вызов все еще unsigned char, который не соответствует int тип аргумента.

Коррекция - Использование int как va_arg Аргумент

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

#include <stdarg.h>
#include <stdio.h>

unsigned char func(size_t count, ...) {
    va_list ap;
    unsigned char result = 0;
    va_start(ap, count);
    if (count > 0) {
        result = va_arg(ap, int); 
    }
    va_end(ap);
    return result;
}

void func_caller(void) {
    unsigned char c = 0x12;
    (void)func(1, c); 
}
Проблема

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

Слишком много va_arg вызовов для текущего списка аргументов не вызывает дефекта, когда:

  • Количество вызовов в va_arg внутри вариадная функция неопределенна. Для примера, если вызовы выполняются из внешнего источника.

  • The va_list используется в va_arg недопустимо.

Риск

Когда вы звоните va_arg и нет следующего аргумента, доступного в va_list, поведение не определено. Вызов va_arg может повредить данные или вернуть неожиданный результат.

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

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

Пример - Аргумент недоступен при вызове va_arg
#include <stdarg.h>
#include <stddef.h>
#include <math.h>

/* variadic function defined with
* one named argument 'count'
*/
int variadic_func(int count, ...) {
    int result = -1;
    va_list ap;
    va_start(ap, count);
    if (count > 0) {
        result = va_arg(ap, int);
        count --;
        if (count > 0) {
/* No further argument available 
* in va_list when calling va_arg
*/	

            result += va_arg(ap, int);
        }
    }
    va_end(ap);
    return result;
}

void func(void) {

    (void)variadic_func(2, 100); 

}

В этом примере именованный аргумент и только один вариадный аргумент передаются в variadic_func() когда он вызывается внутрь func(). При втором вызове va_arg, дальнейший вариативный аргумент не доступен в ap и поведение не определено.

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

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

#include <stdarg.h>
#include <stddef.h>
#include <math.h>

/* variadic function defined with
* one named argument 'count'
*/

int variadic_func(int count, ...) {
    int result = -1;
    va_list ap;
    va_start(ap, count);
    if (count > 0) {
        result = va_arg(ap, int);
        count --;
        if (count > 0) {

/* The correct number of arguments is
* passed to va_list when variadic_func()
* is called inside func()
*/			
            result += va_arg(ap, int); 
        }
    }
    va_end(ap);
    return result;
}

void func(void) {

    (void)variadic_func(2, 100, 200); 

} 

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

Группа: Правило 03. Выражения (EXP)
Введенный в R2019a

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

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

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