CERT C++: EXP37-C

Функции вызова с правильным номером и типом аргументов

Описание

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

Функции вызова с правильным номером и типом аргументов.[1]

Реализация Polyspace

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

  • Неправильный режим доступа к файлам или состояние.

  • Ненадежное приведение указателя на функцию.

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

  • Несоответствие объявления функции

Примеры

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

Проблема

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

  • Режимы доступа включают O_RDONLY, O_WRONLY, и O_RDWR

  • Флаги создания файлов включают O_CREAT, O_EXCL, O_NOCTTY, и O_TRUNC.

  • Флаги состояния файлов включают O_APPEND, O_ASYNC, O_CLOEXEC, O_DIRECT, O_DIRECTORY, O_LARGEFILE, O_NOATIME, O_NOFOLLOW, O_NONBLOCK, O_NDELAY, O_SHLOCK, O_EXLOCK, O_FSYNC, O_SYNC и так далее.

Дефект может возникнуть в следующих ситуациях.

СитуацияРискЗафиксировать

Вы передаете пустой или недопустимый режим доступа к fopen функция.

Согласно ANSI® Стандарт C, допустимые режимы доступа для fopen являются:

  • r, r+

  • w, w+

  • a, a+

  • rb, wb, ab

  • r+b, w+b, a+b

  • rb+, wb+, ab+

fopen имеет неопределенное поведение для недопустимых режимов доступа.

Некоторые реализации допускают расширение режима доступа, такого как:

  • GNU®: rb+cmxe,ccs=utf

  • Визуальный C++®: a+t, где t задает текстовый режим.

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

Передайте допустимый режим доступа к fopen.
Вы передаете флаг состояния O_APPEND на open функция, не объединяя ее ни с одним из O_WRONLY или O_RDWR.

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

The open функция не возвращает -1 для этой логической ошибки.

Пройдите или O_APPEND|O_WRONLY или O_APPEND|O_RDWR как режим доступа.
Вы передаете флаги состояния O_APPEND и O_TRUNC вместе с open функция.

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

The open функция не возвращает -1 для этой логической ошибки.

В зависимости от того, что вы намерены сделать, передайте один из двух режимов.
Вы передаете флаг состояния O_ASYNC на open функция. В некоторых реализациях режим O_ASYNC не включает операции ввода-вывода, управляемые сигналом.Используйте fcntl(pathname, F_SETFL, O_ASYNC); вместо этого.

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

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

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

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

Пример - Недопустимый режим доступа с fopen
#include <stdio.h>

void func(void) {
    FILE *file = fopen("data.txt", "rw");
    if(file!=NULL) {
        fputs("new data",file);
        fclose(file);
    }
}

В этом примере режим доступа rw недопустимо. Потому что r указывает, что вы открываете файл для чтения и w указывает, что вы создаете новый файл для записи, два режима доступа несовместимы.

Коррекция - используйте любой из r или w как режим доступа

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

#include <stdio.h>

void func(void) {
    FILE *file = fopen("data.txt", "w");
    if(file!=NULL) {
        fputs("new data",file);
        fclose(file);
    }
}
Проблема

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

Этот дефект применяется только в том случае, если языком кода для проекта является C.

Риск

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

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

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

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

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

Пример - Ненадежное приведение ошибки указателя на функцию
#include <stdio.h>
#include <math.h>
#include <stdio.h>
#define PI 3.142
 
double Calculate_Sum(int (*fptr)(double))
{
    double  sum = 0.0;
    double  y;
	  
    for (int i = 0;  i <= 100;  i++)
    {
        y = (*fptr)(i*PI/100);
        sum += y;
    }
    return sum / 100;
}
 
int main(void)
{
    double  (*fp)(double);      
    double  sum;
 
    fp = sin;
    sum = Calculate_Sum(fp); 
    /* Defect: fp implicitly cast to int(*) (double) */

    printf("sum(sin): %f\n", sum);
    return 0;
}

Указатель на функцию fp объявлен как double (*)(double). Однако, проходя его, чтобы функционировать Calculate_Sum, fp неявно приведено к int (*)(double).

Коррекция - Избегайте приведения указателя на функцию

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

#include <stdio.h>
#include <math.h>
#include <stdio.h>
# define PI 3.142
 
/*Fix: fptr has same argument and return type everywhere*/
double Calculate_Sum(double (*fptr)(double)) 
{
    double  sum = 0.0;
    double y;
	    
    for (int i = 0;  i <= 100;  i++)
    {
        y = (*fptr)(i*PI/100);
        sum += y;
    }
    return sum / 100;
}
 
int main(void)
{
    double  (*fp)(double);      
    double  sum;
 
    
    fp = sin;
    sum = Calculate_Sum(fp);
    printf("sum(sin): %f\n", sum);
 
    return 0;
}
Проблема

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

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

Тип функцииСитуацияРискЗафиксировать
Функции манипуляции строками, такие как strlen и strcpyАргументы указателя не указывают на NULL-terminated string.Поведение функции не определено.Передайте NULL-terminated string to string mandipulation functions.
Функции обработки файлов в stdio.h такие как fputc и freadThe FILE* аргумент указателя может иметь значение NULL.Поведение функции не определено.Протестируйте FILE* указатель на NULL перед использованием в качестве аргумента функции.
Функции обработки файлов в unistd.h такие как lseek и read Аргумент дескриптора файла может быть -1.

Поведение функции не определено.

Большинство реализаций open функция возвращает значение дескриптора файла -1. В сложение задают errno чтобы указать, что при открытии файла произошла ошибка.

