Call functions with the correct number and type of arguments
Call functions with the correct number and type of arguments.[1]
This checker checks for these issues:
Bad file access mode or status.
Unreliable cast of function pointer.
Standard function call with incorrect arguments.
Unsupported complex arguments
Function declaration mismatch
Bad file access mode or status occurs
when you use functions in the fopen or open group
with invalid or incompatible file access modes, file creation flags,
or file status flags as arguments. For instance, for the open function,
examples of valid:
Access modes include O_RDONLY, O_WRONLY,
and O_RDWR
File creation flags include O_CREAT, O_EXCL, O_NOCTTY,
and O_TRUNC.
File status flags include 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 and
so on.
The defect can occur in the following situations.
| Situation | Risk | Fix |
|---|---|---|
You pass an empty or invalid access mode to the According
to the ANSI® C standard, the valid access modes for
|
Some implementations allow extension of the access mode such as:
However, your access mode string must begin with one of the valid sequences. | Pass a valid access mode to fopen. |
You pass the status flag O_APPEND to the open function
without combining it with either O_WRONLY or O_RDWR. |
The | Pass either O_APPEND|O_WRONLY or O_APPEND|O_RDWR as
access mode. |
You pass the status flags O_APPEND and O_TRUNC together
to the open function. |
The | Depending on what you intend to do, pass one of the two modes. |
You pass the status flag O_ASYNC to the open function. | On certain implementations, the mode O_ASYNC does
not enable signal-driven I/O operations. | Use the fcntl(pathname, F_SETFL, O_ASYNC); instead. |
The fix depends on the root cause of the defect. Often the result details show a sequence of events that led to the defect. You can implement the fix on any event in the sequence. If the result details do not show the event history, you can trace back using right-click options in the source code and see previous related events. See also .
See examples of fixes below.
If you do not want to fix the issue, add comments to your result or code to avoid another review. See Address Results in Polyspace Access Through Bug Fixes or Justifications.
fopen#include <stdio.h>
void func(void) {
FILE *file = fopen("data.txt", "rw");
if(file!=NULL) {
fputs("new data",file);
fclose(file);
}
}In this example, the access mode rw is invalid.
Because r indicates that you open the file for
reading and w indicates that you create a new file
for writing, the two access modes are incompatible.
r or w as
Access ModeOne possible correction is to use the access mode corresponding to what you intend to do.
#include <stdio.h>
void func(void) {
FILE *file = fopen("data.txt", "w");
if(file!=NULL) {
fputs("new data",file);
fclose(file);
}
}Unreliable cast of function pointer occurs when a function pointer is cast to another function pointer that has different argument or return type.
This defect applies only if the code language for the project is C.
If you cast a function pointer to another function pointer with different argument or return type and then use the latter function pointer to call a function, the behavior is undefined.
Avoid a cast between two function pointers with mismatch in argument or return types.
See examples of fixes below.
If you do not want to fix the issue, add comments to your result or code to avoid another review. See Address Results in Polyspace Access Through Bug Fixes or Justifications.
#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;
}The function pointer fp is
declared as double (*)(double). However in passing
it to function Calculate_Sum, fp is
implicitly cast to int (*)(double).
One possible correction is to check that the
function pointer in the definition of Calculate_Sum has
the same argument and return type as fp. This step
makes sure that fp is not implicitly cast to a
different argument or return type.
#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;
}Standard function call with incorrect arguments occurs when the arguments to certain standard functions do not meet the requirements for their use in the functions.
For instance, the arguments to these functions can be invalid in the following ways.
| Function Type | Situation | Risk | Fix |
|---|---|---|---|
String manipulation functions such as strlen and strcpy | The pointer arguments do not point to a NULL-terminated
string. | The behavior of the function is undefined. | Pass a NULL-terminated string to string
manipulation functions. |
File handling functions in stdio.h such
as fputc and fread | The FILE* pointer argument can have the
value NULL. | The behavior of the function is undefined. | Test the FILE* pointer for NULL before
using it as function argument. |
File handling functions in unistd.h such
as lseek and read | The file descriptor argument can be -1. | The behavior of the function is undefined. Most
implementations of the | Test the return value of the If
the return value is -1, check the value of |
| The file descriptor argument represents a closed file descriptor. | The behavior of the function is undefined. | Close the file descriptor only after you have completely finished using it. Alternatively, reopen the file descriptor before using it as function argument. | |
Directory name generation functions such as mkdtemp and mkstemps | The last six characters of the string template are not XXXXXX. | The function replaces the last six characters with a string
that makes the file name unique. If the last six characters are not XXXXXX,
the function cannot generate a unique enough directory name. | Test if the last six characters of a string are XXXXXX before
using the string as function argument. |
Functions related to environment variables such
as getenv and setenv | The string argument is "". | The behavior is implementation-defined. | Test the string argument for "" before using
it as getenv or setenv argument. |
The string argument terminates with an equal sign, =.
For instance, "C=" instead of "C". | The behavior is implementation-defined. | Do not terminate the string argument with =. | |
String handling functions such as strtok and strstr |
| Some implementations do not handle these edge cases. | Test the string for "" before using it as
function argument. |
The fix depends on the root cause of the defect. Often the result details show a sequence of events that led to the defect. You can implement the fix on any event in the sequence. If the result details do not show the event history, you can trace back using right-click options in the source code and see previous related events. See also .
See examples of fixes below.
If you do not want to fix the issue, add comments to your result or code to avoid another review. See Address Results in Polyspace Access Through Bug Fixes or Justifications.
NULL Pointer Passed as strnlen Argument#include <string.h>
#include <stdlib.h>
enum {
SIZE10 = 10,
SIZE20 = 20
};
int func() {
char* s = NULL;
return strnlen(s, SIZE20);
}
In this example, a NULL pointer is passed
as strnlen argument instead of a NULL-terminated
string.
Before running analysis on the code, specify a
GNU compiler. See Compiler
(-compiler).
For more information on analysis options, see the documentation for
Polyspace®
Bug Finder™ or Polyspace
Bug Finder
Server™.
NULL-terminated
StringPass a NULL-terminated string as the first
argument of strnlen.
#include <string.h>
#include <stdlib.h>
enum {
SIZE10 = 10,
SIZE20 = 20
};
int func() {
char* s = "";
return strnlen(s, SIZE20);
}
Unsupported complex arguments occurs when
these functions are called with a complex argument:
atan2
erf
fdim
fmin
ilogb
llround
logb
nextafter
rint
tgamma
cbrt
erfc
floor
fmod
ldexp
log10
lrint
nexttoward
round
trunc
ceil
exp2
fma
frexp
lgamma
log1p
round
remainder
scalbn
copysign
expm1
fmax
hypot
llrint
log2
nearbyint
remquo
scalbln
Calling any of the preceding functions with a complex
argument is undefined behavior in the C++ standard, which might lead to
unexpected results. Because some mathematical functions support
complex arguments while the functions in the preceding
list do not, the unexpected results might be difficult to debug. Performing some
of these mathematical operations on a complex number might
not be mathematically sound, which indicates an issue in the underlying logic of
your code.
Avoid calling the preceding functions with a complex input
argument. To perform the preceding mathematical operations on a complex number,
define alternative functions that support complex arguments.
log2 and trunc
Functions with Complex Arguments#include <complex.h>
#include <tgmath.h>
typedef double complex cDouble;
cDouble Noncompliant (void)
{
cDouble Z = 2.0 + 4.0 * I;
cDouble result = log2 (Z); //Noncompliant
return trunc(result);//Noncompliant
}In this example, the function Noncompliant calculates the
base two logarithm of a complex number, truncates the result, and returns it.
The functions log2 and trunc do not
support a complex argument. Polyspace flags these operations.
One possible correction is to define alternative functions that support
complex numbers. For instance, while log2 does not support
complex numbers, the function log
does. Define a function complexLog2 that uses
log to calculate the base two logarithm of a complex
number. Similarly, trunc does not support
complex numbers and the mathematical rule for truncating
a complex number is not well-defined. Define a function
complexTrunc that truncates a complex number by
truncating its real and imaginary parts separately.
#include <complex.h>
#include <tgmath.h>
typedef double complex cDouble;
cDouble complexLog2(cDouble z) {
return log (z) / log (2); // Compliant
}
cDouble complexTrunc(cDouble z){
return trunc(creal(z)) + I*trunc(cimag(z)); //Compliant
}
cDouble Compliant (void)
{
cDouble Z = 2.0 + 4.0 * I;
cDouble result = complexLog2 (Z); //Compliant
return complexTrunc(result);//Compliant
}Function declaration mismatch occurs when the prototype of a function does not match its definition. If a function lacks a prototype in the file where it is called, Polyspace deduces its prototype based on the signature of the call. If the deduced prototype does not match the definition of the function, Polyspace raises this defect. The prototype of a variadic function cannot be deduced from its function call. If you call a variadic function without specifying its prototype in the same file, Polyspace raises this defect.
When deducing the prototype of a function from a call to such a function, Polyspace makes these assumptions:
The number of arguments of the deduced prototype is equal to the input argument of the function call.
The argument types of the deduced prototype are set by implicitly
promoting the argument types of the function call. For instance,
both signed and unsigned char or
short type arguments are promoted to
int. Similarly float type
arguments are promoted to double.
Type mismatch between the arguments of the function definition and
the function prototype might depend on your environment. Polyspace considers two types as compatible if they have the
same size and signedness in the environment that you use. For
instance, if your specify -target as i386,
Polyspace considers long and
int as compatible types.
The checker does not flag this issue in a default Polyspace as You Code analysis. See Checkers Deactivated in Polyspace as You Code Default Analysis.
According to the C standard, function declaration mismatch might result in undefined behavior even though such code might compile successfully producing only warnings during compilation. Because code with this issue might compile successfully, function declaration mismatches might result in unexpected results that are difficult to diagnose.
Before you call a function, provide its complete prototype, even if you define the function later in the same file.
Avoid any mismatch between the number arguments in the function prototype declaration and the function definition.
Avoid any mismatch between the argument types of the function prototype declaration and the function definition.
When complete prototypes of the called functions are provided, the compiler tries to resolve any function declaration mismatches through implicit casting. If the compiler fails to resolve the mismatch, the compilation fails, which prevents unexpected behavior. To fix such compile errors, call the functions using argument types and numbers that match the function definition.
// file1.c
void foo(int iVar){
//...
}
void bar(float fVar1, float fVar2){
//...
}
void bar2(float fVar1){
//...
}
void fubar(const char* str,...){
//...
}
void foo2(char cVar){
//...
}
void call_variadic(){
fubar("String");
} |
//file2.c
void bar2(float);
void foo2(int);
void call_funcs(){
int iTemp;
float fTemp;
foo();//Noncompliant
bar(fTemp,fTemp);//Noncompliant
fubar("String"); //Noncompliant
bar2(iTemp);//Compliant
foo2(iTemp); //Noncompliant
} |
In this example, the functions foo,
foo2, bar, bar2, and
fubar are defined in the file file1.c.
These functions are then called in the file file2.c.
The function foo is defined in
file1.c with one int input
and called in file2.c without any input. Because
file2.c does not have a prototype for
foo, Polyspace deduces a prototype based on
the call foo(), which takes no input. This
deduced prototype does not match the function declaration in
file1.c. Polyspace flags the call.
The function bar is defined in
file1.c with two float
inputs and called in file2.c with two
float inputs. Because
file2.c does not have a prototype for
bar, Polyspace deduces a prototype based on
the call bar(fTemp,fTemp). By promoting the
argument types of the function call, the signature of this deduced
prototype is bar(double, double), which does not
match the function declaration in file1.c.
Polyspace flags the call.
The function bar2 is defined in
file1.c with one float
input. The complete prototype for bar2, which
matches the definition, is provided in file2.c.
Because a complete prototype is present in this file, when
bar2 is called with an incorrect input, the
compiler implicitly converts the int input
iTemp into a float.
Because the call to the function matches the declaration after an
implicit conversion facilitated by the prototype, Polyspace does not flag the call.
The function foo2 is defined in
file1.c with a char input.
Its prototype in file2.c is defined with a
int input. Because the definition and the
prototype do not match, Polyspace flags the call to foo2.
The variadic function fubar is defined in
file1.c. The call to it in
call_variadic is compliant because the call
comes after the definition. The function fubar
does not have a prototype in file2.c. Because the
function takes a variable number of inputs, its prototype cannot be
deduced. The call to fubar in
file2.c lacks a prototype and Polyspace flags the call.
The fix for this defect is to declare complete prototypes for the called
functions in all compilation modules. It is a best practice to combine the
function prototype declarations in a header file, and then include it in files
where the functions are called. In this case, resolve the flagged issues by
including such a header file prototype.h in
file2.c. Once a correct prototype is declared, the call
foo() in file2.c causes a compilation
failure because the compiler cannot resolve the mismatch between the call and
the declared prototype. Call foo with an
int to resolve the compilation failure.
// file1.c
void foo(int iVar){
//...
}
void bar(float fVar1, float fVar2){
//...
}
void bar2(float fVar1){
//...
}
void fubar(const char* str,...){
//...
}
void foo2(char cVar){
//...
}
void call_variadic(){
fubar("String");
} |
//prototypes.h void foo(int iVar); void bar(float fVar1, float fVar2); void fubar(const char* str,...); void bar2(float); void foo2(char); void call_variadic(void); void call_funcs(void); |
//file2.c
#include"prototype.h"
void call_funcs(){
int iTemp;
float fTemp;
//foo(); This call results in compile failure
foo(iTemp);//Compliant
bar(fTemp,fTemp);//Compliant
fubar("String"); //Compliant
bar2(iTemp);//Compliant
foo2('a'); //Compliant
} | |
| Group: Rule 03. Expressions (EXP) |
[1] This software has been created by MathWorks incorporating portions of: the “SEI CERT-C Website,” © 2017 Carnegie Mellon University, the SEI CERT-C++ Web site © 2017 Carnegie Mellon University, ”SEI CERT C Coding Standard – Rules for Developing safe, Reliable and Secure systems – 2016 Edition,” © 2016 Carnegie Mellon University, and “SEI CERT C++ Coding Standard – Rules for Developing safe, Reliable and Secure systems in C++ – 2016 Edition” © 2016 Carnegie Mellon University, with special permission from its Software Engineering Institute.
ANY MATERIAL OF CARNEGIE MELLON UNIVERSITY AND/OR ITS SOFTWARE ENGINEERING INSTITUTE CONTAINED HEREIN IS FURNISHED ON AN "AS-IS" BASIS. CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY KIND, EITHER EXPRESSED OR IMPLIED, AS TO ANY MATTER INCLUDING, BUT NOT LIMITED TO, WARRANTY OF FITNESS FOR PURPOSE OR MERCHANTABILITY, EXCLUSIVITY, OR RESULTS OBTAINED FROM USE OF THE MATERIAL. CARNEGIE MELLON UNIVERSITY DOES NOT MAKE ANY WARRANTY OF ANY KIND WITH RESPECT TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT.
This software and associated documentation has not been reviewed nor is it endorsed by Carnegie Mellon University or its Software Engineering Institute.