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

ARR01-C. 配列のサイズを求めるときに sizeof 演算子をポインタに適用しない

sizeof 演算子は、オペランドのサイズ(バイト単位)を求める。オペランドは、式または括弧で囲まれた型の名前のいずれかである。sizeof 演算子を使って配列のサイズを計算すると、コーディングエラーとなりやすい。

違反コード

以下の違反コード例では、関数 clear() が配列の要素を 0 で初期化する。この関数は、int array[] として宣言された引数を 1 つ取り、12 個の int からなる静的配列が渡されている。関数 clear()sizeof(array) / sizeof(array[0]) という構文を使用して配列内の要素数を求めている。しかし、array は引数なので、ポインタ型となる。したがって、sizeof(array)sizeof(int *) に等しくなる。たとえば、sizeof(int) == 4 および sizeof(int *) == 4 であるアーキテクチャ(IA-32など)の場合、渡された配列のサイズに関係なく、式 sizeof(array) / sizeof(array[0]) の値は 1 となり、配列の残りの要素は初期化されないままになる。

void clear(int array[]) {
  for (size_t i = 0; i < sizeof(array) / sizeof(array[0]); ++i) {
     array[i] = 0;
   }
}

void dowork(void) {
  int dis[12];

  clear(dis);
  /* ... */
}

C 言語規格 [ISO/IEC 9899:2011] のセクション 6.5.3.4 の脚注 103 には次のように書かれている。

配列型又は関数型を持つと宣言された仮引数に適用する場合、sizeof 演算子は、型調整された(ポインタ)型の大きさを与える。

これはすべての配列型引数に当てはまる。

適合コード

以下の適合コードでは、配列が宣言されたブロックの中で配列サイズを計算して、それから引数として関数に渡している。

void clear(int array[], size_t len) {
    for (size_t i = 0; i < len; i++) {
     array[i] = 0;
  }
}

void dowork(void) {
  int dis[12];

  clear(dis, sizeof(dis) / sizeof(dis[0]));
  /* ... */
}

構文 sizeof(array) / sizeof(array[0]) が成立するのは、array の定義が同じスコープ内にある場合である。

違反コード

以下の違反コード例では、sizeof a100 * sizeof(int) と等しくならない。sizeof 演算子は、配列型または関数型として宣言された引数に適用されると、たとえ引数宣言で長さが指定されていても、型調整された(ポインタ)型のサイズを求めるからである。

enum {ARR_LEN = 100};

void clear(int a[ARR_LEN]) {
  memset(a, 0, sizeof(a)); /* 間違い */
}

int main(void) {
  int b[ARR_LEN];
  clear(b);
  assert(b[ARR_LEN / 2]==0); /* おそらく失敗 */
  return 0;
}
適合コード

以下の適合コードでは、式 len * sizeof(int) でサイズを指定している。

enum {ARR_LEN = 100};

void clear(int a[], size_t len) {
  memset(a, 0, len * sizeof(int));
}

int main(void) {
  int b[ARR_LEN];
  clear(b, ARR_LEN);
  assert(b[ARR_LEN / 2]==0); /* 失敗しない */
  return 0;
}
リスク評価

sizeof 演算子を誤用して配列サイズを計算すると、バッファオーバーフローにつながり、任意のコード実行を許してしまう可能性がある。

レコメンデーション

深刻度

可能性

修正コスト

優先度

レベル

ARR01-C

P18

L1

自動検出

ツール

バージョン

チェッカー

説明

Compass/ROSE

 

 

このレコメンデーションの違反を検出できるが、不完全な配列の宣言とポインタ型の宣言を区別することはできない。

LDRA tool suite

V. 8.5.4

401 S

部分的に実装済み

Splint

V. 3.1.1

 

 

関連するガイドライン
CERT C++ Secure Coding Standard ARR01-CPP. Do not apply the sizeof operator to a pointer when taking the size of an array
MITRE CWE CWE-467, Use of sizeof() on a pointer type
ISO/IEC TS 17961 (Draft) Taking the size of a pointer to determine the size of the pointed-to type [sizeofptr]
参考資料
[Drepper 2006] Section 2.1.1, "Respecting Memory Bounds"
[ISO/IEC 9899:2011] Section 6.5.3.4, "The sizeof and _Alignof Operators"
翻訳元

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

ARR01-C. Do not apply the sizeof operator to a pointer when taking the size of an array (revision 74)

Top へ

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