Проверьте возвращаемое значение open функция для -1 перед использованием ее в качестве аргумента для read или lseek.

Если возврат значение -1, проверьте значение errno чтобы увидеть, какая ошибка произошла.

Аргумент дескриптора файла представляет дескриптор закрытого файла.Поведение функции не определено.Закройте дескриптор файла только после того, как вы полностью закончите его использование. Кроме того, повторно откройте дескриптор файла перед использованием его в качестве аргумента функции.
Функции генерации имен директорий, такие как mkdtemp и mkstempsПоследние шесть символов шаблона строки не XXXXXX.Функция заменяет последние шесть символов строкой, которая делает имя файла уникальным. Если последние шесть символов не XXXXXXфункция не может сгенерировать достаточно уникальное имя директории.Проверяйте, XXXXXX ли последние шесть символов строки перед использованием строки в качестве аргумента функции.
Функции, связанные с переменными окружения, такими как getenv и setenvСтроковый аргумент "".Поведение определяется реализацией.Протестируйте строковый аргумент для "" прежде чем использовать его как getenv или setenv аргумент.
Строковый аргумент заканчивается знаком равенства, =. Для образца, "C=" вместо "C".Поведение определяется реализацией.Не завершайте строковый аргумент =.
Функции обработки строк, такие как strtok и strstr

  • strtok: Разделитель аргумента "".

  • strstr: Аргумент строки поиска "".

Некоторые реализации не обрабатывают эти краевые случаи.Проверьте строку на "" перед использованием в качестве аргумента функции.

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

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

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

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

Пример - NULL Указатель прошел как strnlen Аргумент
#include <string.h>
#include <stdlib.h>

enum {
    SIZE10 = 10,
    SIZE20 = 20
};

int func() {
    char* s = NULL;
    return strnlen(s, SIZE20);
}

В этом примере a NULL указатель передается следующим strnlen аргумент вместо NULL-terminated string.

Перед выполнением анализа кода укажите компилятор GNU. См. Compiler (-compiler).

Коррекция - Прохождение NULL-терминатная строка

Передайте NULL-терминатная строка как первый аргумент strnlen.

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

enum {
    SIZE10 = 10,
    SIZE20 = 20
};

int func() {
    char* s = "";
    return strnlen(s, SIZE20);
}
Проблема

Примечание

В коде С++ эта проверка применяется к функциям, которые заданы как extern "C".

Несоответствие объявления функции происходит, когда прототип extern "C" функция не соответствует ее определению. Несоответствие типов аргументов определения функции и прототипа функции может зависеть от вашего окружения. Polyspace® рассматривает два типа как совместимые, если они имеют одинаковый размер и сигнальность в окружении, которую вы используете. Например, если вы задаете -target как i386, Polyspace рассматривает long и int как совместимые типы.

В C++, если функция не задана как extern "C" и его прототип не соответствует никакому определению функции, компилятор обрабатывает прототип как образец неопределенной перегрузки функции. Polyspace не помечает вызовы таких неопределенных функций.

Чекер не помечает эту проблему в Polyspace по умолчанию во время анализа You Code. Смотрите Checkers Deactivated in Polyspace как You Code Default Analysis (Polyspace Bug Finder Access).

Риск

Несоответствие объявления функции может привести к неопределенному поведению. Когда объявления функций заданы как extern "C"несоответствия между определением и объявлением функции могут вызывать только предупреждения во время компиляции, в результате чего код компилируется, но ведет себя неожиданным образом.

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

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

  • Избегайте любого несоответствия между числовыми аргументами в объявлении прототипа функции и определении функции.

  • Избегайте любого несоответствия между типами аргументов объявления прототипа функции и определением функции.

Пример - Несоответствующие вызовы функции
// file1.c
extern "C" void foo(int iVar){
	//...
}
extern "C" void bar(int iVar){
	//...
}
extern "C" void fubar(int A, ...){
	//...
}


//prototype.h
extern "C" void foo(void);
extern "C" void fubar(int A, ...);
extern "C" void bar(long iVar);
//file2.c
//file2.c
#include"prototype.h"
void call_funcs(){
	int iTemp;
	float fTemp;
	long lTemp;
	foo(); //Noncompliant
	bar(lTemp);//Noncompliant in x86_64
	fubar(iTemp,fTemp);//Compliant
	
}

В этом примере функции foo, bar, и fubar определяются в файле file1.c. Их прототипы заявлены в prototype.h. Затем эти функции вызываются в файле file2.c.

  • Функция foo определяется как int аргумент, но его прототип объявлен без какого-либо аргумента. Из-за этого несоответствия Polyspace помечает вызов функции.

  • Функция bar определяется как int аргумент, но его прототип объявлен с long аргумент. Эти два типа несовместимы с x86_64 окружение. Когда вы задаете -target как x86_64, Polyspace помечает вызов функции.

  • Вызов вариадной функции fubar является совместимым, поскольку его сигнатура вызова, прототип и определение совпадают.

Коррекция - совместимые вызовы функций

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

// file1.c
extern "C" void foo(int iVar){
	//...
}
extern "C" void bar(int iVar){
	//...
}
extern "C" void fubar(int A, ...){
	//...
}


//prototype.h
extern "C" void foo(int);
extern "C" void fubar(int A, ...);
extern "C" void bar(int iVar);
//file2.c
//file2.c
#include"prototype.h"
void call_funcs(){
	int iTemp;
	float fTemp;
	long lTemp;
	foo(iTemp); //Compliant
	bar(iTemp);//Compliant in x86_64
	fubar(iTemp,fTemp);//Compliant
	
}

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

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

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

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