Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

The C Standard identifies four five distinct situations in which undefined behavior may arise as a result of invoking a function using a declaration that is incompatible with its definition or with incorrect types or numbers of arguments:

UBDescription

26

A pointer is used to call a function whose type is not compatible with the pointed-to the referenced type (6.3.2.3).

38

For a call to a function without a function prototype in scope, the number of arguments does not equal the number of parameters (6.5.2.2).

39

For call to a function without a function prototype in scope where the function is defined with a function prototype, either the prototype ends with an ellipsis or the types of the arguments after promotion are not compatible with the types of the parameters (6.5.2.2).

40

— For a call to a function without a function prototype in scope where the function is not defined with a function prototype, the types of the arguments after promotion are not compatible with those of the parameters after promotion (with certain exceptions) (6.5.2.2).

41

A function is defined with a type that is not compatible with the type (of the expression) pointed to by the expression that denotes the called function (6.5.2.2).

Functions that are appropriately declared (as in DCL07-C. Include the appropriate type information in function declarators) will typically fail compilation if typically result in an error if they are supplied with the wrong number or types of arguments. However, there are cases in which supplying the incorrect arguments to a function will, at best, generate compiler warnings. These warnings should be resolved but do not prevent program compilation. (See MSC00-C. Compile cleanly at high warning levels.)

...

In this noncompliant example, the function copy() is defined to take two three arguments but is called with three two arguments:

Code Block
bgColor#FFCCCC
langc
/* In another source file */
#include <string.h>
void copy(char *dst, const char *src, size_t size) {
  if (!strcpystrncpy(dst, src, size)) {
    /* Report error */
  }
}
 
/* In this source file, no copy prototype in scope */
void copy();
 
void g(const char *s) {
  enum { BUFFERSIZE = 20 };
  char buf[20BUFFERSIZE];
  copy(buf, s, sizeof(buf));  /* Violation */
  /* ... */
}

Compliant Solution

In this compliant solution, the prototype for the copy() function is included in the scope in the source file where it is used, and the copy() function is passed the correct number and type of arguments:

Code Block
bgColor#ccccff
langc
/* In another source file */
#include <string.h>
void copy(char *dst, const char *src, size_t size) {
  if (strcpystrncpy(dst, src, size) == 0) {
    /* Report error */
  }
}
 
/* Copy prototype in scope in this source file */
void copy(char *dst, const char *src, size_t size);
 
void g(const char *s) {
  enum { BUFFERSIZE = 20 };
  char buf[20BUFFERSIZE];
  copy(buf, s, BUFFERSIZE); 
  /* ... */
}

Noncompliant Code Example

In this noncompliant example, the function buginf() is defined to take a variable number of arguments but is declared in another file with no prototype and is calledand expects them all to be signed integers, with a sentinel value of -1:

Code Block
bgColor#FFCCCC
langc
/* In another source file */
void buginf(const char *fmt, ...) {
   /* ... */
}
 
/* In this source file, no buginf prototype in scope */
void buginf();
 
void h(void) {
  buginf("bug in function %s, line %d\n", "h", __LINE__);
  /* ... */
}}

While this code appears to be well-defined due to the prototype-less declaration of buginf(), this code exhibits undefined behavior per subclause 6.5.2.2 paragraph 6 [ISO/IEC 9899:2011]:

If the expression that denotes the called function has a type that does not include a prototype, the integer promotions are performed on each argument, and arguments that have type float are promoted to double. These are called the default argument promotions. If the number of arguments does not equal the number of parameters, the behavior is undefined. If the function is defined with a type that includes a prototype, and either the prototype ends with an ellipsis (, ...) or the types of the arguments after promotion are not compatible with the types of the parameters, the behavior is undefined.

Compliant Solution

In this compliant solution, the prototype for the function buginf() is included in the scope in the source file where it is used:

Code Block
bgColor#ccccff
langc
/* In another source file */
void buginf(const char *fmt, ...) {
   /* ... */
}

/* buginf prototype in scope in this source file */

void buginf(const char *fmt, ...);
 
void h(void) {
  buginf("bug in function %s, line %d\n", "h", __LINE__);
  /* ... */
}

...

Code Block
bgColor#FFCCCC
langc
/* In another source file */
long f(long x) {
  return x < 0 ? -x : x;
}

/* In this source file, no f prototype in scope */
long f();
 
intlong g(int x) {
  return f(x);  /* Violation */
}

Compliant Solution

In this compliant solution, the prototype for the function f() is included in the scope in the source file where it is used, and the function f() is correctly called with an argument of type int long:

Code Block
bgColor#ccccff
langc
/* In another source file */
 
long f(long x) {
  return x < 0 ? -x : x;
}

/* f prototype in scope in this source file */

long f(long x); 

intlong g(longint x) {
  return f((long)x);  
}

Noncompliant Code Example (POSIX)

...

Code Block
bgColor#ccccff
langc
#include <fcntl.h>
 
void func(const char *ms, mode_t perms) {
  /* ... */
  fd = open(ms, O_CREAT|O_EXCL|O_WRONLY|O_TRUNC, perms);
  if (fd == -1){
    /* Handle error */
  }
  /* ... */
}

Risk Assessment

Calling a function with incorrect arguments can result in unexpected or unintended program behavior.

...

[CVE]CVE-2006-1174
[ISO/IEC 9899:2011]Subclause 6.3.2.3, "Pointers"
Subclause 6.5.2.2, "Function Calls"
[Spinellis 2006]Section 2.6.1, "Incorrect Routine or Arguments"

...