メインコンテンツ

CERT C: Rec.STR07-C

文字列操作での範囲チェック インターフェイスの使用

説明

ルール定義

文字列操作の際に範囲チェック インターフェイスを使用します。1

Polyspace 実装

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

  • 危険な標準関数を使用しています

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

  • 格納先バッファー サイズが不十分

すべて展開する

問題

この問題は、コードで、バッファー オーバーフローになる可能性があるやり方でデータをバッファーに書き込む標準関数が使用されている場合に発生します。

次の表に、危険な標準関数、各関数を使用した場合のリスク、および代用する関数を示します。チェッカーは以下にフラグを設定します。

  • 本質的に危険な関数の使用。

  • コンパイル時にデータが書き込まれるバッファーのサイズを決定可能な場合にのみ、危険な可能性のある関数を使用する。チェッカーは、バッファーが動的に割り当てられるこのような関数の使用にフラグを設定しません。

危険な関数リスク レベルより安全な関数
gets本質的に危険 — コンソールからは入力の長さを制御できない。fgets
cin本質的に危険 — コンソールからは入力の長さを制御できない。使用を避けるか、cin の呼び出しの前に cin.width を追加する。
strcpy潜在的に危険 — ソースの長さがコピー先より大きいと、バッファー オーバーフローが発生する可能性がある。strncpy
stpcpy潜在的に危険 — ソースの長さがコピー先より大きいと、バッファー オーバーフローが発生する可能性がある。stpncpy
lstrcpy または StrCpy潜在的に危険 — ソースの長さがコピー先より大きいと、バッファー オーバーフローが発生する可能性がある。StringCbCopyStringCchCopystrncpystrcpy_s または strlcpy
strcat潜在的に危険 — 連結された結果が作成先より大きいと、バッファー オーバーフローが発生する可能性がある。strncatstrlcat または strcat_s
lstrcat または StrCat潜在的に危険 — 連結された結果が作成先より大きいと、バッファー オーバーフローが発生する可能性がある。StringCbCatStringCchCatstrncaystrcat_s または strlcat
wcpcpy潜在的に危険 — ソースの長さがコピー先より大きいと、バッファー オーバーフローが発生する可能性がある。wcpncpy
wcscat潜在的に危険 — 連結された結果が作成先より大きいと、バッファー オーバーフローが発生する可能性がある。wcsncatwcslcat または wcncat_s
wcscpy潜在的に危険 — ソースの長さがコピー先より大きいと、バッファー オーバーフローが発生する可能性がある。wcsncpy
sprintf潜在的に危険 — 出力の長さが不明な長さや値に依存していると、バッファー オーバーフローが発生する可能性がある。snprintf
vsprintf潜在的に危険 — 出力の長さが不明な長さや値に依存していると、バッファー オーバーフローが発生する可能性がある。vsnprintf
リスク

これらの関数はバッファー オーバーフローの原因となることがあり、攻撃者はこれを利用してプログラムに侵入できます。

修正方法

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

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

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

例 - 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) //Noncompliant
    {
        r += 1;
        dst[BUFF_SIZE-1] = '\0';
    }
    
    return r;
}

この関数例では、sprintf を使用して文字列 strdst にコピーしています。しかし、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;
}
問題

文字列操作で格納先バッファーがオーバーフローしていますは、特定の文字列操作関数が、その格納先バッファー引数にバッファー サイズより大きいオフセットで書き込む場合に発生します。

たとえば、関数 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);
}
問題

"格納先バッファー サイズが不十分" は、strcpy 演算内の格納先バッファーにソース バッファーと null 終端が収まらない場合に発生します。この問題は、ソース バッファーのサイズが不明な場合に報告されます。次のコードについて考えます。

int main (int argc, char *argv[])
{
  const char *const input = ((argc && argv[0]) ? argv[0] : "");
  char str[100];
  strcpy(input, str); // Noncompliant
}
このケースでは、ソース バッファー input のサイズが不明です。格納先バッファー str のサイズが、値 (strlen(input)+1) より小さい可能性があります。Polyspace® は、strcpy 演算に対する違反を報告します。

リスク

サイズが不十分な格納先バッファーを使用すると、攻撃者がバッファー オーバーフローを引き起こすことができるようになる可能性があります。前のコード例では、argv[0] に 100 個以上の文字が含まれている場合に、strcpy 演算でバッファー オーバーフローが発生します。

修正方法

関数 strcpy() を呼び出す前に、十分なメモリを動的に割り当てます。たとえば、関数 strlen() を使用してソース バッファーのサイズを決定してから、格納先バッファーを、そのサイズが値 strlen(source) + 1 を上回るように割り当てます。

例 — 格納先バッファーが小さすぎる

この例では、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(source, destination);//Noncompliant
  
  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(source, destination);//Compliant
  }else{
      /*Handle Error*/
  }
  //...
  free(destination);
  return 0;
}

チェック情報

グループ: Rec.07.文字と文字列 (STR)

バージョン履歴

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.