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

FIO41-C. 副作用を持つストリーム引数を getc()、putc()、getwc()、putwc() に渡さない

FIO41-C. 副作用を持つストリーム引数を getc()、putc()、getwc()、putwc() に渡さない

getc()putc()、これらのワイド文字版の getwc()putwc() の呼び出しに副作用を持つストリーム引数を使わないこと。これらの関数が安全でないマクロとして実装されている場合、ストリーム引数は複数回評価される可能性がある。(詳細は PRE31-C. Avoid side effects in arguments to unsafe macros を参照。)

このルールは putc() の文字引数や putwc のワイド文字引数には適用されない。これらの文字引数は一度だけ評価されることが保証されている。

違反コード (getc())

以下の違反コード例は、ストリーム引数で getc() 関数を呼び出している。getc() がマクロとして実装されている場合、ファイルが複数回オープンされる可能性がある(「FIO24-C. すでにオープンされているファイルをオープンしない」を参照)。

#include <stdio.h>

void func(const char *file_name) {
  FILE *fptr;

  int c = getc(fptr = fopen(file_name, "r"));
  if (feof(fptr) || ferror(fptr)) {
    /* エラー処理 */
  }

  if (fclose(fptr) == EOF) {
    /* エラー処理 */
  }
}

この違反コード例は、fopen() がエラーを返していないかどうかをチェックしていないため、「ERR33-C. 標準ライブラリ関数のエラーを検出し対処する」にも違反している。

適合コード (getc())

以下の適合コードでは、getc() の前に fopen() を呼び出しており、その返り値のエラーもチェックしている。

#include <stdio.h>
 
void func(const char *file_name) {
  int c;
  FILE *fptr;

  fptr = fopen(file_name, "r");
  if (fptr == NULL) {
    /* エラー処理 */
  }

  c = getc(fptr);
  if (c == EOF) {
    /* エラー処理 */
  }

  if (fclose(fptr) == EOF) {
    /* エラー処理 */
  }
}
違反コード (putc())

以下の違反コード例では、putc() にはストリーム引数が渡されている。putc() がマクロとして実装されている場合、引数は複数回評価されてしまう可能性がある。

#include <stdio.h>
 
void func(const char *file_name) {
  FILE *fptr = NULL;
  int c = 'a';
 
  while (c <= 'z') {
    if (putc(c++, fptr ? fptr :
         (fptr = fopen(file_name, "w"))) == EOF) {
      /* エラー処理 */
    }
  }

  if (fclose(fptr) == EOF) {
    /* エラー処理 */
  }
}

putc() マクロがその第2引数を複数回評価しても、?: 演算子が fopen() の複数回の呼び出しを防止しているため、安全なように見えるかもしれない。しかし、第2引数が複数回評価されるときに fptr への代入と fptr の評価が同じタイミングで発生する可能性があるため、未定義動作となる。また、fopen() の返り値をチェックしていないため、「ERR33-C. 標準ライブラリ関数のエラーを検出し対処する」にも違反している。

適合コード (putc())

以下の適合コードでは、putc() の引数は副作用を持たないものになっている。

#include <stdio.h>

void func(const char *file_name) {
  int c = 'a';
  FILE *fptr = fopen(file_name, "w");

  if (fptr == NULL) {
    /* エラー処理 */
  }

  while (c <= 'z') {
    if (putc(c++, fptr) == EOF) {
      /* エラー処理 */
    }
  }

  if (fclose(fptr) == EOF) {
    /* エラー処理 */
  }
}

putc() は第1引数の評価が一度だけであることを保証しているため、c++ は安全である。

NOTE: このコード例の出力は、実行環境が扱っている文字セットにより異なる。たとえば、ISO-8859 や Unicode など ASCII 由来のコードセットを使用する実行環境で実行した場合、アルファベットの小文字 26 文字が出力される。しかし、Codepage 037 や Codepage 285 など EBCDIC 由来のコードセットを使用する実行環境で実行すると、引用符や記号も出力されるだろう。

リスク評価

副作用のある式を getc()putc()getwc() のストリーム引数として使用すると、予期せぬ動作プログラムの異常終了を引き起こす可能性がある。

ルール

深刻度

可能性

修正コスト

優先度

レベル

FIO41-C

P2

L3

自動検出

ツール

バージョン

チェッカー

説明

Astrée
20.10
stream-argument-with-side-effects 実装済み
Helix QAC


2021.1

C5036

C++3225, C++3229


LDRA tool suite
9.7.1

35 D, 1 Q, 9 S,
30 S, 134 S

実装済み

Parasoft C/C++test
2021.1

CERT_C-FIO41-a
CERT_C-FIO41-b
CERT_C-FIO41-c
CERT_C-FIO41-d
CERT_C-FIO41-e

The value of an expression shall be the same under any order of evaluation that the standard permits

Don't write code that depends on the order of evaluation of function arguments
Don't write code that depends on the order of evaluation of function designator and function arguments
Don't write code that depends on the order of evaluation of expression that involves a function call
A full expression containing an increment (++) or decrement (--) operator should have no other potential side effects

Polyspace Bug Finder

R2021a

CERT C: Rule FIO41-C Checks for stream arguments with possibly unintended side effects (rule fully covered)
PRQA QA-C

9.7

5036
PRQA QA-C++

4.4

3225, 3229 
RuleChecker

20.10

stream-argument-with-side-effects 実装済み
関連するガイドライン

(テーブルの説明)

Taxonomy

Taxonomy item

Relationship

CERT C コーディングスタンダード FIO24-C. すでにオープンされているファイルをオープンしない Prior to 2018-01-12: CERT: Unspecified Relationship
CERT C コーディングスタンダード EXP30-C. 副作用が発生する式の評価順序に依存しない Prior to 2018-01-12: CERT: Unspecified Relationship
翻訳元

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

FIO41-C. Do not call getc(), putc(), getwc(), or putwc() with a stream argument that has side effects (revision 103)

Top へ

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