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

MEM36-C. realloc() 関数呼び出しでオブジェクトのアラインメントを変更しない

MEM36-C. realloc() 関数呼び出しでオブジェクトのアラインメントを変更しない

メモリ割り当てされたオブジェクトのアラインメントが malloc() で保証されるアラインメントよりもさらに厳しいとき、そのオブジェクトのサイズを realloc() 呼出しを使って変更しないこと。例えば、標準ライブラリの aligned_alloc() 関数で割り当てられたメモリ領域は、通常よりも厳しいアラインメントに従って割り当てられている可能性がある。C 標準では、realloc() が返すポインタは、基本アラインメント("fundamental alignment")に従う任意の型へのポインタに代入できるようアラインメントされていること、とされているのみである。

違反コード

次のコード例では、まず 4096 バイト境界にアラインされたポインタ ptr を返している。次に realloc() 関数が呼び出されるが、引数 resize の値が ptr で参照されているオブジェクトのサイズよりも大きい場合、realloc() は基本アラインメント要請を満たす新たなメモリ領域を割り当てる。しかしこの割り当ては、元のオブジェクトの(基本アラインメント要請よりも厳しい)アラインメント要請を満たしていない可能性がある。

#include <stdlib.h>

void func(void) {
  size_t resize = 1024;
  size_t alignment = 1 << 12;
  int *ptr;
  int *ptr1;

  if (NULL == (ptr = (int *)aligned_alloc(alignment, sizeof(int)))) {
    /* エラー処理 */
  }

  if (NULL == (ptr1 = (int *)realloc(ptr, resize))) {
    /* エラー処理 */
  }
}
処理系固有の詳細

以下に、コード例とそれを GCC 4.1.2 でコンパイルして x86_64 Red Hat Linux プラットフォーム上で実行した場合の出力を示す。

コード

#include <stdlib.h>
#include <stdio.h>

int main(void) {
  size_t  size = 16;
  size_t resize = 1024;
  size_t align = 1 << 12;
  int *ptr;
  int *ptr1;

  if (posix_memalign((void **)&ptr, align , size) != 0) {
    exit(EXIT_FAILURE);
  }

  printf("memory aligned to %zu bytes\n", align);
  printf("ptr = %p\n\n", ptr);

  if ((ptr1 = (int*) realloc((int *)ptr, resize)) == NULL) {
    exit(EXIT_FAILURE);
  }

  puts("After realloc(): \n");
  printf("ptr1 = %p\n", ptr1);

  free(ptr1);
  return 0;
}

出力

memory aligned to 4096 bytes
ptr = 0x1621b000

After realloc():
ptr1 = 0x1621a010

ptr1 は 4096 バイトにアラインされていない。

適合コード

次の適合コードでは、元のメモリと同じアラインメントで resize バイトの新しいメモリを割り当ててから、元のメモリ内容をコピーし、最後に元のメモリを解放している。このコードの動作は実装定義となる。なぜならば、_Alignof (max_align_t) を超える拡張アラインメント("extended alignment")がサポートされているか、そして、どのようなコンテキストでサポートされているかに依存しているからである。拡張アラインメントをサポートしていない環境では、動作は未定義となる。

#include <stdlib.h>
#include <string.h>

void func(void) {
  size_t resize = 1024;
  size_t alignment = 1 << 12;
  int *ptr;
  int *ptr1;

  if (NULL == (ptr = (int *)aligned_alloc(alignment,
                                          sizeof(int)))) {
    /* エラー処理 */
  }

  if (NULL == (ptr1 = (int *)aligned_alloc(alignment,
                                           resize))) {
    /* エラー処理 */
  }

  if (NULL == (memcpy(ptr1, ptr, sizeof(int))) {
    /* エラー処理 */
  }

  free(ptr);
}
適合コード (Windows)

Windows では、指定したアラインメントに合せてメモリ割り当てを行う _aligned_malloc() 関数と、割り当てられたメモリのサイズを変更するための _aligned_realloc() 関数が提供されている[MSDN]。次のコードに、これらの関数の使用例を示す。

#include <malloc.h>

void func(void) {
  size_t alignment = 1 << 12;
  int *ptr;
  int *ptr1;

  /* Original allocation */
  if (NULL == (ptr = (int *)_aligned_malloc(sizeof(int),
                                            alignment))) {
    /* エラー処理 */
  }

  /* Reallocation */
  if (NULL == (ptr1 = (int *)_aligned_realloc(ptr, 1024,
                                              alignment))) {
    _aligned_free(ptr);
    /* エラー処理 */
  }

  _aligned_free(ptr1);
}

_aligned_malloc() 関数の size 引数と alignment 引数は、C 標準の aligned_alloc() 関数と順序が逆であることに注意。

リスク評価

アラインメントを適切に行わないと、任意のメモリ領域へのアクセスや書き込みが行われる可能性がある。

レコメンデーション

深刻度

可能性

修正コスト

優先度

レベル

MEM36-C

P2

L3

Bibliography
[ISO/IEC 9899:2011]  7.22.3.1, "The aligned_alloc Function"
[MSDN] aligned_malloc()
翻訳元

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

MEM36-C. Do not modify the alignment of objects by calling realloc() (revision 90)

Top へ

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