メインコンテンツ

ISO/IEC TS 17961 [libptr]

Forming invalid pointers by library function

説明

ルール定義

ライブラリ関数による無効なポインターの形成。1

Polyspace 実装

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

  • 最大バッファー サイズのチェックがないパス操作関数を使用しています

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

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

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

すべて展開する

問題

最大バッファー サイズのチェックがないパス操作関数を使用していますは、realpathgetwd のようなパス操作関数の格納先引数に PATH_MAX バイトより小さいバッファー サイズが指定されている場合に発生します。

リスク

バッファーが PATH_MAX バイトより小さいとオーバーフローすることがありますが、関数の戻り値をテストしても、オーバーフローが発生したかどうかは判別できません。オーバーフローが発生すると、関数呼び出しの後にバッファーの内容が未定義になります。

たとえば char *getwd(char *buf) はその引数に、現在のフォルダーの絶対パス名をコピーします。絶対パス名の長さが PATH_MAX バイトより大きい場合、getwdNULL を返し、*buf の内容は未定義になります。getwd の戻り値を NULL についてテストすることで、関数呼び出しが正常終了したかどうかを確認できます。

しかし、buf に許可されているバッファーが PATH_MAX バイトより小さい場合、絶対パス名が小さくてもエラーが発生することがあります。この場合、エラーが発生しても getwdNULL を返しません。したがって、buf に許可されるバッファーは、PATH_MAX バイトの長さでなければなりません。

修正方法

考えられる修正方法は次のとおりです。

  • PATH_MAX バイトのバッファー サイズを使用する。不明なソースからバッファーを取得する場合は、バッファーを関数 getwd または realpath の引数として使用する前に、サイズが PATH_MAX バイトより小さいことを確認します。

  • パス操作関数を使用して、バッファー サイズを指定できるようにする。

    たとえば、getwd を使用して現在のフォルダーの絶対パス名を取得している場合、代わりに char *getcwd(char *buf, size_t size); を使用します。追加の引数 size により、PATH_MAX 以上のサイズを指定できるようになります。

  • 可能な場合は、関数が追加メモリを動的に割り当てられるようにする。

    たとえば、char *realpath(const char *path, char *resolved_path); は、resolved_pathNULL の場合にメモリを動的に割り当てます。ただし、後で関数 free を使用してこのメモリの割り当てを解除する必要があります。

例 - 関数 getwd の使用で発生し得るバッファー オーバーフロー
#include <unistd.h>
#include <linux/limits.h>
#include <stdio.h>

void func(void) {
    char buf[PATH_MAX];
    if (getwd(buf+1)!= NULL)         {
        printf("cwd is %s\n", buf);
    }
}

この例では、配列 bufPATH_MAX バイトであっても、getwd の引数は buf + 1 であり、許可されるバッファーは PATH_MAX バイトより小さくなります。

修正 — サイズが PATH_MAX バイトの配列を使用

1 つの修正方法として、サイズが PATH_MAX バイトに等しい配列引数を使用します。

#include <unistd.h>
#include <linux/limits.h>
#include <stdio.h>

void func(void) {
    char buf[PATH_MAX];
    if (getwd(buf)!= NULL)         {
        printf("cwd is %s\n", buf);
    }
}
問題

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

リスク

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

修正方法

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

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

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

例 - 標準ライブラリ メモリ ルーチンの無効な使用エラー
#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); 
  /* 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 を使用するとコピーされるバイト数を制御できます。Polyspace デスクトップ ユーザー インターフェイスでの Bug Finder の結果の解釈も参照してください。

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

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

例 - 標準ライブラリ文字列ルーチンの無効な使用によるエラー
 #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 より大きくなっています。したがって、関数 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);
 }
問題

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

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

この例では、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);
}

チェック情報

決定可能性:決定不可能

バージョン履歴

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.