C言語:strncpyの挙動
C言語の文字列操作APIの一つとなっている strncpy は、適切な文字列の取り扱いに失敗するケースが多く、バグの温床になっている。
https://pubs.opengroup.org/onlinepubs/9699919799/functions/strncpy.html
char *stpncpy(char *restrict s1, const char *restrict s2, size_t n);
s2 から s1 に文字列をコピーする。
ただし、nバイトまで。
s1の文字列の長さがnバイトに満たない場合にのみ、終端に NUL 文字を付ける。
この挙動のため、以下のようにするとs1のバッファにはNUL文字が付かない。
code:bad1.c
char buf5 + 1;
int i;
strncpy(buf, "Hello!", sizeof(buf)); // 配列の最後が破壊される。
for (i = 0; i < sizeof(buf); i++)
{
printf("%02hhx ", bufi);
}
printf("%s", buf); // バッファオーバーランで死ぬ
code:bad2.c
char buf5 + 1;
int i;
// buf は初期化されていないので何が入っているか分からない。
strncpy(buf, "Hello!", sizeof(buf) - 1); // 対策したつもり
for (i = 0; i < sizeof(buf); i++)
{
printf("%02hhx ", bufi);
}
printf("%s", buf); // やはりバッファオーバーランで死ぬ
この挙動を避けるために、memset でバッファをクリアすることがよく行われている。
未クリア領域からの情報読み出しのようなクラッキング防止にはよいが、パフォーマンスは落ちる。
code:ok.c
// 未クリア部がなく安全
char buf5 + 1;
int i
memset(buf, 0, sizeof(buf)); // クリアしたので安全になった。
strncpy(buf, "Hello!", sizeof(buf) - 1); // 配列の最後の0は壊れない。
for (i = 0; i < sizeof(buf); i++)
{
printf("%02hhx ", bufi);
}
printf("%s", buf);
code:ok2.c
// 最速
char buf5 + 1;
int i
bufsizeof(buf) - 1 = '\0'; // 少なくとも終端は確保。
strncpy(buf, "Hello!", sizeof(buf) - 1); // 配列の最後の0は壊れない。
for (i = 0; i < sizeof(buf); i++)
{
printf("%02hhx ", bufi);
}
printf("%s", buf);
NUL終端文字列の追加について期待通りに動くのは snprintf になる。
書式文字列の解釈が入るので少し遅い。
code:good.c
char buf5 + 1;
int i
snprintf(buf, sizeof(buf), "%s", "Hello!");
for (i = 0; i < sizeof(buf); i++)
{
printf("%02hhx ", bufi);
}
printf("%s", buf);
#文字列操作API