不適切にデリファレンスされたポインター
ポインターが範囲外でデリファレンスされる
説明
ポインターのデリファレンスに関するこのチェックでは、ポインターが NULL であるか、その境界外を指すかどうか、または動的に割り当てられた後に割り当てを解除されたメモリを指すかどうかが判別されます。チェックは、ポインターをデリファレンスしたときのみ発生し、ポインターにアドレスを割り当てたり、別のポインターに再割り当てしたり、ポインターを関数に渡したりしたときには発生しません。
チェック メッセージには、ポインターのポイント先のサイズ、ポインター オフセット、および割り当てられたバッファーがバイト単位で示されます。"ポイント先のサイズとオフセットの合計がバッファー サイズよりも大きい" 場合、ポインターは範囲外を指します。
ポイント先のサイズ:ポインターをデリファレンスする場合にアクセスするバイト数です。たとえば、
int*
型ポインターをデリファレンスする場合、最も一般的なターゲットでアクセスするバイト数は 4 となります。バッファー サイズ:ポインターにアドレスを割り当てると、そのポインターにメモリのブロックが割り当てられます。そのポインターを使用してそのブロック外のメモリにアクセスすることはできません。このブロックのサイズはバッファー サイズです。たとえば、
char
変数のアドレスをポインターに代入する場合、割り当てられるバッファー サイズは最も一般的なターゲットで 1 バイトとなります。場合によっては、サイズが明確な値ではなく範囲になる可能性があります。たとえば、サイズの不明な入力を指定して
malloc
を使用し、動的にバッファーを作成すると、Polyspace® では、配列サイズは入力データ型で許容される全範囲の値を取り得ると仮定されます。オフセット:ポインター演算を使用して、割り当てられたメモリ ブロック内でポインターを移動できます。ポインターの初期位置と現在の位置の差がオフセットです。
場合によっては、オフセットが明確な値ではなく範囲になる可能性があります。たとえば、ループ内で配列にアクセスすると、オフセットはループの反復ごとに値を変更し、ループ全体を通じて値の範囲を取ります。
たとえば、ポインターが配列を指す場合、
バッファー サイズは配列サイズです。
オフセットは配列の先頭とポインターの現在の位置の差です。
このチェックでは [ターゲット プロセッサ タイプ] (-target)
の指定を使用して、データ型のサイズを判別します。
このチェックの診断
例
int main() {
short x=0;
int *ptr = (int *) &x;
*ptr = 2;
return 0;
}
この例では、変数 x
のデータ型は short
です。このデータ型は、ほとんどのターゲットで 2 バイトのバッファー サイズに相当します。
ただし、このバッファーのアドレスは int*
型ポインター ptr
にキャストされます。int*
型ポインターは、ほとんどのターゲットでサイズが 4 バイトのバッファーを指します。ptr
のデリファレンスには、これらの 4 バイトへのアクセスが必要になります。元の割り当ては 2 バイトのみであったため、ポインターのデリファレンスにより、許容範囲外のアクセスとなります。したがって、[不適切にデリファレンスされたポインター] チェックでレッド エラーが示されます。
[結果の詳細]:チェック メッセージで、ポインターが 2 バイトのバッファー内でオフセット 0 にある 4 バイトを指しているため、範囲外であることが確認されます。
"ポイント先のサイズ" は 4 バイトです。これは、ポインター
ptr
がint
を指しているためです。"バッファー サイズ" は 2 バイトです。これは、バッファー サイズは指されている変数である
x
の型に基づくためです。x
の型はshort
です。"オフセット" は 0 です。これは、ポインターがバッファーの先頭を指しているためです。ポインター演算は関与しません。
データ型のサイズはターゲットによって異なることに注意してください。この例でのサイズは、最も一般的なターゲットの場合を表しています。
#define Size 1024 int input(void); void main() { int arr[Size]; int *p = arr; for (int index = 0; index < Size ; index++, p++){ *p = input(); } *p = input(); }
この例では以下のようになります。
for
ループの前では、p
は配列arr
の先頭を指します。for
ループの後では、p
は配列の外部を指します。
for
ループ後の p
のデリファレンスに対し、[不適切にデリファレンスされたポインター] チェックでレッド エラーが示されます。
[結果の詳細]:チェック メッセージで、ポインターが 4096 バイトのバッファー内でオフセット 4096 にある 4 バイトを指しているため、範囲外であることが通知されます。
"ポイント先のサイズ" は 4 バイトです。これは、ポインター
p
がint
を指しているためです。"バッファー サイズ" は 4096 バイトです。これは、バッファー サイズは配列のサイズに個々の配列要素型 (
int
) のサイズを乗算した値 (1024 * 4 バイト) であるためです。"オフセット" は 4096 です。これは、演算
p++
により、ポインターが配列を移動してバッファーの先頭から 1024*4 バイト離れたところを指しているためです。
データ型のサイズはターゲットによって異なることに注意してください。この例でのサイズは、最も一般的なターゲットの場合を表しています。
1 つの修正方法として、for
ループ後の p
の不適切なデリファレンスの削除があります。
#define Size 1024
int input(void);
void main() {
int arr[Size];
int *p = arr;
for (int index = 0; index < Size ; index++, p++) {
*p = input();
}
}
typedef struct S { int f1; int f2; int f3; } S; void Initialize(int *ptr) { *ptr = 0; *(ptr+1) = 0; *(ptr+2) = 0; } void main(void) { S myStruct; Initialize(&myStruct.f1); }
この例では、Initialize
の本文で、ptr
は構造体の第 1 のフィールドを指す int
ポインターです。ptr
を通して 2 番目のフィールドへのアクセスを試みると、[不適切にデリファレンスされたポインター] チェックでレッド エラーが示されます。
[結果の詳細]:チェック メッセージで、ポインターが 4 バイトのバッファー内でオフセット 4 にある 4 バイトを指しているため、範囲外であることが通知されます。
"ポイント先のサイズ" は 4 バイトです。これは、ポインター
ptr
がint
を指しているためです。"バッファー サイズ" は 4 バイトです。これは、バッファー サイズは指されている変数である
myStruct.f1
の型に基づくためです。myStruct.f1
の型はint
です。"オフセット" は 4 です。これは、演算
(ptr + 1)
により、ポインターがバッファーの先頭からint
の 1 サイズ分移動されるためです。
データ型のサイズはターゲットによって異なることに注意してください。この例でのサイズは、最も一般的なターゲットの場合を表しています。
可能な修正の 1 つは、構造体全体へのポインターを Initialize
に渡すことです。
typedef struct S { int f1; int f2; int f3; } S; void Initialize(S* ptr) { ptr->f1 = 0; ptr->f2 = 0; ptr->f3 = 0; } void main(void) { S myStruct; Initialize(&myStruct); }
#include<stdlib.h>
void main() {
int *ptr=NULL;
*ptr=0;
}
この例では、ptr
に値 NULL
が割り当てられます。したがって、ptr
をデリファレンスすると、[不適切にデリファレンスされたポインター] チェックでレッド エラーが示されます。
ポインターが 0x0000
などの絶対アドレスで初期化される場合は、同様のエラーが表示されます。
#define RAM_START 0x0000
void main() {
int *ptr;
ptr = RAM_START;
*ptr = 0;
}
[結果の詳細]:チェック メッセージで、ポインターが null であることが通知されます。ポインターが null であるため、チェックは、ポインターが割り当てられたバッファー内を指しているかどうかの検証に進みません。
1 つの修正方法として、NULL
の代わりに変数のアドレスを使った ptr
の初期化があります。
void main() {
int var;
int *ptr=&var;
*ptr=0;
}
エラーを回避するには、解析目的のためだけに 0x0000
を別のアドレスで置き換えます。次に例を示します。
#ifdef POLYSPACE
#define RAM_START 0x0001
#else
#define RAM_START 0x0000
#endif
void main() {
int *ptr;
ptr = (int*)RAM_START;//Cast int to int*
*ptr = 0;
}
-D POLYSPACE
を使用して、Polyspace 解析のためにアドレス 0x0000
が代替アドレス (この例では 0x0001
) に置き換えられるようにします。プリプロセッサ定義 (-D)
も参照してください。この解決策は、RAM_START
が有効なアドレスであることがわかっている場合にのみ使用してください。int getOffset(void);
void main() {
int *ptr = (int*) 0 + getOffset();
if(ptr != (int*)0)
*ptr = 0;
}
この例では、オフセットが (int*) 0
に追加されますが、Polyspace ではその結果が有効なアドレスとして扱われません。したがって、ptr
をデリファレンスすると、[不適切にデリファレンスされたポインター] チェックでレッド エラーが示されます。
[結果の詳細]:チェック メッセージで、ポインター自体は null ではないものの、メモリが割り当てられていない可能性があることが通知されます。
int arr[10];
int main(int arg, char* argv[]) {
int *ptr = &arr[10];
int val_ptr = *ptr;
return 0;
}
この例では、ポインター ptr
に、配列 arr
に割り当てられているメモリを超えたアドレスが代入されています。ただし、この代入では [不適切にデリファレンスされたポインター] チェックはトリガーされません。このチェックは、ポインターがデリファレンスされた場合にのみ行われ、明確なエラー (レッド) を示します。
struct flagCollection {
unsigned int flag1: 1;
unsigned int flag2: 1;
unsigned int flag3: 1;
unsigned int flag4: 1;
unsigned int flag5: 1;
unsigned int flag6: 1;
unsigned int flag7: 1;
};
char getFlag(void);
int main()
{
unsigned char myFlag = getFlag();
struct flagCollection* myFlagCollection;
myFlagCollection = (struct flagCollection *) &myFlag;
if (myFlagCollection->flag1 == 1)
return 1;
return 0;
}
この例では以下のようになります。
flagCollection
のフィールドはタイプがunsigned int
になります。したがって、フィールドそのものの占有は 7 ビットですが、flagCollection
構造体は 32 ビット アーキテクチャ内に 32 ビットのメモリを必要とします。char
アドレス&myFlag
をflagCollection
ポインターmyFlagCollection
にキャストする場合、ポインターには 8 ビットのメモリしか割り当てられません。したがって、myFlagCollection
のデリファレンスに対し、[不適切にデリファレンスされたポインター] チェックでレッド エラーが示されます。
[結果の詳細]:チェック メッセージで、ポインターが 1 バイトのバッファー内でオフセット 0 にある 4 バイトを指しているため、範囲外であることが通知されます。
"ポイント先のサイズ" は 4 バイトです。これは、ポインターが実質的に 1 つの
unsigned int
フィールドをもつ構造体を指しているためです。すべてのビット フィールドを 1 つのunsigned int
内に格納できます。"バッファー サイズ" は 1 バイトです。これは、バッファー サイズは指されている変数である
myFlag
の型に基づくためです。この変数のデータ型はchar
です。"オフセット" は 0 です。これは、ポインターがバッファーの先頭を指しているためです。ポインター演算は関与しません。
データ型のサイズはターゲットによって異なることに注意してください。この例でのサイズは、最も一般的なターゲットの場合を表しています。
1 つの修正方法として、flagCollection
のフィールド タイプとして unsigned int
ではなく unsigned char
を使用します。この例の場合は次のようになります。
構造体
flagCollection
は 8 ビットのメモリを必要とします。char
アドレス&myFlag
をflagCollection
ポインターmyFlagCollection
にキャストする場合、ポインターにも 8 ビットのメモリが割り当てられます。したがって、myFlagCollection
のデリファレンスに対し [不適切にデリファレンスされたポインター] チェックはグリーンになります。
struct flagCollection {
unsigned char flag1: 1;
unsigned char flag2: 1;
unsigned char flag3: 1;
unsigned char flag4: 1;
unsigned char flag5: 1;
unsigned char flag6: 1;
unsigned char flag7: 1;
};
char getFlag(void);
int main()
{
unsigned char myFlag = getFlag();
struct flagCollection* myFlagCollection;
myFlagCollection = (struct flagCollection *) &myFlag;
if (myFlagCollection->flag1 == 1)
return 1;
return 0;
}
#include <stdlib.h>
void main(void)
{
char *p = (char*)malloc(1);
char *q = p;
*q = 'a';
}
この例では、malloc
が NULL
を p
に返す可能性があります。したがって、p
を q
に割り当てて q
をデリファレンスすると、[不適切にデリファレンスされたポインター] チェックでオレンジ エラーが示されます。
[結果の詳細]:チェック メッセージで、ポインターが null である可能性があることが通知されます。
malloc
の戻り値を NULL
についてチェック1 つの修正方法として、q
のデリファレンスの前に p
を NULL
についてチェックします。
#include <stdlib.h>
void main(void)
{
char *p = (char*)malloc(1);
char *q = p;
if(p!=NULL) *q = 'a';
}
#include <stdlib.h>
typedef struct {
int state;
union {
char myChar;
int myInt;
} myVar;
} myType;
void main() {
myType* myTypePtr;
myTypePtr = (myType*)malloc(sizeof(int) + sizeof(char));
if(myTypePtr != NULL) {
myTypePtr->state = 0;
}
}
この例では以下のようになります。
共用体
myVar
はint
変数をフィールドとしてもつため、32 ビット アーキテクチャにおいて 4 バイトをこれに割り当てなければなりません。したがって、構造体myType
には 4+4 = 8 バイトを割り当てる必要があります。malloc
は、sizeof(int) + sizeof(char)
=4+1=5 バイトのメモリをmyTypePtr
に返します。これはmyType
構造体を指すポインターです。したがって、myTypePtr
をデリファレンスすると、[不適切にデリファレンスされたポインター] チェックはレッド エラーを返します。
[結果の詳細]:チェック メッセージで、ポインターが 5 バイトのバッファー内でオフセット 0 にある 8 バイトを指しているため、範囲外であることが通知されます。
"ポイント先のサイズ" は 8 バイトです。これは、ポインター
myTypePtr
はmyType
型を指しているためです。構造体myType
には次の 2 つのフィールドがあります。int
フィールド (4 バイト)。union
フィールド。共用体に含まれる最大の型はint
(4 バイト) です。
"バッファー サイズ" は 5 バイトです。これは、ヒープに割り当てられたメモリのサイズが
sizeof(int) + sizeof(char)
、つまり 4+1 バイトであるためです。"オフセット" は 0 です。これは、ポインターがバッファーの先頭を指しているためです。ポインター演算は関与しません。
データ型のサイズはターゲットによって異なることに注意してください。この例でのサイズは、最も一般的なターゲットの場合を表しています。
1 つの修正方法として、デリファレンスの前に 8 バイトのメモリを myTypePtr
に割り当てるとします。
#include <stdlib.h>
typedef struct {
int state;
union {
char myChar;
int myInt;
} myVar;
} myType;
void main() {
myType* myTypePtr;
myTypePtr = (myType*)malloc(sizeof(int) * 2);
if(myTypePtr != NULL) {
myTypePtr->state = 0;
}
}
#include <stdlib.h>
typedef struct {
int length;
int breadth;
} rectangle;
typedef struct {
int length;
int breadth;
int height;
} cuboid;
void main() {
cuboid *cuboidPtr = (cuboid*)malloc(sizeof(rectangle));
if(cuboidPtr!=NULL) {
cuboidPtr->length = 10;
cuboidPtr->breadth = 10;
}
}
この例では、cuboidPtr
はフィールドのうちの 2 つに十分対応するだけのメモリを獲得します。ANSI® C 標準ではこのような部分的なメモリ割り当ては許可されないため、cuboidPtr
のデリファレンスに対し、[不適切にデリファレンスされたポインター] チェックでレッド エラーが示されます。
[結果の詳細]:チェック メッセージで、ポインターが 8 バイトのバッファー内でオフセット 0 にある 12 バイトを指しているため、範囲外であることが通知されます。
"ポイント先のサイズ" は 12 バイトです。これは、ポインター
ptr
はcuboid
型を指しているためです。cuboid
型には 3 つのint
メンバーがあるため、そのサイズは 3 * 4 バイトとなります。"バッファー サイズ" は 8 バイトです。これは、ヒープに割り当てられたメモリのサイズは
sizeof(rectangle)
と等しくなるためです。rectange
型には 2 つのint
メンバーがあるため、そのサイズは 2 * 4 バイトとなります。"オフセット" は 0 です。これは、ポインターがバッファーの先頭を指しているためです。ポインター演算は関与しません。
データ型のサイズはターゲットによって異なることに注意してください。この例でのサイズは、最も一般的なターゲットの場合を表しています。
ANSI C 規格を順守するには、cuboidPtr
に完全なメモリを割り当てなければなりません。
#include <stdlib.h> typedef struct { int length; int breadth; } rectangle; typedef struct { int length; int breadth; int height; } cuboid; void main() { cuboid *cuboidPtr = (cuboid*)malloc(sizeof(cuboid)); if(cuboidPtr!=NULL) { cuboidPtr->length = 10; cuboidPtr->breadth = 10; } }
構造体に対して部分的にメモリを割り当て、しかも [不適切にデリファレンスされたポインター] のレッド エラーが発生しないようにできます。部分的なメモリの割り当てを可能にするには、[構成] ペインの [チェック動作] で、[構造体の不完全または部分的割り当てを許可する] を選択します。
#include <stdlib.h> typedef struct { int length; int breadth; } rectangle; typedef struct { int length; int breadth; int height; } cuboid; void main() { cuboid *cuboidPtr = (cuboid*)malloc(sizeof(rectangle)); if(cuboidPtr!=NULL) { cuboidPtr->length = 10; cuboidPtr->breadth = 10; } }
#include <stdlib.h> typedef struct { int length; int breadth; } square; void main() { square mySquare; char* squarePtr = (char*)&mySquare.length; //Assign zero to mySquare.length byte by byte for(int byteIndex=1; byteIndex<=4; byteIndex++) { *squarePtr=0; squarePtr++; } //Assign zero to first byte of mySquare.breadth *squarePtr=0; }
この例では、squarePtr
は char
ポインターですが、これに整数 mySquare.length
のアドレスが割り当てられています。その理由は次のとおりです。
char
は 1 バイトを占めます。int
は 32 ビット アーキテクチャにおいて 4 バイトを占めます。
squarePtr
はポインター演算を通して mySquare.length
の 4 バイトにアクセスできます。ただし、別のフィールド mySquare.breadth
の最初のバイトにアクセスすると、[不適切にデリファレンスされたポインター] チェックでレッド エラーが示されます。
[結果の詳細]:チェック メッセージで、ポインターが 4 バイトのバッファー内でオフセット 4 にある 1 バイトを指しているため、範囲外であることが確認されます。
"ポイント先のサイズ" は 1 バイトです。これは、ポインター
squarePtr
がchar
を指しているためです。"バッファー サイズ" は 4 バイトです。これは、バッファー サイズは指されている変数である
mySquare.length
の型に基づくためです。mySquare.length
の型はint
です。"オフセット" は 4 です。これは、演算
squarePtr++
を使用することで、バッファーの先頭から 1 つのint
のサイズ分、ポインターが移動されているためです。
データ型のサイズはターゲットによって異なることに注意してください。この例でのサイズは、最も一般的なターゲットの場合を表しています。
1 つの修正方法として、mySquare.length
ではなく、全構造体 mySquare
のアドレスを squarePtr
に割り当てるとします。これによって squarePtr
は、ポインター演算を通して mySquare
のすべてのバイトにアクセスできるようになります。
#include <stdlib.h> typedef struct { int length; int breadth; } square; void main() { square mySquare; char* squarePtr = (char*)&mySquare; //Assign zero to mySquare.length byte by byte for(int byteIndex=1; byteIndex<=4; byteIndex++) { *squarePtr=0; squarePtr++; } //Assign zero to first byte of mySquare.breadth *squarePtr=0; }
ポインターを使用して構造体の複数のフィールドに移動し、かつ [不適切にデリファレンスされたポインター] のレッド エラーが生じないようにできます。そのような移動を可能にするには、[構成] ペインの [チェック動作] で、[フィールド間のポインター演算を有効にする] を選択します。
このオプションは C++ プロジェクトでは使用できません。C++ では、多様型などの概念を扱う場合はポインター演算が非トリビアルになります。
#include <stdlib.h> typedef struct { int length; int breadth; } square; void main() { square mySquare; char* squarePtr = (char*)&mySquare.length; //Assign zero to mySquare.length byte by byte for(int byteIndex=1; byteIndex<=4; byteIndex++) { *squarePtr=0; squarePtr++; } //Assign zero to first byte of mySquare.breadth *squarePtr=0; }
void func2(int *ptr) {
*ptr = 0;
}
int* func1(void) {
int ret = 0;
return &ret ;
}
void main(void) {
int* ptr = func1() ;
func2(ptr) ;
}
次のコードで、ptr
は ret
を指します。ret
のスコープは func1
に制限されているため、func2
内で ptr
がアクセスされると、そのアクセスは無効になります。検証では、*ptr
に対してレッドの [不適切にデリファレンスされたポインター] チェックが示されます。
Polyspace Code Prover™ の既定の設定では、ローカル変数にポインターを返す関数は検出されません。このような場合に検出されるようにするには、オプションスコープ外のスタック ポインター デリファレンスを検出 (-detect-pointer-escape)
を使用します。
[結果の詳細]:チェック メッセージで、ポインターがスコープ外でアクセスされるローカル変数を指していることが通知されます。
#include <stdlib.h>
#include <stdio.h>
int increment_content_of_address(int base_val, int shift)
{
int j;
int* pi = (int*)malloc(sizeof(int));
if (pi == NULL) return 0;
*pi = base_val;
if(shift < 0) {
free(pi);
}
j = *pi + shift;
return j;
}
この例では、引数 shift
が負である場合、割り当てられたメモリが関数 free()
を使用して解放された後に、ポインター pi
がデリファレンスされます。[不適切にデリファレンスされたポインター] チェックは、この問題を検出して、潜在的なランタイム エラーを報告することができます (オレンジ チェック)。
この例では、ポインター ptr1
のエイリアスが条件付きで再割り当てされます。randomVar
が true の場合、エイリアス ptr2
は新しい場所を指します。そうでない場合は、ptr1
と ptr2
の両方が同じ場所を指します。free
ステートメントの後の ptr2
のデリファレンスでは、randomVar
が false の場合に既に解放されているポインターのデリファレンスが試みられます。Polyspace は、このランタイム エラーのオレンジ チェックを報告します。
int main() {
volatile int randomVar;
int *ptr1 = (int *)malloc(sizeof(int));
void *ptr2 = (void *)ptr1;
if(randomVar) {
ptr2 = malloc(sizeof(int));
assert(ptr2 != 0);
}
free(ptr1);
*(int *)ptr2 = randomVar; // Potential IDP
}
チェック情報
グループ: 静的メモリ |
言語: C | C++ |
頭字語: IDP |
MATLAB Command
You clicked a link that corresponds to this MATLAB command:
Run the command by entering it in the MATLAB Command Window. Web browsers do not support MATLAB commands.
Web サイトの選択
Web サイトを選択すると、翻訳されたコンテンツにアクセスし、地域のイベントやサービスを確認できます。現在の位置情報に基づき、次のサイトの選択を推奨します:
また、以下のリストから Web サイトを選択することもできます。
最適なサイトパフォーマンスの取得方法
中国のサイト (中国語または英語) を選択することで、最適なサイトパフォーマンスが得られます。その他の国の MathWorks のサイトは、お客様の地域からのアクセスが最適化されていません。
南北アメリカ
- América Latina (Español)
- Canada (English)
- United States (English)
ヨーロッパ
- Belgium (English)
- Denmark (English)
- Deutschland (Deutsch)
- España (Español)
- Finland (English)
- France (Français)
- Ireland (English)
- Italia (Italiano)
- Luxembourg (English)
- Netherlands (English)
- Norway (English)
- Österreich (Deutsch)
- Portugal (English)
- Sweden (English)
- Switzerland
- United Kingdom (English)