メインコンテンツ

CWE Rule 467

Use of sizeof() on a Pointer Type

R2023a 以降

説明

ルールの説明

The code calls sizeof() on a malloced pointer type, which always returns the wordsize/8.This can produce an unexpected result if the programmer intended to determine how much memory has been allocated.

Polyspace 実装

ルール チェッカーは以下の問題をチェックします。

  • sizeof を誤って使用している可能性があります

  • sizeof において使用された誤った型

すべて展開する

問題

この問題は、Polyspace® Bug Finder™sizeof 演算子の使用が原因の、おそらく想定外である結果を検出した場合に発生します。次に例を示します。

  • 配列のサイズを求めるため、sizeof 演算子が配列パラメーター名で使用されている。しかし、配列パラメーター名それ自体がポインターである。sizeof 演算子からは、そのポインターのサイズが返される。

  • 配列のサイズを求めるため、sizeof 演算子が配列要素で使用されている。しかし、演算子はその配列要素のサイズを返す。

  • 正しくない想定のもとに sizeof 演算子を先に使用したため、特定の関数 (strncmpwcsncpy など) の size 引数が不適切になっている。次に例を示します。

    • 関数呼び出し strncmp(string1, string2, num) で、num が、ポインターでの sizeof 演算子の誤使用により取得されている。

    • 関数呼び出し wcsncpy(destination, source, num) で、num がワイド文字の数ではなく、sizeof 演算子を使用して取得されたバイト単位のサイズとなっている。たとえば、wcsncpy(destination, source, (sizeof(desintation)/sizeof(wchar_t)) - 1) ではなく wcsncpy(destination, source, sizeof(destination) - 1) を使用した場合などです。

リスク

sizeof 演算子の不適切な使用は、次の問題の原因となることがあります。

  • sizeof 演算子により配列サイズが返され、その戻り値がループの制限に使用されることを想定している場合に、ループの実行回数が想定より少なくなる。

  • sizeof 演算子の戻り値がバッファーの割り当てに使用される場合に、バッファー サイズが必要なサイズより小さくなる。バッファーが不十分だと、結果としてバッファー オーバーフローなどの脆弱性などにつながることがあります。

  • sizeof 演算子の戻り値が関数呼び出しで不適切に使用される場合に、関数が想定どおりに動作しない。

修正方法

考えられる修正方法は次のとおりです。

  • sizeof 演算子を、配列サイズを決定するために配列パラメーター名や配列要素で使用しない。

    ベスト プラクティスは、配列サイズを別の関数パラメーターとして渡し、そのパラメーターを関数本体で使用することです。

  • sizeof 演算子を慎重に使用して、strncmpwcsncpy のような関数の数値引数を決定する。たとえば、wcsncpy などのワイド文字列関数で、バイト数ではなくワイド文字の数を引数として使用します。

例 — 配列サイズを決定する際の sizeof の誤使用
#define MAX_SIZE 1024

void func(int a[MAX_SIZE]) {
    int i;

    for (i = 0; i < sizeof(a)/sizeof(int); i++)  //Noncompliant
{
        a[i] = i + 1;
    }
}

この例では、sizeof(a) は配列サイズではなくポインター a のサイズを返します。

修正 — 配列サイズを別の方法で決定

1 つの修正方法として、別の方法を使用して配列サイズを決定します

#define MAX_SIZE 1024

void func(int a[MAX_SIZE]) {
    int i;

    for (i = 0; i < MAX_SIZE; i++)    {
        a[i] = i + 1;
    }
}
問題

この問題は、以下の条件が両方当てはまる場合に発生します。

  1. メモリ ブロックのアドレスをポインターに代入するか、2 つのメモリ ブロック間でデータを転送します。この代入またはコピーで、sizeof 演算子を使用します。

    たとえば、malloc(sizeof(type)) を使用してポインターを初期化するか、memcpy(destination_ptr, source_ptr, sizeof(type)) を使用して 2 つのアドレス間でデータをコピーします。

  2. sizeof 演算子の引数に間違った型を使用します。次に例を示します。

    • ポインターが指す型ではなく、ポインター型を使用している可能性がある。たとえば、type* ポインターを初期化する際に、malloc(sizeof(type)) ではなく、malloc(sizeof(type*)) を使用している可能性があります。

    • sizeof 引数としてまったく無関係な型を使用している可能性がある。たとえば、type* ポインターを初期化する際に、malloc(sizeof(anotherType)) を使用している可能性があります。

リスク

type が何型でも、式 sizeof(type*) では常に固定サイズが返されます。返されるサイズは、お使いのプラットフォームでのポインターのサイズ (バイト単位) です。sizeof(type*) の出現は、多くの場合、意図とは異なる使い方を示しています。このエラーにより、必要とするよりもかなり小さなメモリ ブロックの割り当てになり、バッファー オーバーフローなどの脆弱性などにつながる可能性があります。

たとえば、structType が 10 個の int 変数を含む構造体だとします。32 ビット プラットフォームで malloc(sizeof(structType*)) を使用して structType* ポインターを初期化すると、ポインターには 4 バイトのメモリ ブロックが割り当てられます。しかし、1 つの structType 変数全体を割り当てるには、structType* ポインターは sizeof(structType) = 10 * sizeof(int) バイトのメモリ ブロックを指さなければなりません。必要なサイズは、実際に割り当てられた 4 バイトのサイズよりもはるかに大きなサイズです。

修正方法

type* ポインターを初期化するには、ポインターの初期化式の sizeof(type*)sizeof(type) に置き換えます。

例 — sizeof による文字配列の割り当て
#include <stdlib.h>

void test_case_1(void) {
    char* str;

    str = (char*)malloc(sizeof(char*) * 5);  //Noncompliant
    free(str);

}

この例では、5 つの文字ポインターの malloc を使用して文字ポインター str にメモリを割り当てています。しかし、str は文字を指すポインターであり、文字ポインターを指すポインターではありません。したがって、sizeof の引数 char* は正しくありません。

修正 — ポインターの型を sizeof の引数と一致させる

1 つの修正方法として、引数をポインターの型に一致させることができます。この例では、str は文字ポインターなので引数も文字でなければなりません。

#include <stdlib.h>

void test_case_1(void) {
    char* str;

    str = (char*)malloc(sizeof(char) * 5);
    free(str);

}

チェック情報

カテゴリ: Pointer Issues

バージョン履歴

R2023a で導入