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

DCL09-C. errno を返す関数は返り値を errno_t 型として定義する

errno を返す既存の関数の多くは、int 型を返す関数として定義されている。これらの関数が、エラーステータスを返すのか値を返すのか、あるいはひどい場合はその両方の組み合わせを返すのか、関数宣言やプロトタイプを眺めただけではセマンティック上は分からない(「ERR02-C. 正常終了時の値とエラーの値は別の手段で通知する」を参照)。

TR 24731-1 では新たな型 errno_t が導入されている。これは errno.h および他のヘッダで int 型として定義された。TR 24731-1 で定義されている関数の多くはこの型の値を返す [TR 24731-1]。errno_t 型は、errno に出てくる値のみを含むオブジェクトの型として使用すべきである。たとえば、errno の値を返す関数は、返り値の型としてerrno_t を持つ関数として定義すべきである。

このレコメンデーションは TR 24731-1 に依存したもので、新規のコードの適切な場所で errno_t を使用することを推奨している。

違反コード

以下のコードは、opener() という関数が errno エラーコードを返すことを示している。しかし、この関数は int を返す関数として定義されている。したがって、返り値の持つ意味が本来あるべきほど明らかではない。

enum { NO_FILE_POS_VALUES = 3 };

int opener(
  FILE *file,
  int *width,
  int *height,
  int *data_offset
) {
  int file_w;
  int file_h;
  int file_o;
  fpos_t offset;

  if (file == NULL) { return EINVAL; }
  errno = 0;
  if (fgetpos(file, &offset) != 0) { return errno; }
  if (fscanf(file, "%i %i %i", &file_w, &file_h, &file_o)
        != NO_FILE_POS_VALUES) {
    return EIO;
  }

  errno = 0;
  if (fsetpos(file, &offset) != 0) { return errno; }

  if (width != NULL) { *width = file_w; }
  if (height != NULL) { *height = file_h; }
  if (data_offset != NULL) { *data_offset = file_o; }

  return 0;
}

ただし、このレコメンデーションに違反したコード例は、「ERR30-C. 関数を呼び出す前に errno をゼロに初期化し、関数の異常終了時にのみ errno を参照する」には適合している。

適合コード

以下の適合コードでは、opener() 関数が errno_t 型の値を返しており、この関数がエラーコードを返すことが明確になっている。

#include <errno.h>

enum { NO_FILE_POS_VALUES = 3 };

errno_t opener(
  FILE *file,
  int *width,
  int *height,
  int *data_offset
) {
  int file_w;
  int file_h;
  int file_o;
  fpos_t offset;

  if (file == NULL) { return EINVAL; }
  errno = 0;
  if (fgetpos(file, &offset) != 0 ) { return errno; }
  if (fscanf(file, "%i %i %i", &file_w, &file_h, &file_o)
        != NO_FILE_POS_VALUES) {
    return EIO;
  }

  errno = 0;
  if (fsetpos(file, &offset) != 0 ) { return errno; }

  if (width != NULL) { *width = file_w; }
  if (height != NULL) { *height = file_h; }
  if (data_offset != NULL) { *data_offset = file_o; }

  return 0;
}

注:EINVALEIO は C 標準では定義されていないが、大概の処理系では利用可能であり、POSIX には定義されている。

リスク評価

エラー条件をテストし損ねると様々な脅威の脆弱性につながる恐れがある。errno を返す関数を errno_t 型の返り値を返す関数として定義したとしてもこの手の問題を廃絶することはできないかもしれない。しかし、プログラマが返り値の目的を誤解することによって引き起こされるエラーは減らせるだろう。

レコメンデーション

深刻度

可能性

修正コスト

優先度

レベル

DCL09-C

P3

L3

関連するガイドライン
CERT C++ Secure Coding Standard DCL09-CPP. Declare functions that return errno with a return type of errno_t
ISO/IEC TR 24731-1:2007  
ISO/IEC TR 24772:2013 Ignored Error Status and Unhandled Exceptions [OYB]
MISRA-C Rule 20.5
参考資料
[IEEE Std 1003.1:2013]
翻訳元

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

DCL09-C. Declare functions that return errno with a return type of errno_t (revision 80)

Top へ

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