メインコンテンツ

CWE Rule 129

Improper Validation of Array Index

R2023a 以降

説明

ルールの説明

The product uses untrusted input when calculating or using an array index, but the product does not validate or incorrectly validates the index to ensure the index references a valid position within the array.

Polyspace 実装

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

  • 汚染されたインデックスによる配列へのアクセス

  • 標準ライブラリ文字列ルーチンの無効な使用

  • 汚染されたオフセットによるポインターのデリファレンス

  • 汚染された除算演算子

  • 汚染されたモジュロ演算子

すべて展開する

問題

この問題は、セキュリティで保護されていないソースから取得され、検証されていないインデックスを使用して配列にアクセスした場合に発生します。

リスク

インデックスは有効な配列範囲を外れている可能性があります。汚染されたインデックスが配列範囲を外れていると、以下の原因となることがあります。

  • バッファー アンダーフローまたはアンダーライト — バッファーの先頭より前のメモリへの書き込み。

  • バッファー オーバーフロー — バッファーの末尾より後のメモリへの書き込み。

  • バッファーのオーバーリード — 対象バッファーの末尾より後のメモリへのアクセス。

  • バッファーのアンダーリード、すなわち、対象バッファーの先頭より前のメモリへのアクセス。

攻撃者は無効な読み取りや書き込みの操作を利用して、プログラムに問題を生じさせることができます。

修正方法

インデックスを使用して配列にアクセスする前に、インデックス値を検証して、それが配列範囲内にあることを確認します。

チェッカーの拡張

既定では、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  
}

この例では、インデックス num により配列 tab にアクセスします。インデックス num は保護されないソースから取得され、関数 taintedarrayindexnumtab の範囲内にあるかどうかをチェックしません。

修正 — 使用前に範囲をチェック

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;
	}
}

問題

この問題は、文字列ライブラリ関数が無効な引数で呼び出された場合に発生します。

リスク

リスクは無効な引数のタイプによって異なります。たとえば、コピー先引数より大きいコピー元引数を指定して関数 strcpy を使用すると、バッファー オーバーフローが発生する可能性があります。

修正方法

修正方法は欠陥に関連する標準ライブラリ関数に依存します。場合によっては、関数呼び出しの前に関数の引数を制約することができます。たとえば、次の関数 strcpy を考えます。

char * strcpy(char * destination, const char* source)
これが、使用可能なバッファーに比べて大きすぎるバイト数をコピー先引数にコピーしようとする場合、strcpy を呼び出す前にコピー元引数を制約します。場合によっては、代替となる関数を使用してエラーを回避できます。たとえば、strcpy の代わりに strncpy を使用するとコピーされるバイト数を制御できます。

以下の修正例を参照してください。

問題を修正しない場合は、改めてレビューされないように結果またはコードにコメントを追加します。詳細は、以下を参照してください。

例 — 標準ライブラリ文字列ルーチンの無効な使用によるエラー
 #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 より大きくなっています。したがって、関数 strcpytextgbuffer にコピーできません。

修正 — 有効な引数を使用

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);
 }
問題

この問題は、ポインターのデリファレンスで、不明なソースまたはセキュリティで保護されていないソースからのオフセット変数が使用された場合に発生します。

このチェックでは、動的に割り当てられたバッファーに注目します。静的バッファーのオフセットについては、汚染されたインデックスによる配列へのアクセスを参照してください。

リスク

インデックスは有効な配列範囲を外れている可能性があります。汚染されたインデックスが配列範囲を外れていると、以下の原因となることがあります。

  • バッファー アンダーフローまたはアンダーライト、すなわち、バッファーの先頭より前でのメモリへの書き込み。

  • バッファー オーバーフロー、すなわち、バッファーの末尾より後でのメモリへの書き込み。

  • バッファー オーバーリード、すなわち、対象バッファーの末尾より後でのメモリへのアクセス。

  • バッファーのアンダーリード、すなわち、対象バッファーの先頭より前のメモリへのアクセス。

攻撃者は、無効な読み取りや書き込みを使用してプログラムを侵害できます。

修正方法

変数を使用してポインターにアクセスする前に、インデックスを検証します。変数が有効範囲内にありオーバーフローしないことを確認します。

チェッカーの拡張

既定では、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;
}
問題

