CWE Rule 119
説明
ルールの説明
The software performs operations on a memory buffer, but it can read from or write to a memory location that is outside of the intended boundary of the buffer.
Polyspace 実装
ルール チェッカーは以下の問題をチェックします。
配列が範囲外にアクセス
汚染されたインデックスによる配列へのアクセス
不正確な文字列形式指定子によるバッファー オーバーフロー
異なる型のオブジェクトを指すポインターへのキャスト
文字列操作で格納先バッファーがオーバーフローしています
文字列操作で格納先バッファーがアンダーフローしています
標準ライブラリ メモリ ルーチンの無効な使用
標準ライブラリ ルーチンの無効な使用
標準ライブラリ文字列ルーチンの無効な使用
データの長さとサイズが一致していません
string 配列での null 値の欠落
範囲外にアクセスするポインター
汚染されたオフセットによるポインターのデリファレンス
sizeof を誤って使用している可能性があります
最初に再初期化せずに、再割り当てされたメモリを別の型のオブジェクトから読み取る
汚染された NULL 文字列または非 NULL 終端文字列
危険な標準関数を使用しています
不確定文字列の使用
例
この問題は、配列へのアクセス中に配列のインデックスが範囲 [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® は外部ソースからのデータは汚染されていると仮定します。Polyspace 解析での汚染のソースを参照してください。Polyspace 解析の現在のスコープ以外から発生したすべてのデータを汚染されたものと見なすには、コマンド ライン オプション [-consider-analysis-perimeter-as-trust-boundary]
を使用します。
#include <stdlib.h>
#include <stdio.h>
#define SIZE100 100
extern int tab[SIZE100];
static int tainted_int_source(void) {
return strtol(getenv("INDEX"),NULL,10);
}
int taintedarrayindex(void) {
int num = tainted_int_source();
return tab[num];//Noncompliant //Noncompliant
}
この例では、インデックス num
により配列 tab
にアクセスします。インデックス num
は保護されないソースから取得され、関数 taintedarrayindex
は num
が tab
の範囲内にあるかどうかをチェックしません。
1 つの修正方法として、num
が範囲内にあることを使用前にチェックします。
#include <stdlib.h>
#include <stdio.h>
#define SIZE100 100
extern int tab[SIZE100];
static int tainted_int_source(void) {
return strtol(getenv("INDEX"),NULL,10);
}
int taintedarrayindex(void) {
int num = tainted_int_source();
if (num >= 0 && num < SIZE100) {
return tab[num];
} else {
return -1;
}
}
この問題は、sscanf
などの関数の書式指定子引数が、メモリ バッファー引数でのオーバーフローまたはアンダーフローにつながる場合に発生します。
形式指定子で指定した精度がメモリ バッファー サイズより大きいと、オーバーフローが発生します。オーバーフローは、メモリ破損のような予期しない動作を引き起こす可能性があります。
メモリ バッファー サイズに適合する形式指定子を使用します。
#include <stdio.h>
void func (char *str[]) {
char buf[32];
sscanf(str[1], "%33c", buf); //Noncompliant
}
この例では、buf
に char
要素を 32 個格納できます。したがって、形式指定子 %33c
はバッファー オーバーフローの原因となります。
考えられる修正の 1 つは、バッファーに読み込む要素の数を減らすことです。
#include <stdio.h>
void func (char *str[]) {
char buf[32];
sscanf(str[1], "%32c", buf);
}
この問題は、あるオブジェクト型へのポインターと別のオブジェクト型へのポインターの間でキャストを実行する場合に発生します。
オブジェクトへのポインターが別のオブジェクトへのポインターにキャストされる場合、結果のポインターは正しく配置されない可能性があります。不適切なアライメントは未定義の動作を発生させます。
変換によって正しく配置されたポインターが生成されたとしても、そのポインターがオブジェクトへのアクセスに使用される場合、動作が未定義となることがあります。
例外:オブジェクト型を指すポインターは、以下のいずれかの型を指すポインターに変換できます。
char
signed char
unsigned char
signed char *p1;
unsigned int *p2;
void foo(void){
p2 = ( unsigned int * ) p1; /* Non-compliant */
}
この例では、p1
は signed char
オブジェクトを指します。しかしながら、p1
は範囲のより広いオブジェクト型 unsigned int
を指すポインターにキャストされています。
extern unsigned int read_value ( void );
extern void display ( unsigned int n );
void foo ( void ){
unsigned int u = read_value ( );
unsigned short *hi_p = ( unsigned short * ) &u; /* Non-compliant */
*hi_p = 0;
display ( u );
}
この例では、u
は unsigned int
変数です。&u
は、範囲のより狭いオブジェクト型 unsigned short
を指すポインターにキャストされています。
ビッグエンディアン マシンでは、ステートメント *hi_p = 0
は &u
が指すメモリ位置の高位ビットをクリアしようと試みます。しかし、display(u)
の結果を見ると、その高位ビットがクリアされていないことがあります。
const short *p;
const volatile short *q;
void foo (void){
q = ( const volatile short * ) p; /* Compliant */
}
この例では、p
と q
のいずれも short
オブジェクトを指します。この両者間のキャストは volatile
修飾子のみを追加し、したがって準拠性をもちます。
この問題は、特定の文字列操作関数によって、その格納先バッファー引数にバッファー サイズを超えるオフセットで書き込まれた場合に発生します。
たとえば、関数 sprintf(char* buffer, const char* format)
を呼び出す際に、buffer
より大きいサイズの定数文字列 format
を使用する場合などです。
バッファー オーバーフローにより、メモリ破損やシステム停止といった予期しない動作を引き起こす可能性があります。また、バッファー オーバーフローは、コード インジェクションのリスクにもつながります。
1 つの解決策として、代替となる関数を使用して、書き込まれる文字の数を制限します。次に例を示します。
書式設定されたデータを文字列に書き込むのに
sprintf
を使用している場合は、代わりにsnprintf
、_snprintf
またはsprintf_s
を使用して長さを制御します。あるいは、asprintf
を使用して、格納先バッファーに必要なメモリを自動で割り当てます。書式設定されたデータを可変引数リストから文字列に書き込むのに
vsprintf
を使用している場合は、代わりにvsnprintf
またはvsprintf_s
を使用して長さを制御します。ワイド文字列をコピーするのに
wcscpy
を使用している場合は、代わりにwcsncpy
、wcslcpy
またはwcscpy_s
を使用して長さを制御します。
別の解決策として、バッファー サイズを増やします。
#include <stdio.h>
void func(void) {
char buffer[20];
char *fmt_string = "This is a very long string, it does not fit in the buffer";
sprintf(buffer, fmt_string); //Noncompliant
}
この例では、buffer
は char
要素を 20 個格納できますが、fmt_string
はより大きいサイズとなっています。
snprintf
を sprintf
の代わりに使用1 つの修正方法として、関数 snprintf
を使用して長さを制御します。
#include <stdio.h>
void func(void) {
char buffer[20];
char *fmt_string = "This is a very long string, it does not fit in the buffer";
snprintf(buffer, 20, fmt_string);
}
この問題は、特定の文字列操作関数によって、その格納先バッファー引数にバッファーの先頭から負のオフセットで書き込まれた場合に発生します。
たとえば、関数 sprintf(char* buffer, const char* format)
では buffer
を演算 buffer = (char*)arr; ... buffer += offset;
から取得します。arr
は配列で、offset
は負の値です。
バッファー アンダーフローにより、メモリ破損やシステム停止のような予期しない動作を引き起こす可能性があります。また、バッファー アンダーフローは、コード インジェクションのリスクにもつながります。
格納先バッファーの引数がポインター演算の結果得られたものである場合は、ポインターをデクリメントしているかどうかを確認します。デクリメント前の元の値かデクリメント値を変更することで、ポインターのデクリメントを修正します。
#include <stdio.h>
#define offset -2
void func(void) {
char buffer[20];
char *fmt_string ="Text";
sprintf(&buffer[offset], fmt_string); //Noncompliant
}
この例では、&buffer[offset]
は buffer
に割り当てられているメモリから負のオフセットの位置にあります。
1 つの修正方法として、offset
の値を変更します。
#include <stdio.h>
#define offset 2
void func(void) {
char buffer[20];
char *fmt_string ="Text";
sprintf(&buffer[offset], fmt_string);
}
この問題は、メモリ ライブラリ関数が無効な引数で呼び出された場合に発生します。たとえば、関数 memcpy
でコピー先の配列に格納できないバイト数をコピーする場合が該当します。
無効な引数でメモリ ライブラリ関数を使用すると、バッファー オーバーフローなどの問題が発生する可能性があります。
修正方法は欠陥の根本原因によって異なります。多くの場合、結果の詳細 (または Polyspace as You Code のソース コード ツールヒント) には欠陥につながる一連のイベントが表示されます。そのシーケンス内のどのイベントについても修正を実装できます。結果の詳細にイベント履歴が表示されない場合は、ソース コード内で右クリック オプションを使用して、欠陥に関連する変数のこれまでの参照を検索し、関連するイベントを検出できます。Polyspace デスクトップ ユーザー インターフェイスでの Bug Finder の結果の解釈またはPolyspace Access Web インターフェイスでの Bug Finder の結果の解釈 (Polyspace Access)も参照してください。
以下の修正例を参照してください。
問題を修正しない場合は、改めてレビューされないように結果またはコードにコメントを追加します。詳細は、以下を参照してください。
Polyspace ユーザー インターフェイスでのバグ修正または正当化による結果への対処 (Polyspace ユーザー インターフェイスで結果をレビューする場合)。
Polyspace Access でのバグ修正または正当化による結果への対処 (Polyspace Access) (Web ブラウザーで結果をレビューする場合)。
コードへの注釈付けと既知の結果または許容可能な結果の非表示 (IDE で結果をレビューする場合)
#include <string.h>
#include <stdio.h>
char* Copy_First_Six_Letters(void)
{
char str1[10],str2[5];
printf("Enter string:\n");
scanf("%9s",str1);
memcpy(str2,str1,6); //Noncompliant
/* Defect: Arguments of memcpy invalid: str2 has size < 6 */
return str2;
}
文字列 str2
のサイズは 5 ですが、関数 memcpy
を使用して 6 文字の文字列 str1
が str2
にコピーされています。
1 つの修正方法として、関数 memcpy
でコピーされる文字が収まるように、str2
のサイズを調整するとします。
#include <string.h>
#include <stdio.h>
char* Copy_First_Six_Letters(void)
{
/* Fix: Declare str2 with size 6 */
char str1[10],str2[6];
printf("Enter string:\n");
scanf("%9s",str1);
memcpy(str2,str1,6);
return str2;
}
この問題は、文字列ライブラリ関数が無効な引数で呼び出された場合に発生します。
リスクは無効な引数のタイプによって異なります。たとえば、コピー先引数より大きいコピー元引数を指定して関数 strcpy
を使用すると、バッファー オーバーフローが発生する可能性があります。
修正方法は欠陥に関連する標準ライブラリ関数に依存します。場合によっては、関数呼び出しの前に関数の引数を制約することができます。たとえば、次の関数 strcpy
を考えます。
char * strcpy(char * destination, const char* source)
strcpy
を呼び出す前にコピー元引数を制約します。場合によっては、代替となる関数を使用してエラーを回避できます。たとえば、strcpy
の代わりに strncpy
を使用するとコピーされるバイト数を制御できます。以下の修正例を参照してください。
問題を修正しない場合は、改めてレビューされないように結果またはコードにコメントを追加します。詳細は、以下を参照してください。
Polyspace ユーザー インターフェイスでのバグ修正または正当化による結果への対処 (Polyspace ユーザー インターフェイスで結果をレビューする場合)。
Polyspace Access でのバグ修正または正当化による結果への対処 (Polyspace Access) (Web ブラウザーで結果をレビューする場合)。
コードへの注釈付けと既知の結果または許容可能な結果の非表示 (IDE で結果をレビューする場合)
#include <string.h>
#include <stdio.h>
char* Copy_String(void)
{
char *res;
char gbuffer[5],text[20]="ABCDEFGHIJKL";
res=strcpy(gbuffer,text); //Noncompliant
/* Error: Size of text is less than gbuffer */
return(res);
}
文字列 text
はサイズが gbuffer
より大きくなっています。したがって、関数 strcpy
は text
を gbuffer
にコピーできません。
1 つの修正方法として、コピー先の文字列 gbuffer
をソース文字列 text
以上のサイズで宣言するとします。
#include <string.h>
#include <stdio.h>
char* Copy_String(void)
{
char *res;
/*Fix: gbuffer has equal or larger size than text */
char gbuffer[20],text[20]="ABCDEFGHIJKL";
res=strcpy(gbuffer,text);
return(res);
}
この問題は、バッファー オーバーフローから保護するために、memcpy
、memset
、memmove
などのメモリ コピー関数の長さ引数とデータ バッファー引数がチェックされていない場合に発生します。
データ バッファーや長さの引数を攻撃者が操作できる場合、その攻撃者は、実際のデータ サイズをその長さより小さくすることによってバッファー オーバーフローを引き起こすことができます。
長さのこうした不一致のため、攻撃者は、データ バッファーに収まらないメモリを新しい場所にコピーできます。余剰分のメモリに機密情報が含まれている場合、攻撃者はそのデータにアクセスできるようになります。
この欠陥は、SSL Heartbleed バグと類似しています。
メモリのコピーまたは操作を行う際、長さの引数をデータから直接計算して、サイズが一致するようにします。
#include <stdlib.h>
#include <string.h>
typedef struct buf_mem_st {
char *data;
size_t max; /* size of buffer */
} BUF_MEM;
extern BUF_MEM beta;
int cpy_data(BUF_MEM *alpha)
{
BUF_MEM *os = alpha;
int num, length;
if (alpha == 0x0) return 0;
num = 0;
length = *os->data;
memcpy(&(beta.data[num]), os->data + 2, length); //Noncompliant
return(1);
}
この関数では、バッファー alpha
をバッファー beta
にコピーします。しかし、変数 length
は data+2
とは関連していません。
1 つの修正方法として、バッファーの長さを、最大値から 2 を引いた値に対してチェックします。このチェックにより、構造体 beta
にデータをコピーする十分なスペースが確保されます。
#include <stdlib.h>
#include <string.h>
typedef struct buf_mem_st {
char *data;
size_t max; /* size of buffer */
} BUF_MEM;
extern BUF_MEM beta;
int cpy_data(BUF_MEM *alpha)
{
BUF_MEM *os = alpha;
int num, length;
if (alpha == 0x0) return 0;
num = 0;
length = *os->data;
if (length<(os->max -2)) {
memcpy(&(beta.data[num]), os->data + 2, length);
}
return(1);
}
この問題は、null 文字 '\0'
で終了するだけの十分なスペースが文字列にない場合に発生します。
この欠陥は C のプロジェクトのみに当てはまります。
暗黙的な NULL 終端を仮定せずに文字列を配列にコピーすると、バッファー オーバーフローが発生する可能性があります。
文字配列をリテラルで初期化する場合、配列範囲の指定を避けます。
char three[] = "THREE";
初期化後に問題が発生した場合、NULL 終端を考慮するために配列のサイズを 1 だけ増やさなければならない場合があります。
特定の状況では、文字列の代わりに文字のシーケンスを使用して文字配列を初期化することが必要な場合があります。この場合、改めてレビューされないように結果またはコードにコメントを追加します。詳細は、以下を参照してください。
Polyspace ユーザー インターフェイスでのバグ修正または正当化による結果への対処 (Polyspace ユーザー インターフェイスで結果をレビューする場合)。
Polyspace Access でのバグ修正または正当化による結果への対処 (Polyspace Access) (Web ブラウザーで結果をレビューする場合)。
コードへの注釈付けと既知の結果または許容可能な結果の非表示 (IDE で結果をレビューする場合)
void countdown(int i)
{
static char one[5] = "ONE";
static char two[5] = "TWO";
static char three[5] = "THREE"; //Noncompliant
}
文字配列 three
のサイズは 5 で、この配列には 'T'
、'H'
、'R'
、'E'
および 'E'
の 5 つの文字があります。three
の大きさは 5 バイトしかないため、最後に null 文字を入れる余地がありません。
1 つの修正方法として、配列のサイズを変更して 5 つの文字と null 文字が入るようにできます。
void countdown(int i)
{
static char one[5] = "ONE";
static char two[5] = "TWO";
static char three[6] = "THREE";
}
1 つの修正方法として、配列のサイズを空白にしたままで文字列を初期化することができます。この初期化方法では、5 つの文字と終端の null 文字のために十分なメモリが割り当てられます。
void countdown(int i)
{
static char one[5] = "ONE";
static char two[5] = "TWO";
static char three[] = "THREE";
}
この問題は、ポインターがその範囲外でデリファレンスされた場合に発生します。
ポインターにアドレスが割り当てられると、そのポインターにメモリのブロックが関連付けられます。そのポインターを使用してそのブロック外のメモリにアクセスすることはできません。
範囲外のポインターのデリファレンスは未定義の動作です。予測不能な値を読み取ったり、許可されていない位置へのアクセスを試みてセグメンテーション違反が発生したりする可能性があります。
修正方法は欠陥の根本原因によって異なります。たとえば、ループ内のポインターをデリファレンスする際、次のいずれかの状況が発生したとします。
ループの上限が大きすぎる。
ポインターをインクリメントするためにポインター演算を使用する際、不適切な値でポインターを進めている。
この問題を修正するには、ループの範囲またはポインターのインクリメント値を変更する必要があります。
多くの場合、結果の詳細 (または 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
は割り当てられたメモリ ブロックの外を指しますが、もうデリファレンスされることはありません。
この問題は、ポインターのデリファレンスで、不明なソースまたはセキュリティで保護されていないソースからのオフセット変数が使用された場合に発生します。
このチェックでは、動的に割り当てられたバッファーに注目します。静的バッファーのオフセットについては、汚染されたインデックスによる配列へのアクセス
を参照してください。
インデックスは有効な配列範囲を外れている可能性があります。汚染されたインデックスが配列範囲を外れていると、以下の原因となることがあります。
バッファー アンダーフローまたはアンダーライト、すなわち、バッファーの先頭より前でのメモリへの書き込み。
バッファー オーバーフロー、すなわち、バッファーの末尾より後でのメモリへの書き込み。
バッファー オーバーリード、すなわち、対象バッファーの末尾より後でのメモリへのアクセス。
バッファーのアンダーリード、すなわち、対象バッファーの先頭より前のメモリへのアクセス。
攻撃者は、無効な読み取りや書き込みを使用してプログラムを侵害できます。
変数を使用してポインターにアクセスする前に、インデックスを検証します。変数が有効範囲内にありオーバーフローしないことを確認します。
既定では、Polyspace は外部ソースからのデータは汚染されていると仮定します。Polyspace 解析での汚染のソースを参照してください。Polyspace 解析の現在のスコープ以外から発生したすべてのデータを汚染されたものと見なすには、コマンド ライン オプション [-consider-analysis-perimeter-as-trust-boundary]
を使用します。
#include <stdio.h>
#include <stdlib.h>
enum {
SIZE10 = 10,
SIZE100 = 100,
SIZE128 = 128
};
extern void read_pint(int*);
int taintedptroffset(void) {
int offset;
scanf("%d",&offset);
int* pint = (int*)calloc(SIZE10, sizeof(int));
int c = 0;
if(pint) {
/* Filling array */
read_pint(pint);
c = pint[offset];//Noncompliant
free(pint);
}
return c;
}
この例では、関数が整数ポインター pint
を初期化しています。ポインターは、入力インデックス offset
を使用してデリファレンスされています。offset
の値はポインター範囲外である可能性があり、値域外エラーの原因となります。
1 つの修正方法として、offset
の値を検証します。offset
が有効範囲内にある場合のみ、ポインターのデリファレンスを続行します。
#include <stdlib.h>
#include <stdio.h>
enum {
SIZE10 = 10,
SIZE100 = 100,
SIZE128 = 128
};
extern void read_pint(int*);
int taintedptroffset(void) {
int offset;
scanf("%d",&offset);
int* pint = (int*)calloc(SIZE10, sizeof(int));
int c = 0;
if (pint) {
/* Filling array */
read_pint(pint);
if (offset>0 && offset<SIZE10) {
c = pint[offset];
}
free(pint);
}
return c;
}
この問題は、Polyspace Bug Finder™ が sizeof
演算子の使用が原因の、おそらく想定外である結果を検出した場合に発生します。次に例を示します。
配列のサイズを求めるため、
sizeof
演算子が配列パラメーター名で使用されている。しかし、配列パラメーター名それ自体がポインターである。sizeof
演算子からは、そのポインターのサイズが返される。配列のサイズを求めるため、
sizeof
演算子が配列要素で使用されている。しかし、演算子はその配列要素のサイズを返す。正しくない想定のもとに
sizeof
演算子を先に使用したため、特定の関数 (strncmp
やwcsncpy
など) の 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
演算子を慎重に使用して、strncmp
やwcsncpy
のような関数の数値引数を決定する。たとえば、wcsncpy
などのワイド文字列関数で、バイト数ではなくワイド文字の数を引数として使用します。
#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;
}
}
この問題は、以下の操作を順に実行すると発生します。
元の割り当てとは異なる型のオブジェクトにメモリを再割り当てする。
たとえばこのコードの抜粋では、本来
struct A*
型のポインターに割り当てられていたメモリが、struct B*
型のポインターに再割り当てされます。struct A; struct B; struct A *Aptr = (struct A*) malloc(sizeof(struct A)); struct B *Bptr = (struct B*) realloc(Aptr, sizeof(struct B));
再割り当てされたメモリを最初に再初期化せずに、このメモリから読み取る。
再割り当てされたメモリを指すポインターでの読み取りアクセスは、ポインター デリファレンスまたは配列のインデックス付けにおいて発生する可能性があります。
const
修飾子付きオブジェクトへのポインターを受け取る関数に、対応するパラメーターとしてこのポインターを渡す操作も、読み取りアクセスとしてカウントされます。
再初期化されていない再割り当てメモリからの読み取りを行うと、未定義の動作につながります。
再割り当ての後、最初の読み取りアクセスの前にメモリを再初期化します。
チェッカーでは、再割り当てされたメモリを指すポインターでの書き込みアクセスすべてが (オブジェクトの再初期化が部分的である場合でも)、再初期化の要件を満たしているものと解釈されます。再割り当てされたメモリを指すポインターでの書き込みアクセスは、ポインター デリファレンスと配列のインデックス付けにおいて発生する可能性があります。const
修飾子付きでないオブジェクトへのポインターを受け取る関数に、対応するパラメーターとしてこのポインターを渡す操作も、書き込みアクセスとしてカウントされます。
#include<stdlib.h>
struct group {
char *groupFirst;
int groupSize;
};
struct groupWithID {
int groupID;
char *groupFirst;
int groupSize;
};
char* readName();
int readSize();
void createGroup(int nextAvailableID) {
struct group *aGroup;
struct groupWithID *aGroupWithID;
aGroup = (struct group*) malloc(sizeof(struct group));
if(!aGroup) {
/*Handle error*/
}
aGroup->groupFirst = readName();
aGroup->groupSize = readSize();
/* Reassign to group with ID */
aGroupWithID = (struct groupWithID*) realloc(aGroup, sizeof(struct groupWithID));
if(!aGroupWithID) {
free(aGroup);
/*Handle error*/
}
if(aGroupWithID -> groupSize > 0) { /* Noncompliant */
/* ... */
}
/* ...*/
free(aGroupWithID);
}
この例では、関数 malloc
を使用して group*
ポインターに割り当てられたメモリを、関数 realloc
を使用して、groupWithID*
ポインターに再割り当てします。再割り当てされたメモリを再初期化する前に、このメモリへの読み取りアクセスが行われます。
最初の読み取りアクセスの前に、groupWithID*
ポインターに割り当てられているメモリを再初期化します。メモリのすべてのビットは、関数 memset
を使用して再初期化できます。
#include<stdlib.h>
#include<string.h>
struct group {
char *groupFirst;
int groupSize;
};
struct groupWithID {
int groupID;
char *groupFirst;
int groupSize;
};
char* readName();
int readSize();
void createGroup(int nextAvailableID) {
struct group *aGroup;
struct groupWithID *aGroupWithID;
aGroup = (struct group*) malloc(sizeof(struct group));
if(!aGroup) {
/*Handle error*/
}
aGroup->groupFirst = readName();
aGroup->groupSize = readSize();
/* Reassign to group with ID */
aGroupWithID = (struct groupWithID*) realloc(aGroup, sizeof(struct groupWithID));
if(!aGroupWithID) {
free(aGroup);
/*Handle error*/
}
memset(aGroupWithID, 0 , sizeof(struct groupWithID));
/* Reinitialize group */
if(aGroupWithID -> groupSize > 0) {
/* ... */
}
/* ...*/
free(aGroupWithID);
}
この問題は、strcpy
や sprintf
などの文字列バッファーを暗黙的にデリファレンスする文字列操作ルーチンで、セキュリティで保護されていないソースからの文字列が使用された場合に発生します。
汚染された NULL 文字列または非 NULL 終端文字列では、scanf
ファミリの可変個引数関数の呼び出しから返された文字列に関する欠陥が報告されません。同様に、文字列と一緒に %s
指定子を printf
ファミリの可変個引数関数に渡した場合も欠陥は報告されません。
文字列がセキュリティで保護されないソースに由来している場合、攻撃者により文字列が操作されている可能性や、文字列ポインターが異なるメモリ位置に向けられている可能性があります。
文字列が NULL である場合、文字列ルーチンは文字列をデリファレンスできず、プログラムがクラッシュする原因となります。文字列が null で終了しない場合、文字列ルーチンでは文字列がいつ終了するかわからない可能性があります。このエラーは範囲外への書き込みの原因となり、バッファー オーバーフローを引き起こします。
文字列は、使用する前に検証します。以下についてチェックします。
文字列が NULL でない。
文字列が null で終了している。
文字列のサイズが、必要なサイズと一致している。
既定では、Polyspace は外部ソースからのデータは汚染されていると仮定します。Polyspace 解析での汚染のソースを参照してください。Polyspace 解析の現在のスコープ以外から発生したすべてのデータを汚染されたものと見なすには、コマンド ライン オプション [-consider-analysis-perimeter-as-trust-boundary]
を使用します。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define SIZE128 128
#define MAX 40
extern void print_str(const char*);
void warningMsg(void)
{
char userstr[MAX];
read(0,userstr,MAX);
char str[SIZE128] = "Warning: ";
strncat(str, userstr, SIZE128-(strlen(str)+1));//Noncompliant
print_str(str);
}
この例では、文字列 str
は引数 userstr
と連結しています。userstr
の値は不明です。userstr
のサイズが使用可能なスペースより大きい場合、この連結はオーバーフローします。
1 つの修正方法として、strncat
で使用する前に、userstr
のサイズをチェックして、文字列が必ず null で終了するようにします。この例では、補助関数 sansitize_str
を使用して文字列を検証しています。欠陥はこの関数に集中しています。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define SIZE128 128
#define MAX 40
extern void print_str(const char*);
int sanitize_str(char* s) {
int res = 0;
if (s && (strlen(s) > 0)) { // Noncompliant-TAINTED_STRING only flagged here
// - string is not null
// - string has a positive and limited size
// - TAINTED_STRING on strlen used as a firewall
res = 1;
}
return res;
}
void warningMsg(void)
{
char userstr[MAX];
read(0,userstr,MAX);
char str[SIZE128] = "Warning: ";
if (sanitize_str(userstr))
strncat(str, userstr, SIZE128-(strlen(str)+1));
print_str(str);
}
別の修正方法として、特定の文字列を含む関数 errorMsg
および warningMsg
を呼び出します。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define SIZE128 128
extern void print_str(const char*);
void warningMsg(char* userstr)
{
char str[SIZE128] = "Warning: ";
strncat(str, userstr, SIZE128-(strlen(str)+1));
print_str(str);
}
void errorMsg(char* userstr)
{
char str[SIZE128] = "Error: ";
strncat(str, userstr, SIZE128-(strlen(str)+1));
print_str(str);
}
int manageSensorValue(int sensorValue) {
int ret = sensorValue;
if ( sensorValue < 0 ) {
errorMsg("sensor value should be positive");
exit(1);
} else if ( sensorValue > 50 ) {
warningMsg("sensor value greater than 50 (applying threshold)...");
sensorValue = 50;
}
return sensorValue;
}
この問題は、コードで、バッファー オーバーフローになる可能性があるやり方でデータをバッファーに書き込む標準関数が使用されている場合に発生します。
次の表に、危険な標準関数、各関数を使用した場合のリスク、および代用する関数を示します。チェッカーは以下にフラグを設定します。
本質的に危険な関数の使用。
コンパイル時にデータが書き込まれるバッファーのサイズを決定可能な場合にのみ、危険な可能性のある関数を使用する。チェッカーは、バッファーが動的に割り当てられるこのような関数の使用にフラグを設定しません。
危険な関数 | リスク レベル | より安全な関数 |
---|---|---|
gets | 本質的に危険 — コンソールからは入力の長さを制御できない。 | fgets |
std::cin::operator>> および std::wcin::operator>> | 本質的に危険 — コンソールからは入力の長さを制御できない。 | 入力長を制御するには、 バッファー オーバーフローと入力切り捨てが発生しないようにするには、 |
strcpy | 潜在的に危険 — 格納先バッファーのサイズが小さすぎて、ソース バッファーと null 終端が収まらない場合は、バッファー オーバーフローが発生する可能性がある。 | 関数 strlen() を使用してソース バッファーのサイズを決定し、格納先バッファーがソース バッファーと null 終端を収められるように十分なメモリを割り当てます。strcpy の代わりに、関数 strncpy を使用します。 |
stpcpy | 潜在的に危険 — ソースの長さがコピー先より大きいと、バッファー オーバーフローが発生する可能性がある。 | stpncpy |
lstrcpy または StrCpy | 潜在的に危険 — ソースの長さがコピー先より大きいと、バッファー オーバーフローが発生する可能性がある。 | StringCbCopy 、StringCchCopy 、strncpy 、strcpy_s または strlcpy |
strcat | 潜在的に危険 — 連結された結果が作成先より大きいと、バッファー オーバーフローが発生する可能性がある。 | strncat 、strlcat または strcat_s |
lstrcat または StrCat | 潜在的に危険 — 連結された結果が作成先より大きいと、バッファー オーバーフローが発生する可能性がある。 | StringCbCat 、StringCchCat 、strncay 、strcat_s または strlcat |
wcpcpy | 潜在的に危険 — ソースの長さがコピー先より大きいと、バッファー オーバーフローが発生する可能性がある。 | wcpncpy |
wcscat | 潜在的に危険 — 連結された結果が作成先より大きいと、バッファー オーバーフローが発生する可能性がある。 | wcsncat 、wcslcat または wcncat_s |
wcscpy | 潜在的に危険 — ソースの長さがコピー先より大きいと、バッファー オーバーフローが発生する可能性がある。 | wcsncpy |
sprintf | 潜在的に危険 — 出力の長さが不明な長さや値に依存していると、バッファー オーバーフローが発生する可能性がある。 | snprintf |
vsprintf | 潜在的に危険 — 出力の長さが不明な長さや値に依存していると、バッファー オーバーフローが発生する可能性がある。 | vsnprintf |
これらの関数はバッファー オーバーフローの原因となることがあり、攻撃者はこれを利用してプログラムに侵入できます。
修正方法は欠陥の根本原因によって異なります。上の表に記載されている修正と以下の修正付きのコード例を参照してください。
問題を修正しない場合は、改めてレビューされないように結果またはコードにコメントを追加します。詳細は、以下を参照してください。
Polyspace ユーザー インターフェイスでのバグ修正または正当化による結果への対処 (Polyspace ユーザー インターフェイスで結果をレビューする場合)。
Polyspace Access でのバグ修正または正当化による結果への対処 (Polyspace Access) (Web ブラウザーで結果をレビューする場合)。
コードへの注釈付けと既知の結果または許容可能な結果の非表示 (IDE で結果をレビューする場合)
#include <stdio.h>
#include <string.h>
#include <iostream>
#define BUFF_SIZE 128
int dangerous_func(char *str)
{
char dst[BUFF_SIZE];
int r = 0;
if (sprintf(dst, "%s", str) == 1) //Noncompliant
{
r += 1;
dst[BUFF_SIZE-1] = '\0';
}
return r;
}
この関数例では、sprintf
を使用して文字列 str
を dst
にコピーしています。しかし、str
がバッファーより大きいと、sprintf
がバッファー オーバーフローの原因となる場合があります。
snprintf
を使用1 つの修正方法として、snprintf
を代わりに使用し、バッファー サイズを指定します。
#include <stdio.h>
#include <string.h>
#include <iostream>
#define BUFF_SIZE 128
int dangerous_func(char *str)
{
char dst[BUFF_SIZE];
int r = 0;
if (snprintf(dst, sizeof(dst), "%s", str) == 1)
{
r += 1;
dst[BUFF_SIZE-1] = '\0';
}
return r;
}
この問題が発生するのは、下記に示すような fgets
ファミリ関数を使用した書き込み操作が成功したかどうかをチェックせず、
char * fgets(char* buf, int n, FILE *stream)
buf
である場合、チェッカーは以下の場合に欠陥を報告します。
文字列やワイド文字列を表示または操作する標準関数の引数として
buf
を渡す。関数から
buf
を返す。パラメーターの型として
const char *
またはconst wchar_t *
を使用する外部関数の引数としてbuf
を渡す。buf
をbuf[index]
または*(buf + offset)
として読み取る (ここでindex
またはoffset
は、バッファーの先頭からの距離を表す数値です)。
fgets
ファミリ関数が失敗した場合、出力バッファーの内容は不確定になります。このようなバッファーの使用には未定義の動作があり、プログラムによる処理の停止、他のセキュリティの脆弱性発生の原因になる可能性があります。
fgets
ファミリ関数が失敗した場合、関数の出力バッファーを既知の文字列値にリセットします。
#include <stdio.h>
#include <wchar.h>
#include <string.h>
#include <stdlib.h>
#define SIZE20 20
extern void display_text(const char *txt);
void func(void) {
char buf[SIZE20];
/* Check fgets() error */
if (fgets (buf, sizeof (buf), stdin) == NULL)
{
/* 'buf' may contain an indeterminate string. */
;
}
/* 'buf passed to external function */
display_text(buf); //Noncompliant
}
この例では、出力 buf
が外部関数 display_text()
に渡されますが、fgets()
が失敗しても出力値がリセットされません。
fgets()
の出力をリセットfgets()
が失敗した場合、外部関数に渡す前に buf
を既知の値にリセットします。
#include <stdio.h>
#include <wchar.h>
#include <string.h>
#include <stdlib.h>
#define SIZE20 20
extern void display_text(const char *txt);
void func1(void) {
char buf[SIZE20];
/* Check fgets() error */
if (fgets (buf, sizeof (buf), stdin) == NULL)
{
/* value of 'buf' reset after fgets() failure. */
buf[0] = '\0';
}
/* 'buf' passed to external function */
display_text(buf);
}
チェック情報
カテゴリ: その他 |
バージョン履歴
R2023a で導入修飾子がないオブジェクト型へのポインターを次のいずれかの型のポインターに変換する場合、Polyspace は違反を報告しません。
char
signed char
unsigned char
修飾子付きオブジェクトのポインターを変換する場合は、変換後の型が char*
、signed char*
、unsigned char*
のいずれかである場合でも、Polyspace が違反を報告します。
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)