JPCERT コーディネーションセンター

EXP37-C. 正しい引数の数と型で関数を呼び出す

関数を呼び出す際の引数の数または型を間違えないこと。

C 標準では、定義に適合しない宣言を使用して、または正しくない型や数の引数を使用して関数を呼び出した結果として未定義の動作が発生する 4 つの状況を示している。

UB 説明

26

ポインタを使用して、ポイント対象型と適合しない型を持つ関数を呼び出す (6.3.2.3)。

38

範囲内の関数のプロトタイプを持たない関数呼び出しの場合、引数の数はパラメータの数と等しくない (6.5.2.2)。

39

範囲内の関数のプロトタイプを持たない関数呼び出しで、関数が関数のプロトタイプを使用して定義されている場合、プロトタイプが省略記号で終わるか、拡張後の引数の型がパラメータの型に適合しない (6.5.2.2)。

41

関数が、呼び出された関数を指定している式で参照される(式の)型と互換性がない型で定義されている(6.5.2.2)。

通常、適切に宣言された関数は(「DCL07-C. 関数宣言子には適切な型情報を含める」を参照)、引数の数や型が誤って指定されるとコンパイルに失敗する。しかし、関数に誤った引数を指定しても、コンパイラの警告が出るにとどまる場合もある。このような警告は解決する必要があるが、プログラムのコンパイルは通ってしまう(「MSC00-C. 厳しい警告レベルで警告を出さずにコンパイルする」を参照)。

違反コード

以下のコード例では、C 標準ライブラリ関数 strchr() は引数の型が適切でない関数ポインタ fp を通して呼び出される。C 標準 [ISO/IEC 9899:2011] によれば:

ある型の関数へのポインタを、別の型の関数へのポインタに変換することができる。さらに再び型変換で元の型に戻すことができるが、その結果は元のポインタと比較して等しくなければならない。型変換されたポインタを関数呼び出しに用い、関数の型がポインタが指すものの型と適合しない場合、その動作は未定義とする。

(C 標準の附属書 J 「未定義の動作」の 26 も参照すること。)

#include <stdio.h>
#include <string.h>

char *(*fp) ();

int main(void) {
  char *c;
  fp = strchr;
  c = fp(12, 2);
  printf("%s\n", c);
  return 0;
}
適合コード

以下のコード例では、関数ポインタ fp は適切な数と型の引数を使用し、char * を返す関数を指している。

#include <string.h>

char *(*fp) (const char *, int);

int main(void) {
  char *c;
  fp = strchr;
  c = fp("Hello",'H');
  printf("%s\n", c);
  return 0;
}
違反コード

以下のコード例では、関数 copy() は 2 つの引数をとるよう定義されているが、3 つの引数を使用して呼び出されている。

/* 別のソースファイルで */
void copy(char *dst, const char *src) {
  if (!strcpy(dst, src)) {
    /* エラーをレポート */
  }
}
 
/* このソースファイルで -- copy のプロトタイプが範囲内でない */
void copy();
 
void g(const char *s) {
  char buf[20];
  copy(buf, s, sizeof buf);  /* 違反 */
  /* ... */
}
適合コード

以下のコード例では、copy() 関数のプロトタイプは使用されるソースファイルの範囲内に含まれ、copy() 関数は正しい数と型の引数を渡されている。

/* 別のソースファイルで */
void copy(char *dst, const char *src) {
  if (!strcpy(dst, src)) {
    /* エラーをレポート */
  }
}
 
/* このソースファイルでは copy のプロトタイプが範囲内  */
void copy(char *dst, const char *src);
 
void g(const char *s) {
  char buf[20];
  copy(buf, s); 
  /* ... */
}
違反コード

以下のコード例では、関数 buginf() は可変数の引数をとるよう定義されているが、プロトタイプなしで別のファイル内で宣言され、呼び出されている。

/* 別のソースファイルで */
void buginf(const char *fmt, ...) {
   /* ... */
}

/* このソースファイルで -- buginf のプロトタイプが範囲内でない */
void buginf();
 
void h(void) {
  buginf("bug in function %s, line %d\n", __func__, __LINE__);  /* 違反 */
  /* ... */
}
適合コード

