メインコンテンツ

このページの内容は最新ではありません。最新版の英語を参照するには、ここをクリックします。

CERT C: Rec.INT04-C

Enforce limits on integer values originating from tainted sources

説明

ルール定義

汚染されたソースに由来する整数値に制限を適用します。1

Polyspace 実装

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

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

  • 汚染された値で制限されたループ

  • 汚染されたサイズでのメモリの割り当て

  • 可変長配列の汚染されたサイズ

チェッカーの拡張

既定の Bug Finder 解析では、現在の解析境界の外部からの特定の入力に対しては、このチェッカーにフラグを設定しない場合があります。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;
	}
}

問題

汚染された値で制限されたループでは、セキュリティで保護されないソース由来の値で制限されているループを検出します。

リスク

汚染された値は、過剰ループや無限ループの原因となることがあります。攻撃者はこの脆弱性を利用してプログラムをクラッシュさせ、また、他の想定外の動作を引き起こすことができます。

修正方法

ループを開始する前に、不明な境界値と反復値について検証します。

例 - 入力引数に由来するループ境界
#include<stdio.h>
enum {
    SIZE10  =  10,
    SIZE100 = 100,
    SIZE128 = 128
};

int taintedloopboundary(void) {
    int count;
    scanf("%d", &count);
    int res = 0;
    for (int i=0 ; i < count; ++i) { //Noncompliant
        res += i;
    }
    return res;
}

この例では、関数で入力引数を使用して count 回のループを行っています。count は、for ループの開始前に値がチェックされないため、任意の数値になる可能性があります。

修正: 汚染されたループ制御をクランプ

1 つの修正方法として、汚染されたループ制御をクランプします。汚染されたループ変数 count を検証するために、この例では、インライン関数の minmax を使用することによって count を最小値と最大値に制限します。ユーザー入力に関係なく、count の値は既知の範囲内に留まります。

#include<stdio.h>
#include<algorithm>
#define MIN 50
#define MAX 128
static  inline int max(int a, int b) { return a > b ? a : b;}
static inline int min(int a, int b) { return a < b ? a : b; }

int taintedloopboundary(void) {
	int count;
	scanf("%d", &count);
	int res = 0;
	count = max(MIN, min(count, MAX));
	for (int i=0 ; i<count ; ++i) { 
		res += i;
	} 
	return res;
}
修正 — 汚染されたループ制御をチェック

別の修正方法として、for ループが開始する前に、汚染されたループ境界変数の下限と上限をチェックします。この例では、count の下限と上限をチェックし、count が 0 ~ 127 の範囲内にある場合にのみループを実行します。

#include<stdio.h>

enum {
    SIZE10  =  10,
    SIZE100 = 100,
    SIZE128 = 128
};


int taintedloopboundary(void) {
    int count;
    scanf("%d", &count);
    int res = 0;

    if (count>=0 && count<SIZE128) {
        for (int i=0 ; i<count ; ++i) { 
            res += i;
        }
    }
    return res;
}
問題

汚染されたサイズでのメモリの割り当てでは、callocmalloc のようなメモリ割り当て関数を、セキュリティで保護されないソース由来のサイズ引数についてチェックします。

リスク

メモリ割り当てが制御されていないと、プログラムによってシステム メモリが過剰に要求されることがあります。その結果、メモリ不足の状態やリソースの過剰割り当てによりクラッシュすることがあります。

修正方法

メモリを割り当てる前に引数の値をチェックして、範囲を超えないことを確認します。

例 - ユーザーからの入力を使用したメモリの割り当て
#include<stdio.h>
#include <stdlib.h>

int* bug_taintedmemoryallocsize(void) {
    size_t size;
    scanf("%zu", &size);
    int* p = (int*)malloc(size); //Noncompliant
    return p;
}

この例では、malloc がポインター p 用のメモリを size バイト割り当てます。変数 size はプログラムのユーザーから取得されます。その値がチェックされないため、使用可能なメモリ量を超える可能性があります。size が使用可能なバイト数を上回ると、プログラムがクラッシュする可能性があります。

修正 — 割り当てられるメモリのサイズをチェック

1 つの修正方法として、malloc 操作を実行する前に、割り当てるメモリのサイズをチェックします。この例では、size が正で、最大サイズより小さいかどうかを確認します。

#include<stdio.h>
#include <stdlib.h>

enum {
    SIZE10  =  10,
    SIZE100 = 100,
    SIZE128 = 128
};

int* corrected_taintedmemoryallocsize(void) {
    size_t size;
    scanf("%zu", &size);
    int* p = NULL;
    if (size>0 && size<SIZE128) {          /* Fix: Check entry range before use */
        p = (int*)malloc((unsigned int)size);
    }
    return p;
}
問題

可変長配列の汚染されたサイズでは、セキュリティで保護されないソースに由来するサイズの可変長配列 (VLA) を検出します。

リスク

攻撃者が VLA のサイズを予期しない値に変更した場合、プログラムのクラッシュや予期しない動作の原因となることがあります。

サイズが正でない場合、VLA の動作は未定義となります。プログラムは想定どおりには実行されません。

サイズが無制限の場合、VLA はメモリ枯渇やスタック オーバーフローを引き起こすことがあります。

修正方法

VLA のサイズを検証して、正であり最大値より小さいことを確認します。

例 - VLA のサイズとして使用される入力引数
#include<stdio.h>
#inclule<stdlib.h>
#define LIM 40

long squaredSum(int size) {

	int tabvla[size]; //Noncompliant
	long res = 0;
	for (int i=0 ; i<LIM-1 ; ++i) {
		tabvla[i] = i*i;
		res += tabvla[i];
	}
	return res;
}
int main(){
	int size;
	scanf("%d",&size);
	//...
	long result = squaredSum(size);
	//...
	return 0;
}

この例では、可変長配列のサイズが入力引数に基づいています。この入力引数の値はチェックされていないため、サイズが負になるか大きすぎる可能性があります。

修正 — VLA のサイズをチェック

1 つの修正方法として、可変長配列を作成する前にサイズ変数をチェックします。この例では、VLA を作成する前に、サイズが 0 より大きく 40 より小さいかどうかをチェックしています。

#include <stdio.h>
#include <stdlib.h>
#define LIM 40

long squaredSum(int size) {
	long res = 0;
	if (size>0 && size<LIM){
		int tabvla[size];
		for (int i=0 ; i<size || i<LIM-1 ; ++i) {
			tabvla[i] = i*i;
			res += tabvla[i];
		}
	}else{
		res = -1;
	}
	return res;
}
int main(){
	int size;
	scanf("%d",&size);
	//...
	long result = squaredSum(size);
	//...
	return 0;
}

チェック情報

グループ: Rec.04.整数 (INT)

バージョン履歴

R2019a で導入


1 This software has been created by MathWorks incorporating portions of: the “SEI CERT-C Website,” © 2017 Carnegie Mellon University, the SEI CERT-C++ Web site © 2017 Carnegie Mellon University, ”SEI CERT C Coding Standard – Rules for Developing safe, Reliable and Secure systems – 2016 Edition,” © 2016 Carnegie Mellon University, and “SEI CERT C++ Coding Standard – Rules for Developing safe, Reliable and Secure systems in C++ – 2016 Edition” © 2016 Carnegie Mellon University, with special permission from its Software Engineering Institute.

ANY MATERIAL OF CARNEGIE MELLON UNIVERSITY AND/OR ITS SOFTWARE ENGINEERING INSTITUTE CONTAINED HEREIN IS FURNISHED ON AN "AS-IS" BASIS. CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY KIND, EITHER EXPRESSED OR IMPLIED, AS TO ANY MATTER INCLUDING, BUT NOT LIMITED TO, WARRANTY OF FITNESS FOR PURPOSE OR MERCHANTABILITY, EXCLUSIVITY, OR RESULTS OBTAINED FROM USE OF THE MATERIAL. CARNEGIE MELLON UNIVERSITY DOES NOT MAKE ANY WARRANTY OF ANY KIND WITH RESPECT TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT.

This software and associated documentation has not been reviewed nor is it endorsed by Carnegie Mellon University or its Software Engineering Institute.