この問題は、除算演算内の整数オペランドの一方または両方がセキュリティで保護されていないソースから取得された場合に発生します。

リスク

  • 分子 (被除数) が可能な限りの最小値であり、分母 (除数) が -1 である場合、除算演算は結果が現在の変数のサイズでは表現できないためオーバーフローします。

  • 分母がゼロである場合、除算演算は失敗し、プログラムがクラッシュがする可能性があります。

こうしたリスクは、恣意的なコードの実行に利用される可能性があります。このようなコードは通例において、プログラムの暗黙的なセキュリティ ポリシーの範囲外です。

修正方法

除算を実行する前に、オペランドの値を検証します。0 または -1 の分母や、最小の整数値の分子についてチェックします。

チェッカーの拡張

既定では、Polyspace は外部ソースからのデータは汚染されていると仮定します。Polyspace 解析での汚染のソースを参照してください。Polyspace 解析の現在のスコープ以外から発生したすべてのデータを汚染されたものと見なすには、コマンド ライン オプション [-consider-analysis-perimeter-as-trust-boundary] を使用します。

例 — 関数の引数の除算
#include <limits.h>
#include <stdio.h>

extern void print_int(int);

int taintedintdivision(void) {
    long num, denum;
    scanf("%lf %lf", &num, &denum);
    int r =  num/denum; //Noncompliant
    print_int(r);
    return r;
}

この関数例では、2 つの引数変数の除算を行い、それを表示して結果を返します。その引数値は不明であり、ゼロ除算や整数オーバーフローの原因となることがあります。

修正 — 値をチェック

1 つの修正方法として、除算を実行する前に分子と分母の値をチェックします。

#include <limits.h>
#include <stdio.h>

extern void print_long(long);

int taintedintdivision(void) {
    long num, denum;
    scanf("%lf %lf", &num, &denum);
    long res= 0;
    if (denum!=0 && !(num==INT_MIN && denum==-1)) {
        res = num/denum;
    }
    print_long(res);
    return res;
}
問題

この問題は、残余演算 (%) 内の整数オペランドの一方または両方がセキュリティで保護されていないソースから取得された場合に発生します。

リスク

  • 2 番目の残余オペランドが 0 である場合、残余演算は失敗し、プログラムはクラッシュします。

  • 2 番目の残余オペランドが -1 である場合、オーバーフローし得る除算演算を基に残余演算が実装されていると、その残余演算はオーバーフローする可能性があります。

  • オペランドの 1 つが負である場合、演算の結果は不確定になります。C89 ではモジュロ演算は標準化されていないため、負のオペランドによる結果は処理系定義となります。

こうしたリスクは、攻撃者によってプログラムあるいはターゲット一般へのアクセスに利用される場合があります。

修正方法

モジュロ演算を実行する前に、オペランドの値を検証します。2 番目のオペランドの値が 0-1 かどうかをチェックします。両方のオペランドの値が負の値かどうかをチェックします。

チェッカーの拡張

既定では、Polyspace は外部ソースからのデータは汚染されていると仮定します。Polyspace 解析での汚染のソースを参照してください。Polyspace 解析の現在のスコープ以外から発生したすべてのデータを汚染されたものと見なすには、コマンド ライン オプション [-consider-analysis-perimeter-as-trust-boundary] を使用します。

例 — ユーザー入力によるモジュロ
#include <stdio.h>
extern void print_int(int);

int taintedintmod(void) {
    int userden;
    scanf("%d", &userden);
    int rem =  128%userden; //Noncompliant
    print_int(rem);
    return rem;
}

この例では、関数がユーザー入力を使用してモジュロ演算を実行します。残余を計算する前に、プログラムをクラッシュさせる可能性のある 0 や -1 などの値に対して入力がチェックされていません。

修正 — オペランドの値をチェック

1 つの修正方法として、モジュロ演算を実行する前にオペランドの値をチェックします。次に示す修正例では、モジュロ演算は 2 番目のオペランドが 0 より大きい場合にのみ続行します。

#include<stdio.h>
extern void print_int(int);

int taintedintmod(void) {
    int userden;
    scanf("%d", &userden);
    int rem = 0;
    if (userden > 0 ) { 
        rem = 128 % userden; 
    }
    print_int(rem);
    return rem;
}

チェック情報

カテゴリ: Data Validation Issues

バージョン履歴

R2023a で導入