strncpy? just say no
All content outside of comments is copyrighted by Lars Wirzenius, and licensed under a
Creative Commons Attribution-Share Alike 3.0 Unported License. Comments are
copyrighted by their authors.
This is a rant. I've made it in various forms in various places over the years, and I
think it's time to put it somewhere so I can point people at it, and stop repeating
myself.
Here's the pebble in my shoe: people keep claiming that strncpy is a safer alternative
to strcpy. It's not. It's a slower function that is not going to make you significantly
safer.
#pebble 小石
strncpy は strcpy の safer alternative であると皆が主張し続けています。そうではないのに。
strncpy はあなたをはっきりと安全に導いていくれるものではない、遅い関数です。
The problem with strcpy is that it's somewhat easy to overflow the target array:
strcpy にまつわる問題はそれが target array でオーバーフローし易いといったものです:
char src[] = "hello, world";
char dst[5];
strcpy(dst, src); // Oops.
The suggested alternative with strncpy would be this:
strncpy を使った代替案はこんな感じになるでしょう:
strncpy(dst, src, sizeof(dst));
strncpy gets the size of the destination array as a parameter, and can therefore make sure
the string will fit there. Problem solved? Not so much.
strncpy はパラメーターとして、destination array のサイズを取ります。そして
これによって文字列が destination array に fit することを確実にできるのです。
問題は解決されたでしょうか? いいえ、そうでもないのです。
Every time you do any string handling with C's native string types and functions, you need
to make sure you have allocated enough memory for each string, you need to keep track of
how much memory you've allocated, and you neet to make sure the zero byte ('NUL', '\0') is
put there correctly. This is a lot of details to keep track of, and it's not surprising
things sometimes go wrong.
C のnative string の型や関数でなんらかの文字列処理を行う度に、
各文字列に対して充分なメモリーを割り当てていることを保障する必要があります。また、
どの程度の大きさのメモリーを割り当てたのかを記録しておく必要もありますし、
ゼロのバイトが正しく置かれたことも保証しなければなりません。
This is a lot of details to keep track of, and it's not surprising things sometimes go wrong.
If your arrays are allocated statically, which is the simplest, but least useful case, you
need to something like this with strcpy:
最も単純ではあってもほとんど有用でないケースとして、もしあなたの使う配列が静的に割り当てられた
ものの場合、次のように strcpy を使う必要があるでしょう:
if (strlen(src) + 1 > sizeof(dst))
panic(); // or however you want to handle your errors
strcpy(dst, src);
The +1 after strlen is to make room for the zero byte (NUL, '\0') that is used in C to
indicate the end of a string.
この strlen のあとにある +1 は、Cにおいて文字列の終端を示すのに使われるゼロを納めるバイト
(NUL、'\0')のための領域です。
With strncpy, your code is slightly simpler. This is its attraction.
strncpy を使うと、コードが格段に単純になります。これが strncpy の attraction です。
Problem is, this only works if dst is an array defined within the scope of the code.
If, as is common, you have received dst as an argument to a function, sizeof(dst) is
the size of a pointer to an element of the array, not the size of the array. So now
you need to track the size explicitly in a second variable:
問題は、これがdstがそのコードが置かれているスコープで定義された配列であったときにのみ
うまく動作するということです。仮に、よくあるようにdst を関数に対する引数として
受け取った場合、sizeof(dst) は配列の大きさではなくその配列の要素へのポインターの大きさと
なってしまいます。ですから、ここで二つめの変数を用意して自前で配列の大きさを
記録しておかなければなりません:
strncpy(dst, src, dst_size);
Further, strncpy has an unfortunate error handling mode: it merely fills the destination
array and stops. It does not indicate failure in any way. It does not terminate the target
array with a zero byte. Since all C string processing assume the zero byte is there, as soon
as you try to use dst as a string, you're in trouble.
さらに、strncpy には unfortunate error handling mode があります:
この関数は単に destination array を埋めて止まるだけなのです。
何らかの手段でもってエラーを示すことはありません。
target array をゼロのバイトで終端することもありません。
すべてのC文字列の処理ではゼロのバイトがあることを仮定していますから、
このdstを文字列として使おうとした途端、トラブルが降りかかることになります。
But luckily you can check for that before you call strncpy:
しかし幸運にもstrncpyを呼び出す前にチェックができます:
if (strlen(src) + 1 > dst_size)
panic(); // or whatever
strncpy(dst, src, dst_size);
By this time, the code is equally hard whether you use strcpy or strncpy. With strncpy
you often waste some extra time, though, filling the unused parts of the target array
with zero bytes: they're almost never necessary.
この場合、コードはstrcpyとstrncpとでほとんど同じものとなります。
ただし、strncpyを使った場合には多少余計な時間を浪費することになるのがほとんどです。
それは、target array の未使用部分をゼロのバイトで埋めていく手順が入るからですが
これはほとんど不必要なものです。
Alternatively, you can force the zero byte there yourself, silently truncating a string.
代替策として、自分自身でゼロのバイトを埋め込むことを強制して
こっそりと文字列を丸めることが可能です。
strncpy(dst, src, dst_size);
dst[dst_size - 1] = '\0'; // let's hope dst_size > 0
If your string handling is OK with strings being randomly shorter than they should be,
possibly you could replace all of them with empty strings, and save yourself a world of
pain.
もしあなたの文字列処理が本来あるべき長さよりもランダムに短くなる文字列に対しても問題ない
のであれば、すべての文字列を空文字列に変えることができて
save yourself a world of pain.
Instead of strncpy, use snprintf instead. It has sensible error behavior, and is much
more versatile:
strncpy の代わりに、snprintfを使いましょう。
この関数は sensible error behavior を備えていて、また、ずっと汎用的なものです:
n = snprintf(dst, dst_size, "%s", src);
if (n > dst_size)
panic();
snprintf still isn't very nice to use. It still requires you to allocate memory
yourself, and keep track of the size of each array manually.
それでも snprintf は very nice to use ではありません。
まだメモリーの割り当てを自分でやらなければなりませんし、
各配列の大きさも手作業で記録しておかなければなりません。
The real solution is to abandon C's antiquated string handling, and use a more sensible
helper library instead. There's a bunch of them around, though none have gained much
popularity. I'm not going to name any of them, but just imagine how much nicer it would
be to write code like this instead:
real solution は C の antiquated string handling を諦めて、より sensible な
ヘルパーライブラリを使うようにすることです。そういったものはたくさんありますが、
much popularity を得たものはありません。
Dynstr *src = dynstr_new_from_cstring("hello, world\n");
Dynstr *cat = dynstr_cat_many(src, src, src, src, src, NULL);
Dynstr *dog = dynstr_substr(cat, 15, 5);
Let the library take care of memory allocations and related tracking, and concentrate on
real problem solving instead.
We did this in Kannel, back in 1999, and it wiped out all the buffer overflow problems
(until a co-worker decided it was "more efficient" to use native C strings
instead, promptly introducing a buffer overflow).
All of this string handling is, of course, way nicer in higher level languages. In fact,
whenever you need to deal with string handling in C, it is probably a sign that you should
consider switching to a higher level language instead.
But whatever you do, stop claiming that strncpy is safe or solves any real problems strcpy
has.
Update: Fixed "It does terminate" to say "It does not terminate".
Thanks to Matthew Garrett for pointing that out.
Update2: Fixed example call to snprintf.
strlcpy
You could also mention the OpenBSD-originated strlcpy, even though it is only marginally
better than strncpy.
strncpy より優れたもののように見えるだけかもしれませんが、
OpenBSD起源の strlcpy にも言及した方が良いかもしれません。
Comment by Paul
snprintf ftw
snprintf はどうでしょう
strlcpy(dst,src,dst_size) → snprintf(dst_size, dst, "%s", src);
Should be the same semantics, and it's completely standard in C99.
これは同じ semantics になっているはずですし、snprintf の方は C99 において
完全に標準のものです。
Comment by Lars Wirzenius
@lars, re: syntax
You entered the syntax for snprintf correctly in the article but not in your followup comment.
Should be snprintf(dst, dst_size, "%s", src) according to the man page.
Thanks for the article, btw! :)
Comment by Carl
strbuf
Git has a very nice "strbuf" module which provides a simple abstraction around C
strings. Unfortunately, nobody has extracted it from Git for use as a general-purpose
library.
Comment by JoshTriplett
re: strbuf
A fragment of git's strbuf module has escaped into the wild. But presumably a general-purpose
version would need a different name to avoid conflicting with the other strbufs out there.
I like the snprintf hint.
Comment by JonathanNieder