ISO/IEC TS 17961 [taintformatio]
Using a tainted value to write to an object using a formatted input or output function
説明
ルール定義
書式設定付きの入力関数または出力関数を使用してオブジェクトに書き込む際に汚染された値を使用。1
Polyspace 実装
このチェッカーは以下の問題をチェックします。
不正確な文字列形式指定子によるバッファー オーバーフロー。
文字列操作で格納先バッファーがオーバーフローしています。
標準ライブラリ ルーチンの無効な使用。
標準ライブラリ文字列ルーチンの無効な使用。
汚染された NULL 文字列または非 NULL 終端文字列。
汚染された文字列形式指定子。
標準ライブラリ文字列ルーチンの無効な使用。
危険な標準関数を使用しています。
格納先バッファー サイズが不十分
チェッカーの拡張
既定の Bug Finder 解析では、現在の解析境界の外部からの特定の入力に関する "汚染された NULL 文字列または非 NULL 終端文字列" または "汚染された文字列形式指定子" 問題にフラグを設定しない場合があります。Polyspace 解析での汚染のソースを参照してください。Polyspace 解析の現在のスコープ以外から発生したすべてのデータを汚染されたものと見なすには、コマンド ライン オプション [-consider-analysis-perimeter-as-trust-boundary] を使用します。
例
不正確な文字列形式指定子によるバッファー オーバーフローは、sscanf のような関数の形式指定子の引数が、メモリ バッファー引数でのオーバーフローまたはアンダーフローにつながる場合に発生します。
形式指定子で指定した精度がメモリ バッファー サイズより大きいと、オーバーフローが発生します。オーバーフローは、メモリ破損のような予期しない動作を引き起こす可能性があります。
メモリ バッファー サイズに適合する形式指定子を使用します。
#include <stdio.h>
void func (char *str[]) {
char buf[32];
sscanf(str[1], "%33c", buf);
}この例では、buf に char 要素を 32 個格納できます。したがって、形式指定子 %33c はバッファー オーバーフローの原因となります。
1 つの修正方法として、より低い精度を形式指定子に使用します。
#include <stdio.h>
void func (char *str[]) {
char buf[32];
sscanf(str[1], "%32c", buf);
}文字列操作で格納先バッファーがオーバーフローしていますは、特定の文字列操作関数が、その格納先バッファー引数にバッファー サイズより大きいオフセットで書き込む場合に発生します。
たとえば、関数 sprintf(char* buffer, const char* format) を呼び出す際に、buffer より大きいサイズの定数文字列 format を使用する場合などです。
バッファー オーバーフローにより、メモリ破損やシステム停止といった予期しない動作を引き起こす可能性があります。また、バッファー オーバーフローは、コード インジェクションのリスクにもつながります。
1 つの解決策として、代替となる関数を使用して、書き込まれる文字の数を制限します。次に例を示します。
書式設定されたデータを文字列に書き込むのに
sprintfを使用している場合は、代わりにsnprintf、_snprintfまたはsprintf_sを使用して長さを制御します。あるいは、asprintfを使用して、格納先バッファーに必要なメモリを自動で割り当てます。書式設定されたデータを可変引数リストから文字列に書き込むのに
vsprintfを使用している場合は、代わりにvsnprintfまたはvsprintf_sを使用して長さを制御します。ワイド文字列をコピーするのに
wcscpyを使用している場合は、代わりにwcsncpy、wcslcpyまたは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);
}この例では、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);
}この問題は、標準ライブラリの関数で無効な引数が使用された場合に発生します。この欠陥では、浮動小数点、整数、メモリまたは文字列の標準ライブラリ ルーチンでカバーされない他の関数に関係するエラーが検出されます。
標準ライブラリ関数で無効な引数を使用すると未定義の動作が発生します。
修正方法は欠陥の根本原因によって異なります。たとえば、ポインターが NULL で初期化されて、特定の実行パスでその初期値が上書きされない場合、関数 printf の引数が NULL になる可能性があります。
以下の修正例を参照してください。
問題を修正しない場合は、改めてレビューされないように結果またはコードにコメントを追加します。詳細は、以下を参照してください。
Polyspace ユーザー インターフェイスでのバグ修正または正当化による結果への対処 (Polyspace ユーザー インターフェイスで結果をレビューする場合)。
Polyspace Access でのバグ修正または正当化による結果への対処 (Polyspace Access) (Web ブラウザーで結果をレビューする場合)。
コードへの注釈付けと既知の結果または許容可能な結果の非表示 (IDE で結果をレビューする場合)
printf の呼び出し#include <stdio.h>
#include <stdlib.h>
void print_null(void) {
printf(NULL);
}
関数 printf は文字列の入力引数または書式指定子のみを取ります。この関数では入力値が NULL であるため、有効な文字列ではありません。
1 つの修正方法として、入力引数を標準ライブラリ ルーチンの要件に適合するよう変更することができます。この例では、入力引数を文字に変更しました。
#include <stdio.h>
void print_null(void) {
char zero_val = '0';
printf((const char*)zero_val);
}
標準ライブラリ文字列ルーチンの無効な使用は、文字列ライブラリ関数が無効な引数で呼び出された場合に発生します。
リスクは無効な引数のタイプによって異なります。たとえば、コピー先引数より大きいコピー元引数を指定して関数 strcpy を使用すると、バッファー オーバーフローが発生する可能性があります。
修正方法は欠陥に関連する標準ライブラリ関数に依存します。場合によっては、関数呼び出しの前に関数の引数を制約することができます。たとえば、次の関数 strcpy を考えます。
char * strcpy(char * destination, const char* source);strcpy を呼び出す前にコピー元引数を制約します。場合によっては、代替となる関数を使用してエラーを回避できます。たとえば、strcpy の代わりに strncpy を使用するとコピーされるバイト数を制御できます。Polyspace デスクトップ ユーザー インターフェイスでの Bug Finder の結果の解釈も参照してください。以下の修正例を参照してください。
問題を修正しない場合は、改めてレビューされないように結果またはコードにコメントを追加します。詳細は、以下を参照してください。
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);
/* 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);
}
この問題は、strcpy や sprintf などの文字列バッファーを暗黙的にデリファレンスする文字列操作ルーチンで、セキュリティで保護されていないソースからの文字列が使用された場合に発生します。
チェッカーは、scanf ファミリの可変個引数関数の呼び出しから返された文字列に関する欠陥を報告しません。同様に、文字列と一緒に %s 指定子を printf ファミリの可変個引数関数に渡した場合も欠陥は報告されません。
文字列がセキュリティで保護されないソースに由来している場合、攻撃者により文字列が操作されている可能性や、文字列ポインターが異なるメモリ位置に向けられている可能性があります。
文字列が NULL である場合、文字列ルーチンは文字列をデリファレンスできず、プログラムがクラッシュする原因となります。文字列が null で終了しない場合、文字列ルーチンでは文字列がいつ終了するかわからない可能性があります。このエラーは範囲外への書き込みの原因となり、バッファー オーバーフローを引き起こします。
文字列は、使用する前に検証します。以下についてチェックします。
文字列が NULL でない。
文字列が null で終了している。
文字列のサイズが、必要なサイズと一致している。
#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));
print_str(str);
}
void errorMsg(void)
{
char userstr[MAX];
read(0,userstr,MAX);
char str[SIZE128] = "Error: ";
strncat(str, userstr, SIZE128-(strlen(str)+1));
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)) { // Defect only raised 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;
}この問題は、printf 形式の関数で、セキュリティで保護されていないソースから構築された書式指定子が使用された場合に発生します。
外部で制御される要素を使用して文字列を形式化すると、バッファー オーバーフローやデータ表現の問題の原因となることがあります。攻撃者はこうした文字列形式の設定要素を利用して、%x でスタックの内容を表示し、あるいは %n でスタックへの書き込みを行うことができます。
静的文字列を渡して文字列関数を形式化します。この修正により、外部アクターは文字列を制御できなくなります。
別の修正方法として、必要な数の引数のみを許可します。可能な場合は、脆弱性のある %n 演算子をサポートしない関数を、文字列形式で使用します。
#include <stdio.h>
#include <unistd.h>
#define MAX 40
void taintedstringformat(void) {
char userstr[MAX];
read(0,userstr,MAX);
printf(userstr);
}この例では、入力引数 userstr を出力しています。この文字列は不明です。そこに % などの要素が含まれている場合、printf では userstr が文字列でなく文字列形式と解釈され、プログラムがクラッシュする原因となります。
1 つの修正方法として、userstr を明示的に文字列として出力し、あいまいさをなくします。
#include <stdio.h>
#include <unistd.h>
#define MAX 40
void taintedstringformat(void) {
char userstr[MAX];
read(0,userstr,MAX);
printf("%.20s", userstr);;
}危険な標準関数を使用していますチェックでは、特定の環境において本質的に危険か潜在的に危険な関数の使用が強調表示されます。次の表に、潜在的に危険な関数、各関数を使用する場合のリスクおよび代用する関数をリストします。
| 危険な関数 | リスク レベル | より安全な関数 |
|---|---|---|
gets | 本質的に危険 — コンソールからは入力の長さを制御できない。 | fgets |
cin | 本質的に危険 — コンソールからは入力の長さを制御できない。 | 使用を避けるか、cin の呼び出しの前に cin.width を追加する。 |
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 デスクトップ ユーザー インターフェイスでの Bug Finder の結果の解釈も参照してください。
以下の修正例を参照してください。
問題を修正しない場合は、改めてレビューされないように結果またはコードにコメントを追加します。詳細は、以下を参照してください。
Polyspace ユーザー インターフェイスでのバグ修正または正当化による結果への対処 (Polyspace ユーザー インターフェイスで結果をレビューする場合)。
Polyspace Access でのバグ修正または正当化による結果への対処 (Polyspace Access) (Web ブラウザーで結果をレビューする場合)。
コードへの注釈付けと既知の結果または許容可能な結果の非表示 (IDE で結果をレビューする場合)
sprintf の使用#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)
{
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;
}"格納先バッファー サイズが不十分" は、strcpy 演算内の格納先バッファーにソース バッファーと null 終端が収まらない場合に発生します。この問題は、ソース バッファーのサイズが不明な場合に報告されます。次のコードについて考えます。
int main (int argc, char *argv[])
{
const char *const input = ((argc && argv[0]) ? argv[0] : "");
char str[100];
strcpy(str, input); // Noncompliant
}input のサイズが不明です。格納先バッファー str のサイズが、値 (strlen(input)+1) より小さい可能性があります。Polyspace® は、strcpy 演算に対する違反を報告します。サイズが不十分な格納先バッファーを使用すると、攻撃者がバッファー オーバーフローを引き起こすことができるようになる可能性があります。前のコード例では、argv[0] に 100 個以上の文字が含まれている場合に、strcpy 演算でバッファー オーバーフローが発生します。
関数 strcpy() を呼び出す前に、十分なメモリを動的に割り当てます。たとえば、関数 strlen() を使用してソース バッファーのサイズを決定してから、格納先バッファーを、そのサイズが値 strlen(source) + 1 を上回るように割り当てます。
または、関数 strcpy() の代わりに関数 strncpy() を使用します。関数 strncpy() は、既知の数の文字をソース バッファーから格納先バッファーにコピーします。関数 strncpy() は既知の数の文字をコピーするため、strcpy() よりも安全な代替手段です。
この例では、source バッファーのサイズは不明ですが、destination バッファーのサイズは 128 で固定されます。この destination バッファーのサイズは、source バッファーからの文字を収め、そのバッファーを null で終了するには不十分な可能性があります。Polyspace は、ルールの違反を報告します。
#include <string.h>
int main(int argc, char *argv[]) {
const char *const source = (argc && argv[0]) ? argv[0] : "";
char destination[128];
strcpy(destination,source);//Noncompliant
return 0;
}strncpy を strcpy の代わりに使用この問題を修正するには、strncpy() を使用して、既知の数の文字を source バッファーから destination バッファーにコピーします。
#include <string.h>
#define MAX_ARGS 128
int main(int argc, char *argv[]) {
const char *const source = (argc && argv[0]) ? argv[0] : "";
char destination[MAX_ARGS];
int num = (strlen(source)+1>MAX_ARGS)?MAX_ARGS:strlen(source)+1;
strncpy(destination,source,num);//Compliant
return 0;
}この違反は、destination バッファーに十分なメモリを割り当てることによって解決されます。たとえば、関数 strlen() を使用して source バッファーのサイズを計算し、source バッファーからのすべての文字と null 終端 ('\0') を収めるのに十分なメモリを destination バッファーに割り当てます。
#include <string.h>
int main(int argc, char *argv[]) {
const char *const source = (argc && argv[0]) ? argv[0] : "";
char* destination = (char *)malloc(strlen(source)+ 1);
if(destination!=NULL){
strcpy(destination, source);//Compliant
}else{
/*Handle Error*/
}
//...
free(destination);
return 0;
}チェック情報
| 決定可能性:決定不可能 |
バージョン履歴
R2019a で導入
1 Extracts from the standard "ISO/IEC TS 17961 Technical Specification - 2013-11-15" are reproduced with the agreement of AFNOR. Only the original and complete text of the standard, as published by AFNOR Editions - accessible via the website www.boutique.afnor.org - has normative value.
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)