SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

C/C++セキュアコーディング入門

sizeofオペレータを正しく使おう
――C/C++セキュアコーディング入門(5)

「動けばいいってもんじゃない」 脆弱性を作り込まないコーディング 第5回

  • X ポスト
  • このエントリーをはてなブックマークに追加

 今回はsizeofオペレータをとりあげます。sizeofは、引数に与えたオブジェクトや型名から、その型のデータがメモリ上に占めるバイト数を求めるオペレータです。まずはsizeofオペレータの使用上の注意について説明し、その後でsizeofオペレータの誤用例を2つ紹介します。

  • X ポスト
  • このエントリーをはてなブックマークに追加

はじめに

 今回はsizeofオペレータをとりあげます。sizeofは、引数に与えたオブジェクトや型名から、その型のデータがメモリ上に占めるバイト数を求めるオペレータです。皆さんも、メモリ領域を動的に確保したいときや文字列操作などに関連して使ったことがあるでしょう。まずはsizeofオペレータの使用上の注意について説明し、その後でsizeofオペレータの誤用例を2つ紹介します。

sizeofオペレータ使用上の注意

 「sizeofオペレータの使用上の注意 その1」は、プログラマが意図する正しい引数を渡すこと、です。メモリ上のオブジェクトのコピーや移動などの操作は、オブジェクトのメモリ上のサイズにもとづいて行います。オブジェクトのサイズを間違っていたら、アクセス違反やオーバフローといった脆弱性につながることは容易に想像できるでしょう。

 「sizeofオペレータの使用上の注意 その2」は、引数に副作用を持つような式を渡さないこと、です。C言語仕様では、sizeofオペレータが引数を評価するのは引数の型が可変長配列型であるとき、それ以外の場合は引数を評価しない、と明記しています(C言語仕様セクション6.5.3.4)。

 例えば、次のようなコードではaの値はインクリメントされません。

int a = 14;
int b = sizeof(a++);
printf("%d\n", a);

 3行めのprintfが出力するaの値は14のままです。sizeofは「オペレータ」(演算子)なのでかっこの中の式は、関数のように評価されるわけではありません。かっこの中はa++という式として認識されますが、sizeofオペレータにとってその計算をすることが仕事ではなく、指定された引数の型(サイズ)が分かればよいのです。

 sizeofオペレータは引数の型を知りたいだけなので、不要な計算をしないのだ、と理解するとよいでしょう。コード上には a++ と書いてあるのにそれが評価されないというのは、プログラマの誤解を招くので避けるべきです。

 ちなみに引数に可変長配列型の式が渡される例として、C言語仕様には以下のコード例が掲載されています(C言語仕様セクション6.5.3.4 EXAMPLE 3)。

#include <stddef.h>
size_t fsize3(int n)
{
  char b[n+3];      // variable length array
  return sizeof b;  // execution time sizeof
}

 可変長配列bのサイズは関数 fsize3の実引数nに依存しており、実行時にbを評価してはじめてそのサイズが分かるというわけです。

 「sizeof オペレータの使用上の注意 その3」は、間違ってポインタ自身のサイズを求めないように注意、です。これは注意その1とかぶりますが、あえて書きました。例えば、以下のコード例はdouble型データの配列を動的に確保しようとしています。

double *allocate_array(size_t num_elems){
  double *d_array;

  if (num_elems > SIZE_MAX/(sizeof d_array)){
    /* エラー処理 */
  }
  d_array = (double *)malloc((sizeof d_array) * num_elems);
  if (d_array == NULL){
    /* エラー処理 */
  }
  return d_array;
}

 2か所使われている sizeofオペレータの引数がどちらもポインタなので、返り値はポインタ自身のサイズになります。実際に求めたいのはポインタが指している先のメモリ領域のサイズなので、sizeof(d_array)ではなくsizeof(*d_array)と書くべきです。あるいは型名を使ってsizeof(double)としてもいいでしょう。

 「その1」と「その3」で挙げた問題が実際のライブラリで見つかっています。標準的な画像データを扱うライブラリとIMAPサーバのデーモンで見つかった例を以下に紹介します。

次のページ
libpngでの誤用例

修正履歴

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
C/C++セキュアコーディング入門連載記事一覧

もっと読む

この記事の著者

戸田 洋三(JPCERT コーディネーションセンター)(トダ ヨウゾウ(JPCERT コーディネーションセンター))

リードアナリストJPCERTコーディネーションセンター東京工業大学情報理工学研究科修士課程修了。学生時代は、型理論および証明からのプログラム抽出を研究。その後、千葉大学総合情報処理センターのスタッフとして、学内ネットワークの運営、地域ネットワーク、IPマルチキャストの実験ネットワークであるJP-MB...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/4785 2010/01/22 14:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング