メインコンテンツ

CWE Rule 120

Buffer Copy without Checking Size of Input ('Classic Buffer Overflow')

R2023a 以降

説明

ルールの説明

The program copies an input buffer to an output buffer without verifying that the size of the input buffer is less than the size of the output buffer, leading to a buffer overflow.

Polyspace 実装

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

  • 文字列操作で格納先バッファーがオーバーフローしています

  • 標準ライブラリ メモリ ルーチンの無効な使用

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

  • 汚染された NULL 文字列または非 NULL 終端文字列

すべて展開する

問題

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

たとえば、関数 sprintf(char* buffer, const char* format) を呼び出す際に、buffer より大きいサイズの定数文字列 format を使用する場合などです。

リスク

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

修正方法

1 つの解決策として、代替となる関数を使用して、書き込まれる文字の数を制限します。次に例を示します。

  • 書式設定されたデータを文字列に書き込むのに sprintf を使用している場合は、代わりに snprintf_snprintf または sprintf_s を使用して長さを制御します。あるいは、asprintf を使用して、格納先バッファーに必要なメモリを自動で割り当てます。

  • 書式設定されたデータを可変引数リストから文字列に書き込むのに vsprintf を使用している場合は、代わりに vsnprintf または vsprintf_s を使用して長さを制御します。

  • ワイド文字列をコピーするのに wcscpy を使用している場合は、代わりに wcsncpywcslcpy または wcscpy_s を使用して長さを制御します。

別の解決策として、バッファー サイズを増やします。

例 — sprintf の使用におけるバッファー オーバーフロー
#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
}

この例では、bufferchar 要素を 20 個格納できますが、fmt_string はより大きいサイズとなっています。

修正 — snprintfsprintf の代わりに使用

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

この問題は、メモリ ライブラリ関数が無効な引数で呼び出された場合に発生します。たとえば、関数 memcpy でコピー先の配列に格納できないバイト数をコピーする場合が該当します。

リスク

無効な引数でメモリ ライブラリ関数を使用すると、バッファー オーバーフローなどの問題が発生する可能性があります。

修正方法

修正方法は欠陥の根本原因によって異なります。多くの場合、結果の詳細 (または Polyspace as You Code のソース コード ツールヒント) には欠陥につながる一連のイベントが表示されます。そのシーケンス内のどのイベントについても修正を実装できます。結果の詳細にイベント履歴が表示されない場合は、ソース コード内で右クリック オプションを使用して、欠陥に関連する変数のこれまでの参照を検索し、関連するイベントを検出できます。Polyspace デスクトップ ユーザー インターフェイスでの Bug Finder の結果の解釈またはPolyspace Access Web インターフェイスでの Bug Finder の結果の解釈 (Polyspace Access)も参照してください。

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

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

例 — 標準ライブラリ メモリ ルーチンの無効な使用エラー
#include <string.h>
#include <stdio.h>

char* Copy_First_Six_Letters(void)
 {
  char str1[10],str2[5];

  printf("Enter string:\n");
  scanf("%s",str1);

  memcpy(str2,str1,6);  //Noncompliant
  /* Defect: Arguments of memcpy invalid: str2 has size < 6 */

  return str2;
 }

文字列 str2 のサイズは 5 ですが、関数 memcpy を使用して 6 文字の文字列 str1str2 にコピーされています。

修正 — 有効な引数による関数の呼び出し

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("%s",str1);

  memcpy(str2,str1,6);
  return str2;
 }
問題

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

リスク

リスクは無効な引数のタイプによって異なります。たとえば、コピー先引数より大きいコピー元引数を指定して関数 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);
 }
問題

この問題は、strcpysprintf などの文字列バッファーを暗黙的にデリファレンスする文字列操作ルーチンで、セキュリティで保護されていないソースからの文字列が使用された場合に発生します。

汚染された 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;
}

チェック情報

カテゴリ: Memory Buffer Errors

バージョン履歴

R2023a で導入