CERT.FIO.FGETS

'{0}' を呼び出すと空の文字列が返される可能性があるが、後で配列要素にアクセスするときに、その文字列が空ではないと誤って想定される

このチェッカーは、fgets() 関数または fgetws() 関数の使用後に誤った想定によって発生する潜在的なバッファアンダーフローの欠陥を探します。これらの関数は、正常実行時に空の文字列を返すことがあります。これらの関数によって空の文字列が返されることはないと設計者が考えている場合、潜在的なバッファアンダーフローのリスクにさらされることになります。

脆弱性とリスク

バッファアンダーフローは、メモリアクセスエラー、不正確な結果、クラッシュ、またはシステムセキュリティ違反など、異常なプログラム動作につながる可能性があります。

バッファアンダーフローの影響として、有効なデータの上書きや、恣意的なコードや潜在的に悪意のあるコードの実行などが発生します。たとえば、以下のようないくつかの方法で、バッファアンダーフローによってプログラムが操作される可能性があります。

  • メモリ内のバッファの近くにあるローカル変数を上書きし、攻撃者の都合の良いようにプログラムの動作を変更する
  • スタックフレームの戻りアドレスを上書きし、攻撃者が指定した戻りアドレス (通常はユーザー入力を格納しているバッファ) から実行が再開されるようにする
  • 関数ポインターまたは例外ハンドラーを上書きして、後で実行する

脆弱コード例

コピー
#include <stdio.h>
#include <string.h>
 
enum { BUFFER_SIZE = 1024 };
 
void func(void) {
    char buf[BUFFER_SIZE];
 
    if (fgets(buf, sizeof(buf), stdin) == NULL) {
        return;
    }
    buf[strlen(buf) - 1] = '\0';
}

Klocwork で 9 行目について欠陥 CERT.FIO.FGETS が報告され、「'fgets' を呼び出すと空の文字列が返される可能性があるが、後で配列要素にアクセスするときに、その文字列が空ではないと誤って想定される」ことが示されます。このコードは入力文字列から末尾の改行文字を削除するはずですが、9 行目で関数 fgets の戻り値が配列 'buf' に格納されている有効な (非 null の) ポインターである場合は空になる可能性があります。その場合、12 行目の strlen(buf) の呼び出し結果が値 0 になり、その後、1 ずつ減分されて、配列 'buf' にアクセスするために使用されます。その結果、12 行目のインデックス -1 でバッファアンダーフローが発生します。

修正コード例

コピー
#include <stdio.h>
#include <string.h>
 
enum { BUFFER_SIZE = 1024 };
 
void func(void) {
    char buf[BUFFER_SIZE];
 
    if (fgets(buf, sizeof(buf), stdin) == NULL) {
        return;
    }
    size_t length = strlen(buf);
    if (length > 0) {
        buf[length - 1] = '\0';
    }
}

このコードでは返された文字列が空である場合が考慮されており、この場合には改行文字を削除しようとしないため、欠陥が回避されるようになっています。