以下のコード例では、関数のプロトタイプ buginf() は使用されるソースファイルの範囲内に含まれる。

/* 別のソースファイルで */
void buginf(const char *fmt, ...) {
   /* ... */
}

/* このソースファイルでは buginf のプロトタイプが範囲内 */

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

以下のコード例では、関数 f()long 型の引数をとるよう定義されているが、int 型の引数を使用して別のファイルから呼び出されている。

/* 別のソースファイルで */
 
long f(long x) {
  return x < 0 ? -x : x;
}

/* このソースファイルで -- f のプロトタイプが範囲内でない */
 
int g(int x) {
  return f(x);  /* 違反 */
}
適合コード

以下のコード例では、f() 関数のプロトタイプは使用されるソースファイルの範囲内に含まれ、f() 関数は int 型の引数を使用して正しく呼び出されている。

/* 別のソースファイルで */
 
long f(long x) {
  return x < 0 ? -x : x;
}

/* このソースファイルでは f のプロトタイプが範囲内 */

long f(long x); 

int g(long x) {
  return f(x);  
}
違反コード (POSIX)

POSIX 関数 open() は次のようなプロトタイプを持つ可変引数関数である [Open Group 2004]。

int open(const char *path, int oflag, ... );

open() 関数は第3引数を取り、新しく作成されたファイルのアクセスモードを決定する。open() を使用して新しいファイルを作成し、第3引数を省略した場合、作成されるファイルのパーミッションは意図しないものになることがある(「FIO06-C. 適切なパーミッションを持つファイルを作成する」を参照)。

以下に示す shadow-utils パッケージの useradd() 関数における脆弱性 CVE-2006-1174 の例では、open() の第3引数が誤って省略されている。

fd = open(ms, O_CREAT|O_EXCL|O_WRONLY|O_TRUNC);

新規ファイルを作成するのでない場合(つまり O_CREAT フラグをつけないで)、open() 関数に第3引数を渡すのは厳密には正しくない。POSIX 実装によってはこの場合 EINVAL エラーを返してもよい。しかし、実際にはほとんど問題にはならない。

適合コード (POSIX)

この例を修正するために、open() の呼び出し中で第3引数を指定している。

/* ... */
fd = open(ms, O_CREAT|O_EXCL|O_WRONLY|O_TRUNC, file_access_permissions);
if (fd == -1){
  /* エラー処理 * /
}
/* ... */
リスク評価

誤った引数で関数を呼び出すと、予期しないまたは意図しないプログラム動作が引き起こされる可能性がある。

ルール

深刻度

可能性

修正コスト

優先度

レベル

EXP37-C

P4

L3

自動検出
ツール バージョン チェッカー 説明
Compass/ROSE    

このルールの違反を検出できる。とくに、すべての open() 呼び出しにおいて、第2引数に O_CREAT を含まない場合には引数が二つであること、第2引数に O_CREAT を含む場合には引数が三つであることをチェックする。

ECLAIR

1.1

callargs

部分的に実装済み

EDG      
Fortify SCA 5.0    
GCC V. 4.3.5  

-Wstrict-prototypes フラグを使用すると、このルールの違反を検出できる。しかし、上記 open() の例のような、可変引数関数を含むケースは検出できない。

LDRA tool suite

V. 8.5.4

41 D
98 S
170 S
496 S
576 S

部分的に実装済み
PRQA QA-C 8.1 3001
0674(C)
部分的に実装済み
関連するガイドライン
CERT C++ Secure Coding Standard EXP37-CPP. Call variadic functions with the arguments intended by the API
ISO/IEC TR 24772:2013 Subprogram Signature Mismatch [OTR]
ISO/IEC TS 17961 (ドラフト) Calling functions with incorrect arguments [argcomp]
MISRA-C Rule 16.6
MITRE CWE CWE-628, Function call with incorrectly specified arguments
参考資料
[CVE] CVE-2006-1174
[ISO/IEC 9899:2011] Section 6.3.2.3, "Pointers"
[Spinellis 2006] Section 2.6.1, "Incorrect Routine or Arguments"
翻訳元

これは以下のページを翻訳したものです。

EXP37-C. Call functions with the correct number and type of arguments (revision 101)

Top へ

Topへ
最新情報(RSSメーリングリストTwitter