メインコンテンツ

readlink() の不適切な使用

readlink の 3 番目の引数がバッファー内に NULL 終端の領域を残さない

説明

この欠陥は、バッファー内に NULL 終端用のスペースを残さない、readlink() へのバッファー サイズ引数を渡した場合に発生します。

次に例を示します。

ssize_t len = readlink("/usr/bin/perl", buf, sizeof(buf));
この 3 番目の引数のサイズは 2 番目の引数のサイズとまったく同じです。シンボリック リンクがバッファーを占有する場合、このように readlink() を使用すると、NULL 終端を入れる領域が残りません。

リスク

関数 readlink() は、シンボリック リンク (最初の引数) のコンテンツをバッファー (2 番目の引数) にコピーします。ただし、この関数はコピーしたコンテンツに NULL 終端を追加しません。readlink() を使用後、NULL 終端をバッファーに明示的に追加しなければなりません。

readlink の使用時にバッファー全体が占有されると、NULL 終端の領域が残りません。

修正方法

関数 readlink() を使用するときは、3 番目の引数をバッファー サイズよりも 1 文字分短くします。

その後、NULL 終端をバッファーに追加します。NULL 終端の追加場所を決めるには、readlink() の戻り値をチェックします。戻り値が -1 の場合、エラーが発生しています。そうでない場合、戻り値はコピーされた文字数 (バイト) です。

すべて展開する

#include <unistd.h>

#define SIZE1024 1024

extern void display_path(const char *);

void func() {
    char buf[SIZE1024];
    ssize_t len = readlink("/usr/bin/perl", buf, sizeof(buf));
    if (len > 0) {
        buf[len - 1] = '\0';
    }
    display_path(buf);
}

この例では、readlink の 3 番目の引数がバッファー (2 番目の引数) のサイズとまったく同じです。最初の引数がバッファーを占有する場合、このように readlink を使用すると、NULL 終端の領域が残りません。

また、文字がコピーされないとき、readlink の戻り値は 0 です。以下のステートメントでは、len が 0 のときにバッファー アンダーフローになります。

buf[len - 1] = '\0';

修正 — サイズの引数をバッファー サイズよりも 1 文字分短くする

1 つの修正方法として、readlink の 3 番目の引数を 2 番目の引数のサイズよりも 1 文字分短くします。

修正した次のコードでは、readlink が 0 を返す場合も考慮します。

#include <stdlib.h>
#include <unistd.h>

#define fatal_error() abort()
#define SIZE1024 1024

extern void display_path(const char *);

void func() {
    char buf[SIZE1024];
    ssize_t len = readlink("/usr/bin/perl", buf, sizeof(buf) - 1); 
    if (len != -1) {
        buf[len] = '\0';
        display_path(buf);
    }
    else {
        /* Handle error */
        fatal_error();
    }
}

結果情報

グループ: セキュリティ
言語: C | C++
既定値: オフ
コマンド ライン構文: READLINK_MISUSE
影響度: Medium

バージョン履歴

R2017a で導入

すべて展開する