CWE Rule 468
説明
ルールの説明
In C and C++, one may often accidentally refer to the wrong memory due to the semantics of when math operations are implicitly scaled.
Polyspace 実装
ルール チェッカーは以下の問題をチェックします。
配列が範囲外にアクセス
ポインターのスケーリングが無効です
範囲外にアクセスするポインター
異なる配列を指すポインター間の減算または比較
例
この問題は、配列へのアクセス中に配列のインデックスが範囲 [0...array_size-1]
を外れた場合に発生します。
配列の範囲外へのアクセスは未定義の動作です。予測不能な値を読み取ったり、許可されていない位置へのアクセスを試みてセグメンテーション違反が発生したりする可能性があります。
修正方法は欠陥の根本原因によって異なります。たとえば、ループ内の配列にアクセスする際、次のいずれかの状況が発生したとします。
ループの上限が大きすぎる。
ループ インデックスより 1 小さい配列インデックスを使用せずに、ループ インデックスと同じ配列インデックスを使用している。
この問題を修正するには、ループの範囲または配列インデックスを変更する必要があります。
配列インデックスが配列範囲を超える別の理由として、事前に行われた符号付き整数から符号なし整数への変換が考えられます。この変換はインデックス値のラップ アラウンドを発生させる可能性があり、最終的に配列インデックスが配列範囲を超える原因になります。
多くの場合、結果の詳細 (または Polyspace as You Code のソース コード ツールヒント) には欠陥につながる一連のイベントが表示されます。そのシーケンス内のどのイベントについても修正を実装できます。結果の詳細にイベント履歴が表示されない場合は、ソース コード内で右クリック オプションを使用して、欠陥に関連する変数のこれまでの参照を検索し、関連するイベントを検出できます。Polyspace デスクトップ ユーザー インターフェイスでの Bug Finder の結果の解釈またはPolyspace Access Web インターフェイスでの Bug Finder の結果の解釈 (Polyspace Access)も参照してください。
以下の修正例を参照してください。
問題を修正しない場合は、改めてレビューされないように結果またはコードにコメントを追加します。詳細は、以下を参照してください。
Polyspace ユーザー インターフェイスでのバグ修正または正当化による結果への対処 (Polyspace ユーザー インターフェイスで結果をレビューする場合)。
Polyspace Access でのバグ修正または正当化による結果への対処 (Polyspace Access) (Web ブラウザーで結果をレビューする場合)。
コードへの注釈付けと既知の結果または許容可能な結果の非表示 (IDE で結果をレビューする場合)
入力値が不明であり、入力のサブセットのみがエラーの原因となっている場合、既定の Bug Finder 解析ではこの欠陥が報告されない可能性があります。特定のシステム入力値を原因とする欠陥の有無をチェックするには、より厳密な Bug Finder 解析を実行してください。特定のシステム入力値から欠陥を見つけるための Bug Finder チェッカーの拡張を参照してください。
#include <stdio.h>
void fibonacci(void)
{
int i;
int fib[10];
for (i = 0; i < 10; i++)
{
if (i < 2)
fib[i] = 1;
else
fib[i] = fib[i-1] + fib[i-2];
}
printf("The 10-th Fibonacci number is %i .\n", fib[i]); //Noncompliant
/* Defect: Value of i is greater than allowed value of 9 */
}
配列 fib
にはサイズ 10 が割り当てられています。fib
の配列インデックスに許可される値は [0,1,2,...,9]
です。変数 i
が for
ループから出るときの値は 10 です。したがって、printf
ステートメントは i
を用いて fib[10]
にアクセスしようと試みます。
1 つの修正方法として、for
ループの後で、fib[i]
ではなく fib[i-1]
を出力するとします。
#include <stdio.h>
void fibonacci(void)
{
int i;
int fib[10];
for (i = 0; i < 10; i++)
{
if (i < 2)
fib[i] = 1;
else
fib[i] = fib[i-1] + fib[i-2];
}
/* Fix: Print fib[9] instead of fib[10] */
printf("The 10-th Fibonacci number is %i .\n", fib[i-1]);
}
printf
ステートメントは、fib[10]
ではなく fib[9]
にアクセスします。
この問題は、Polyspace® Bug Finder™ によって、ポインター演算での暗黙的なスケーリングが無視されているとみなされた場合に発生します。
たとえば、欠陥は次のような状況で発生します。
状態 | リスク | 考えられる解決方法 |
---|---|---|
ポインターでの算術演算で sizeof 演算子を使用する。 |
ポインター演算は既に、指されている変数のデータ型のサイズによって暗黙的にスケーリングされている。そのため、ポインター演算で | ポインター演算で sizeof 演算子を使用しない。 |
ポインターに対し算術演算を実行してから、キャストを適用する。 | ポインター演算は暗黙的にスケーリングされている。この暗黙的なスケーリングを考慮せずにポインター演算の結果をキャストすると、想定外の結果が生成される。 | キャストをポインター演算の前に適用する。 |
修正方法は欠陥の根本原因によって異なります。上の表に記載されている修正と以下の修正付きのコード例を参照してください。
問題を修正しない場合は、改めてレビューされないように結果またはコードにコメントを追加します。詳細は、以下を参照してください。
Polyspace ユーザー インターフェイスでのバグ修正または正当化による結果への対処 (Polyspace ユーザー インターフェイスで結果をレビューする場合)。
Polyspace Access でのバグ修正または正当化による結果への対処 (Polyspace Access) (Web ブラウザーで結果をレビューする場合)。
コードへの注釈付けと既知の結果または許容可能な結果の非表示 (IDE で結果をレビューする場合)
void func(void) {
int arr[5] = {1,2,3,4,5};
int *ptr = arr;
int value_in_position_2 = *(ptr + 2*(sizeof(int))); //Noncompliant
}
この例では、演算 2*(sizeof(int))
で変数 int
の 2 倍のサイズ (バイト単位) が返されます。ただし、ポインター演算は暗黙的にスケーリングされているため、ptr
がオフセットされるバイト数は 2*(sizeof(int))*(sizeof(int))
となります。
この例では、不正確なスケーリングにより、ptr
が配列の境界外にシフトされます。したがって、[範囲外にアクセスするポインター] エラーが *
演算に表示されます。
sizeof
演算子を削除1 つの修正方法として、sizeof
演算子を削除します。
void func(void) {
int arr[5] = {1,2,3,4,5};
int *ptr = arr;
int value_in_position_2 = *(ptr + 2);
}
int func(void) {
int x = 0;
char r = *(char *)(&x + 1); //Noncompliant
return r;
}
この例では、演算 &x + 1
により &x
が sizeof(int)
の分オフセットされます。演算後、その結果であるポインターは、許容されているバッファーの外を指しています。ポインターをデリファレンスすると、[範囲外にアクセスするポインター] エラーが *
演算に表示されます。
x
の 2 番目のバイトにアクセスする場合は、まず &x
を char*
ポインターにキャストしてからポインター演算を実行します。その結果得られるポインターは、sizeof(char)
バイト分オフセットされたうえで、サイズが sizeof(int)
バイトの許容されているバッファー内を指しています。
int func(void) {
int x = 0;
char r = *((char *)(&x )+ 1);
return r;
}
この問題は、ポインターがその範囲外でデリファレンスされた場合に発生します。
ポインターにアドレスが割り当てられると、そのポインターにメモリのブロックが関連付けられます。そのポインターを使用してそのブロック外のメモリにアクセスすることはできません。
範囲外のポインターのデリファレンスは未定義の動作です。予測不能な値を読み取ったり、許可されていない位置へのアクセスを試みてセグメンテーション違反が発生したりする可能性があります。
修正方法は欠陥の根本原因によって異なります。たとえば、ループ内のポインターをデリファレンスする際、次のいずれかの状況が発生したとします。
ループの上限が大きすぎる。
ポインターをインクリメントするためにポインター演算を使用する際、不適切な値でポインターを進めている。
この問題を修正するには、ループの範囲またはポインターのインクリメント値を変更する必要があります。
多くの場合、結果の詳細 (または Polyspace as You Code のソース コード ツールヒント) には欠陥につながる一連のイベントが表示されます。そのシーケンス内のどのイベントについても修正を実装できます。結果の詳細にイベント履歴が表示されない場合は、ソース コード内で右クリック オプションを使用して、欠陥に関連する変数のこれまでの参照を検索し、関連するイベントを検出できます。Polyspace デスクトップ ユーザー インターフェイスでの Bug Finder の結果の解釈またはPolyspace Access Web インターフェイスでの Bug Finder の結果の解釈 (Polyspace Access)も参照してください。
以下の修正例を参照してください。
問題を修正しない場合は、改めてレビューされないように結果またはコードにコメントを追加します。詳細は、以下を参照してください。
Polyspace ユーザー インターフェイスでのバグ修正または正当化による結果への対処 (Polyspace ユーザー インターフェイスで結果をレビューする場合)。
Polyspace Access でのバグ修正または正当化による結果への対処 (Polyspace Access) (Web ブラウザーで結果をレビューする場合)。
コードへの注釈付けと既知の結果または許容可能な結果の非表示 (IDE で結果をレビューする場合)
int* Initialize(void)
{
int arr[10];
int *ptr=arr;
for (int i=0; i<=9;i++)
{
ptr++;
*ptr=i; //Noncompliant
/* Defect: ptr out of bounds for i=9 */
}
return(arr);
}
ptr
には、サイズ 10*sizeof(int)
のメモリ ブロックを指すアドレス arr
が割り当てられます。for
ループで、ptr
は 10 回インクリメントされます。ループの最後の反復で、ptr
は割り当てられたメモリ ブロックの外を指します。したがって、デリファレンスはできません。
1 つの修正方法として、ptr
のインクリメントとデリファレンスの順序を逆にすることができます。
int* Initialize(void)
{
int arr[10];
int *ptr=arr;
for (int i=0; i<=9;i++)
{
/* Fix: Dereference pointer before increment */
*ptr=i;
ptr++;
}
return(arr);
}
最後のインクリメントの後で ptr
は割り当てられたメモリ ブロックの外を指しますが、もうデリファレンスされることはありません。
この問題は、null ポインターまたは別の配列内の要素を指しているポインターを減算または比較した場合に発生します。比較の関係演算子は >
、<
、>=
、および <=
です。
同じ配列内の要素を指す 2 つのポインターを減算する場合、結果は 2 つの配列要素の添字の間の差になります。同様に、配列の要素を指す 2 つのポインターを比較する場合、結果は相対的なポインターの位置になります。ポインターが null または異なる配列の要素を指している場合、減算や比較演算は未定義です。減算結果をバッファー インデックスとして使用する場合、バッファー オーバーフローが発生する可能性があります。
配列要素を指すポインターの減算や比較演算子による比較を行う前に、ポインターが非 null であり同じ配列を指していることをチェックします。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define SIZE20 20
size_t func(void)
{
int nums[SIZE20];
int end;
int *next_num_ptr = nums;
size_t free_elements;
/* Increment next_num_ptr as array fills */
/* Subtraction operation is undefined unless array nums
is adjacent to variable end
in memory. */
free_elements = &end - next_num_ptr; //Noncompliant
return free_elements;
}
この例では、配列 nums
がインクリメントされて埋めらます。その後、空の要素がいくつ残っているか判断するためにポインターの減算が使用されます。end
が nums
の最後の要素を超えたメモリ位置を指していない限り、減算演算は未定義です。
配列の最後の要素を指すポインターから埋められた最後の要素を指すポインターを減算します。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define SIZE20 20
size_t func(void)
{
int nums[SIZE20];
int *next_num_ptr = nums;
size_t free_elements;
/* Increment next_num_ptr as array fills */
/* Subtraction operation involves pointers to the same array. */
free_elements = &(nums[SIZE20 - 1]) - next_num_ptr;
return free_elements + 1;
}
チェック情報
カテゴリ: Pointer Issues |
バージョン履歴
R2023a で導入
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)