色んな本のバックナンバーなどを読み漁っている今日このごろ、とある
本に「関数の戻り値を見ないプログラム書く学生多過ぎ」とか書かれて
あって、「うう、耳が痛い」という状態です。
そこでちゃんと戻り値を見ようという事で、今書いているコードの中に
頻出する strncpy について考える時、
char *strncpy (char *s1, const char *s2, size_t n);
で、s1の値が返される訳ですが、これを見るという事は、例えば以下の
ような str1, str2 があって、str2 に str1 の内容を複写する時は、
#define STR_LENGTH 256
char str1 [ STR_LENGTH ];
char str2 [ STR_LENGTH ];
if (strncmp (strncpy (str2, str1, STR_LENGTH),
str1,
STR_LENGTH) != 0)
{
printf ("FAILED.\n");
exit (EXIT_FAILURE);
}
このようにチェックをすれば良い、ということでよろしいのでしょうか。
それとも全然見当外れ ?
--
柏崎 礼生 (Hiroki Kashiwazaki)@HUIIC
Ph.D candidate in the Division of Electronics & Information
Engineering, Hokkaido University
mailto:r...@cc.hokudai.ac.jp
Tel:+81-11-706-2998
Hiroki Kashiwazaki <r...@cc.hokudai.ac.jp> writes:
> 柏崎@北海道です。
>
> 色んな本のバックナンバーなどを読み漁っている今日このごろ、とある
> 本に「関数の戻り値を見ないプログラム書く学生多過ぎ」とか書かれて
> あって、「うう、耳が痛い」という状態です。
ちゃちゃです。
きっと著者は「Exceptionを気にしない学生多過ぎ」という意味で書いたのでは
ないでしょうか?
FILE fp=fopen("foo.txt","r");
fread(buf,10,sizeof(char),fp);
とかがそこいらじゅうにあってやだというような...
# その著者も、ただのprintfの返り値は滅多なことでは見たりはせんでしょうにねぇ。
At 09 Jan 2004 09:27:46 +0900,
Takahide Nojima wrote:
> # その著者も、ただのprintfの返り値は滅多なことでは見たりはせんでしょ
> # うにねぇ。
(僕の記憶はあやふやですが)、以前にここかどこかで
(void) printf ....
がずらずら書かれている記述を見たような気がしたようなしないような。
lint前提のコードですね。
#さすがにうるさいのでつかわん。
--
___ わしは、山吹色のかすてーらが大好きでのぅ
[[o o]] ふぉっふぉっふぉ
'J' 森下 お代官様 MaNMOS 英夫@ステラクラフト
PGP Finger = CD EA D5 A8 AD B2 FE 7D 02 74 87 52 7C B7 39 37
下手すりゃprintfの返り値は変換したフィールドの数だったり吐いたバイト数
だったりするんで、互換性が…
In article <86wu82...@xh6.cc.hokudai.ac.jp>
Hiroki Kashiwazaki <r...@cc.hokudai.ac.jp> writes:
> そこでちゃんと戻り値を見ようという事で、今書いているコードの中に
> 頻出する strncpy について考える時、
> char *strncpy (char *s1, const char *s2, size_t n);
> で、s1の値が返される訳ですが、
strncpy() の戻り値は、元々、意味的に調べる必要はありません。
雰囲気として
if( (x = y) == z ) ...
と書くのを真似て、
if( strcmp(strcpy(x,y),z)==0 ) ...
と書きやすくしただけなので。
バッファ・オーバーフローを防ぐには、strncpy() は実は今一つで
す。n を取りますが、最後に埋めるだけなので。結構、誤用される
関数だと思います。私もだいぶ長い間誤用していた気がします。
本当は、strlcpy() がいいんですよね。FreeBSD のマニュアルより。
------------------------------------------------------------
STRLCPY(3) FreeBSD Library Functions Manual STRLCPY(3)
NAME
strlcpy, strlcat - size-bounded string copying and concatenation
SYNOPSIS
#include <string.h>
size_t
strlcpy(char *dst, const char *src, size_t size)
size_t
strlcat(char *dst, const char *src, size_t size)
DESCRIPTION
The strlcpy() and strlcat() functions copy and concatenate strings re-
spectively. They are designed to be safer, more consistent, and less er-
ror prone replacements for strncpy(3) and strncat(3). Unlike those func-
tions, strlcpy() and strlcat() take the full size of the buffer (not just
the length) and guarantee to NUL-terminate the result (as long as size is
larger than 0). Note that you should include a byte for the NUL in size.
The strlcpy() function copies up to size - 1 characters from the NUL-ter-
minated string src to dst, NUL-terminating the result.
The strlcat() function appends the NUL-terminated string src to the end
of dst. It will append at most size - strlen(dst) - 1 bytes, NUL-termi-
nating the result.
RETURN VALUES
The strlcpy() and strlcat() functions return the total length of the
string they tried to create. For strlcpy() that means the length of src.
For strlcat() that means the initial length of dst plus the length of
src. While this may seem somewhat confusing it was done to make trunca-
tion detection simple.
------------------------------------------------------------
単にバッファ・オーバーフローを防ぎたいだけならこうします。
------------------------------------------------------------
char *s, *p, buf[BUFSIZ];
...
(void)strlcpy(buf, s, sizeof(buf));
(void)strlcat(buf, p, sizeof(buf));
------------------------------------------------------------
後ろがちょん切れてもいいなら、結果を調べる必要はありません。
(あ、(void)だ。)
後ろが切れてはいけないなら、こうします。
------------------------------------------------------------
char *dir, *file, pname[MAXPATHNAMELEN];
...
if (strlcpy(pname, dir, sizeof(pname)) >= sizeof(pname))
goto toolong;
if (strlcat(pname, file, sizeof(pname)) >= sizeof(pname))
goto toolong;
------------------------------------------------------------
ただ、strlcpy() は、Linux に入ってなくて困るんですよね。しょ
うがないので代りに snprintf() を使うように勧めているんですけ
ど。いくら snprintf() を使えと言っても、strcpy() とか
strcat() 使う人が多くて困ります。どうしたらいいでしょうか。
ライブラリ関数の名前の影響は怖いですね。
\\ 新城 靖 (しんじょう やすし) \\
\\ 筑波大学 電子・情報 \\
In article <86wu82...@xh6.cc.hokudai.ac.jp>,
r...@cc.hokudai.ac.jp writes:
> 色んな本のバックナンバーなどを読み漁っている今日このごろ、とある
> 本に「関数の戻り値を見ないプログラム書く学生多過ぎ」とか書かれて
> あって、「うう、耳が痛い」という状態です。
エラーチェックをどの程度きちんとしなければならないかは、場合によります
よね。自分あるいはそのプログラムをよく知っている人だけが使うプログラムや、
使い捨てに近いプログラムの場合は、それほどきちんとしなくてもよい(開発の
スピードの方が重要視される)ことも多々あるでしょうし。逆に、配布して他人
にも使わせる汎用プログラムを意図している場合などは、バウンダリチェック以
外にも気にしなければならないことは多いですから、結構大変。例えば(これも
場合にもよりますが)
> printf ("FAILED.\n");
→ fprintf(stderr, "FAILED.\n"); とか。
そういう場合は、strlcpy()互換品を作ってプログラムに添付しておいて、
Linuxで使う場合はそれがリンクされるようにしておくのがよくある手ですよね。
1度作っておけば以後使い回しもできるし。snprintf()だと(マニュアル見ても難
しそうなので?)使ってくれない人も、strlcpy()だと(慣れているstrncpy()に似
ているからと)使ってくれませんかね。
ni...@ics.nara-wu.ac.jp
> バッファ・オーバーフローを防ぐには、strncpy() は実は今一つで
> す。n を取りますが、最後に埋めるだけなので。結構、誤用される
> 関数だと思います。私もだいぶ長い間誤用していた気がします。
> 本当は、strlcpy() がいいんですよね。
すみません、もう少し詳しく教えてください。
この文は、
strncpy(dest, src, n) は n > strlen(src) の場合に
dest+strlen(src) 以降に nul を埋めるだけで、
strlcpy の場合はこれに加え、n <= strlen(src) の場合も
dest+n に nul を埋めるので、バッファ・オーバーフローを防ぐ
には strlcpy のほうがベターである
ということをおっしゃっているのでしょうか?
# そうじゃないとすると私も長い間誤用しているのに気付かない
# でいたことになりそうなんで。
> ただ、strlcpy() は、Linux に入ってなくて困るんですよね。しょ
> うがないので代りに snprintf() を使うように勧めているんですけ
> ど。いくら snprintf() を使えと言っても、strcpy() とか
> strcat() 使う人が多くて困ります。どうしたらいいでしょうか。
私は strcpy や strcat を好んで使いますし、これからも使ってい
くと思います。strncpy などは n なしに比べてだいぶ遅いんです
もん。
strlcpy/strlcat は今まで知りませんでしたが、FreeBSD や
OpenBSD なんかにしかないならまず使うことはないです。
たぶん strcpy/strcat を使わないように指導するのではなく、そ
れらの関数の危険性と、安全に使うための方法を教えたほうがいい
のではないでしょうか?
そうじゃないと goto 否定論者みたいになっちゃいませんか。
--
01/10 16:03頃
水戸
想像ですが多分,strlcpy/strlcatはNUL-terminate が保証されているが,strncpyは
必ずしもそうとは言えない(strncatは保証されているけど)ということと,
strlcpy/strlcatは,コピーの結果のサイズが常にsize_t sizeで固定されていると言
うことではないでしょうか。(strncatでは,バッファを超えてコピーされる。)
でも,strlcpy/strlcatで間違って,char *dstのバッファサイズ以上の数をsize_t
sizeに入れてしまった場合の動作はどうなっているのだろう。やっぱ,バッファ・
オーバーフローをおこすのだろうかな。(それとも自動的にバッファサイズに合わす
の
だろうか。でも,そうなっているとしたら初めからsize_t sizeのパラメータなんて
必要なくなるか。)
--
******************************
keizi kounoike
******************************
In article <m3ad4wl...@kzin.dip.jp>
Mito <co_...@ybb.ne.jp> writes:
> > 本当は、strlcpy() がいいんですよね。
> strncpy(dest, src, n) は n > strlen(src) の場合に
> dest+strlen(src) 以降に nul を埋めるだけで、
> strlcpy の場合はこれに加え、n <= strlen(src) の場合も
> dest+n に nul を埋めるので、バッファ・オーバーフローを防ぐ
> には strlcpy のほうがベターである
>
> ということをおっしゃっているのでしょうか?
strncpy() は、0 を埋めるので遅いけど、strlcpy() は埋めないの
で速い。たとえば、こんな使い方をすると。
char s1[BUFSIZE],s2[BUFSIZE] ;
...
strncpy(s1,s2,sizeof(s1))
strlcpy(s1,s2,sizeof(s1))
strncpy() には、もともとバッファ・オーバーフローを防ぐという
意味はなかったのでしょう。0 で埋めてうれしい局面も、少なかっ
たろうに。
> 私は strcpy や strcat を好んで使いますし、これからも使ってい
> くと思います。strncpy などは n なしに比べてだいぶ遅いんです
> もん。
速度を気にする時代じゃないと思いますよ。
> strlcpy/strlcat は今まで知りませんでしたが、FreeBSD や
> OpenBSD なんかにしかないならまず使うことはないです。
自分で書いて入れればいいんじゃないかなあ。たとえば
strlcpy( s1, s2, l )
{
return( snprintf(s1,l,"%s",s2) );
}
strcat() は、単純にはいきませんが、
strcpy( buf, s1 );
strcat( buf, s2 );
というパタンは、こうです。
snprintf( buf,sizeof(buf),"%s%s",s1,s2);
> たぶん strcpy/strcat を使わないように指導するのではなく、そ
> れらの関数の危険性と、安全に使うための方法を教えたほうがいい
> のではないでしょうか?
人間は間違える。それが前提です。その前提で安全に使う方法とは、
どういう方法ですか。是非とも教えてください。
その方法と、strlcpy()、snprintf() を使う方法との比較しましょう。
> > strncpy(dest, src, n) は n > strlen(src) の場合に
> > dest+strlen(src) 以降に nul を埋めるだけで、
> > strlcpy の場合はこれに加え、n <= strlen(src) の場合も
> > dest+n に nul を埋めるので、バッファ・オーバーフローを防ぐ
> > には strlcpy のほうがベターである
> >
> > ということをおっしゃっているのでしょうか?
結局↑はどうなんでしょう?
新城さんがおっしゃる「誤用」というのをはっきり知りたいと思っ
ていますので教えてください。しつこくてすみません。
> > 私は strcpy や strcat を好んで使いますし、これからも使ってい
> > くと思います。strncpy などは n なしに比べてだいぶ遅いんです
> > もん。
>
> 速度を気にする時代じゃないと思いますよ。
ですね。たいていは気分の問題だと思います。中にはほんとに必要
な場合もあることにはあると思いますが。
> > たぶん strcpy/strcat を使わないように指導するのではなく、そ
> > れらの関数の危険性と、安全に使うための方法を教えたほうがいい
> > のではないでしょうか?
>
> 人間は間違える。それが前提です。その前提で安全に使う方法とは、
> どういう方法ですか。是非とも教えてください。
こう聞かれると、そんなものはありませんとしか答えられません
ねぇ。strlcpy なんかでも使いかたを間違えればバッファオーバー
フローも起りえますし。
> その方法と、strlcpy()、snprintf() を使う方法との比較しましょう。
一概にどの方法がいいということは言えないと思うんです。
strlcpy を使ったほうがいい場合もありますし、strcpy で十分な
場合もあると思うのですが、その判断を行うには、それらの関数の
仕様の理解と、想定される危険性についての知識が必要だと思うの
です。
というのを踏まえて、たとえばですが、こんなのでも多くのケース
では十分安全だと思います。
void copy(char *dst, char *src, size_t n)
{
char *b;
size_t l = strlen(src);
if (!(b = (char*)malloc(l + 1)))
abort();
strcpy(b, src);
if (l > n)
b[n] = '\0';
strcpy(dst, b);
free(b);
}
これはある程度汎用的に使えますが、局所的な用途では、
src[MAX] = '\0';
strcpy(dst, src);
で事足りてしまうケースもよくあります。
--
01/12 10:51頃
水戸
本筋からは離れますが,上とほぼ同じことでよいなら,strcpyにこだわらなくとも
char * copyn(char *dst, char *src, size_t n) {
int i;
char *s;
s = dst;
for(i = 0; !(*dst++ = *src++) || i < n - 1; i++);
*dst = '\0';
return s;
}
くらいでもよいような。また,仕様は若干違ってきますが,
strncpy(dst, src, n);
dst[n] = '\0';
でも十分な気がします。
strncpyでスピードのことを言われていた水戸さんなのに,なんか変な感じがしまし
た。
>
> The strlcpy() function copies up to size - 1 characters from the
NUL-ter-
> minated string src to dst, NUL-terminating the result.
>
> The strlcat() function appends the NUL-terminated string src to the
end
> of dst. It will append at most size - strlen(dst) - 1
bytes,NUL-termi-
> nating the result.
>
> RETURN VALUES
> The strlcpy() and strlcat() functions return the total length of the
> string they tried to create.
> 後ろが切れてはいけないなら、こうします。
> ------------------------------------------------------------
> char *dir, *file, pname[MAXPATHNAMELEN];
> ...
> if (strlcpy(pname, dir, sizeof(pname)) >= sizeof(pname))
> goto toolong;
> if (strlcat(pname, file, sizeof(pname)) >= sizeof(pname))
> goto toolong;
> ------------------------------------------------------------
些細なことなんですが,(strlcpy/strlcatが無いので確認できないので)。FreeBSD
のマニュアルマニュアルのNote that you should include a byte
for the NUL in size.等から言えば,上のは
if (strlcpy(pname, dir, sizeof(pname)) >= sizeof(pname) - 1)
goto toolong;
if (strlcat(pname, file, sizeof(pname)) >= sizeof(pname) - 1)
goto toolong;
となるような気がするんですが。どうなんでしょうか。
> (strlcpy/strlcatが無いので確認できないので)。
http://www.openbsd.org/cgi-bin/cvsweb/src/lib/libc/string/strlcpy.c
http://www.openbsd.org/cgi-bin/cvsweb/src/lib/libc/string/strlcat.c
--
池田研二 稲城駅前在住
なるほど。池田さん,情報有難うございました。
strlcpyは,NUL-terminate が保証がされているだけでなく,strncpyみたいにバッ
ファの残りをnulで埋めるものと思っていましたが,そうじゃないのか。
In article <m31xq69...@kzin.dip.jp>
Mito <co_...@ybb.ne.jp> writes:
> 新城さんがおっしゃる「誤用」というのをはっきり知りたいと思っ
> ていますので教えてください。しつこくてすみません。
誤用というのは、strncpy() で、n があるからバッファオーバーラ
ンしないだろうということで、strlcpy() の代りに使うというもの
です。n があると 0 で埋めるという動作を知らないで。
あと、strlen() してバイト数を数えてから strncpy() したり。
strncpy(dst,src,strlen(src));
> 一概にどの方法がいいということは言えないと思うんです。
> strlcpy を使ったほうがいい場合もありますし、strcpy で十分な
> 場合もあると思うのですが、その判断を行うには、それらの関数の
> 仕様の理解と、想定される危険性についての知識が必要だと思うの
> です。
「場合による」というと、まあ、小泉首相みたいで、それはそうな
んだけど。
> というのを踏まえて、たとえばですが、こんなのでも多くのケース
> では十分安全だと思います。
>
> void copy(char *dst, char *src, size_t n)
> {
> char *b;
> size_t l = strlen(src);
> if (!(b = (char*)malloc(l + 1)))
> abort();
> strcpy(b, src);
> if (l > n)
> b[n] = '\0';
> strcpy(dst, b);
> free(b);
> }
たしかに、malloc() した直後で大きさが分かっていれば strcpy()
でもOKというのはわかります。でも、大きさわかつているなら、
memcpy() したい。
void copy(char *dst, char *src, size_t n)
{
size_t l = strlen(src);
if( l > n-1 )
l = n-1 ;
memcpy(dst,src,l);
dst[l] = 0;
}
とまあ、strcpy() が必要になる例は、消えました。
In article <btvfe0$9r$1...@caraway.media.kyoto-u.ac.jp>
<koun...@mbh.nifty.com> writes:
> 本筋からは離れますが,上とほぼ同じことでよいなら,strcpyにこだわらなくとも
> char * copyn(char *dst, char *src, size_t n) {
> int i;
> char *s;
> s = dst;
> for(i = 0; !(*dst++ = *src++) || i < n - 1; i++);
> *dst = '\0';
> return s;
> }
バイト単位でコピーするのと memcpy() とどうなんでしょうね。
memcpy() は、大きいとバイト単位ではなくて、ワード単位でコピー
したりするんじゃないかなあ。CPU にもよるかもしれないけれど。
Pentium って、バイト単位の演算が妙に速いんですよね。
> くらいでもよいような。また,仕様は若干違ってきますが,
> strncpy(dst, src, n);
> dst[n] = '\0';
> でも十分な気がします。
src が短い時、これは重いんです。strlcpy() ならいいんだけど。
y...@is.tsukuba.ac.jp (Yasushi Shinjo) writes:
> バイト単位でコピーするのと memcpy() とどうなんでしょうね。
> memcpy() は、大きいとバイト単位ではなくて、ワード単位でコピー
> したりするんじゃないかなあ。CPU にもよるかもしれないけれど。
RISC 系の CPU なら、memcpy() だけでなく、strcpy() でもワード単位で転送
する実装の方が多いと思います。
> Pentium って、バイト単位の演算が妙に速いんですよね。
x86 の GNU libc を見てみたら、memcpy() はワード単位、strcpy() はバイト
単位の転送になってました。
--
片山@PFU
In article <YAS.04Ja...@kirk.is.tsukuba.ac.jp>, y...@is.tsukuba.ac.jp (Yasushi Shinjo) writes
> あと、strlen() してバイト数を数えてから strncpy() したり。
> strncpy(dst,src,strlen(src));
ありがち。見たことあるし。
> バイト単位でコピーするのと memcpy() とどうなんでしょうね。
> memcpy() は、大きいとバイト単位ではなくて、ワード単位でコピー
> したりするんじゃないかなあ。CPU にもよるかもしれないけれど。
....
> Pentium って、バイト単位の演算が妙に速いんですよね。
最近のIntel系は、CPUが勝手にまとめてアクセスするように変換
してしまうみたいですね。
アライメントをずらしてword copyとかしてもベンチマークで時間
が変わらなくてびっくりしたことがあります。
---
Shinji KONO @ Information Engineering, University of the Ryukyus,
河野真治 @ 琉球大学工学部情報工学科,
>
> > くらいでもよいような。また,仕様は若干違ってきますが,
> > strncpy(dst, src, n);
> > dst[n] = '\0';
> > でも十分な気がします。
>
> src が短い時、これは重いんです。strlcpy() ならいいんだけど。
質問です。バイト単位,ワード単位の違いはあるかも知れませんが,memcpyって
size_t n分きちっりコピーするんでは。だとしたら,strncpyが重いと言うのは
ちょっと納得できません。つまり,ワード単位を考慮したstrncpyを作れば(あるの
かも知れませんが)memcpy採用よりは速くなるはずと思いますが。(strlen(src)が
不要な分だけ。)
> たしかに、malloc() した直後で大きさが分かっていれば strcpy()
> でもOKというのはわかります。でも、大きさわかつているなら、
> memcpy() したい。
[...]
> とまあ、strcpy() が必要になる例は、消えました。
前の例は strcpy() が必要な例ではなくて、strcpy() を使っても
安全な例だったのですが、どうも私の書きかたがわるいのか誤解を
与えてしまっているようですね。
とにかく、stlcpy() は strncpy() と違い、n 未満の文字列をコピー
する場合も、dst[n] まで nul を埋めることはしないということは
わかりましたし、strlcpy() があるなら使うべきで、ない環境でも
ある程度以上の規模のソフトを作るなら自前のものを用意して使う
べきだという意見にも同意します。
引用が前後しますが、
> 誤用というのは、strncpy() で、n があるからバッファオーバーラ
> ンしないだろうということで、strlcpy() の代りに使うというもの
> です。n があると 0 で埋めるという動作を知らないで。
> あと、strlen() してバイト数を数えてから strncpy() したり。
>
> strncpy(dst,src,strlen(src));
やっぱりそういう方には「strlcpy使え」じゃなくてちゃんと教え
てあげるのがよさそうな気がしますが、そうもいかないんでしょう
か?
> > 一概にどの方法がいいということは言えないと思うんです。
> > strlcpy を使ったほうがいい場合もありますし、strcpy で十分な
> > 場合もあると思うのですが、その判断を行うには、それらの関数の
> > 仕様の理解と、想定される危険性についての知識が必要だと思うの
> > です。
>
> 「場合による」というと、まあ、小泉首相みたいで、それはそうな
> んだけど。
「...それはそうなんだけど、説明がめんどくさいんだよね」でしょ
うかねぇ?
新城さんの考えは、たとえ strcpy の正しい使いかたを知ったとし
ても使うべきではない、というふうに思われるのですが、もしそう
なら理由を教えてもらえませんか?
私は正しい使いかたを知っていれば別に使ってはいけない関数では
ないと思うのです。
> バイト単位でコピーするのと memcpy() とどうなんでしょうね。
> memcpy() は、大きいとバイト単位ではなくて、ワード単位でコピー
> したりするんじゃないかなあ。CPU にもよるかもしれないけれど。
本題とははずれますが、私は memcpy() ってほとんど使いません。
代わりに memmove() を使います。memcpy() が必要な場面って思い
付きませんが、何かありましたっけ?
--
01/14 22:54頃
水戸
> たしかに、malloc() した直後で大きさが分かっていれば strcpy()
> でもOKというのはわかります。でも、大きさわかつているなら、
> memcpy() したい。
[...]
> とまあ、strcpy() が必要になる例は、消えました。
前の例は strcpy() が必要な例ではなくて、strcpy() を使っても
安全な例だったのですが、どうも私の書きかたがわるいのか誤解を
与えてしまっているようですね。
とにかく、strlcpy() は strncpy() と違い、n 未満の文字列をコ
ピーする場合も、dst[n] まで nul を埋めることはしないというこ
とはわかりましたし、strlcpy() があるなら使うべきで、ない環境
でもある程度以上の規模のソフトを作るなら自前のものを用意して
使うべきだという意見にも同意します。
引用が前後しますが、
> 誤用というのは、strncpy() で、n があるからバッファオーバーラ
> ンしないだろうということで、strlcpy() の代りに使うというもの
> です。n があると 0 で埋めるという動作を知らないで。
> あと、strlen() してバイト数を数えてから strncpy() したり。
>
> strncpy(dst,src,strlen(src));
やっぱりそういう方には「strlcpy使え」じゃなくてちゃんと教え
てあげるのがよさそうな気がしますが、そうもいかないんでしょう
か?
> > 一概にどの方法がいいということは言えないと思うんです。
> > strlcpy を使ったほうがいい場合もありますし、strcpy で十分な
> > 場合もあると思うのですが、その判断を行うには、それらの関数の
> > 仕様の理解と、想定される危険性についての知識が必要だと思うの
> > です。
>
> 「場合による」というと、まあ、小泉首相みたいで、それはそうな
> んだけど。
「...それはそうなんだけど、説明がめんどくさいんだよね」でしょ
うかねぇ?
新城さんの考えは、たとえ strcpy の正しい使いかたを知ったとし
ても使うべきではない、というふうに思われるのですが、もしそう
なら理由を教えてもらえませんか?
私は正しい使いかたを知っていれば別に使ってはいけない関数では
ないと思うのです。
> バイト単位でコピーするのと memcpy() とどうなんでしょうね。
> memcpy() は、大きいとバイト単位ではなくて、ワード単位でコピー
> したりするんじゃないかなあ。CPU にもよるかもしれないけれど。
本題とははずれますが、私は memcpy() ってほとんど使いません。
>だとしたら,strncpyが重いと言うのは
> ちょっと納得できません。つまり,ワード単位を考慮したstrncpyを作れば(ある
の
> かも知れませんが)memcpy採用よりは速くなるはずと思いますが。(strlen(src)
が
> 不要な分だけ。)
自己フォロー。すいません,これ撤回させて頂きます。早とちりでした。ワード単位
を考慮したstrncpyのスマートな実装ってそう簡単ではないような気がします。でき
たところでmemcpyより速くなるとは思えません。つまり,どうやって残り(これも
strlen(src)しなければ分からない。)をワード単位に,しかも'\0'で埋めるのかと
考えると。ちょっと私では思いつきません。
蛇足ですが,上の例では,文字列のコピーだけ考えるなら
memcpy(dst,src,n);
dst[n] = 0;
で用はなすように思います。dst[l] = 0は保証されているから。
In article <m34quy6...@kzin.dip.jp>
Mito <co_...@ybb.ne.jp> writes:
> 新城さんの考えは、たとえ strcpy の正しい使いかたを知ったとし
> ても使うべきではない、というふうに思われるのですが、もしそう
> なら理由を教えてもらえませんか?
strcpy() を使った方がよい局面というのが、なかなか出てこない
ので、「strcpy()使うな」でいいんじゃないか、ということです。
何か「こういう時には strcpy() がいい」という状況はありますか?
> 本題とははずれますが、私は memcpy() ってほとんど使いません。
> 代わりに memmove() を使います。memcpy() が必要な場面って思い
> 付きませんが、何かありましたっけ?
memmove() か。なるほど。これは、重なっていても OK なわけね。
それは、memcpy() は、領域が重なっていたらアウト。それは、
memmove() の方が、いいんじゃないですか。
In article <bu4io3$63j$1...@caraway.media.kyoto-u.ac.jp>
<koun...@mbh.nifty.com> writes:
> 自己フォロー。すいません,これ撤回させて頂きます。早とちりでした。ワード単位
> を考慮したstrncpyのスマートな実装ってそう簡単ではないような気がします。でき
> たところでmemcpyより速くなるとは思えません。つまり,どうやって残り(これも
> strlen(src)しなければ分からない。)をワード単位に,しかも'\0'で埋めるのかと
> 考えると。ちょっと私では思いつきません。
最近は、メモリのアクセス回数で速度は図れます。残りを 0 で埋
めたいという要求は、そんなにはないんじゃないか、ということで
す。そうなると、メモリを埋めるだけ損だということです。
char s1[100];
strncpy(s1,s2,sizeof(s1));
strlcpy(s1,s2,sizeof(s1));
を考えると、strncpy() だと 100 バイト全部 store が入るけれど、
strlcpy() なら、s2 が短ければその文しかアクセスしません。
n = strlen(s2);
strncpy(s1,s2,n);
s1[n]=0;
strlen() と cpy で2回 s2 をスキャンしているので、strlcpy()
より遅そうだというのは、そうなんでしょう。キャッシュが効くの
で、そんなには差はないかもしれませんね。
In article <3989351...@insigna.ie.u-ryukyu.ac.jp>
ko...@ie.u-ryukyu.ac.jp (Shinji KONO) writes:
> 河野真治 @ 琉球大学情報工学です。
> 最近のIntel系は、CPUが勝手にまとめてアクセスするように変換
> してしまうみたいですね。
> アライメントをずらしてword copyとかしてもベンチマークで時間
> が変わらなくてびっくりしたことがあります。
複数バイトのストアを、ワード単位ののストアに自動的にまとめた
りはしないんですか。ロードの方は、自動的にそうなるんだろうけ
れど。キャッシュでライトバックなら自動的にそうなるということ
かなあ。
> > 新城さんの考えは、たとえ strcpy の正しい使いかたを知ったとし
> > ても使うべきではない、というふうに思われるのですが、もしそう
> > なら理由を教えてもらえませんか?
>
> strcpy() を使った方がよい局面というのが、なかなか出てこない
> ので、「strcpy()使うな」でいいんじゃないか、ということです。
そういうことでしたか。ちょっと残念。
> 何か「こういう時には strcpy() がいい」という状況はありますか?
コピー先のサイズが分らない場合は strcpy() がいいです。:-P
ということじゃなくて、もともとの話の流れの buffer overflow
を防ぐために strcpy や strcat を使わせないようにするにはどう
したらいいかという話での、strcpy がいい状況ってことですよね。
たぶんないんじゃないでしょうか。strlcpy なり snprintf ででき
ますし。
ただ、「strcpy() で十分」という状況は多々あると思うわけで、
そんな時にも「strcpy()使うな」っていやだなぁと。
P.S.
そういや strncpy は構造体のメンバにセットする時便利だっての
を思い出しました。
struct {
char s[128];
} s1, s2;
strncpy(s1.s, "abc", sizeof(s1.s));
strncpy(s2.s, "abc", sizeof(s2.s));
if (memcmp(&s1, &s2, sizeof(s1)) == 0) {
puts("便利!");
}
--
01/17 10:50頃
水戸
> 蛇足ですが,上の例では,文字列のコピーだけ考えるなら
> memcpy(dst,src,n);
> dst[n] = 0;
> で用はなすように思います。dst[l] = 0は保証されているから。
でも,そうすると, memcpy が src の(配列としての)最後を超えて
アクセスすることがあり得ます.その場合,結果は未定義になりますね.
dst[n] = 0;は dst[n-1] = 0 の間違いです。(どうも配列のサイズと配列のイン
デックス数はいつも間違えてしまう。)
で,言われている「結果は未定義」の意味がちょっと分かりません。
l > n-1 ----> l = n-1 ----> dst[l] = 0; (== dst[n-1] = 0 )
l <= n-1 ----> dst[l] = 0; ----> memcpy(dst,src,n) で 既にdst[l] = 0になっ
いる。
で,dstは文字列としてみた場合,必ずnulで終わっている。
新城さんのは,memcpyを使用してstrlcpyと同じコピー仕様を実現する方法なので,
私の”dst[n] = 0; で用はなすように思います。”は,またまた撤回させて頂きま
す。(口は災いの元とはよく言ったものだ。しゃべればしゃべるほどボロが出る。)
> > > 蛇足ですが,上の例では,文字列のコピーだけ考えるなら
> > > memcpy(dst,src,n);
> > > dst[n] = 0;
> > > で用はなすように思います。dst[l] = 0は保証されているから。
> >
> > でも,そうすると, memcpy が src の(配列としての)最後を超えて
> > アクセスすることがあり得ます.その場合,結果は未定義になりますね.
>
> dst[n] = 0;は dst[n-1] = 0 の間違いです。(どうも配列のサイズと配列のイン
> デックス数はいつも間違えてしまう。)
>
> で,言われている「結果は未定義」の意味がちょっと分かりません。
char src[10] = "abc";
char dst[100];
size_t n = sizeof(dst);
memcpy(dst, src, n);
という場合 memcpy は src[10] 以降にもアクセスするので bus
error になる場合もある?ってことじゃないでしょうか。
--
01/17 15:04頃
水戸
う~ん。となると, memcpy は文字列のコピーには危なっかしくて使えないというこ
とになるのかな。よく,分かりませんが。
> そういや strncpy は構造体のメンバにセットする時便利だっての
> を思い出しました。
>
> struct {
> char s[128];
> } s1, s2;
> strncpy(s1.s, "abc", sizeof(s1.s));
> strncpy(s2.s, "abc", sizeof(s2.s));
> if (memcmp(&s1, &s2, sizeof(s1)) == 0) {
> puts("便利!");
> }
この場合は良さそうすが,
一般の構造体には応用不可ですね,
たとえば,
struct { char a; int b; char s[128];} s1, s2;
s1.a='a'; s1.b=0;
s2.a='a'; s2.b=0;
strncpy(s1.s, "abc", sizeof(s1.s));
strncpy(s2.s, "abc", sizeof(s2.s));
if (memcmp(&s1, &s2, sizeof(s1)) == 0) {
puts("a と s の間に詰め物をする処理系の場合,それも含めて一致!");
puts("そうでない処理系ではちょっと便利かも");
puts("でも,移植性低すぎ!");
}
でしゃばってすいません。たぶん,水戸さんは
if (memcmp(&s1, &s2, sizeof(s1)) == 0)
を
if (memcmp(&s1.s, &s2.s, sizeof(s1.s)) == 0)
と書き仕損じたというより,手を抜いただけだと思っていますが。
まあ,上の場合で,strncpyを使う(今までの水戸さんの言われていることからは)
意図についてはよく分かりませんけど。
> でしゃばってすいません。たぶん,水戸さんは
> if (memcmp(&s1, &s2, sizeof(s1)) == 0)
> を
> if (memcmp(&s1.s, &s2.s, sizeof(s1.s)) == 0)
> と書き仕損じたというより,手を抜いただけだと思っていますが。
いえ、構造体同士の比較を行う場合に便利だという例で、上のほう
が意図したものです。
> まあ,上の場合で,strncpyを使う(今までの水戸さんの言われていることからは)
> 意図についてはよく分かりませんけど。
すみません、またまた言葉足らずでしたか。
Mito <co_...@ybb.ne.jp> writes:
> struct {
> char s[128];
> } s1, s2;
> strncpy(s1.s, "abc", sizeof(s1.s));
> strncpy(s2.s, "abc", sizeof(s2.s));
> if (memcmp(&s1, &s2, sizeof(s1)) == 0) {
> puts("便利!");
> }
strncpy なら、 s1.sは {'a','b','c','\0','\0',....'\0'} にな
りますが、strcpy では {'a','b','c','\0'} の後はいじりません
ので不定です。
構造体同士の比較を memcmp で行う場合は strncpy のような状態
になるのが望ましいので、strncpy のほうがいいということです。
mihoga...@yahoo.com (miho) writes:
> この場合は良さそうすが,
> 一般の構造体には応用不可ですね,
> たとえば,
>
> struct { char a; int b; char s[128];} s1, s2;
> s1.a='a'; s1.b=0;
> s2.a='a'; s2.b=0;
> strncpy(s1.s, "abc", sizeof(s1.s));
> strncpy(s2.s, "abc", sizeof(s2.s));
> if (memcmp(&s1, &s2, sizeof(s1)) == 0) {
> puts("a と s の間に詰め物をする処理系の場合,それも含めて一致!");
> puts("そうでない処理系ではちょっと便利かも");
> puts("でも,移植性低すぎ!");
> }
パディングの処理は strcpy/strncpy に関係なく必要ですよね。
実際に memcmp で比較するような時は、memset などで初期化をや
るのが普通だと思うんですが、こういうのって移植性が低いんです
か?memcmp で構造体の比較っての自体が普通じゃないのかなぁ?
--
01/18 11:40頃
水戸
"Mito" <co_...@ybb.ne.jp> wrote in message
news:m3oet33...@kzin.dip.jp...
> <koun...@mbh.nifty.com> writes:
> > で,言われている「結果は未定義」の意味がちょっと分かりません。
> という場合 memcpy は src[10] 以降にもアクセスするので bus
> error になる場合もある?ってことじゃないでしょうか。
mihoさん,水戸さんどうもです。
言われている意味について,納得です。神頼みのプログラムといことですね。
用は,関数を正しく理解し,正しく使用しなさいということですね。(余りにも,当
然すぎてコメントにも値しないな。)
P.S memcpyのdocumentには,そんなこと一言も書かれてないので,メモリを参照
するだけなら,文句は言わないのかな思ってしまいました。OSのエラー動作につい
てよく理解してないと,こういうdocumentの読み方しかできないと言う素人の読み方
でした。(反省)
In article <m3oet33...@kzin.dip.jp>
Mito <co_...@ybb.ne.jp> writes:
> char src[10] = "abc";
> char dst[100];
> size_t n = sizeof(dst);
> memcpy(dst, src, n);
>
> という場合 memcpy は src[10] 以降にもアクセスするので bus
> error になる場合もある?ってことじゃないでしょうか。
char src[10]; のような配列の場合、未定義でアクセスしても、そ
れだけでは決して bus error になったりはしません。単に前に使っ
ていたメモリの内容が出てくるだけです。
バスエラーになる可能性があるのは、ポインタの時です。初期化し
ていないポインタをアクセスすると、変なメモリをさして、
segmentation fault とか bus error になります。
char の配列は、0 終端で使う限りは、後ろを 0 などで埋める必要
はありません。ただ、0 で埋めないと、そこから情報が盗まれると
いうことはあるので、そういう時には埋めます。bss とか data を
0 で埋めるのを忘れたのは大昔の話として、最近でも、スタック領
域を 0 で埋めるのを手抜きしていたOSがありました。配列の初
期化とは別の話で問題です。
> char src[10] = "abc";
こういう auto 変数の初期化って、C言語の規格として、OK なん
ですか。昔はだめだったんだけど。gcc が受付けるのは知っています。
私が書くならこんな感じ。
strlcpy(src,"abc",sizeof(src));
snprintf(src,sizeof(src),"abc");
In article <m3smif4...@kzin.dip.jp>
Mito <co_...@ybb.ne.jp> writes:
> > 何か「こういう時には strcpy() がいい」という状況はありますか?
> コピー先のサイズが分らない場合は strcpy() がいいです。:-P
なるほど。それはそうですね。(←念のために書いておきますが、
コピー先のサイズが分からないという状況は、非常に危険な状況な
ので、そういうことがないようにプログラムを作るべきだという意
味です。)
> そういや strncpy は構造体のメンバにセットする時便利だっての
> を思い出しました。
> struct {
> char s[128];
> } s1, s2;
> strncpy(s1.s, "abc", sizeof(s1.s));
> strncpy(s2.s, "abc", sizeof(s2.s));
> if (memcmp(&s1, &s2, sizeof(s1)) == 0) {
> puts("便利!");
> }
たしかに。これは、使えます。メモリ中のデータ構造というよりは、
ディスクに書込むようなデータ構造の時には、0で埋めたくなりま
す。たとえば、昔の14文字いないのディレクトリとか、utmp と
か wtmp とか。
構造体の memcmp() は、やらないけど。オブジェクト指向的に構造
体ごとに equal() 関数を定義します。
<YAS.04Ja...@kirk.is.tsukuba.ac.jp>の記事において
y...@is.tsukuba.ac.jpさんは書きました。
yas> > char src[10] = "abc";
yas> > char dst[100];
yas> > size_t n = sizeof(dst);
yas> > memcpy(dst, src, n);
yas> > という場合 memcpy は src[10] 以降にもアクセスするので bus
yas> > error になる場合もある?ってことじゃないでしょうか。
yas> char src[10]; のような配列の場合、未定義でアクセスしても、そ
yas> れだけでは決して bus error になったりはしません。単に前に使っ
yas> ていたメモリの内容が出てくるだけです。
a[10] という配列の場合, アドレスとしては a+0~a+10 が, データとし
ては a[0]~a[9] が有効であり, それ以外のアクセスについては
undefined behavior となっています. 従って
現実的には:
bus error はないかもしれませんが segmentation fault はありえます.
規格上は:
undefined behavior なので, 何が起きても文句は言えません.
yas> こういう auto 変数の初期化って、C言語の規格として、OK なん
yas> ですか。昔はだめだったんだけど。gcc が受付けるのは知っています。
ISO C では全く合法的です.
--
名古屋大学大学院 情報科学研究科 計算機数理科学専攻
小野 孝男
In article <0401192051...@flame.hirata.nuee.nagoya-u.ac.jp>
ta...@hirata.nuee.nagoya-u.ac.jp (Takao Ono) writes:
> 小野@名古屋大学 です.
>> char src[10]; のような配列の場合、未定義でアクセスしても、そ
>> れだけでは決して bus error になったりはしません。単に前に使っ
>> ていたメモリの内容が出てくるだけです。
> a[10] という配列の場合, アドレスとしては a+0~a+10 が, データとし
> ては a[0]~a[9] が有効であり, それ以外のアクセスについては
> undefined behavior となっています. 従って
> 現実的には:
> bus error はないかもしれませんが segmentation fault はありえます.
そうですか。私は、segmentation fault も、あり得ないと思いま
す。どういう仕組みで segmentation fault が起きるのか、説明し
てもらえませんか。
たとえば、
char a[10];
a[5] = 10 ;
は、 OK ですよね。初期化だし。これが OK ということは、メモリ
は既に割り当てられていることが保証されているわけです。機械語
命令だと番地の計算はできて、store は OK。これが OK の状態で、
a[5] = 10 ; の代りに
x = a[5] ;
とすると、これは、番地の計算は OK で、store が load に変った
だけです。segmentation fault は、番地の計算が狂った時に出る
ので、store が ok で load で出るという話は、ありません。
というのが、新城の説明。segmentation fault が起きるという納
得の行く説明ができるなら、聞きたいです。
ROM とかテキストに置く文字列かなにかで load はできるけど
store できないというのは、あります。逆に store できて load
できないというのは、普通のメモリではないんじゃないですか。な
にかありますか。ハードウェアのレジスタならあるんでしょうけれど。
> 規格上は:
> undefined behavior なので, 何が起きても文句は言えません.
文句は言えないから起きるというのでは、ちょっと納得できません。
Java だと、array は、0 か何かが入っていることが保証されてい
るんですよね。
<YAS.04Ja...@kirk.is.tsukuba.ac.jp>の記事において
y...@is.tsukuba.ac.jpさんは書きました。
yas> >> char src[10]; のような配列の場合、未定義でアクセスしても、そ
yas> >> れだけでは決して bus error になったりはしません。単に前に使っ
yas> >> ていたメモリの内容が出てくるだけです。
yas> > a[10] という配列の場合, アドレスとしては a+0~a+10 が, データとし
yas> > ては a[0]~a[9] が有効であり, それ以外のアクセスについては
yas> > undefined behavior となっています. 従って
yas> > 現実的には:
yas> > bus error はないかもしれませんが segmentation fault はありえます.
yas> そうですか。私は、segmentation fault も、あり得ないと思いま
yas> す。どういう仕組みで segmentation fault が起きるのか、説明し
yas> てもらえませんか。
(中略)
yas> とすると、これは、番地の計算は OK で、store が load に変った
yas> だけです。segmentation fault は、番地の計算が狂った時に出る
yas> ので、store が ok で load で出るという話は、ありません。
もちろん現実的な状況で「store は ok だけど load で segmentation
fault」はないですね... というより, そんなメモリ保護はありえないで
すね. load じゃなく exec ならありえますが.
ただ,
yas> >> char src[10]; のような配列の場合、未定義でアクセスしても、そ
yas> >> れだけでは決して bus error になったりはしません。単に前に使っ
yas> >> ていたメモリの内容が出てくるだけです。
というのと「ポインタでは~」というのを対比させたときに「(配列かポ
インタかには無関係に) 変なところをアクセスすれば segmentation
fault になりうる」ということを強調したかっただけですので.
yas> > 規格上は:
yas> > undefined behavior なので, 何が起きても文句は言えません.
yas> 文句は言えないから起きるというのでは、ちょっと納得できません。
そこまでは言っていないんですけど....
# そもそもそんなプログラムを作る時点でアウトですけど.
yas> Java だと、array は、0 か何かが入っていることが保証されてい
yas> るんですよね。
Java だと, いかなる場合においても何らかの値 (数値型だと 0,
boolean だと false, オブジェクト型だと null) で初期化されることが
保証されています. これは array の中身についても成り立ちます.
# このように「とにかく何らかの値で初期化される」ことを保証した最
# 初の言語ってなんなんでしょうね? Lisp?
fj.comp.lang.cの記事<YAS.04Ja...@kirk.is.tsukuba.ac.jp>で
y...@is.tsukuba.ac.jpさんは書きました。
> そうですか。私は、segmentation fault も、あり得ないと思いま
> す。どういう仕組みで segmentation fault が起きるのか、説明し
> てもらえませんか。
Mitoさんの示された
| char src[10] = "abc";
| char dst[100];
| size_t n = sizeof(dst);
| memcpy(dst, src, n);
ではsrc[0]~src[99]がアクセスされるわけです。
char src[10]がプロセスに割り当てられたセグメントの
末尾付近に配置されていた場合、セグメント境界を越え
たアドレスにアクセスすることになるわけで、例外が発
生する可能性はあるのではないですか?
--
太田純(Junn Ohta) (株)リコー/新横浜事業所
oh...@sdg.mdd.ricoh.co.jp
今は、Subject: にあるように、配列を初期化しないで使った時に
どうなかという話です。初期化しないで使っても、segmentation
fault はないと。
In article <0401192051...@flame.hirata.nuee.nagoya-u.ac.jp>
ta...@hirata.nuee.nagoya-u.ac.jp (Takao Ono) writes:
> 小野@名古屋大学 です.
>> char src[10]; のような配列の場合、未定義でアクセスしても、そ
>> れだけでは決して bus error になったりはしません。単に前に使っ
>> ていたメモリの内容が出てくるだけです。
> a[10] という配列の場合, アドレスとしては a+0~a+10 が, データとし
> ては a[0]~a[9] が有効であり, それ以外のアクセスについては
> undefined behavior となっています. 従って
> 現実的には:
> bus error はないかもしれませんが segmentation fault はありえます.
bus error と segmentation fault は、なにか違いがあるんですか。
アドレスとして a[10] が有効というのは、なんか変です。規格に
あるんですか。
外部変数かなにかで
int a[4096-100];
int a[4096-104];
という具合いに宣言したら調べられるんでしょうけど。
In article <buic3k$6ie$1...@ns.src.ricoh.co.jp>
oh...@src.ricoh.co.jp (Junn Ohta) writes:
> Mitoさんの示された
> | char src[10] = "abc";
> | char dst[100];
> | size_t n = sizeof(dst);
> | memcpy(dst, src, n);
> ではsrc[0]~src[99]がアクセスされるわけです。
これは、0 が入っている src[4] で止まるんじゃないですか。
と思ったら、strncpy() じゃなくて、memcpy() か。
それは、memcpy() の使い方が間違っています。
> char src[10]がプロセスに割り当てられたセグメントの
> 末尾付近に配置されていた場合、セグメント境界を越え
> たアドレスにアクセスすることになるわけで、例外が発
> 生する可能性はあるのではないですか?
はい。memcpy() で segmentation fault になることがあると思います。
Subject: は、初期化しない配列を使うとどうなるかという話でし
た。src[10] = "abc" で、src[5] を使うとどうなるかという話。
もともとは。
In article <YAS.04Ja...@kirk.is.tsukuba.ac.jp>, Yasushi Shinjo wrote:
>新城@筑波大学情報です。こんにちは。
>たとえば、
> char a[10];
> a[5] = 10 ;
>は、 OK ですよね。初期化だし。これが OK ということは、メモリ
>は既に割り当てられていることが保証されているわけです。機械語
>命令だと番地の計算はできて、store は OK。これが OK の状態で、
>a[5] = 10 ; の代りに
> x = a[5] ;
>とすると、これは、番地の計算は OK で、store が load に変った
>だけです。segmentation fault は、番地の計算が狂った時に出る
>ので、store が ok で load で出るという話は、ありません。
Segmentation fault と言ったかどうか分かりませんが,記憶領域を動的に
割り当てるシステムで,まだ一度も書き込んでないアドレスを読もうとする
とエラーになるというのが(昔?)有ったような記憶があります.
#書き込んだ時に初めて実記憶領域を割り当てるという仕組み.
別の例として,こういう振る舞いをチェックするためにそういうハードある
いはエミュレータを作ることは(当然ですが)可能です.
--
Hideki Kato <mailto:ka...@pop12.odn.ne.jp>
----== Posted via Newsfeed.Com - Unlimited-Uncensored-Secure Usenet News==----
http://www.newsfeed.com The #1 Newsgroup Service in the World! >100,000 Newsgroups
---= 19 East/West-Coast Specialized Servers - Total Privacy via Encryption =---
<YAS.04Ja...@kirk.is.tsukuba.ac.jp>の記事において
y...@is.tsukuba.ac.jpさんは書きました。
yas> >> char src[10]; のような配列の場合、未定義でアクセスしても、そ
yas> >> れだけでは決して bus error になったりはしません。単に前に使っ
yas> >> ていたメモリの内容が出てくるだけです。
yas> > a[10] という配列の場合, アドレスとしては a+0~a+10 が, データとし
yas> > ては a[0]~a[9] が有効であり, それ以外のアクセスについては
yas> > undefined behavior となっています. 従って
yas> > 現実的には:
yas> > bus error はないかもしれませんが segmentation fault はありえます.
yas> bus error と segmentation fault は、なにか違いがあるんですか。
意味は違うんじゃないですか?
yas> アドレスとして a[10] が有効というのは、なんか変です。規格に
yas> あるんですか。
規格を調べていただければよいのですが....
# 私は「アドレスとして a[10] が有効」とは書いていないです.
6.5.6 Additive operators, paragraph 8 から:
.... Moreover, if the expression P points to the last element of
an array object, the expression (P)+1 points one past the last
element of the array element, and if the expression Q points one
past the last element of the array object, the expression (Q)-1
points to the last element of the array object. If both the
pointer operand and the result point to elements of the same
array object, or one past the last element of the array object,
the eveluation shall not produce an overflow; otherwise, the
behabior is undefined. If the result points one past the last
element of the array object, it shall not be used as the operand
of a unary * operator that is evaluated.
大きさ 10 の配列 a に対して a+10 は one past the last element of
the array object を指すので合法的に使うことができる. a+11 は one
past the last element of the array object を指さないし element of
the same array object も指さないから undefined behavior.
もちろん a+10 は one past the last element of the array object を
指すので *(a+10) はやっちゃいけない (undefined behavior) だし, 等
価な a[10] も同じく undefined behavior.
yas> Subject: は、初期化しない配列を使うとどうなるかという話でし
yas> た。src[10] = "abc" で、src[5] を使うとどうなるかという話。
yas> もともとは。
こちらも 6.7.8 Initialization, paragraph 21 から:
If there are fewer initializers in a brace-enclosed list than
there are elements or members of an aggregate, or fewer
characters in a string literal used to initialize an array of
known size than there are elements in the array, the remainder of
the aggregate shall be initialized implicitly the same as objects
that have static storage duration.
つまり,
char src[10] = "abc";
だったら src[5] は 0 になります.
> > a[10] という配列の場合, アドレスとしては a+0~a+10 が, データとし
> > ては a[0]~a[9] が有効であり, それ以外のアクセスについては
> > undefined behavior となっています. 従って
> > 現実的には:
> > bus error はないかもしれませんが segmentation fault はありえます.
>
> そうですか。私は、segmentation fault も、あり得ないと思いま
> す。どういう仕組みで segmentation fault が起きるのか、説明し
> てもらえませんか。
もしかして用語の行き違いがあるんじゃないでしょうか。
小野さんがおっしゃってる「アドレスとして有効」というのは、「ポインタと
して有効な(たとえば、比較して正しい結果となる)値である」ということでしょ
う。「アドレス空間がマップされている」というOS的な意味ではなくて。
つまり、
・オブジェクトへのポインタ
・関数へのポインタ
・nullポインタ
・配列の最後の要素の「すぐ次」を指すポインタ
のいずれかです。nullポインタや配列の最後+1番めへのポインタは、「ポインタ
として有効」ですが、dereferenceしてはいけませんよね。
規格が想定しているのはたとえば、リニアでない(セグメント方式の)アドレス
空間を持つマシンです。 任意のポインタ同士では、アドレスが全順序でない
ので、比較がうまくいかなかったりします。
でも、配列の先頭から配列の最後の要素の「すぐ次」のアドレスまでは比較が
できないと困るので、その範囲は全順序を保証するように処理系を作りなさい、
ということです。で、もちろん「最後のすぐ次」を読み書きできるようにする
必要はないわけですけど。
http://www.lysator.liu.se/c/rat/c3.html#3-3-6
http://www.lysator.liu.se/c/rat/c2.html#3-2-2-3
前田敦司
ta...@hirata.nuee.nagoya-u.ac.jp (Takao Ono) writes:
> yas> > char src[10] = "abc";
> yas> > char dst[100];
> yas> > size_t n = sizeof(dst);
> yas> > memcpy(dst, src, n);
> yas> char src[10]; のような配列の場合、未定義でアクセスしても、そ
> yas> れだけでは決して bus error になったりはしません。単に前に使っ
> yas> ていたメモリの内容が出てくるだけです。
> a[10] という配列の場合, アドレスとしては a+0~a+10 が, データとし
> ては a[0]~a[9] が有効であり, それ以外のアクセスについては
> undefined behavior となっています. 従って
> 現実的には:
> bus error はないかもしれませんが segmentation fault はありえます.
最近の環境(32 ビット空間の hosted environment)では、90 バイトくらい
はみ出しても SIGSEGV は、まず起きないでしょうが、16 ビット空間の頃は危
なかったです。
当時は環境変数が今ほど多くなかったので、90 バイトのオーバーで 0 番地ア
クセス(*)の可能性がありました。PDP-11 のように 64 KB すべてメモリとな
る CPU では、0 番地アクセスでも読み出しなら SIGSEGV は起きませんが、読
み出しだけでも SIGSEGV となる CPU もありました。
*スタックは 0xffff 番地から若いアドレスへ伸びていきます
*スタックの底に環境変数、その上にコマンド引数が積まれます
*その上に *envp や *argv が積まれます
*それらの総和(+α)が 90 バイトに満たないと 0 番地アクセス
> yas> こういう auto 変数の初期化って、C言語の規格として、OK なん
> yas> ですか。昔はだめだったんだけど。gcc が受付けるのは知っています。
> ISO C では全く合法的です.
更に、C99 では初期値が定数式でなくてもよくなっています。
C90:
6.5.7 Initialization
Constraints
...
All the expressions in an initializer for an object that has static
strage duration or in an initializer list for an object that has
aggregate or union type shall be constant expressions.
C99:
6.7.8 Initialization
Constraints
...
All the expressions in an initializer for an object that has static
strage duration shall be constant expressions or string literals.
--
片山@PFU
In article <m3ad4ju...@maedapc.cc.tsukuba.ac.jp>
MAEDA Atusi <maeda...@ialab.is.tsukuba.ac.jp> writes:
> でも、配列の先頭から配列の最後の要素の「すぐ次」のアドレスまでは比較が
> できないと困るので、その範囲は全順序を保証するように処理系を作りなさい、
> ということです。で、もちろん「最後のすぐ次」を読み書きできるようにする
> 必要はないわけですけど。
> http://www.lysator.liu.se/c/rat/c3.html#3-3-6
> http://www.lysator.liu.se/c/rat/c2.html#3-2-2-3
In article <0401201609...@flame.hirata.nuee.nagoya-u.ac.jp>
ta...@hirata.nuee.nagoya-u.ac.jp (Takao Ono) writes:
> 小野@名古屋大学 です.
> # 私は「アドレスとして a[10] が有効」とは書いていないです.
なるほど。Pentium でまじめセグメント使ったりするような環境だ
と、一般にはポインタが比較できないけれど、char a[10] で、
for( p = &a[0]; p < &a[10] ; p++ )
は、OK にしようという話ですね。しかし、この規格、a[10] は駄
目で、&a[10] は、ok というのは、やめて欲しい。抽象化すると
・「&なんとか」は、ok
・「 なんとか」は、だめ
ということでしょ。
In article <400cd04b.6909%ka...@pop12.odn.ne.jp>
Hideki Kato <ka...@pop12.odn.ne.jp> writes:
> Segmentation fault と言ったかどうか分かりませんが,記憶領域を動的に
> 割り当てるシステムで,まだ一度も書き込んでないアドレスを読もうとする
> とエラーになるというのが(昔?)有ったような記憶があります.
> #書き込んだ時に初めて実記憶領域を割り当てるという仕組み.
C言語の配列で、ですか。オブジェクト指向データベース(永続的
なC++)のオブジェクトの参照を MMU 使ってやるという話は、
今でもあります。
In article <0401201609...@flame.hirata.nuee.nagoya-u.ac.jp>
ta...@hirata.nuee.nagoya-u.ac.jp (Takao Ono) writes:
>> Subject: は、初期化しない配列を使うとどうなるかという話でし
>> た。src[10] = "abc" で、src[5] を使うとどうなるかという話。
>> もともとは。
> char src[10] = "abc";
> だったら src[5] は 0 になります.
この規格は、ひどいね。char a[10] ならか可愛いけど、char
a[10000] = "abc" とかなると、想像したくない。去年は、配列の
代入の話をしていたわけなので、それがOKならこれもOKにしな
いといけないんだろうけど。
最初の問題にもどします。文字列リテラルによる初期化という話で
はなくて、単に初期化していない要素をアクセスした時に、
segmentation fault か bus error が起きるかどうかです。
main()
{
char a[10], x ;
a[0] = 'a' ;
a[1] = 'b' ;
a[2] = 'c' ;
a[3] = 0 ;
x = a[5] ; /* ここ */
}
もちろん、a[4] でも a[6] でもいいんですけど。
> 規格を調べていただければよいのですが....
規格には、興味は、あまりないので。興味があるのは、よいプログ
ラムを書くことや、よいデバッグの方法です。segmentation fault
が起きたという時に、「初期化していない char の配列を使ったか
らかもしれない、よし、全部初期化しよう」とは、思わなくてもい
いですよね。つまり、「初期化していない char の配列の要素をア
クセスした所で、segmentation fault なんか起きない」(起きて
欲しい、そういう処理系も欲しいけど、今の環境ではなかなか手に
入らない)ということです。
In article <0401192051...@flame.hirata.nuee.nagoya-u.ac.jp>
ta...@hirata.nuee.nagoya-u.ac.jp (Takao Ono) writes:
> 現実的には:
> bus error はないかもしれませんが segmentation fault はありえます.
> 規格上は:
> undefined behavior なので, 何が起きても文句は言えません.
現実の方ね。
<YAS.04Ja...@kirk.is.tsukuba.ac.jp>の記事において
y...@is.tsukuba.ac.jpさんは書きました。
yas> > char src[10] = "abc";
yas> > だったら src[5] は 0 になります.
yas> この規格は、ひどいね。char a[10] ならか可愛いけど、char
yas> a[10000] = "abc" とかなると、想像したくない。去年は、配列の
yas> 代入の話をしていたわけなので、それがOKならこれもOKにしな
yas> いといけないんだろうけど。
この「ひどい」というのは, 「初期化子がなければどんな値になってい
るかわからないけど, 初期化子があれば必ず 0 になる」のがひどいって
意味ですね?
yas> 最初の問題にもどします。文字列リテラルによる初期化という話で
yas> はなくて、単に初期化していない要素をアクセスした時に、
yas> segmentation fault か bus error が起きるかどうかです。
一応規格上は
6.7.8 Initialization, paragraph 10:
If an object that has automatic storage duration is not
initialized explicitly, its value is indeterminate.
でこの indeterminate は 3.17.2:
indeterminate value
either an unspecified value or a trap representation.
unspecified value は 3.17.3:
unspecified value
valid value of the relevant type where this International
Standard imposes no requirements on which value is chosen in any
instance.
trap representation については 6.2.6.1 General, paragraph 5:
Certain object representations need not represent a value of the
object type. If the stored value of an object has such a
representation and is read by an lvalue expression that does not
have character type, the behavior is undefined. If such a
representation is produced by a side effect that modifies all or
any part of the object by an lvalue expression that does not have
character type, the behavior is undefined. Such a representation
is called a trap representation.
配列要素に限らないんですが, 初期化しない自動変数の値を参照という
のは, 規格上は「何が起きても知らない」ってことになりますかね.
yas> > 規格を調べていただければよいのですが....
yas> 規格には、興味は、あまりないので。興味があるのは、よいプログ
yas> ラムを書くことや、よいデバッグの方法です。
「よいプログラム」とはどういう意味でしょうか? その意味によっては
規格が重要になりえると思います.
yas> > 現実的には:
yas> > bus error はないかもしれませんが segmentation fault はありえます.
yas> > 規格上は:
yas> > undefined behavior なので, 何が起きても文句は言えません.
yas> 現実の方ね。
残念ながら現実についてはほとんど知りませんので....
ふと思ったのですけど、コンパイラの規格って、コンパイラを作る
人が読むもので、コンパイラを作る人が読むものではないんじゃな
いかなあ。
In article <0401210917...@flame.hirata.nuee.nagoya-u.ac.jp>
ta...@hirata.nuee.nagoya-u.ac.jp (Takao Ono) writes:
> 小野@名古屋大学 です.
>> 最初の問題にもどします。文字列リテラルによる初期化という話で
>> はなくて、単に初期化していない要素をアクセスした時に、
>> segmentation fault か bus error が起きるかどうかです。
> 一応規格上は
> 6.7.8 Initialization, paragraph 10:
> If an object that has automatic storage duration is not
> initialized explicitly, its value is indeterminate.
「値が決定できない」ということは、「segmentation fault」は起
きなくて、なにか値は返ってくるという意味では。
> trap representation については 6.2.6.1 General, paragraph 5:
> Certain object representations need not represent a value of the
> object type. If the stored value of an object has such a
> representation and is read by an lvalue expression that does not
> have character type, the behavior is undefined. If such a
> representation is produced by a side effect that modifies all or
> any part of the object by an lvalue expression that does not have
> character type, the behavior is undefined. Such a representation
> is called a trap representation.
>
> 配列要素に限らないんですが, 初期化しない自動変数の値を参照という
> のは, 規格上は「何が起きても知らない」ってことになりますかね.
a trap representation か。これは、「segmentation fault」とい
う意味なんですか。
>> > 規格を調べていただければよいのですが....
>> 規格には、興味は、あまりないので。興味があるのは、よいプログ
>> ラムを書くことや、よいデバッグの方法です。
> 「よいプログラム」とはどういう意味でしょうか? その意味によっては
> 規格が重要になりえると思います.
よいプログラムとは、一番は、人間が読んで分かりやすいプログラ
ムかなあ。それから、ちゃんと動く(よいプログラムでも動かない
のも場合によっては許す)。次は効率がよい(よいプログラムでも
効率が悪くても場合によっては許す)。
もちろん、規格で、「分け分からないことになる」と書いてあるよ
うな動作を使うプログラムは、人間が読んでも分けわからないので、
だいたい悪いプログラムというのは、その通りです。だけど、「規
格だからどうの」というよう因果関係で言われると引っ掛かる。
規格で、「分け分からないことになる」と書いてあるのは、どちら
かというと、コンパイラを作る人に対して「そこまではコンパイラ
で面倒みなくてもいいよ」と言っているんじゃないですか。
In article <0401210917...@flame.hirata.nuee.nagoya-u.ac.jp>
ta...@hirata.nuee.nagoya-u.ac.jp (Takao Ono) writes:
>> この規格は、ひどいね。char a[10] ならか可愛いけど、char
>> a[10000] = "abc" とかなると、想像したくない。去年は、配列の
>> 代入の話をしていたわけなので、それがOKならこれもOKにしな
>> いといけないんだろうけど。
> この「ひどい」というのは, 「初期化子がなければどんな値になってい
> るかわからないけど, 初期化子があれば必ず 0 になる」のがひどいって
> 意味ですね?
いいえ。遅いプログラムが簡単に書けるという所がひどい。たとえ
ば、次の似たようなプログラムがあったします。
f1()
{
char a[10000] = "abc" ;
...
}
f2()
{
char a[10000];
strlcpy( a,"abc",sizeof(a) );
...
}
一見、上の方が関数呼出しない分速そうだけど、たぶん下の方が速
いでしょう。
<YAS.04Ja...@kirk.is.tsukuba.ac.jp>の記事において
y...@is.tsukuba.ac.jpさんは書きました。
yas> ふと思ったのですけど、コンパイラの規格って、コンパイラを作る
yas> 人が読むもので、コンパイラを作る人が読むものではないんじゃな
yas> いかなあ。
言語の規格ってのは, 処理系を作る人にとっては「ここまでは動作を保
証しなければならない」という線引きを, 処理系を使う人にとっては
「ここまでなら*どの処理系でも*動作が保証できる」という線引きを
するための, 最終的な判断基準ではないですかね?
だから, 処理系を作る人は必ず読まないといけないし, 処理系を使う人
であっても読んでおいた方がいいものだと思います.
yas> > 一応規格上は
yas> > 6.7.8 Initialization, paragraph 10:
yas> > If an object that has automatic storage duration is not
yas> > initialized explicitly, its value is indeterminate.
yas> 「値が決定できない」ということは、「segmentation fault」は起
yas> きなくて、なにか値は返ってくるという意味では。
いや, だからちゃんと indeterminate value という言葉についても意味
を与えてあるんですが.
indeterminate value だからそこからの読み出しが undefined behavior
になるかもしれない. で, undefined behavior だからどう振舞うかは全
く分らない. 何か値が返ってくるかもしれないし, プログラムが異常終
了するかもしれない.
yas> a trap representation か。これは、「segmentation fault」とい
yas> う意味なんですか。
もちろんそんな意味はありません... というか, むしろ「何も意味しな
い」と言った方が適切かも.
# undefined behavior って書いてあるんだし.
yas> >> > 規格を調べていただければよいのですが....
yas> >> 規格には、興味は、あまりないので。興味があるのは、よいプログ
yas> >> ラムを書くことや、よいデバッグの方法です。
yas> > 「よいプログラム」とはどういう意味でしょうか? その意味によっては
yas> > 規格が重要になりえると思います.
yas> よいプログラムとは、一番は、人間が読んで分かりやすいプログラ
yas> ムかなあ。それから、ちゃんと動く(よいプログラムでも動かない
yas> のも場合によっては許す)。次は効率がよい(よいプログラムでも
yas> 効率が悪くても場合によっては許す)。
プログラムが「ちゃんと動く」ためには, 本来規格は必須のはずなんで
すが....
# もちろん「ポータビリティなんか無視, とにかく目の前の問題が解け
# ればよし」という前提であればいいのかもしれませんがね.
yas> 規格で、「分け分からないことになる」と書いてあるのは、どちら
yas> かというと、コンパイラを作る人に対して「そこまではコンパイラ
yas> で面倒みなくてもいいよ」と言っているんじゃないですか。
一方で, 処理系を使う人に対しても「どういう振舞いをしようとそれは
あなたの責任だからね」と宣言していることにもなります.
小野さんは、規格の通りにコンパイラが作れるということを前提に
していますか。人間が作るものなので、規格の通りを目指しても
バグは入るし、もともと規格自体に問題があることもあります。
In article <0401211910...@flame.hirata.nuee.nagoya-u.ac.jp>
ta...@hirata.nuee.nagoya-u.ac.jp (Takao Ono) writes:
> だから, 処理系を作る人は必ず読まないといけないし, 処理系を使う人
> であっても読んでおいた方がいいものだと思います.
読んでおいた方がいい。うまい表現です。で、C言語でプログラム
を書く人は規格を読まなくてもいいんですよね?
だいたい gcc にしても、規格にしたがって作られているわけでは
ありません。規格を破って規格外の機能を入れていたりします。
Microsoft Visual C++ とかも。
> indeterminate value だからそこからの読み出しが undefined behavior
> になるかもしれない. で, undefined behavior だからどう振舞うかは全
> く分らない. 何か値が返ってくるかもしれないし, プログラムが異常終
> 了するかもしれない.
「undefined behavior だから、異常終了するかもしれない」ですか。
その知識は、今の問題の配列の初期化には役に立ちません。
問題は、次のようなプログラムで segmentation fault が起きるかどうかです。
main()
{
char a[10], x ;
a[0] = 'a' ;
a[1] = 'b' ;
a[2] = 'c' ;
a[3] = 0 ;
x = a[5] ; /* ここ */
}
もちろん、a[4] でも a[6] でもいいんですけど。
a[5] のアクセスでは、segmentation fault は起きないというのは、
いいですか? (規格はともかく、現実のコンパイラで存在しない。)
> プログラムが「ちゃんと動く」ためには, 本来規格は必須のはずなんで
> すが....
プログラムが「ちゃんと動く」ためには、コンパイラがどう作られ
ているかを知るのが必須です。
>> 規格で、「分け分からないことになる」と書いてあるのは、どちら
>> かというと、コンパイラを作る人に対して「そこまではコンパイラ
>> で面倒みなくてもいいよ」と言っているんじゃないですか。
> 一方で, 処理系を使う人に対しても「どういう振舞いをしようとそれは
> あなたの責任だからね」と宣言していることにもなります.
それは、規格が決めているというよりは、コンパイラと使う人の間
で決めている話じゃないですか。
結局、規格を持ち出しても持ち出さなくても結果が同じです。C言
語でプログラムを書くなら、規格を勉強するより、OSとかCPU
の勉強をした方がいいんじゃないですか。
自分の手元で動けばよいプログラムを作るのか、配布して様々な環境で使われ
ることを目的としたプログラムを作るのか、にもよるんじゃないですかね。
後者の場合、手元の処理系でたまたま動いたというだけでは、他の様々な環境
で動くことは保証できません。で、少なくとも規格を満たしたコンパイラではちゃ
んと動くように作りたければ、ある書き方をしたとき規格ではどう動くことになっ
ているか、は理解しておかねばならないわけです。
ni...@ics.nara-wu.ac.jp
> >> 規格には、興味は、あまりないので。興味があるのは、よいプログ
> >> ラムを書くことや、よいデバッグの方法です。
> > 「よいプログラム」とはどういう意味でしょうか? その意味によっては
> > 規格が重要になりえると思います.
>
> よいプログラムとは、一番は、人間が読んで分かりやすいプログラ
> ムかなあ。それから、ちゃんと動く(よいプログラムでも動かない
> のも場合によっては許す)。次は効率がよい(よいプログラムでも
> 効率が悪くても場合によっては許す)。
この、「よいプログラム」の定義はどこから来たものなのでしょう?
読みやすければ、いくら効率がわるくっても、たとえ動かなくって
もいいってことですよね。
最近はこういうのが主流なんでしょうか?とっても興味があります。
ぜひこういう考えにいたる過程というか論理というか、そういうの
を教えてください。
P.S.
segmentation fault とかの話題はよくわかりませんので静観させ
ていただきます。私の考えは太田さんにフォローしていただいた通
りです。
P.P.S
配列の初期化は単なる書き損じです。^^; すみません。
Cでできるようになったとか、なるっていうのは小野さんの記事を
拝見するまで知りませんでした。
--
01/21 22:33頃
水戸
In article <m3u12p2z...@kzin.dip.jp>, Mito <co_...@ybb.ne.jp> writes
> 読みやすければ、いくら効率がわるくっても、たとえ動かなくって
> もいいってことですよね。
>
> 最近はこういうのが主流なんでしょうか?とっても興味があります。
> ぜひこういう考えにいたる過程というか論理というか、そういうの
> を教えてください。
人間のための仕様としてプログラムを使うなら、そういう論理で問
題ないです。ま、どう使ってもいいっちゃいいわけだし。
> この、「よいプログラム」の定義はどこから来たものなのでしょう?
規格をどうこうしようって人達って、「プログラムは人間が
読む仕様である」ってことを忘れているのかも...
規格が一つ、そして、実装が一つ。実は同じとは限らない。規格み
ないのも馬鹿ではあるけど、規格だけを信用するのも良くないです
ね。
---
Shinji KONO @ Information Engineering, University of the Ryukyus,
河野真治 @ 琉球大学工学部情報工学科,
C言語の話から外れてきたんだけれど、
In article <m3u12p2z...@kzin.dip.jp>
Mito <co_...@ybb.ne.jp> writes:
>> よいプログラムとは、一番は、人間が読んで分かりやすいプログラ
>> ムかなあ。それから、ちゃんと動く(よいプログラムでも動かない
>> のも場合によっては許す)。次は効率がよい(よいプログラムでも
>> 効率が悪くても場合によっては許す)。
> この、「よいプログラム」の定義はどこから来たものなのでしょう?
そのよいプログラムの定義は、新城の直感です。まあ説明すると、
技術の進歩を考えると、
・効率の方は、コンパイラが頑張ればいい。部分評価とか。
・よいプログラムが動かないなら、コンパイラと規格が悪い。だか
ら、よいプログラムはそのままでコンパイラと規格の方を替えればいい。
というのがあります。
部分評価の技術を使えば、たぶん、こんなコードがあったとして、
a[10000] = "abc" ;
0終端以降は使われないくらいは解析して、それ以降の0で埋める
コードを出さないくらいは、そんなに難しくはないんでしょう。
まあ、部分評価の理論的には、配列の扱いは難しいみたいんだけど。
> 読みやすければ、いくら効率がわるくっても、たとえ動かなくって
> もいいってことですよね。
「いくら悪くても」とは、ちょっと言えませんね。さすがに。
O(n^2) のプログラムと O(n log n) のプログラムがあって、いく
ら O(n^2) のプログラムがいくら読みやすくても、O(n log n) よ
りいいとは言えない、いくらコンパイラの技術が進んでも、O(n^2)
のプログラムを O(n log n) には自動変換はしてくれないと思います。
> segmentation fault とかの話題はよくわかりませんので静観させ
> ていただきます。私の考えは太田さんにフォローしていただいた通
> りです。
記事が多くても別に困らないで、自分の読みたい記事を誘導するよ
うな記事を書くといいです。
In article <3989370...@insigna.ie.u-ryukyu.ac.jp>
ko...@ie.u-ryukyu.ac.jp (Shinji KONO) writes:
> 河野真治 @ 琉球大学情報工学です。
> 規格が一つ、そして、実装が一つ。実は同じとは限らない。規格み
> ないのも馬鹿ではあるけど、規格だけを信用するのも良くないです
> ね。
規格が出た所で思考停止するのもなんとかして欲しい。
昔、RFC 読めというのも、あったんだけど。
Symbolics C では,segmentation fault にはなりませんが この場
合 x は未定義の値になりますね.もちろん,char 型ではありませ
んから,ゴミの値というわけですらありません.
> > この、「よいプログラム」の定義はどこから来たものなのでしょう?
>
> そのよいプログラムの定義は、新城の直感です。まあ説明すると、
> 技術の進歩を考えると、
>
> ・効率の方は、コンパイラが頑張ればいい。部分評価とか。
> ・よいプログラムが動かないなら、コンパイラと規格が悪い。だか
> ら、よいプログラムはそのままでコンパイラと規格の方を替えればいい。
>
> というのがあります。
なるほど、ちょっと考えていたこととは違いましたが、納得です。
> C言語の話から外れてきたんだけれど、
では、ちょっと戻して「よいプログラムが動かないなら、コンパイ
ラと規格が悪い」について。
現在のC言語でも読みやすく、よいプログラムを作ることは可能だ
と思いますが、コンパイラや規格のために阻害されるようなことが
あるということなんでしょうか?
前に出てきた、
> なるほど。Pentium でまじめセグメント使ったりするような環境だ
> と、一般にはポインタが比較できないけれど、char a[10] で、
>
> for( p = &a[0]; p < &a[10] ; p++ )
>
> は、OK にしようという話ですね。しかし、この規格、a[10] は駄
> 目で、&a[10] は、ok というのは、やめて欲しい。抽象化すると
> ・「&なんとか」は、ok
> ・「 なんとか」は、だめ
> ということでしょ。
というのは、たしかにこのような規格がよくないというのはわかり
ますが、この例自体がポインタとアドレスを混同しているよくない
プログラムですよね?
# Cのポインタには大小関係なんて存在しないと思っているんです
# が、これは間違った見識だったでしょうか?
#
# つまり、上の例では↓ならまだいいかなってところだと思うん
# ですが。
# for (p = &a[0]; p != &a[10]; p++)
また、Cは歴史的な経緯であまり大きな規格の変更はできないので
はないかと思いますが、過去の資産を捨ててもコンパイラと規格を
変更するだけの意義があるのでしょうか?
単細胞の私には他の言語に乗り換えたほうが幸せになれそうな気が
しているのですが。
# 毎度、教えてくんで申し訳ないです。
--
01/22 23:04頃
水戸
> > 読みやすければ、いくら効率がわるくっても、たとえ動かなくって
> > もいいってことですよね。
> >
> > 最近はこういうのが主流なんでしょうか?とっても興味があります。
> > ぜひこういう考えにいたる過程というか論理というか、そういうの
> > を教えてください。
>
> 人間のための仕様としてプログラムを使うなら、そういう論理で問
> 題ないです。ま、どう使ってもいいっちゃいいわけだし。
それは仕様書とか、ドキュメントっていうんじゃないでしょうか?
人間が見て仕様を理解するための文書ということですよね?
> > この、「よいプログラム」の定義はどこから来たものなのでしょう?
>
> 規格をどうこうしようって人達って、「プログラムは人間が
> 読む仕様である」ってことを忘れているのかも...
>
> 規格が一つ、そして、実装が一つ。実は同じとは限らない。規格み
> ないのも馬鹿ではあるけど、規格だけを信用するのも良くないです
> ね。
う~ん、仰る内容は理解できているつもりなのですが、私の問いに
対する返事としては繋がりがわからないのですが、行間を読めない
私にもわかるようにもう少し解説してはいただけないでしょうか?
--
01/22 23:28頃
水戸
ポインタとアドレスって同じようなものだと思っていたんですが。違うんですか。
>
> # Cのポインタには大小関係なんて存在しないと思っているんです
> # が、これは間違った見識だったでしょうか?
> #
> # つまり、上の例では↓ならまだいいかなってところだと思うん
> # ですが。
> # for (p = &a[0]; p != &a[10]; p++)
ポインタに大小関係が存在しないとすれば,どうやってポインタを進めたり,戻した
りする概念を規定すればいいんでしょうか。
<bupnv1$1ei$1...@caraway.media.kyoto-u.ac.jp>の記事において
koun...@mbh.nifty.comさんは書きました。
kounoike> > # Cのポインタには大小関係なんて存在しないと思っているんです
kounoike> > # が、これは間違った見識だったでしょうか?
ポインタの大小は「アドレス空間中における相対位置」として定義され
ています.
従って, 同じ配列にある要素を指すポインタ同士の間では大小関係が確
定します:
int a[N];
int i, j;
int *p = &a[i], *q = &a[j];
のとき 0 ≦ i < j ≦ N ならば p < q.
あと, 1個の構造体オブジェクトにおける, 同じ型を持つ異なるメンバー
を指すポインタ同士の大小関係も確定します:
struct A {
int x;
int y;
} a;
int *p = &a.x, *q = &x.y;
ならば p < q.
kounoike> ポインタに大小関係が存在しないとすれば,どうやってポインタを進めたり,戻した
kounoike> りする概念を規定すればいいんでしょうか。
「大小関係の存在」と「ポインタを進める/戻す」とは別問題ではないか
と. 現に C++ のイテレータでは「大小関係」はなくても「進める/戻す」
という操作が可能な例があります.
# というか, 「大小関係」があるのは random access iterator だけの
# ような.
> > > # Cのポインタには大小関係なんて存在しないと思っているんです
> > > # が、これは間違った見識だったでしょうか?
> ポインタの大小は「アドレス空間中における相対位置」として定義され
> ています.
あらら。いままで間違って覚えていたようです。ありがとうござい
ました。
大小関係は存在するけど制約があるので、< や > なんかで比較す
べきものじゃぁないとでも覚えておきますね。
--
01/23 12:15頃
水戸
ta...@hirata.nuee.nagoya-u.ac.jp (Takao Ono) writes:
> 従って, 同じ配列にある要素を指すポインタ同士の間では大小関係が確
> 定します:
> int a[N];
> int i, j;
> int *p = &a[i], *q = &a[j];
> のとき 0 ≦ i < j ≦ N ならば p < q.
>
> あと, 1個の構造体オブジェクトにおける, 同じ型を持つ異なるメンバー
> を指すポインタ同士の大小関係も確定します:
> struct A {
> int x;
> int y;
> } a;
> int *p = &a.x, *q = &x.y;
> ならば p < q.
ほんとに配列だからとか構造体だからという制約なんでしょうか?
メモリ上に連続したアドレス空間が割当られるようなものに関して
は、たまたま大小関係が成立するということではないのでしょうか。
例えばIA32マシンで4GB以上の配列を作ったとしても大小関係は成
立することが保証されるのでしょうか?
# 実際に配列が作れるかどうかは別として。
また、配列だからとかって制約だとすると、
char *s, *e;
s = (char*)malloc(1000);
e = s + 999;
とした場合は、s < e は成り立たないのでしょうか?
--
01/23 12:33頃
水戸
<m38yjz2...@kzin.dip.jp>の記事において
co_...@ybb.ne.jpさんは書きました。
co_mon> ほんとに配列だからとか構造体だからという制約なんでしょうか?
「アドレス空間中の相対位置で大小関係が定義される」と書いておいた
んですけど....
そのうち, 「自動的に確定する例」として「配列」とか「構造体」が
(規格で) 挙げられているんだと思います.
co_mon> メモリ上に連続したアドレス空間が割当られるようなものに関して
co_mon> は、たまたま大小関係が成立するということではないのでしょうか。
上に書いたように「アドレス空間中の相対位置」で定義されていますの
で, 極端には「連続しているかどうか」とは関係なく大小関係は存在し
ます.
co_mon> 例えばIA32マシンで4GB以上の配列を作ったとしても大小関係は成
co_mon> 立することが保証されるのでしょうか?
co_mon> # 実際に配列が作れるかどうかは別として。
もちろん保証されます.
co_mon> char *s, *e;
co_mon> s = (char*)malloc(1000);
co_mon> e = s + 999;
co_mon> とした場合は、s < e は成り立たないのでしょうか?
この場合も同様で, アドレス空間中で s は e より前にありますので
s < e が成り立つことになります.
> indeterminate value だからそこからの読み出しが undefined behavior
> になるかもしれない. で, undefined behavior だからどう振舞うかは全
> く分らない. 何か値が返ってくるかもしれないし, プログラムが異常終
> 了するかもしれない.
indeterminate value が、直ちに undefined behavior を意味するわけではあ
りません。
trap representation を持つ処理系では、trap representation を参照しよう
とした時は undefined behavior となります。
しかし、ある型が trap representation を持つか否かは implementation
defined です。ただし、char 型は trap representation を持たないことが規
定されています。
--
片山@PFU
In article <m3oesw2...@kzin.dip.jp>
Mito <co_...@ybb.ne.jp> writes:
> 現在のC言語でも読みやすく、よいプログラムを作ることは可能だ
> と思いますが、コンパイラや規格のために阻害されるようなことが
> あるということなんでしょうか?
そういうことです。「現在の」C言語は、もちろん完璧ではないので。
改良の余地はまだまだあります。
> # Cのポインタには大小関係なんて存在しないと思っているんです
> # が、これは間違った見識だったでしょうか?
大小関係は、あります。ポインタとポインタを引き算してもいいで
す。引算した結果は、符合付の整数です。あとポインタと整数を足
算引算してもいいです。
type1 *p, *q ;
int x ;
x = p - q ;
p -= x ;
p++ は、p += 1 と同じですけど、+= 1 でなくて += 100 でも
+= -1000 でもいいです。
> # つまり、上の例では↓ならまだいいかなってところだと思うん
> # ですが。
> # for (p = &a[0]; p != &a[10]; p++)
これは、ループの終了を != でやるのは、やりたくないですね。
安全側に倒したい。
> また、Cは歴史的な経緯であまり大きな規格の変更はできないので
> はないかと思いますが、過去の資産を捨ててもコンパイラと規格を
> 変更するだけの意義があるのでしょうか?
C言語は、歴史的に見ると大改定を繰り返してきた言語じゃないで
すかね。Fortran には負けるかもしれないけど。たとえば、昔のC
言語だと、x += 1 を x =+ 1 と書いていました。x =*y と書くと
ポインタと紛らわしいので、今のように改訂されました。enum を
入れたとか、関数のプロトタイプ宣言ができるようになった改訂も
大きかった。今でもそういうのを使わないでプログラムを書いて
いる人も知っています。
イマイチな改訂は、まず、void * 。void は許せるけど、void *
にそんな意味を持たせるなんて。const もパッとしません。
昔に遡ると、static というキーワードはやめてほしいかった。
変数は、いいとして関数に static って付けるやつ。関数は全部
機械語で静的に存在するものです。Java も static は真似するこ
とはなかったろうに。
改訂されないような言語は、死んだ言語です。昔の言語で誰も使わ
なくなったような言語は、改訂されません。生きている言語である
限り、改訂は避けられないでしょう。
gcc の closure (関数の中に関数を定義)とか typeof とかcase の
... での範囲とかどうなったんでしょうね。gcc とか Visual C++
とか、標準にはない独自の拡張は多いのですけれど、それを標準に
しようというような話は、誰かがやっているんじゃないですか。
私が改訂して欲しいのは、ぱっと思いつくのは、ビット数付きの宣
言です。たとえば、
int:32 x ;
とすると、32 ビットになるとか。私は ASCII でいいけど、文字列
リテラルを Unicode にして欲しいとかいう人もいるんじないです
かね。C言語というと、"\n" の意味がわかるとかっこいい、とい
うのもあったのでしょうが、画面がないコンピュータも多いし。
> 単細胞の私には他の言語に乗り換えたほうが幸せになれそうな気が
> しているのですが。
同感です。今でもC言語ではなくて、Javaとか他の言語で書い
た方がいい場合はかなり多いと思います。
> 「大小関係の存在」と「ポインタを進める/戻す」とは別問題ではないか
> と. 現に C++ のイテレータでは「大小関係」はなくても「進める/戻す」
> という操作が可能な例があります.
> # というか, 「大小関係」があるのは random access iterator だけの
> # ような.
確かに別問題かもしれませんが,こじつけることは出来るような気がします。
単方向(や双方向)順次アクセスでも,例えば
P1=P0.(進める) を P1 > P0
P1=P0.(戻る) を P1 < P0
P1=P0.(n進める,n戻る) を P1 = P0
と決めるとか。(概念上だけの話ですが。)
まあ,利用価値も意味も無いとは思いますが。
> > ほんとに配列だからとか構造体だからという制約なんでしょうか?
> 「アドレス空間中の相対位置で大小関係が定義される」と書いておいた
> んですけど....
>
> そのうち, 「自動的に確定する例」として「配列」とか「構造体」が
> (規格で) 挙げられているんだと思います.
あらら、そういうことですか。勘違いしてました。
ありがとうございました。
--
01/24 16:47頃
水戸
どうも私の感覚と strlcpy って合いません. 特に strcpy の置き換えとして
は考えられない.
つらつらとその理由を考えてみたのですが, 要するに strcpy では問題のある
ようなアプリケーションが, strlcpy によって直ちに正しく動作するようにな
るわけではない, ということかな.
・ほとんどの場合その前にチェック出来るハズ.
可変長の文字列を扱う場合でも, 仕様の上限 (と, 上限に達した時の挙動) は
決まっているはずだし. 汎用的なライブラリでもアロケータを利用者に用意さ
せるとか, 色々やり方はあると思うし.
マズイことの本質は「不適当な大きさのバッファを適当に取ってしまうこと」
だと思うんです. 長さのチェックは入力時や文字列の切り出し時にやればいい
わけで, *cpy なんかする前にわかるでしょ? ということ.
私は strlen なんかも好きではないのでなるべく文字種チェックなんかと一緒
にやるようにしたり. こういう考えに従えば, strlcpy は文字のコピーと文字
列長のカウントを同時に行いたい場合に使うという至極もっともな話になりま
す.
・間違いやすい人間に毎回長さを指定させるなんて.
まぁこれも結構大きい理由でしょう. それと
・長さだけ指定してチェックしないってことはないよね?
if (strlcpy (buf, src, sizeof buf) == sizeof buf)
/* さて, どうするの? */;
間違いやすい人間に毎度毎度チェックさせるの? という話にもなります.
そもそもこれで足りなくなる場合はバグ (バッファ長不足) か仕様外の入力か
のどちらかで, バグなら strlcpy を使ってもバッファを定められた長さまで
増やさなければ正しく動作しないでしょうし, 仕様外の入力がこういうところ
にまで入り込んでくるのなら, そもそも strcpy を使える場面ではなかったわ
けです. 後者を問題にするのなら, 別の問題も抱え込んでいる可能性が大きい.
○
何も考えずに strlcpy 使って, 長さを越えた場合に適当に truncate とかす
る人がいたりすると, "foobar" で登録したデータが "foobar" でも "foo" で
も読めてしまうといった問題が紛れ込んだりするかもしれません.
それよりはテストで落ちてもらった方がありがたいと思うのですが.
In article <c17e5u$2eji$1...@news2.rim.or.jp>, doh...@hf.rim.or.jp (Kazuo Fox DOHZONO) writes
> つらつらとその理由を考えてみたのですが, 要するに strcpy では問題のある
> ようなアプリケーションが, strlcpy によって直ちに正しく動作するようにな
> るわけではない, ということかな.
>
> ・ほとんどの場合その前にチェック出来るハズ.
確かに~ でも、それだと strcpy とかも使わなくなっちゃうかな。
---
Shinji KONO @ Information Engineering, University of the Ryukyus
河野真治 @ 琉球大学工学部情報工学科
古い記事が読まれると、新しい記事よりも嬉しいですね。いい記事
だったんだなあという感じがして。
In article <c17e5u$2eji$1...@news2.rim.or.jp>
doh...@hf.rim.or.jp (Kazuo Fox DOHZONO) writes:
> つらつらとその理由を考えてみたのですが, 要するに strcpy では問題のある
> ようなアプリケーションが, strlcpy によって直ちに正しく動作するようにな
> るわけではない, ということかな.
>
> ・ほとんどの場合その前にチェック出来るハズ.
はい。それはそうです。strlcpy() は、バッファ・オーバーフロー
を起さないという目的で使うものです。strlcpy() を使ったからと
いって、間違ったプログラムが「正しく」動作するようになるわけ
ではありません。
> 何も考えずに strlcpy 使って, 長さを越えた場合に適当に truncate とかす
> る人がいたりすると, "foobar" で登録したデータが "foobar" でも "foo" で
> も読めてしまうといった問題が紛れ込んだりするかもしれません.
> それよりはテストで落ちてもらった方がありがたいと思うのですが.
テストでは、バッファ・オーバーフローは見つかりにくいんじゃな
いですか。それより、"foobar" が "foo" につめられて動作がおか
しいいう方が見つかりやすいんじゃないかなあ。
> ・間違いやすい人間に毎回長さを指定させるなんて.
> まぁこれも結構大きい理由でしょう. それと
> ・長さだけ指定してチェックしないってことはないよね?
> if (strlcpy (buf, src, sizeof buf) == sizeof buf)
> /* さて, どうするの? */;
> 間違いやすい人間に毎度毎度チェックさせるの? という話にもなります.
それは、同感です。まあ、マクロ一発という話もありますけど。
#define Strlcpy(dst, src, size) if( (strlcpy((dst),(src),(size))>=(size)) ) \
error("buffer over flow.")
それで、毎回チェックするのがいやになったら、C言語をやめて
Javaに移るわけです。
> strlcpy() は、バッファ・オーバーフローを起さないという目的で使うもの
> です。strlcpy() を使ったからといって、間違ったプログラムが「正しく」
> 動作するようになるわけではありません。
確認しておきますが, strlcpy によってバッファオーバーフローが「起こらな
くなる」ケースでは間違ったプログラムを前提としているわけであり, それは
strlcpy を使っても依然として間違ったプログラムであることに変わりありま
せん.
人的ミスならそういうものを持ち込ませない, 或は, 万が一持ち込まれてもテ
スト等で発覚するような設計にするべきで (Java を使うってのもこの辺に入っ
てくるわけでしょう), 「持ち込まれても strlcpy で」っていうのは問題に対
処するベクトルがずれている気がするんです. 新人君に教える心得としても甚
だ不適当なものでしょう.
> テストでは、バッファ・オーバーフローは見つかりにくいんじゃな
> いですか。
そういうのも「不適当なバッファを適当にとること (たとえば, FILE * と無
関係な場所で BUFSIZ を使ってみたりとか)」の弊害でしょう. 過不足なく
(過…なく, がミソ) 確保するようにして, 仕様上の最大長やその数倍程度を
入れてみれば大抵発覚するのではないですか. 私の経験上はそんな感じ.
…とはいえやっぱりテストに頼るような設計にはしないなぁ. 今やってる仕事
で grep したら strcpy が 200 個くらい出てきましたけど, バッファ長とか
はプログラムで算出させてチェックは上位層でやってるし. もちろんテストも
やってますけど.
で, これを全部 strlcpy にして一々チェックを入れて「どういうアクション
を起こすか (起こりもしないのに)」と考えるのは, 馬鹿げてるでしょ?
> それより、"foobar" が "foo" につめられて動作がおかしいいう方が見つか
> りやすいんじゃないかなあ。
あるキーで登録された値が別のどんなキーでも読めないことをテストで保証す
るのは到底不可能ですし, リリース後におかしな動作が発覚するのでは遅いと
いう場合も多々あるわけです. というか仕事でそれはマズ過ぎ.
# デバッグ方法にも疎くて「動きません」と泣きついてくるならいざしらず,
# ホントにそうやって隠蔽しちゃう新人君も世の中にはいる, らしい.
また, 仕様にないコードはテストケースから洩れている可能性が高いわけです
から, その部分は未テストのままリリースされることになりかねません. その
後のコードが正しいことはどうやって保証するのでしょうか. strlcpy 判定後
のコードを書くくらいですから, プログラマは当然その辺りに思い当たってい
るはずです. そんなことが予めわかっているのなら, それは仕様に含まれ, テ
スト項目に入るべきものではないのでしょうか.
# ちまちましたテスト項目を増やさないためにも設計が肝要なわけですが.
> > if (strlcpy (buf, src, sizeof buf) == sizeof buf)
> > /* さて, どうするの? */;
> > 間違いやすい人間に毎度毎度チェックさせるの? という話にもなります.
>
> それは、同感です。まあ、マクロ一発という話もありますけど。
ないない.
> #define Strlcpy(dst, src, size) if( (strlcpy((dst),(src),(size))>=(size)) ) \
> error("buffer over flow.")
この error とやらはその時その時の状況に応じて事象の巻き戻しから何から
面倒をみてくれる魔法の仕組みなんですか?
# 戻り値に対する不等号も気持ち悪いなぁ.
In article <c1fl9o$dl5$1...@news2.rim.or.jp>
doh...@hf.rim.or.jp (Kazuo Fox DOHZONO) writes:
> 確認しておきますが, strlcpy によってバッファオーバーフローが「起こらな
> くなる」ケースでは間違ったプログラムを前提としているわけであり, それは
> strlcpy を使っても依然として間違ったプログラムであることに変わりありま
> せん.
まあね。例外を探すといろいろあるでしょうか。
それより重要なことは、間違いには2種類あるということです。
(A)セキュリティ上の弱点に繋がる間違い
(B)セキュリティ上の弱点には繋がらない間違い
strlcpy() を使うと、(A)は出てきません。この性質を有りがた
いと思う人は、多いでしょう。
> 人的ミスならそういうものを持ち込ませない, 或は, 万が一持ち込まれてもテ
> スト等で発覚するような設計にするべきで (Java を使うってのもこの辺に入っ
> てくるわけでしょう), 「持ち込まれても strlcpy で」っていうのは問題に対
> 処するベクトルがずれている気がするんです. 新人君に教える心得としても甚
> だ不適当なものでしょう.
最後の部分、同意できませんね。人間は間違う、だから、間違った
としても安全側に倒れるように作るようにプログラムは作るべしと。
> そういうのも「不適当なバッファを適当にとること (たとえば, FILE * と無
> 関係な場所で BUFSIZ を使ってみたりとか)」の弊害でしょう. 過不足なく
> (過…なく, がミソ) 確保するようにして, 仕様上の最大長やその数倍程度を
> 入れてみれば大抵発覚するのではないですか. 私の経験上はそんな感じ.
その経験は、「バッファ・オーバーフローは、次々見つかる、しか
も深刻なセキュリティ上の問題を引き起すものとして見つかること
が多い」という現実とはずれています。CERT でも Bugtraqq でも
見てみて下さい。
> …とはいえやっぱりテストに頼るような設計にはしないなぁ. 今やってる仕事
> で grep したら strcpy が 200 個くらい出てきましたけど, バッファ長とか
> はプログラムで算出させてチェックは上位層でやってるし. もちろんテストも
> やってますけど.
そのプログラムを明日誰かが直したとして、その直した誰かがバッ
ファ・オーバーフローのバグを入れないという保証は、できないでしょ。
バグを入れた人の責任だと言うのは簡単だけど、最初からセキュリ
ティ上のバグが出にくいような作りになっていたら、それに越した
ことはありません。
> で, これを全部 strlcpy にして一々チェックを入れて「どういうアクション
> を起こすか (起こりもしないのに)」と考えるのは, 馬鹿げてるでしょ?
いいえ。
> > #define Strlcpy(dst, src, size) if( (strlcpy((dst),(src),(size))>=(size)) ) \
> > error("buffer over flow.")
> この error とやらはその時その時の状況に応じて事象の巻き戻しから何から
> 面倒をみてくれる魔法の仕組みなんですか?
そういう仕組みが使える時には、使うべきでしょう。トランザクショ
ンとか longjmp とか。
> # 戻り値に対する不等号も気持ち悪いなぁ.
あれは、FreeBSD のマニュアルに書いてある通りです。気持悪いの
を見たくなければマクロを使えばよろし。
> それより重要なことは、間違いには2種類あるということです。
>
> (A)セキュリティ上の弱点に繋がる間違い
> (B)セキュリティ上の弱点には繋がらない間違い
これをプログラマが区別しないといけないのかなぁ (区別するとしたらどう考
えたら/捉えたらいいのだろう), というのがここ数年私が考えているテーマ
だったり.
うまく説明出来るかどうか自信ありませんが.
たとえば人命に関わるシステムと, 利用者から見てそれほどミスが重要視され
ないようなシステムがあったとします. 社会通念上これらにかかってくる開発
コストとかには, それなりに差があるはずですよね.
で, それがプログラマから見た場合どのような違いとなって現れるのか. どち
らもモニタ・キーボードを前にしてプログラムを書くという作業自体は同じわ
けで. 違いは設計思想やら何やらの方に行って, プログラムを書き下す段には
入ってこないんじゃないか (入れるべきではないのでは) と思うんですけど.
> strlcpy() を使うと、(A)は出てきません。
「strcpy で出てくるようなセキュリティ上の問題」は出てこない. しかし,
個別に一々プログラマに担わせていたのでは駄目なんじゃないですか?
> > そういうのも「不適当なバッファを適当にとること (たとえば, FILE * と無
> > 関係な場所で BUFSIZ を使ってみたりとか)」の弊害でしょう. 過不足なく
> > (過…なく, がミソ) 確保するようにして, 仕様上の最大長やその数倍程度を
> > 入れてみれば大抵発覚するのではないですか. 私の経験上はそんな感じ.
>
> その経験は、「バッファ・オーバーフローは、次々見つかる、しか
> も深刻なセキュリティ上の問題を引き起すものとして見つかること
> が多い」という現実とはずれています。CERT でも Bugtraqq でも
> 見てみて下さい。
それらは私が想定したような厳しいテストを行なっているのですか? (あの頻
度からはちょっと信じられないなぁ). それに, それらは strlcpy で直るとい
う類のものなのでしょうか.
それらは世の中の問題を起こしていないソフトウェア群に比べて (有意に) 多
いのですか? 少ないのですか?
> > …とはいえやっぱりテストに頼るような設計にはしないなぁ. 今やってる仕事
> > で grep したら strcpy が 200 個くらい出てきましたけど, バッファ長とか
> > はプログラムで算出させてチェックは上位層でやってるし. もちろんテストも
> > やってますけど.
>
> そのプログラムを明日誰かが直したとして、その直した誰かがバッ
> ファ・オーバーフローのバグを入れないという保証は、できないでしょ。
> バグを入れた人の責任だと言うのは簡単
責任は書かせた人にもあるわけです. 「このソフトウェアはどのような設計思
想に基づいていて, それがどのように実装されているのか」というレビューま
で普通やるじゃないですか.
もちろん保証は出来ませんが, strlcpy だって保証は出来ないでしょう?
> > で, これを全部 strlcpy にして一々チェックを入れて「どういうアクション
> > を起こすか (起こりもしないのに)」と考えるのは, 馬鹿げてるでしょ?
>
> いいえ。
私にはそういうのは無視出来ない程度に大きなストレスになります. 何故でしょ
うか. 私だけ?
In article <c27vng$arp$1...@news2.rim.or.jp>
doh...@hf.rim.or.jp (Kazuo Fox DOHZONO) writes:
> > (A)セキュリティ上の弱点に繋がる間違い
> > (B)セキュリティ上の弱点には繋がらない間違い
> これをプログラマが区別しないといけないのかなぁ (区別するとしたらどう考
> えたら/捉えたらいいのだろう), というのがここ数年私が考えているテーマ
> だったり.
私は(A)(B)を区別する必要があると思います。両方無くなれ
ばそれに越したことはないけれど、潰すならまず優先的に(A)か
ら潰せという意味です。
> たとえば人命に関わるシステムと, 利用者から見てそれほどミスが重要視され
> ないようなシステムがあったとします. 社会通念上これらにかかってくる開発
> コストとかには, それなりに差があるはずですよね.
>
> で, それがプログラマから見た場合どのような違いとなって現れるのか. どち
> らもモニタ・キーボードを前にしてプログラムを書くという作業自体は同じわ
> けで. 違いは設計思想やら何やらの方に行って, プログラムを書き下す段には
> 入ってこないんじゃないか (入れるべきではないのでは) と思うんですけど.
(A)(B)を区別することと矛盾しないと思います。同じコスト
をかけるなら(A)を潰すことから考えるという話です。
> 「strcpy で出てくるようなセキュリティ上の問題」は出てこない. しかし,
> 個別に一々プログラマに担わせていたのでは駄目なんじゃないですか?
いいえ。トータルで考えれば、バグ入りプログラムからバグを見つ
けるより、最初からバグがないプログラムを書く方が楽(得)です。
> > その経験は、「バッファ・オーバーフローは、次々見つかる、しか
> > も深刻なセキュリティ上の問題を引き起すものとして見つかること
> > が多い」という現実とはずれています。CERT でも Bugtraqq でも
> > 見てみて下さい。
>
> それらは私が想定したような厳しいテストを行なっているのですか? (あの頻
> 度からはちょっと信じられないなぁ). それに, それらは strlcpy で直るとい
> う類のものなのでしょうか.
バッファ・オーバーフローは、テストで見つかるというよりは、シ
ステムが乗っ取られてみて、それから原因を探ってみて見つかるの
が多いです。その原因が strcpy() なら strlcpy() で直ります。
gets() なら fgets() で直ります。その他それぞれ。
> それらは世の中の問題を起こしていないソフトウェア群に比べて (有意に) 多
> いのですか? 少ないのですか?
バグの確率を減らすことが目的ではありません。バグが出た時に被
害を少なくすることが目的です。確率が低くても、被害が大きいな
ら潰さないといけない。確率×被害の総和を最小と言ってもいいで
す。なお、「攻撃」する人がいれば、確率は「1」になります。
> 責任は書かせた人にもあるわけです. 「このソフトウェアはどのような設計思
> 想に基づいていて, それがどのように実装されているのか」というレビューま
> で普通やるじゃないですか.
「普通」というのは、どういう状況でしょうか。もう少し詳しく教
えてください。大学だと何が普通なのかが実はよく分からないとい
う話があります。
> もちろん保証は出来ませんが, strlcpy だって保証は出来ないでしょう?
バッファ・オーバーフローに関して言えば、strlcpy() で保証できます。
> > > で, これを全部 strlcpy にして一々チェックを入れて「どういうアクション
> > > を起こすか (起こりもしないのに)」と考えるのは, 馬鹿げてるでしょ?
> > いいえ。
> 私にはそういうのは無視出来ない程度に大きなストレスになります. 何故でしょ
> うか. 私だけ?
人間がやることではないということには同意します。コンパイラな
ど言語処理系でやるべき仕事でしょう。でもコンパイラがしょぼい
と人間がやるしかありません。最初はきついかもしれませんが、人
間鍛えられます。それに、安全なプログラムを書いていると、気持
いいですよ。悪意を持って攻撃する人に勝ったということですから。
「そんなこともあろうかと」って、言ってみたいセリフでしょ。
ひょっとすると話に水を差すかも知れませんが。
In article <YAS.04Ma...@kirk.is.tsukuba.ac.jp>,
Yasushi Shinjo <y...@is.tsukuba.ac.jp> wrote:
>新城@筑波大学情報です。こんにちは。
>> > (A)セキュリティ上の弱点に繋がる間違い
>> > (B)セキュリティ上の弱点には繋がらない間違い
>(A)(B)を区別することと矛盾しないと思います。同じコスト
>をかけるなら(A)を潰すことから考えるという話です。
(A) と (B) を区別することは無意味ではないと思いますし、そ
ういう policy を元により安全な手段を採用すること自体は正しい
と思いますが、それを過信すると弊害が現れると思います。
>いいえ。トータルで考えれば、バグ入りプログラムからバグを見つ
>けるより、最初からバグがないプログラムを書く方が楽(得)です。
この「最初からバグがない」という点が過信に繋がり兼ねないと
思うので、私はこの考えには危険が伴うと思います。
例えば strlcpy() にしても、第三引数の指定を間違えてしまえ
ば簡単に buffer overflow してしまいます。ところが「strlcpy()
を使っているからこの部分にバグはない」と過信していると、その
可能性を見落としてしまいがちです。
この過ちを侵している代表例が Samba ですね。こいつは一つも
strcpy() を使わないように工夫していますが、長さ指定ミスによ
る buffer overflow は過去幾つも発見されています。
開発チームは strcpy() を使っていないことから慢心してしまい、
文字列 copy に関するチェックを疎かにしているため、新たに何か
実装する度にこのミスが生じ、結果 buffer overflow は後を絶ち
ません。
strlcpy() 自体は有用な関数だと思いますが、それを使うことが
即「セキュリティ上の弱点に繋がる間違い」を完全に回避出来ると
する思い込みは、却って危険だと思います。
むしろ、strcpy() を使っていた方が、常に buffer size を意識
する癖がついて安全かも知れませんね。
>gets() なら fgets() で直ります。その他それぞれ。
長さの指定を programmer に委ねている時点で fgets() も同じ
ことだと思います。私は fgets() の代わりに asprintf() みたい
な仕組みを実装することが多いですね。
尤も、asprintf() を使っても library の bug ということはあ
り得ますが、buffer size を動的に確保する仕組みの方が幾らかは
安心出来るかと。
# 私は library bug に何度も悩まされた挙げ句、library 関数
#の互換品を自作する癖がついちゃいましたが :-)
--
しらい たかし
In article <YAS.04Ma...@kirk.is.tsukuba.ac.jp> y...@is.tsukuba.ac.jp wrote on Sat, 6 Mar 2004 17:33:26 GMT:
>人間がやることではないということには同意します。コンパイラな
>ど言語処理系でやるべき仕事でしょう。でもコンパイラがしょぼい
>と人間がやるしかありません。
コンパイラがしょぼい場合は、人間ではなく、プログラムにやらせれば
よいでしょう。
-----------------------------------------------------------------
電力中央研究所 電力システム部 竹中 清
- kiyos - kiyos - kiyos - kiyos - kiyos - kiyos - kiyos - kiyos -
take...@criepi.denken.or.jp
In article <c2esvq$2ta5$1...@nsvn01.zaq.ne.jp>
shi...@unixusers.net (Takashi SHIRAI) writes:
> >いいえ。トータルで考えれば、バグ入りプログラムからバグを見つ
> >けるより、最初からバグがないプログラムを書く方が楽(得)です。
> この「最初からバグがない」という点が過信に繋がり兼ねないと
> 思うので、私はこの考えには危険が伴うと思います。
strcpy() と strlcpy() の話と「最初からバグを入れない」という
話は別の話です。strcpy() を単純に strlcpy() 変えてもバグは残
ります。つまり、分類(A)(B)両方の話です。
> この過ちを侵している代表例が Samba ですね。こいつは一つも
> strcpy() を使わないように工夫していますが、長さ指定ミスによ
> る buffer overflow は過去幾つも発見されています。
具体的にどんなバグだったのでしょうか。これを検証すると面白い
データが得られると思います。
> strlcpy() 自体は有用な関数だと思いますが、それを使うことが
> 即「セキュリティ上の弱点に繋がる間違い」を完全に回避出来ると
> する思い込みは、却って危険だと思います。
同感です。
> 長さの指定を programmer に委ねている時点で fgets() も同じ
> ことだと思います。私は fgets() の代わりに asprintf() みたい
> な仕組みを実装することが多いですね。
asprintf() って何ですか?
strcpy()/strlcpy() のスタイルよりも snprintf() のスタイルの
方がよいとは思います。
> # 私は library bug に何度も悩まされた挙げ句、library 関数
> #の互換品を自作する癖がついちゃいましたが :-)
具体的に何というライブラリでしょうか。さしつかえなければ教え
て下さい。
「最初からバグが入らないようにする」というのは、雑誌
Software Design の1月号の Bart Eisenberg の Pacific
Connection に出ていたと思います。
In article <c2gqsb$q3u$1...@dnknews.denken.or.jp>
TAKENAKA Kiyoshi <take...@criepi.denken.or.jp> writes:
> >人間がやることではないということには同意します。コンパイラな
> >ど言語処理系でやるべき仕事でしょう。でもコンパイラがしょぼい
> >と人間がやるしかありません。
> コンパイラがしょぼい場合は、人間ではなく、プログラムにやらせれば
> よいでしょう。
プログラムにやらせるとして、具体的にどうすればいいんですか?
コンパイル時に働くプログラムがコンパイラとして、実行時に何か
やればいいということですかね。実行時にライブラリ関数でやると
いうことでもいいですが、その方法の1つが strcpy() じゃなくて
strlcpy() ではあるんですけれど。
コンパイラでやる方法にも、StackGuard とか Fail-safe C とかい
ろいろあります。
In article <YAS.04Ma...@kirk.is.tsukuba.ac.jp> y...@is.tsukuba.ac.jp wrote on Mon, 8 Mar 2004 07:47:12 GMT:
>In article <c2gqsb$q3u$1...@dnknews.denken.or.jp>
> I writes:
>> コンパイラがしょぼい場合は、人間ではなく、プログラムにやらせれば
>> よいでしょう。
>
>プログラムにやらせるとして、具体的にどうすればいいんですか?
人間がするには単純すぎて、いちいち変換したくないわけですよね。
(たとえば、strcpy -> strlcpy)
バグを入れないためにすることをいくつかの方法に分けられれば(たとえば
strcpy -> strlcpy+α or β ...)、あとはstrcpyを検出したら、
strlcpy+αに変換するプログラム(ソース変換プログラム)を作れば
いいんでは。
my-プリコンパイラがお勧めだと思いますが。
In article <c2heoj$1c8$1...@dnknews.denken.or.jp>, TAKENAKA Kiyoshi <take...@criepi.denken.or.jp> writes
> my-プリコンパイラがお勧めだと思いますが。
なんか良い定番ないんですかね?
> バグを入れないためにすることをいくつかの方法に分けられれば(たとえば
> strcpy -> strlcpy+α or β ...)、あとはstrcpyを検出したら、
> strlcpy+αに変換するプログラム(ソース変換プログラム)を作れば
> いいんでは。
C++ の string class を使うっていう手もあるんだけど、どうもねぇ。
In article <YAS.04Ma...@kirk.is.tsukuba.ac.jp>,
Yasushi Shinjo <y...@is.tsukuba.ac.jp> wrote:
>新城@筑波大学情報です。こんにちは。
>> この「最初からバグがない」という点が過信に繋がり兼ねないと
>> 思うので、私はこの考えには危険が伴うと思います。
>
>strcpy() と strlcpy() の話と「最初からバグを入れない」という
>話は別の話です。strcpy() を単純に strlcpy() 変えてもバグは残
>ります。つまり、分類(A)(B)両方の話です。
なら構いませんが、<YAS.04Fe...@kirk.is.tsukuba.ac.jp>
で新城さんご自身が「strlcpy() を使うと、(A)は出てきません」
と書かれているので、この論法は一繋がりになっているのだと思っ
ていました。
では、「最初からバグを入れない」という話も「(A) と (B) を
分類する」という話もこの際置いておきますが、この「strlcpy()
を使うと云々」という発言自体に過信はありませんか?
この辺りは言葉の綾もあるかも知れませんので、本人が自覚さえ
していれば構わないのですが、言葉だけが一人歩きしてしまうと、
「strlcpy() を使うと security 上の弱点に繋がる bug は起きな
い」という思い込みが蔓延してしまい兼ねません。
strlcpy() に限りませんが、programming 上の万全の策なんても
のはそう簡単には手に入りませんから、より安全な関数を用いると
いった工夫以上に、問題意識を常に持ち続けるという心掛けを大切
にして欲しいと思います。
>> この過ちを侵している代表例が Samba ですね。こいつは一つも
>> strcpy() を使わないように工夫していますが、長さ指定ミスによ
>> る buffer overflow は過去幾つも発見されています。
>
>具体的にどんなバグだったのでしょうか。これを検証すると面白い
>データが得られると思います。
Samba では文字列 (正確には char 型配列) は二種類のサイズに
大別されていまして、pathname を表す pstring 型 (実体は 1,024
文字分の char 型配列) と filename を表す fstring 型 (同様に
256 文字分) が用意されています。
strcpy() を使おうとすると compile 時に error を吐くように
指定されていますので、文字列 copy は pstrcpy() か fstrcpy()
しか使えません。
ところが、内部実装を追っていくと pstring 型を fstring 型で
受けたりその逆をしたりという実装が非常に多くて、結局この枠組
は全くと言っていい程役に立っていません。
文字列 copy の際には、strncpy() でも構わないから常に size
を指定するように心がけておけば、このような size の違いは見落
とす可能性が減ると思うのですが、「文字列 copy 対策は既に万全
なのでチェックしなくてよい」という思い込みが幾つも security
hole を生んでしまうのです。
どういう実装で bug を回避しようとも構わないのですが、問題
意識を低下させてしまう結果を生むような実装だけはやめておいた
方がいいというのが私の持論です。
>asprintf() って何ですか?
こんなの(↓)です。
http://www.linux.or.jp/JM/html/LDP_man-pages/man3/asprintf.3.html
内部で malloc() を呼ぶことで、format 文字列を展開した後に
必要となる buffer を動的に確保してくれる形式の printf() 関数
です。
snprintf() と違って、buffer size を指定する必要すらないた
め、library bug さえ無ければこの関数内で buffer overflow は
起こり得ません。
GNU libc が発祥だそうですが、Linux 以外に *BSD にも移植さ
れていますね。
>> # 私は library bug に何度も悩まされた挙げ句、library 関数
>> #の互換品を自作する癖がついちゃいましたが :-)
>
>具体的に何というライブラリでしょうか。さしつかえなければ教え
>て下さい。
色々ありますよ。Y2K の時には library 関数に見つかった bug
も多かったですし、buffer overflow のように security hole に
繋がるものも皆無ではありません。
security hole ではないですけど、指定文字数ぎりぎりに \n が
存在すると無かったことにしてしまう fgets() なんてありました
っけ。今のところ他には適当なものを思い出せません。
あと、OS による仕様の相違なんてのもありますね。「UNIX」を
名乗っている癖に POSIX 仕様に準拠していない OS なんてざらで
すよ。
咄嗟に思いつくところでは、putenv() が引数自身を使うかその
複製を使うかなんていう相違があります。POSIX では前者なんです
が、割と多くの実装で後者ですね。間違えると当然 memory leak。
# library じゃないけど、HP-UX の make(1) は同一 timestamp
#を「古い」と見なすので POSIX 非準拠。最近の CPU だと make
#を何度も実行しないと timestamp の整合性が保てない :-<
--
しらい たかし
In article <3989521...@insigna.ie.u-ryukyu.ac.jp> ko...@ie.u-ryukyu.ac.jp wrote on Mon, 8 Mar 2004 09:39:06 GMT:
>In article <c2heoj$1c8$1...@dnknews.denken.or.jp>, TAKENAKA Kiyoshi <take...@criepi.denken.or.jp> writes
>> my-プリコンパイラがお勧めだと思いますが。
>なんか良い定番ないんですかね?
"my"がみそだから、ないんじゃないですかね。
バグの入れ方は人それぞれで、それを避けるためのプリコンパイラ化の
考えも人それぞれでしょうから。
1)対応バグチェッカを入れる。
2)コンパイラ文法を変更して、そのバグが入らないようにする。
3)マクロみたいにして、そのバグが入らない組合せにしてしまう。
また、myだからこそ、tableの矛盾チェックとかまでやらせることも
できますし、コーディング書式の(自分好みの)簡素化とかまでも
手がだせます。
Takashi SHIRAI wrote:
>>> この過ちを侵している代表例が Samba ですね。こいつは一つも
>>> strcpy() を使わないように工夫していますが、長さ指定ミスによ
>>> る buffer overflow は過去幾つも発見されています。
>>
>>具体的にどんなバグだったのでしょうか。これを検証すると面白い
>>データが得られると思います。
>
> Samba では文字列 (正確には char 型配列) は二種類のサイズに
> 大別されていまして、pathname を表す pstring 型 (実体は 1,024
> 文字分の char 型配列) と filename を表す fstring 型 (同様に
> 256 文字分) が用意されています。
> strcpy() を使おうとすると compile 時に error を吐くように
> 指定されていますので、文字列 copy は pstrcpy() か fstrcpy()
> しか使えません。
> ところが、内部実装を追っていくと pstring 型を fstring 型で
> 受けたりその逆をしたりという実装が非常に多くて、結局この枠組
> は全くと言っていい程役に立っていません。
これが出来る時点で、私には全然文字列コピー対策は万全なんて思えない
のですけれど。
Samba のソースは見たことないから
<http://samba.org/doxygen/samba/winbind__nss__config_8h.html>
を見てみましたが、
typedef char fstring[FSTRING_LEN];
とかしてるんで、
fstring fstrcpy(fstring, fstring);
とか?pstring を fstring で受けれたりということは、せいぜいこん
なところかと思うのですが、これじゃコンパイラの支援は期待できないの
でやっぱり嬉しくないですよね。最低でも、
typedef struct { char name[FSTRING_LEN]; } fstring;
とかにすれば良いのですかね?んで、
result f2p(fstring, pstring);
とか準備しておく?まあ、asprintf の方が簡単か。
> 文字列 copy の際には、strncpy() でも構わないから常に size
> を指定するように心がけておけば、このような size の違いは見落
> とす可能性が減ると思うのですが、「文字列 copy 対策は既に万全
> なのでチェックしなくてよい」という思い込みが幾つも security
> hole を生んでしまうのです。
> どういう実装で bug を回避しようとも構わないのですが、問題
> 意識を低下させてしまう結果を生むような実装だけはやめておいた
> 方がいいというのが私の持論です。
中途半端さが変な安心とそれにまつわるリスクを呼び込んでいる気がします。
本当に「万全」ならそれはそれで良いのでは。もちろん本当の本当に万全な
んてあり得ないということで、頭の片隅に問題意識をすまわせておくことは
必要なことでしょうけれど、常に綱渡りを強要することで問題意識を喚起す
るというのも妙な気もする。
--
「十分間で決断し、短い理由を添えよ」
A.I.Soft, Inc. CS・品質推進課 成田隆興
myプリコンパイラって、現存するんですか?
In article <c2jf7e$g83$1...@dnknews.denken.or.jp>
TAKENAKA Kiyoshi <take...@criepi.denken.or.jp> writes:
> 竹中@狛江.電中研です。
> バグの入れ方は人それぞれで、それを避けるためのプリコンパイラ化の
> 考えも人それぞれでしょうから。
コンパイラの個別化。面白い。
> 1)対応バグチェッカを入れる。
> 2)コンパイラ文法を変更して、そのバグが入らないようにする。
> 3)マクロみたいにして、そのバグが入らない組合せにしてしまう。
> また、myだからこそ、tableの矛盾チェックとかまでやらせることも
> できますし、コーディング書式の(自分好みの)簡素化とかまでも
> 手がだせます。
事前条件とか事後条件のチェックをCで書いて入れるのとどの辺り
が違うのでしょうか。
チェックが重たいなら、後で部分評価系で取り除きます。#ifdef
でもいいけど。assert() 入れてもいいけど、入れなくてもいいで
しょう。
In article <c2hsnp$18oi$1...@nsvn01.zaq.ne.jp>
shi...@unixusers.net (Takashi SHIRAI) writes:
> では、「最初からバグを入れない」という話も「(A) と (B) を
> 分類する」という話もこの際置いておきますが、この「strlcpy()
> を使うと云々」という発言自体に過信はありませんか?
もう少し正確に書くと、こんな感じです。
・strlcpy() を「正しく」使うと、strcpy() を使えば生じていた
ようなバッファオーバーフローが完全に防げる。
・strcpy() を strlcpy() で置換えるように書き換えることは、比
簡単である。
・strlcpy() を「正しく」使うことも簡単である。
> この辺りは言葉の綾もあるかも知れませんので、本人が自覚さえ
> していれば構わないのですが、言葉だけが一人歩きしてしまうと、
> 「strlcpy() を使うと security 上の弱点に繋がる bug は起きな
> い」という思い込みが蔓延してしまい兼ねません。
自覚ですか。私は、自覚とか根性とか気合いとか、そういうのでは
なくて、何か工学的に再生産可能なノウハウで安全なプログラムを
書きたいわけです。
In article <c2k49p$1974$1...@newsnnrp00.cwidc.net>
Narita Takaoki <tak...@aisoft.co.jp> writes:
> typedef struct { char name[FSTRING_LEN]; } fstring;
> とかにすれば良いのですかね?
それ、いいですね。構造体でラップして、並コンパイラの型チェッ
クを働かせる。どうせなら、先頭に文字数を置きたいですけど。
Pascal 文字列風。null 終端もしてもいいんでしょうけれど。
あと、よいGC付きの文字列ライブラリなんかないですかね。
In article <c2hsnp$18oi$1...@nsvn01.zaq.ne.jp>
shi...@unixusers.net (Takashi SHIRAI) writes:
> しらいです。
> >asprintf() って何ですか?
> こんなの(↓)です。
> http://www.linux.or.jp/JM/html/LDP_man-pages/man3/asprintf.3.html
あ。うちの Linux に入ってない。と思ったけど、man に出てこな
いだけか。Libc には入っています。Solaris には入っていないか。
> 内部で malloc() を呼ぶことで、format 文字列を展開した後に
> 必要となる buffer を動的に確保してくれる形式の printf() 関数
> です。
> snprintf() と違って、buffer size を指定する必要すらないた
> め、library bug さえ無ければこの関数内で buffer overflow は
> 起こり得ません。
意味的には、こんな感じですか。
asprintf(char **ret, const char *format, ...)
{
size = snprintf(0,format, ...) + 1 ;
p = malloc( size );
snprintf( p,size, format, ... );
*ret = p ;
return( size );
}
2回 snprintf() するのは、重たいのでやってないのでしょうけど。
asprintf() は、malloc() するスタイルに合えば、いいですね。
read() のスタイルとは少し違います。read() だと、呼出し側がメ
モリを確保します。
Perl とか Ruby とかインタプリタでやると、asprintf() みたいな
感じになっちゃうんですかね。
あと、asprintf() の問題としては、free() 忘れるとメモリ・リー
クになってしまうということでしょうか。
In article <YAS.04Ma...@kirk.is.tsukuba.ac.jp> y...@is.tsukuba.ac.jp wrote on Tue, 9 Mar 2004 14:16:11 GMT:
>事前条件とか事後条件のチェックをCで書いて入れるのとどの辺り
>が違うのでしょうか。
たぶん、同じだと思います。単に私がさぼりたいのと独自変更したい
だけです。
たとえばですけど、以下のようなものです。
1)chk abc
と書いてあれば、abcというtableを矛盾チェック(&チェックライト)する
ルーチンを追加して、別途必要な追加ルーチン(ライブラリ)フラッグを
立てる。
2)";"の解釈を反対にする等。コンパイラを2種類以上使うと、文法が
似ていないと混乱するんですよ(;を入れ忘れたり、キーワードを
間違えたり....)。
3)a社対応、b社対応をソースセレクタで切り替える。
>チェックが重たいなら、後で部分評価系で取り除きます。#ifdef
>でもいいけど。assert() 入れてもいいけど、入れなくてもいいで
>しょう。
myだと、自分のチェックはフラッグ一つでon/off可能です(結構楽)。
In article <YAS.04Ma...@kirk.is.tsukuba.ac.jp>,
Yasushi Shinjo <y...@is.tsukuba.ac.jp> wrote:
>新城@筑波大学情報です。こんにちは。
>asprintf(char **ret, const char *format, ...)
>{
> size = snprintf(0,format, ...) + 1 ;
> p = malloc( size );
> snprintf( p,size, format, ... );
> *ret = p ;
> return( size );
>}
>
>2回 snprintf() するのは、重たいのでやってないのでしょうけど。
最初の snprintf() で引数が一個足りないことを除くと、意味的
にはそういうことになると思います。
現実の実装では、fprintf() の FILE 構造体を疑似的に用いて実
装することが多いようですね。*BSD や glibc ではそういう手法を
使っています。
asprintf() の互換品として用意された実装の中には snprintf()
を使うものもありますが、snprintf() が返す値が実装により異な
るため、余り実用的ではありません。
例えば、C99 では「用意すべき長さ」が返りますが POSIX では
0 以下の不定値が返ります。glibc でも古い実装では -1 が返って
いました。
Samba package 用意されている asprintf() の互換品も上記のよ
うに snprintf() を使っていますが、snprintf() が C99 仕様であ
るか否かを configure 時に判別し、C99 でない場合は snprintf()
の互換品も用意する設計になっています。
>asprintf() は、malloc() するスタイルに合えば、いいですね。
>read() のスタイルとは少し違います。read() だと、呼出し側がメ
>モリを確保します。
read() とか fgets() とかも malloc() で動的に割り当てて貰え
ると嬉しい場合が多いでしょうね。
>あと、asprintf() の問題としては、free() 忘れるとメモリ・リー
>クになってしまうということでしょうか。
何事にも完璧ということはなかなかない訳で、buffer overflow
と比べれば memory leak の方がましといった程度の話になると思
います。
buffer overflow と違って memory leak は程度問題なので、実
装によっては memory leak を承知の上で無視してしまっているよ
うな library 関数も少なくありません。
先の記事に書いた putenv() なんてその代表例で、多くの実装で
はどんどん heap を食い潰していく一方なので、環境変数を繰返し
再設定する可能性の高い interpreter 系 command の実装には使え
ない関数です。
--
しらい たかし
In article <YAS.04Ma...@kirk.is.tsukuba.ac.jp>,
Yasushi Shinjo <y...@is.tsukuba.ac.jp> wrote:
>新城@筑波大学情報です。こんにちは。
>・strlcpy() を「正しく」使うと、strcpy() を使えば生じていた
> ようなバッファオーバーフローが完全に防げる。
>・strcpy() を strlcpy() で置換えるように書き換えることは、比
> 簡単である。
>・strlcpy() を「正しく」使うことも簡単である。
関数とて所詮道具に過ぎない訳で、正しく使えば正しく動くし、
使い方が正しくなければ期待通りに動かないのは当たり前ですね。
問題はその「正しく使う」ための容易さの程度ということでしょう
か?
strcpy() だって正しく使えば buffer overflow なんて起こり得
ない訳ですが、buffer size に対する指定がない分、使う側が意識
的に注意しないといけないので「正しく使う」のがより困難だとい
うことですね。
なのでやはり程度問題でしょう。正しい関数と間違った関数とか、
完璧に bugless な関数と buggy な関数とか、そういう対比ではな
くて、単に、危険回避がより簡単に出来る関数とより簡単に出来な
い関数という相違でしかないと思います。
>> この辺りは言葉の綾もあるかも知れませんので、本人が自覚さえ
>> していれば構わないのですが、言葉だけが一人歩きしてしまうと、
>> 「strlcpy() を使うと security 上の弱点に繋がる bug は起きな
>> い」という思い込みが蔓延してしまい兼ねません。
>
>自覚ですか。私は、自覚とか根性とか気合いとか、そういうのでは
>なくて、何か工学的に再生産可能なノウハウで安全なプログラムを
>書きたいわけです。
勿論、どっちのアプローチもあって構わないと思います。bottom
up なアプローチだと使う側の精度を期待する訳ですが、top down
なアプローチでは枠組の側の精度を期待する訳ですね。
ただ、programming という分野では使う側に要求される skill が
高度なので、fool proof 過ぎる設計は馴染まないと思います。
どこの誰が組んでも絶対に bug の入り込み得ない計算機言語なん
てあったら確かに理想的なんでしょうけど、それを実現するコスト
を考えると使う側の精度を上げた方が安上がりかと。
--
しらい たかし
myプリコンパイラって、現存するんですか?
In article <c2m4b0$6vo$1...@dnknews.denken.or.jp>
TAKENAKA Kiyoshi <take...@criepi.denken.or.jp> writes:
> >事前条件とか事後条件のチェックをCで書いて入れるのとどの辺り
> >が違うのでしょうか。
> たぶん、同じだと思います。単に私がさぼりたいのと独自変更したい
> だけです。
> たとえばですけど、以下のようなものです。
> 1)chk abc
> と書いてあれば、abcというtableを矛盾チェック(&チェックライト)する
> ルーチンを追加して、別途必要な追加ルーチン(ライブラリ)フラッグを
> 立てる。
アスペクト指向っぽい。
> 2)";"の解釈を反対にする等。コンパイラを2種類以上使うと、文法が
> 似ていないと混乱するんですよ(;を入れ忘れたり、キーワードを
> 間違えたり....)。
まあ確かに Ruby 書いたあと C 書くと、「;」忘れたりするんだけ
ど、文法変えると、他の人がプログラムを読めなくなって困るんじゃ
ないですか。この辺りなら、エディタとか統合開発環境で修正した
方がいいんじゃないですか。
もっとも、コンパイラ本体とエディタ/統合開発環境うまく構文解
析器が共有できるといいのかもしれません。
> 3)a社対応、b社対応をソースセレクタで切り替える。
たしかに、#ifdef は読みにくい。けど間に合っているといえば間
に合っている。
> >チェックが重たいなら、後で部分評価系で取り除きます。#ifdef
> >でもいいけど。assert() 入れてもいいけど、入れなくてもいいで
> >しょう。
>
> myだと、自分のチェックはフラッグ一つでon/off可能です(結構楽)。
フラグ1つにしたいなら、こんなのでもいけます。
#define myassert(e) assert(myflag&&(e))
In article <c2n66t$pfo$1...@nsvn01.zaq.ne.jp>
shi...@unixusers.net (Takashi SHIRAI) writes:
> しらいです。
> >・strlcpy() を「正しく」使うことも簡単である。
> strcpy() だって正しく使えば buffer overflow なんて起こり得
> ない訳ですが、buffer size に対する指定がない分、使う側が意識
> 的に注意しないといけないので「正しく使う」のがより困難だとい
> うことですね。
strcpy() を正しく使っているプログラムの例を出してもらえませ
んか。strcpy() を正しく使っているかどうか、パッと見ても分か
らないと思います。局所的に見てもわからなかったり。
> なのでやはり程度問題でしょう。正しい関数と間違った関数とか、
> 完璧に bugless な関数と buggy な関数とか、そういう対比ではな
> くて、単に、危険回避がより簡単に出来る関数とより簡単に出来な
> い関数という相違でしかないと思います。
そんなに程度問題にしたいんですか。まあいいけど。
> >自覚ですか。私は、自覚とか根性とか気合いとか、そういうのでは
> >なくて、何か工学的に再生産可能なノウハウで安全なプログラムを
> >書きたいわけです。
>
> 勿論、どっちのアプローチもあって構わないと思います。bottom
> up なアプローチだと使う側の精度を期待する訳ですが、top down
> なアプローチでは枠組の側の精度を期待する訳ですね。
根性指向のプログラミングがいいと?それは、竹槍じゃないですか。
top down/ bottom up とは、なんか話のつながりが見えていないん
ですが。
> ただ、programming という分野では使う側に要求される skill が
> 高度なので、fool proof 過ぎる設計は馴染まないと思います。
> どこの誰が組んでも絶対に bug の入り込み得ない計算機言語なん
> てあったら確かに理想的なんでしょうけど、それを実現するコスト
> を考えると使う側の精度を上げた方が安上がりかと。
バッファ・オーバーランのバグが出たとして、それを探したり回収
したりするコストは、莫大ですよ。それに比べたら、strcpy() を
strlcpy() にするのなんか、コストはそんなにかからないでしょ。
> 文法変えると、他の人がプログラムを読めなくなって困るんじゃ
> ないですか。
昔、「Cプリプロセッサ パワー」とかいう本を読んでびっくりしたこと
を思い出しました。
#define begin {
#define end }
とかやって、文法を変えていくんですが、
巻末のサンプルプログラムはほとんどCとは別物でした。
In article <0lk4c.5$sq...@news6.dion.ne.jp>,
5470k0 <m4r1...@d2.d10n.n3.jp> wrote:
>昔、「Cプリプロセッサ パワー」とかいう本を読んでびっくりしたこと
>を思い出しました。
>#define begin {
>#define end }
>とかやって、文法を変えていくんですが、
>巻末のサンプルプログラムはほとんどCとは別物でした。
Steve Bourne 氏の書いた Bourne shell の source が正にその
パターンですね。私にはあれは読みこなせませんでした。
現物は下記 URL の Unix Archive Site からを拾って確認して貰
うとして、この言語は Algol をベースにした Bournegol という言
語なんだそうです。
http://www.tuhs.org/archive_sites.html
macro で置換えられる程度の変更なので、文法構造自体は C と
同じなのですが、{} も含め C の標準的な statement が殆んど現
れないので全く別の言語にしか見えません。
これが 4.3BSD の頃辺りになると Bourne 氏以外の手が加わって
くるのか、普通の C の code まで混在してしまうので、もう何が
何だか判らなくなってきます。
一切外に出さない source ならともかく、他人に見せる可能性が
少しでもあるなら、多少のデメリットがあったとしてもスタンダー
ドなスタイルを崩さない方がいいんじゃないかと私は思います。
後で他人がメンテ出来ないようだと色々困りますからね。実際、
Bourne shell は Sun OS 以外では他の互換品に置換えられてしま
いましたけど。
--
しらい たかし
In article <YAS.04Ma...@kirk.is.tsukuba.ac.jp>,
Yasushi Shinjo <y...@is.tsukuba.ac.jp> wrote:
>新城@筑波大学情報です。こんにちは。
>> strcpy() だって正しく使えば buffer overflow なんて起こり得
>> ない訳ですが、buffer size に対する指定がない分、使う側が意識
>> 的に注意しないといけないので「正しく使う」のがより困難だとい
>> うことですね。
>
>strcpy() を正しく使っているプログラムの例を出してもらえませ
>んか。strcpy() を正しく使っているかどうか、パッと見ても分か
>らないと思います。局所的に見てもわからなかったり。
例えば元々同じサイズの char 型配列間で copy する時なんかだ
と、source 側で buffer overflow が起こっていない限り strcpy()
で buffer overflow は発生し得ませんよね。
既に例として挙がっているような、strdup() 時に memcpy() の
代わりに strcpy() を使うような例でも、無駄とか合理性とかの問
題を除けばそう間違った使い方でもないと思います。
>> 勿論、どっちのアプローチもあって構わないと思います。bottom
>> up なアプローチだと使う側の精度を期待する訳ですが、top down
>> なアプローチでは枠組の側の精度を期待する訳ですね。
>
>根性指向のプログラミングがいいと?それは、竹槍じゃないですか。
そもそも「問題意識」って「根性」とは別物だと私は考えている
ので、「精神さえ鍛えれば竹槍で戦闘機が落とせる」なんて世界と
同列には考えられません。
横断歩道を渡る時には信号を過信せず左右の確認をしましょうと
いったような、安全に対する問題意識の話をしているつもりなんで
すが、これって精神論とはまた別ではありませんか?
>top down/ bottom up とは、なんか話のつながりが見えていないん
>ですが。
交通安全の例で言うならば、信号機の設置や道路の整備といった
アプローチが top down で、左右を良く見るとか安全運転を心がけ
るとかいったアプローチが bottom up になると思います。
どっちか一方だけで十分だというようなことはないと思うんです
が、bottom up 側のアプローチが全く不要になるくらいに top down
側のアプローチを整備すべきなんでしょうか?
文法的に strcpy() を許さないような仕組みは簡単に用意出来る
でしょうけど、strcpy() を全て strlcpy() に置換えたからと言っ
て、文字列 copy 時の buffer overflow に関して programmer が
一切注意を払わなくて済むというのは思い上がりなんじゃないかと
思います。
勿論 strlcpy() で置換えるという方向性の努力は大切ですけど、
その方向のアプローチだけで全ての安全を確保しようとするとどこ
かで破綻するんじゃないでしょうか。
それはむしろ危険な方法論だと思います。
>バッファ・オーバーランのバグが出たとして、それを探したり回収
>したりするコストは、莫大ですよ。それに比べたら、strcpy() を
>strlcpy() にするのなんか、コストはそんなにかからないでしょ。
そうですか?char 型 pointer を引数に持つ関数全てに size も
渡してやるのは結構大きなコストになると思いますけど。strlcpy()
の portability もそこそこ大きな問題かと。
--
しらい たかし
> 昔、「Cプリプロセッサ パワー」とかいう本を読んでびっくりしたこと
> を思い出しました。
うわああぁぁぁあぁ~~~~
これって林晴比古さんのでしょ
彼の「C 言語スタイルブック(?)」とか、ネタで持ってましたが、
ほんとにネタにしかなりません。
「プリプロセッサパワー」は読んだことありませんが、
まあ、びっくりするような内容なんでしょうね(いろんな意味で)。
林さんの場合、三田典玄の入門・応用・実践三部作とは違って、
「デタラメをまきちらす有害図書」指定ではないんですけどね。
神田敏広 <ca...@kgc.co.jp>
In article <c2v18b$1g7t$2...@nsvn01.zaq.ne.jp>
shi...@unixusers.net (Takashi SHIRAI) writes:
> しらいです。
> 文法的に strcpy() を許さないような仕組みは簡単に用意出来る
> でしょうけど、strcpy() を全て strlcpy() に置換えたからと言っ
> て、文字列 copy 時の buffer overflow に関して programmer が
> 一切注意を払わなくて済むというのは思い上がりなんじゃないかと
> 思います。
誰も「一切注意を払わなくて済む」とは言っていないと思います。
strcpy() を使うより strlcpy() を使った方が形式的に分かるから、
その分、プログラミングが楽だという話です。
> >strcpy() を正しく使っているプログラムの例を出してもらえませ
> >んか。strcpy() を正しく使っているかどうか、パッと見ても分か
> >らないと思います。局所的に見てもわからなかったり。
> 例えば元々同じサイズの char 型配列間で copy する時なんかだ
> と、source 側で buffer overflow が起こっていない限り strcpy()
> で buffer overflow は発生し得ませんよね。
こういう場合でも、strcpy() より strlcpy() が楽です。
char a[100],b[100];
...
strcpy( a,b ); /* (1) */
if( strlcpy(a,b,sizeof(a)) >= sizeof(a) ) /* (2) */
{
....;
}
ここで、(1) と (2) を比べます。strcpy() でバッファ・オーバー
フローが起きないことを調べるには、次のことを調べる必要があり
ます。
・元々同じサイズの char 型配列間で copy であることを確認する
・source 側で buffer overflow が起こっていないことを確認する
strlcpy() の場合、こうります。
・if文を通り越して下に抜けたことを確認する
こんな感じで、strlcpy() を使った方が、プログラマが楽できます。
コストが下がると言ってもいいです。
> そもそも「問題意識」って「根性」とは別物だと私は考えている
> ので、「精神さえ鍛えれば竹槍で戦闘機が落とせる」なんて世界と
> 同列には考えられません。
> 横断歩道を渡る時には信号を過信せず左右の確認をしましょうと
> いったような、安全に対する問題意識の話をしているつもりなんで
> すが、これって精神論とはまた別ではありませんか?
問題意識は、わかりました。それで、問題意識が高ければ、使える
道具、この場合は、strlcpy() ですが、そういう道具を使うという
のが結論として出て来るんじゃないですか。
エアバッグも付けた方がいいし、横滑り防止装置も付けた方がいい。
交通信号も付けた方がいい。交通信号だけ信じて突っ込むのはバカ
です。
> >バッファ・オーバーランのバグが出たとして、それを探したり回収
> >したりするコストは、莫大ですよ。それに比べたら、strcpy() を
> >strlcpy() にするのなんか、コストはそんなにかからないでしょ。
>
> そうですか?char 型 pointer を引数に持つ関数全てに size も
> 渡してやるのは結構大きなコストになると思いますけど。strlcpy()
> の portability もそこそこ大きな問題かと。
strlcpy() くらい、なければ自分で書いて入れたらいいじゃないで
すか。BSD からコピーしてきてもいいし。
可変長なら、ポインタと大きさをいっしょに渡すのは、安全なプロ
グラムを書く時には当然の話です。関数呼出しで、担当者が違うか
もしれないし、同じ担当者でも何ヵ月も置いてからいじるかもしれ
ないわけでしょ。これは大丈夫なんて、いちいち覚えていられませ
んよ。そんなことを覚えるのはコストがかかります。それよりは、
可変長の場合は全部大きさも引き回すようにした方が覚えるコスト
が下がって楽できるというものです。
> > typedef struct { char name[FSTRING_LEN]; } fstring;
> > とかにすれば良いのですかね?
>
> それ、いいですね。
正直, このコメントには「何を今更…」と思わされました. 私の strcpy が数
百出てくるものでも使われてるし ([] 内は即値で長さが必要な場合 *_LEN は
使わず要素数から得るようにしてますけど).
とかいっても人間の手でサイズを指定させる為ではなくて, データ定義ファイ
ルからスクリプトでそれぞれをメンバとした union を構成させて, 一時領域
(strcpy のストア先とか. 一時とはいえ確保しっぱなしだけど) はそのサイズ
で確保したりするわけです. チェックルーチンも同様.
# ちなみにこの仕組みをバイパスしてデータを定義すると, 諸々の関連ルーチ
# ン (コンストラクタみたいなのとか) が生成されないので簡単にはリンク出
# 来なくなります. 楽しようとした結果こうなっただけですが.
なんか色々話が進んでいるようですが.
In article <YAS.04Ma...@kirk.is.tsukuba.ac.jp>
y...@is.tsukuba.ac.jp (Yasushi Shinjo) writes:
> > > (A)セキュリティ上の弱点に繋がる間違い
> > > (B)セキュリティ上の弱点には繋がらない間違い
> > これをプログラマが区別しないといけないのかなぁ (区別するとしたらどう考
> > えたら/捉えたらいいのだろう), というのがここ数年私が考えているテーマ
> > だったり.
>
> 私は(A)(B)を区別する必要があると思います。両方無くなれ
> ばそれに越したことはないけれど、潰すならまず優先的に(A)か
> ら潰せという意味です。
そういう設計指針 (プログラマに個別に負担させる) であれば, C 言語を選ん
だ時点で間違っていると思います. C を使うのであれば, 別の設計指針を採択
すべきだと思うし.
> > 「strcpy で出てくるようなセキュリティ上の問題」は出てこない. しかし,
> > 個別に一々プログラマに担わせていたのでは駄目なんじゃないですか?
>
> いいえ。トータルで考えれば、バグ入りプログラムからバグを見つ
> けるより、最初からバグがないプログラムを書く方が楽(得)です。
そういう方針で作れるプログラムってとてもとても小さなものに限られちゃう
と思います. 見通しも悪くなるし.
> > 責任は書かせた人にもあるわけです. 「このソフトウェアはどのような設計思
> > 想に基づいていて, それがどのように実装されているのか」というレビューま
> > で普通やるじゃないですか.
>
> 「普通」というのは、どういう状況でしょうか。もう少し詳しく教
> えてください。大学だと何が普通なのかが実はよく分からないとい
> う話があります。
手を入れるソフトウェアに上の「」のような文書がない場合, それを作成する
ところから始まります. で, その設計方針が「ちょっとなぁ」というものであっ
た場合は, ソフトウェア全体を作り直すこともあるわけです.
次に実際のソフトウェアとつき合わせながら, 手を入れることによって及ぶ影
響の範囲とテストの条件などを洗い出していきます (で, 見積りを出したり).
…なんてことを会社ではやるわけですが, 文書に起こすかどうかは別として普
通個人でソフトウェアを改変する場合でもやっていることだと思います (私は
学生時代からそうしていたけどなぁ). 以前何処かでコーディングスタイルの
話が出た時に「基本的に元のスタイルに合わせる」という話になっていたと思
いますが, それのもうちょっと上のレイヤでもそうする, という感じ.
> 「そんなこともあろうかと」って、言ってみたいセリフでしょ。
strlcpy が働いた時点で言える台詞ではありませんよね. それは正しくないソ
フトウェアが, 正しくない動作をした時なのですから.
○
「正しさよりもセキュリティに重点を置く」という指針からは「何もしないプ
ログラム」が導かれちゃう, という話についてはどうお考えですか?
In article <c3sj8v$21rk$1...@news2.rim.or.jp>
doh...@hf.rim.or.jp (Kazuo Fox DOHZONO) writes:
> 堂園です.
> なんか色々話が進んでいるようですが.
> > > > (A)セキュリティ上の弱点に繋がる間違い
> > > > (B)セキュリティ上の弱点には繋がらない間違い
> > 私は(A)(B)を区別する必要があると思います。両方無くなれ
> > ばそれに越したことはないけれど、潰すならまず優先的に(A)か
> > ら潰せという意味です。
> そういう設計指針 (プログラマに個別に負担させる) であれば, C 言語を選ん
> だ時点で間違っていると思います. C を使うのであれば, 別の設計指針を採択
> すべきだと思うし.
最初から私はC言語を使うなと書いています。
> > > 責任は書かせた人にもあるわけです. 「このソフトウェアはどのような設計思
> > > 想に基づいていて, それがどのように実装されているのか」というレビューま
> > > で普通やるじゃないですか.
> 手を入れるソフトウェアに上の「」のような文書がない場合, それを作成する
> ところから始まります. で, その設計方針が「ちょっとなぁ」というものであっ
> た場合は, ソフトウェア全体を作り直すこともあるわけです.
>
> 次に実際のソフトウェアとつき合わせながら, 手を入れることによって及ぶ影
> 響の範囲とテストの条件などを洗い出していきます (で, 見積りを出したり).
それで、どういう方針なら、「strlcpy() を使うな、strcpy() を
使え」と出てくるののですか?
> > 「そんなこともあろうかと」って、言ってみたいセリフでしょ。
> strlcpy が働いた時点で言える台詞ではありませんよね. それは正しくないソ
> フトウェアが, 正しくない動作をした時なのですから.
人間は常に間違うということを認めようという話をしているんだけ
ど。人間は正しいく動作するソフトウェアを書いたつもりなんだけ
ど必ず間違えます。それで、間違いをしないようにプログラムを書
くのはもちろん大事です。でも、間違ったとしても、安全なプログ
ラムを書くというのも大事です。そういう話をしています。
> 「正しさよりもセキュリティに重点を置く」という指針からは「何もしないプ
> ログラム」が導かれちゃう, という話についてはどうお考えですか?
始めて聞きました。堂園さんのオリジナルですか?誰がの言葉ですか?
「正しさ」の中に「セキュリティ的な弱点ががない」も入れておけ
ばいいだけの話だと思います。対立する話じゃないです。
\\ 新城 靖 (しんじょう やすし) \\
\\ 筑波大学 電子・情報 \\
----------------------------------------------------------------------
From: y...@is.tsukuba.ac.jp (Yasushi Shinjo)
Message-ID: <YAS.04Fe...@kirk.is.tsukuba.ac.jp>
Date: 21 Feb 2004 14:53:37 GMT
Organization: Institute of Information Sciences and Electronics, University of
Tsukuba
In-reply-to: doh...@hf.rim.or.jp's message of 21 Feb 2004 11:07:31 GMT
Newsgroups: fj.comp.lang.c
Subject: Re: strcpy, strlcpy.
References: <YAS.04Ja...@kirk.is.tsukuba.ac.jp>
<c17e5u$2eji$1...@news2.rim.or.jp>
新城@筑波大学情報です。こんにちは。
古い記事が読まれると、新しい記事よりも嬉しいですね。いい記事
だったんだなあという感じがして。
In article <c17e5u$2eji$1...@news2.rim.or.jp>
doh...@hf.rim.or.jp (Kazuo Fox DOHZONO) writes:
> つらつらとその理由を考えてみたのですが, 要するに strcpy では問題のある
> ようなアプリケーションが, strlcpy によって直ちに正しく動作するようにな
> るわけではない, ということかな.
>
> ・ほとんどの場合その前にチェック出来るハズ.
はい。それはそうです。strlcpy() は、バッファ・オーバーフロー
を起さないという目的で使うものです。strlcpy() を使ったからと
いって、間違ったプログラムが「正しく」動作するようになるわけ
ではありません。
> 何も考えずに strlcpy 使って, 長さを越えた場合に適当に truncate とかす
> る人がいたりすると, "foobar" で登録したデータが "foobar" でも "foo" で
> も読めてしまうといった問題が紛れ込んだりするかもしれません.
> それよりはテストで落ちてもらった方がありがたいと思うのですが.
テストでは、バッファ・オーバーフローは見つかりにくいんじゃな
いですか。それより、"foobar" が "foo" につめられて動作がおか
しいいう方が見つかりやすいんじゃないかなあ。
> ・間違いやすい人間に毎回長さを指定させるなんて.
> まぁこれも結構大きい理由でしょう. それと
> ・長さだけ指定してチェックしないってことはないよね?
> if (strlcpy (buf, src, sizeof buf) == sizeof buf)
> /* さて, どうするの? */;
> 間違いやすい人間に毎度毎度チェックさせるの? という話にもなります.
それは、同感です。まあ、マクロ一発という話もありますけど。
#define Strlcpy(dst, src, size) if( (strlcpy((dst),(src),(size))>=(size)) ) \
error("buffer over flow.")
それで、毎回チェックするのがいやになったら、C言語をやめて
Javaに移るわけです。
for (i=0; i!=sup; ++i) { ... } /* (a) */
for (i=0; i<sup; ++i) { ... } /* (b) */
昔は, これらのプログラムの断辺の直前までに sup の値の正当性が
確認されていたとしても, 後者の様に書けと言われたもんです.
後者の方が遅い処理系だったとしてもです.
(昔はそんな処理系が本当にあったんです.)
どうも, strcpy と strlcpy の関係は, 上の2例の関係と似ている様な気がします.
なんというか, 気持としては, フェイルセーフだと思うんです.
それはさておき,
全てのプログラムをきちんと設計して全く問題の無い製品を作れれば
それはそれでとても良いことでしょう.
しかし, それは製品の正しさを 100% 保証するということで,
実際問題としてそれは非常に困難なことです.
現代工学および現代の工業製品では, 製品に一定の不良率を許しています.
例えば, アポロ宇宙船なら 99.999999999% の稼働率を目標とする,
といった感じで, この場合なら 0.000000001% の場合は直ちに人命にかかわる
にもかかわらず, です.
これは宇宙船に限ったことではなく,
建造物(建物, 橋, 道路など),
乗り物(電車, 自動車, 飛行機など),
電子部品, コンピュータ,
その他身の回りにある大小さまざまな工業製品といえるもので,
その性能や安全性を 100% 保証しているものはどれひとつとしてありません.
(極東のとある島国では,
原子力発電所などは 100% 壊れないことになっているそうです.)
それがソフトウェアになったとたん 100% の保証が付いてくる,
なんてのはおかしい話です.
ソフトウェアは時間の経過で劣化することはないので,
ほとんどの場合,
壊れるというよりは不具合が発見されることになります.
ソフトウェアは工業製品ではない? そうかもしれません.
だとしたら議論はここで終り.
私はソフトウェアも工業製品だと思いたい(*** 自己矛盾してしまった...).
そして, 現代に生きる我々は, エンジニアもユーザも,
ソフトウェアについて, ある一定の不具合までならそれを受け入れるべきです.
そうした時に初めて,
「不具合がある. そのため, 正しくはないけれども, 悪さはしないソフトウェア」
という考え方が意味を持ってくるのではないでしょうか.
同じ不具合でも, 命やセキュリティに関る不具合の方が
そうでない不具合より罪が重いとは言えませんか.
エンジニアリングでは, それらそれぞれについて
不具合の発生率を設定し, それをクリアすることを目標のひとつとします.
それができないエンジニアはエンジニアとは呼び難い.
そういう意味では, ソフトウェアの多くはエンジニアリングの成果, すなわち
工業製品ではないのかもしれません(*** ここ).
一方, サイエンスではそんなマヌケなことは考えません.
サイエンティストは「この理論が正しいなら, これこれこうなる」という
立場にたって仕事を進めます.
理論から演繹したことはその理論が正しい限り 100% 正しい,
それがサイエンスってもんです.
不具合などという得体の知れないが発生する余地はどこにもありません.
オレの理論に従い, オレの作った言語を使ってプログラミングすれば,
100% 正しいソフトウェアを作ることができる,
そう考えるのです.
(.* Software Science .* という学会名はそいう点ではチャレンジングだと思う.)
私と strcpy 派の人の感覚の違いが,
エンジニアの感覚とサイエンティストの感覚の違いでなければ良いのですが...
ところで, 私もソフトウェアを作りますが, その不良率は計算していません.
いいかげんですみません.
おわび
わざとステレオティピカルに書きました.
実際はサイエンスからエンジニアリングまでは連続していて,
その中間の様々な立場がありますし, 誰しも, 状況に応じて
立場を適切に切り換えていることが多いと思います.
In article <6500ebba.0403...@posting.google.com>
mihoga...@yahoo.com (miho) writes:
> 私と strcpy 派の人の感覚の違いが,
> エンジニアの感覚とサイエンティストの感覚の違いでなければ良いのですが...
なるほど。そういう見方もあったんですね。面白い。ところで、サ
イエンティストの方は、strcpy() を使うのですか、それとも、
strlcpy() を使うのですか。
> オレの理論に従い, オレの作った言語を使ってプログラミングすれば,
> 100% 正しいソフトウェアを作ることができる,
> そう考えるのです.
10年くらい前の仕様の検証の話は、なんかトートロジーのような
感じがしました。正しいものは正しいって。プログラムが仕様にそっ
ていることはわかる。でも仕様が正しいかどうかは結局よくわから
ない。最近は、どうなんでしょうか。
仕様といっても、一般的なものではなくて、「限られた範囲のメモ
リだけアクセスする」というような仕様だったら、実用的な速度で
動く技術はあります。こういうのは使えます。
> (.* Software Science .* という学会名はそいう点ではチャレンジングだと思う.)
私は日本ソフトウェア科学会の会員です。あんまりそういう風に名
前は気にしたことはなかったなあ。
In article <6500ebba.0403...@posting.google.com>
mihoga...@yahoo.com (miho) writes:
> C 言語だと(b)のように書くのが普通で,
> (b)のほうが読み易いと感じる人が非常に多いでしょうから,
> for (i=0; i!=sup; ++i) { ... } /* (a) */
> for (i=0; i<sup; ++i) { ... } /* (b) */
これ見て思い出したのですが、int と unsigned int のバグがOS
のカーネルからちょぼちょぼ見つかっています。OSのカーネルは、
普通のアプリケーションよりも1/20くらいバグが少ないという
話を聞いたことがあります。かなり気をつけて書くからか、あるい
は、元々腕がたつ人が書いているからか。
バグという意味では、Pentium とかちょっとしたOSより複雑そう
なんだけど、バグの話はあんまり聞きません。
> それがソフトウェアになったとたん 100% の保証が付いてくる,
> なんてのはおかしい話です.
100% 保証という話は誰もしていません。CPU にできるなら、OS
にもアプリケーションにもできてもいいような気がするけれど、そ
れができないのはどういう事なんでしょうね。
Yasushi Shinjo wrote:
> 新城@筑波大学情報です。こんにちは。
> バグという意味では、Pentium とかちょっとしたOSより複雑そう
> なんだけど、バグの話はあんまり聞きません。
それは、CPUのバグを話題にするコミュニティに居ないと言うだけでは?
Pentiumは浮動小数点のわり算のバグで有名ですし(このときは
無償交換したはず)。
2002JulyリリースのPentiumIIのSpecificationUpdateは
95ページほどありますが、そのうち22~75ページまでが
ERRATAです。
しかも、結構直してないバグがある。たとえば、浮動小数点
演算後にフラグが正しくならない可能性があるバグは、
「連続する浮動小数点演算の間にはNOPを2つ入れて回避
せよ」ということになっています。
intel最初の8ビットCPU i8080もバグで早々に8080Aに
代わりましたし、CPUのバグは結構あるものだというのが
僕の認識です。しかも、改修できないのをいいことに、
ソフトウェアでの回避をOSに押しつけている。
昔のMIPS Rx000を使ったUNIX WSのシステムダウンの
ある割合いは、ソフトのバグといえばバグだがCPUのバグ
を回避しそこなったせいだとか言う話をきいたことが
あります。誰に聞いたのか忘れたけど。
Windowsのソースが3000万行あるといわれていますね。
Linux2.4カーネルが180万行、2.6が240万行だとか。
ちなみにlinux0.01は1万行ほど。
一方Pentiumは、Pentium4のコア(L2キャッシュを除いた残り)
が2350万トランジスタだそうで。
ソース1行が100トランジスタくらいだと思えば、今の
Pentiumは23万行程度のプログラム相当。
MULTICSが30万行だとか言う話なので、確かにちょっとした
OSよりは複雑かも。