メインコンテンツ

CWE Rule 124

Buffer Underwrite ('Buffer Underflow')

R2023a 以降

説明

ルールの説明

The software writes to a buffer using an index or pointer that references a memory location prior to the beginning of the buffer.

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

問題

この問題は、特定の文字列操作関数によって、その格納先バッファー引数にバッファーの先頭から負のオフセットで書き込まれた場合に発生します。

たとえば、関数 sprintf(char* buffer, const char* format) では buffer を演算 buffer = (char*)arr; ... buffer += offset; から取得します。arr は配列で、offset は負の値です。

リスク

バッファー アンダーフローにより、メモリ破損やシステム停止のような予期しない動作を引き起こす可能性があります。また、バッファー アンダーフローは、コード インジェクションのリスクにもつながります。

修正方法

格納先バッファーの引数がポインター演算の結果得られたものである場合は、ポインターをデクリメントしているかどうかを確認します。デクリメント前の元の値かデクリメント値を変更することで、ポインターのデクリメントを修正します。

例 — sprintf の使用におけるバッファー アンダーフロー
#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);     
}
問題

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

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

リスク

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

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

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

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

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

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

修正方法

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

チェッカーの拡張

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

チェック情報

カテゴリ: Memory Buffer Errors

バージョン履歴

R2023a で導入