Main Content

CWE Rule 628

Function Call with Incorrectly Specified Arguments

Since R2024a

Description

Rule Description

The product calls a function, procedure, or routine with arguments that are not correctly specified, leading to always-incorrect behavior and resultant weaknesses.

Polyspace Implementation

The rule checker checks for these issues:

  • Bad file access mode or status

  • Copy of overlapping memory

  • Invalid va_list argument

  • Modification of internal buffer returned from non-reentrant standard function

  • Standard function call with incorrect arguments

Examples

expand all

Issue

This issue 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.

SituationRiskFix

You pass an empty or invalid access mode to the fopen function.

According to the ANSI® C standard, the valid access modes for fopen are:

  • r,r+

  • w,w+

  • a,a+

  • rb, wb, ab

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

  • rb+, wb+, ab+

fopen has undefined behavior for invalid access modes.

Some implementations allow extension of the access mode such as:

  • GNU®: rb+cmxe,ccs=utf

  • Visual C++®: a+t, where t specifies a text mode.

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.

O_APPEND indicates that you intend to add new content at the end of a file. However, without O_WRONLY or O_RDWR, you cannot write to the file.

The open function does not return -1 for this logical error.

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.

O_APPEND indicates that you intend to add new content at the end of a file. However, O_TRUNC indicates that you intend to truncate the file to zero. Therefore, the two modes cannot operate together.

The open function does not return -1 for this logical error.

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.

Fix

The fix depends on the function and the flags used. See fixes in the table above and code examples with fixes below.

If you do not want to fix the issue, add comments to your result or code to avoid another review. See:

Example — Invalid Access Mode with fopen
#include <stdio.h>

void func(void) {
    FILE *file = fopen("data.txt", "rw"); //Noncompliant
    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.

Correction — Use Either r or w as Access Mode

One 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);
    }
}
Issue

This issue occurs when there is a memory overlap between the source and destination argument of a copy function such as memcpy or strcpy. For instance, the source and destination arguments of strcpy are pointers to different elements in the same string.

Risk

If there is memory overlap between the source and destination arguments of copy functions, according to C standards, the behavior is undefined.

Fix

Determine if the memory overlap is what you want. If so, find an alternative function. For instance:

  • If you are using memcpy to copy values from one memory location to another, use memmove instead of memcpy.

  • If you are using strcpy to copy one string to another, use memmove instead of strcpy, as follows:

    s = strlen(source);
    memmove(destination, source, s + 1);

    strlen determines the string length without the null terminator. Therefore, you must move s+1 bytes instead of s bytes.

Example — Overlapping Copy
#include <string.h>

char str[] = {"ABCDEFGH"};

void my_copy() {
    strcpy(&str[0],(const char*)&str[2]); //Noncompliant
}

In this example, because the source and destination argument are pointers to the same string str, there is memory overlap between their allowed buffers.

Issue

This issue occurs when you use a va_list variable as an argument to a function in the vprintf group but:

  • You do not initialize the variable previously using va_start or va_copy.

  • You invalidate the variable previously using va_end and do not reinitialize it.

For instance, you call the function vsprintf as vsprintf (buffer,format, args). However, before the function call, you do not initialize the va_list variable args using either of the following:

  • va_start(args, paramName). paramName is the last named argument of a variable-argument function. For instance, for the function definition void func(int n, char c, ...) {}, c is the last named argument.

  • va_copy(args, anotherList). anotherList is another valid va_list variable.

Risk

The behavior of an uninitialized va_list argument is undefined. Calling a function with an uninitialized va_list argument can cause stack overflows.

Fix

Before using a va_list variable as function argument, initialize it with va_start or va_copy.

Clean up the variable using va_end only after all uses of the variable.

Example — va_list Variable Used Following Call to va_end
#include <stdarg.h>
#include <stdio.h>

int call_vfprintf(int line, const char *format, ...) {
    va_list ap;
    int r=0;
    
    va_start(ap, format);
    r = vfprintf(stderr, format, ap);
    va_end(ap);

    r += vfprintf(stderr, format, ap); //Noncompliant
    return r;
}

In this example, the va_list variable ap is used in the vfprintf function, after the va_end macro is called.

Correction — Call va_end After Using va_list Variable

One possible correction is to call va_end only after all uses of the va_list variable.

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

int call_vfprintf(int line, const char *format, ...) {
    va_list ap;
    int r=0;
    
    va_start(ap, format);
    r = vfprintf(stderr, format, ap);
    r += vfprintf(stderr, format, ap);
    va_end(ap);
    
    return r;
}
Issue

This issue occurs when the following happens:

  • A nonreentrant standard function returns a pointer.

  • You attempt to write to the memory location that the pointer points to.

Nonreentrant standard functions that return a non const-qualified pointer to an internal buffer include getenv, getlogin, crypt, setlocale, localeconv, strerror and others.

Risk

Modifying the internal buffer that a nonreentrant standard function returns can cause the following issues:

  • It is possible that the modification does not succeed or alters other internal data.

    For instance, getenv returns a pointer to an environment variable value. If you modify this value, you alter the environment of the process and corrupt other internal data.

  • Even if the modification succeeds, it is possible that a subsequent call to the same standard function does not return your modified value.

    For instance, you modify the environment variable value that getenv returns. If another process, thread, or signal handler calls setenv, the modified value is overwritten. Therefore, a subsequent call to getenv does not return your modified value.

Fix

Avoid modifying the internal buffer using the pointer returned from the function.

Example — Modification of getenv Return Value
#include <stdlib.h>
#include <string.h>

void printstr(const char*);

void func() {
    char* env = getenv("LANGUAGE");
    if (env != NULL) {
        strncpy(env, "C", 1); //Noncompliant
        printstr(env);
    }
}

In this example, the first argument of strncpy is the return value from a nonreentrant standard function getenv. The behavior can be undefined because strncpy modifies this argument.

Correction - Copy Return Value of getenv and Modify Copy

One possible solution is to copy the return value of getenv and pass the copy to the strncpy function.

#include <stdlib.h>
#include <string.h>
enum {
    SIZE20 = 20
};

void printstr(const char*);

void func() {
    char* env = getenv("LANGUAGE");
    if (env != NULL) {
        char env_cp[SIZE20];
        strncpy(env_cp, env, SIZE20);  
        strncpy(env_cp, "C", 1);        
        printstr(env_cp);
    }
}
Issue

This issue 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 TypeSituationRiskFix
String manipulation functions such as strlen and strcpyThe 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 freadThe 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 open function return a file descriptor value of -1. In addition, they set errno to indicate that an error has occurred when opening a file.

Test the return value of the open function for -1 before using it as argument for read or lseek.

If the return value is -1, check the value of errno to see which error has occurred.

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 mkstempsThe 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 setenvThe 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

  • strtok: The delimiter argument is "".

  • strstr: The search string argument is "".

Some implementations do not handle these edge cases.Test the string for "" before using it as function argument.

Fix

The fix depends on the root cause of the defect. See fixes in the table above and code examples with fixes below.

If you do not want to fix the issue, add comments to your result or code to avoid another review. See:

Example — 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); //Noncompliant
}

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).

Correction — Pass NULL-terminated String

Pass 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);
}

Check Information

Category: Bad Coding Practices

Version History

Introduced in R2024a