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

EXP36-C. ポインタをより厳密にアラインされるポインタ型に変換しない

ポインタの値を、実際に指している値の型よりも厳密にアラインされるポインタ型に変換しないこと。異なるオブジェクトの型には異なるアラインメントが適用される可能性がある。型チェックシステムの挙動が、明示的キャストによるオーバーライドや、ポインタを void ポインタ(void *)に変換しその後異なる型に変換することにより変更された場合、オブジェクトのアラインメントは変更される可能性がある。

C 標準 [ISO/IEC 9899:2011] セクション 6.3.2.3、パラグラフ 7 には次のように記載されている。

オブジェクトまたは不完全な型へのポインタは、別のオブジェクトまたは不完全な型へのポインタに変換することができる。変換されたポインタがポイント対象型に合わせて正しくアラインされない場合、未定義の動作となる。

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

正しくアラインされなかったポインタがあとで参照された場合、プログラムが異常終了する可能性がある。値が参照されない場合でも、キャストだけで情報の喪失が発生することがある。たとえば、ポインタが参照されない場合でも、一部の規格合致処理系では次のコード例のアサートが失敗する。

char c = 'x';
int *ip = (int *)&c; /* ここで情報が失われる可能性がある。 */
char *cp = (char *)ip;
assert(cp == &c);    /* 一部の規格合致処理系では失敗する。 */

一部の処理系では、cp&c と一致しない。したがって、あるオブジェクト型へのポインタが異なるオブジェクト型へのポインタに変換された場合、後者のオブジェクト型は前者のオブジェクト型より厳密なアラインメントを要求してはならない。

違反コード

以下のコード例では、char ポインタ &c がより厳密にアラインされた int ポインタ i_ptr に変換される。

void f(void) {
  int *i_ptr;
  char c;
 
  i_ptr = (int *)&c;  /* 違反 */
  /* ... */
}
適合コード

以下の適合コードでは、char ポインタ c_ptr によって参照される値が int型にアラインされる。

void f(void) {
  char *c_ptr;
  int *i_ptr;
  int i;
 
  c_ptr = (char *)&i;
  i_ptr = (int *)c_ptr;
  /* ... */
}
違反コード

C 標準では、ポインタを void * との間で相互にキャストすることが許可されている。そのため、ポインタを void * に格納または型変換し、それを最終的な型に格納または型変換することによって、コンパイラが問題を診断することなく、あるポインタ型から別のポインタ型へ変換することが可能である。以下のコード例では、loop_function()char ポインタ loop_ptr を渡されるが、int ポインタを返す。

char *loop_ptr;
int *int_ptr;

int *loop_function(void *v_pointer) {
  /* ... */
  return v_pointer;
}
int_ptr = loop_function(loop_ptr);

このコード例は警告を出すことなくコンパイルされる。しかし、v_pointer は 1 バイトの境界にアラインされる可能性がある。

適合コード

入力パラメータは返り値に直接影響し、loop_function()int * を返すため、仮引数 v_pointerint * 型だけを受け取るよう関数宣言を修正している。

int *loop_ptr;
int *int_ptr;

int *loop_function(int *v_pointer) {
  /* ... */
  return v_pointer;
}
int_ptr = loop_function(loop_ptr);

適合コードの1つは、loop_ptrmalloc() が返すオブジェクトを確実に指すようにすることである。なぜなら、このオブジェクトはどのような要件に対しても適切にアラインされることが保証されているためである。しかし、このやり方だと、将来プログラムが修正されるとき容易に見逃されてしまう。型システムを使ってアラインメントの必要性を明記するほうが容易で、安全である。

違反コード

多くのアーキテクチャでは、1 バイトを超えるオブジェクトにアクセスする際にポインタを正しくアラインする必要がある。しかし、システムコードには、このコード例のように適切にアラインされたメモリにコピーする必要のあるアラインされていないデータを受け取る場合 (ネットワークスタックなど) が多数ある。

char *data;
struct foo_header *tmp;
struct foo_header *header;

tmp = data + offset;
memcpy(&header, tmp, sizeof(header));

if (header.len < FOO)
/* ... */

しかし、アラインされていない値をアラインの必要な型を指すポインタに割り当てた場合の動作は未定義である。処理系では、たとえば tmp および header をアラインする必要があると明記することがあるため、アラインされたデータを前提とする命令を使用するインライン memcpy() を使用することができる。

適合コード

以下のコード例では foo_header ポインタを使用していない。

char *data;
struct foo_header header;

memcpy(&header, data + offset, sizeof(header));

if (header.len < FOO)
/* ... */
違反コード

スタックで宣言されたオブジェクトの場合、C 標準には、alignas でオブジェクトをより厳密にアラインするよう宣言する必要があると記載されている。これに基づいて、次のコード例の問題を解決することができる。

char c = 'x';
int *ip = (int *)&c; /* ここで情報が失われる可能性がある。 */
char *cp = (char *)ip;
assert(cp == &c);    /* 一部の規格合致処理系では失敗する。 */
適合コード

以下のコード例では、alignas を使用して文字 c を整数のアラインメントにアラインする。その結果、2 つのポインタは等しくアラインされたポインタ型を指すことになる。

alignas(int) char c = 'x'; /* c を int のアラインメントにアラインする。 */
int *ip = (int *)&c; 
char *cp = (char *)ip;
assert(cp == &c);    /* cp も &c も両方とも、等しくアラインされたオブジェクトを指す。 */
リスク評価

正しいアラインメントにないポインタやオブジェクトにアクセスすると、プログラムがクラッシュしたり、誤った情報を提供したり、ポインタのアクセス速度が低下したり(アーキテクチャが誤ってアラインされたオブジェクトへのアクセスを許可している場合)する可能性がある。

ルール

深刻度

可能性

修正コスト

優先度

レベル

EXP36-C

P4

L3

自動検出

ツール

バージョン

チェッカー

説明

Compass/ROSE    

このルールの違反を検出できる。しかし、void * に明示的にキャストされ、さらに別の型に変換される場合は検出できない。

ECLAIR

1.1

castexpr

実装済み
EDG      
GCC V. 4.3.5  

GCC は -Wcast-align フラグを使用することでこのルールの違反を検出できる。

LDRA tool suite

V. 8.5.4

94 S
540 S

実装済み
PRQA QA-C 8.1 3305 実装済み
関連するガイドライン
CERT C++ Secure Coding Standard EXP36-CPP. Do not convert pointers into more strictly aligned pointer types
ISO/IEC TR 24772:2013 Pointer Casting and Pointer Type Changes [HFC]
ISO/IEC TS 17961 Converting pointer values to more strictly aligned pointer types [alignconv]
MISRA C:2012 Rule 11.1 (required)
Rule 11.2 (required)
Rule 11.5 (advisory)
Rule 11.7 (required)
参考資料
[Bryant 2003]  
[ISO/IEC 9899:2011] 6.3.2.3, "Pointers"
[Walfridsson 2003] Aliasing, Pointer Casts and GCC 3.3
翻訳元

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

EXP36-C. Do not convert pointers into more strictly aligned pointer types (revision 89)

Top へ

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