web-dev-qa-db-ja.com

snprintfを使用してバッファオーバーランを回避する

バッファオーバーランを回避するために、次のようにsnprintfを使用しています。

char err_msg[32] = {0};
snprintf(err_msg, sizeof(err_msg) - 1, "[ ST_ENGINE_FAILED ]");

文字列の長さが32バイトを超える場合に備えて、ヌルターミネータ用のスペースを予約するために-1を追加しました。

私の考えは正しいですか?

プラットホーム:

  • GCC 4.4.1
  • C99
17
ant2009

他の人が言っているように、この場合は-1は必要ありません。配列が固定サイズの場合、代わりにstrncpyを使用します。文字列をコピーするために作成されました-sprintfは難しいフォーマットを行うために作成されました。ただし、配列のサイズが不明な場合、またはフォーマットされた文字列に必要なストレージの量を決定しようとしている場合。これは、私がsnprintfの標準指定バージョンについて本当に気に入っていることです。

char* get_error_message(char const *msg) {
    size_t needed = snprintf(NULL, 0, "%s: %s (%d)", msg, strerror(errno), errno);
    char  *buffer = malloc(needed+1);
    sprintf(buffer, "%s: %s (%d)", msg, strerror(errno), errno);
    return buffer;
}

この機能をva_copyと組み合わせると、非常に安全なフォーマットされた文字列操作を作成できます。

27
D.Shawley

参照にあるように、-1は必要ありません。

関数snprintf()およびvsnprintf()は、サイズバイト(末尾の「\ 0」を含む)を超えて書き込みません。

「末尾の「\ 0」を含む」の部分に注意してください

12
Eli Bendersky

-1は必要ありません。 C99 snprintf常にゼロで終了します。サイズ引数は、出力バッファのサイズを指定します含むゼロターミネータ。したがって、コードは次のようになります。

char err_msg[32];
int ret = snprintf(err_msg, sizeof err_msg, "[ ST_ENGINE_FAILED ]");

retには、実際に印刷された文字数が含まれます(除外ゼロターミネータ)。

ただし、Microsoftの _snprintf (pre-C99)と混同しないでください。これはnotnull-terminateを実行します。そして、そのことについては、完全に異なる動作をします(たとえば、バッファが十分に大きくない場合に印刷される長さの代わりに-1を返す)。 _snprintfを使用する場合は、質問と同じコードを使用する必要があります。

9
Alex B

snprintf(3) によると:

関数snprintf()およびvsnprintf()は、sizeバイト(末尾の'\0'を含む)を超えて書き込みません。

3

与えられた例では、代わりにこれを行う必要があります。

char err_msg[32];
strncpy(err_msg, "[ ST_ENGINE_FAILED ]", sizeof(err_msg));
err_msg[sizeof(err_msg) - 1] = '\0';

またはさらに良い:

char err_msg[32] = "[ ST_ENGINE_FAILED ]";
1
Matt Joiner