トップページ > 過去ログ > 記事閲覧
FileRead系関数と構造体
名前:kiyo 日時: 2008/01/23 07:13

以下のプログラムについて、ビルドは通るのですが、実行時にFileReadのループのところで「動作を停止しました」となってしまいます。 「問題なし」は表示され、WaitKey();を解除した瞬間にエラーが出るので、for文のループ部に原因があると考えておりますが、 それが何なのか全く分からず、お手上げ状態です。 どなたかわかる方、アドバイスいただけたら幸いです。 (プログラムは、構造体で定義したcre一つ一つに、外部ファイルから情報を入れていき、 その後、それぞれの名前を表示するといったものです。) #include "DxLib.h" #define NUMALL 6 #define NUMCRE 6 struct cre_t{ int power,toughness; char name[256],special[10],mana[10],buf[100]; }; int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, int nCmdShow ){ ChangeWindowMode( TRUE ) ; // ウインドウモードに変更 if( DxLib_Init() == -1 ) return -1; // DXライブラリ初期化処理 エラーが起きたら終了 int a,b,x,y,white; int crefile; char key[256]; struct cre_t cre[NUMCRE]; SetDrawScreen(DX_SCREEN_BACK); y=-180; white=GetColor(255,255,255); /*読み込み*/ crefile=FileRead_open("cretext.txt"); if(crefile==0){ DrawString(50,50,"エラー発生",white); } else{ DrawString(50,50,"問題なし",white); } ScreenFlip(); WaitKey(); for(a=1;a<=NUMCRE;a++){ FileRead_gets(cre[a].name,256,crefile); FileRead_scanf(crefile,"%d",cre[a].power); FileRead_scanf(crefile,"%d",cre[a].toughness); FileRead_gets(cre[a].mana,10,crefile); FileRead_gets(cre[a].special,10,crefile); FileRead_gets(cre[a].buf,100,crefile); } FileRead_close(crefile); /*リスト表示*/ while(1){ ClearDrawScreen(); GetHitKeyStateAll(key); if(key[KEY_INPUT_UP]==1)y+=20; else if(key[KEY_INPUT_DOWN]==1)y-=20; for(a=1;a<=NUMCRE;a++)DrawString(50,200+(b-1)*20+y,cre[a].name,white); DrawBox(45,200,200,225,white,FALSE); ScreenFlip(); if(ProcessMessage()==-1)break; if(key[KEY_INPUT_ESCAPE]==1||key[KEY_INPUT_RETURN]==1)break; } WaitKey() ; // キーの入力待ち(『WaitKey』を使用) DxLib_End() ; // DXライブラリ使用の終了処理 return 0 ; // ソフトの終了 } ちなみに、以前に似たプログラム(上の改編前のもの)を動かしてた際、 DrawBoxで表示された四角の枠の色が白ではなくなるといったことがあったのですが、これも関係しているのでしょうか? この際int変数の定義は以下のようであり、whiteの横にredという変数を加えて定義するとwhiteは正しく白色となりました。 同じことが大学の講義の課題を進めていた時にもあり、同様の手段(空の変数を設ける)で解決したということもあります。 どちらの場合も、scanf系の関数を使用するとそうなっており、これが怪しいと思うのですが、なぜなのかがわかりません。 併せてご教授いただけると幸いです。 int a,b,…; int white; int crefile;  ↓ int a,b,…; int white,red; int crefile; 長文・乱文で申し訳ありません。どうぞよろしくお願いいたします。

Page: 1 |

Re: FileRead系関数と構造体 ( No.1 )
名前:kiyo 日時:2008/01/23 07:15

追加: cretext.txtの内容は以下の通りです。 Skizzik.jpg 5 3 000013 00001 Crosis_the+Purger.jpg 6 6 011103 10001 Spiritmonger.jpg 6 6 000113 00000 Darigaaz_the+Igniter.jpg 6 6 011103 10000 Tsabo+Tavoc.jpg 6 4 001103 *00000 Kavu+Titan.jpg 2 2 100002 00000
Re: FileRead系関数と構造体 ( No.2 )
名前:tok 日時:2008/01/23 07:19

はじめまして。 とりあえずコレ > for(a=1;a<=NUMCRE;a++){ だとメモリアクセス違反が発生しますよ。 struct cre_t cre[NUMCRE]; なんですから、 > for(a=0;a<NUMCRE;a++){ とする必要があります。
Re: FileRead系関数と構造体 ( No.3 )
名前:kiyo 日時:2008/01/23 07:49

返信ありがとうございます。 ご指摘の部分を修正し、他にも誤りを発見したので直しました /*リスト表示*/部分真ん中あたり for(a=1;a<=NUMCRE;a++)DrawString(50,200+(b-1)*20+y,cre[a].name,white); ↓ for(a=0;a<NUMCRE;a++)DrawString(50,200+(a-1)*20+y,cre[a].name,white); bではなくaでした が、それでもまだWaitKey();後に止まってしまいます。プロジェクトのフォルダ内にcretext.txtがあるのは確認済みです。 引き続き、ご指摘をお願いします。
Re: FileRead系関数と構造体 ( No.4 )
名前:tok 日時:2008/01/23 08:08

ファイル読み込み時の最後のループの > FileRead_gets(cre[a].buf,100,crefile); が気になります。 cretext.txtの最後に、空行はちゃんと入っているでしょうか。
Re: FileRead系関数と構造体 ( No.5 )
名前:kiyo 日時:2008/01/23 08:41

お早い返信ありがとうございます。 空行は設けております。 先程、for文の中を詳しく見た結果、scanfに原因があったようです。 cre[a].nameの読み込み、表示がうまくいってWaitKey();を解除するとすぐエラーが出ました。 そこで、 #include<stdlib.h> for(a=0;a<NUMCRE;a++){ FileRead_gets(cre[a].name,256,crefile); DrawString(50,70,cre[a].name,white); ScreenFlip(); WaitKey(); FileRead_gets(cre[a]._power,3,crefile); cre[a].power=atoi(cre[a]._power); DrawFormatString(50,90,white,"%d",cre[a].power); ScreenFlip(); WaitKey(); FileRead_scanf(crefile,"%d",cre[a].toughness); FileRead_gets(cre[a].mana,10,crefile); FileRead_gets(cre[a].special,10,crefile); FileRead_gets(cre[a].buf,100,crefile); } のように、scanfではなくgetsで読み込んで、atoiで変換することで、一応は目的の操作は達成できました。 しかしなぜscanfがうまくいかなかったかが気になります。また、手間と今後の応用具合から言ってもscanfが使えないのは痛いです。 いったいscanfのどこが良くなかったのでしょうか?
Re: FileRead系関数と構造体 ( No.6 )
名前: 日時:2008/01/23 09:43

>FileRead_scanf(crefile,"%d",cre[a].toughness); scanfの数値の読み込みは代入する変数のポインタが 必要です。 正しくは FileRead_scanf(crefile,"%d",&cre[a].toughness); となりますが、scanfでの数値の読み込みは オーバーフローする可能性があるため、fgetsまたは scanfで文字列として読み込んだ後、strtolなどで 数値化するのが最も望ましいです。
Re: FileRead系関数と構造体 ( No.7 )
名前:tok 日時:2008/01/24 03:48

通様の仰ってる通りです。 scanfの引数は変数のアドレスなので、数値変数ならば変数名の前に&が必要になります。 また通様の仰るように、C言語のscanf系の関数を利用する際は、本来は注意が必要です。 scanfと脆弱性、セキュリティホールなどのキーワードで調べてください。
Re: FileRead系関数と構造体 ( No.8 )
名前: 日時:2008/01/23 23:44

> tok さん FileRead_scanf_base確認しました。 確かに与えられた文字を解析していますが、 まだ詳しく見ていませんが、桁数をカウント していないようなので、与えられたオブジェクト型 の表現範囲を超える数字列を入れた場合は va_argへのポインタに直接代入を行っていて 同じようにオーバーフローするような気がします。 strtolでの数値化が有効だといっているのは 正しい値が表現可能な値の範囲外であるとき、 値の符号に従って、LONG_MAXまたはLONG_MINを 返す為、「longで確実に表現できる値が返る」 事を言っています。 ちなみにscanfにセキュリティホールがあると 思っている人が多いですが、scanfは正しく使用 してくれる人が少ないだけで、セキュリティ ホールがあるわけではありません。
Re: FileRead系関数と構造体 ( No.9 )
名前:tok 日時:2008/01/24 04:05

> 通様 仰る通りでした。(視認ですが)%[1-9]*sと%[2-9]+c、%65dなど65桁以上の数値を変数に代入しようとした場合にて同じ脆弱性を確認しました。 バッファ長を伝える引数がないのだから、当然同じ脆弱性が存在するということですね。 投稿したコメントの一部 > DXライブラリのFileRead_scanfは、内部ではscanfを利用していないため、そのまま使用しても支障はありません。 を削除し、訂正とさせていただきます。 > strtolでの数値化が有効だといっているのは > 正しい値が表現可能な値の範囲外であるとき、 > 値の符号に従って、LONG_MAXまたはLONG_MINを > 返す為、「longで確実に表現できる値が返る」 > 事を言っています。 数値の扱い(%dなど)は、Number[64]に1桁分の数値を1文字として入力、 連結後に各桁を10^nで正当な数値に戻してから代入を行っていたので、 一応は自動的に丸められた数値が入るようです。ただ64文字を超える桁の 数値を入れた場合にバッファオーバーフローが起こる状態に見えました。 ポインタを直接参照して書き込んでいるワケではないようなので、通様が危惧されている意味では安全かと思います。 > セキュリティホールがあるわけではない 同意見です。 ただそう思ってる方が多いので、調べやすいかなと:-)
Re: FileRead系関数と構造体 ( No.10 )
名前: 日時:2008/01/24 10:38

>%[1-9]*sと%[2-9]+c、%65dなど65桁以上の数値を >変数に代入しようとした場合 指定される書式に依存する事もそうですが、 渡されたオブジェクト型がint型のポインタの場合で 10桁などでもint型の表現範囲を超えるので オーバーフローするような気がします。 #指定した書式と渡したオブジェクト型の型が違う時 #などはどうしようもありませんが。。。 内部では最終的な数値でlonglong型のint64Numに 入れてから、渡された可変引数への代入をva_argで 行っていますが最終的に指定された書式に従って キャストされているのでやはりこの部分でも オーバーフローするでしょう。 >バッファオーバーフロー バッファオーバフローとオーバーフローは 微妙に意味が異なります。 バッファオーバーフローは文字列の場合の 書き込み違反です。(バッファオーバラン) オーバーフローは演算結果の『桁あふれ』です。 数値の代入においてバッファオーバフローが 起こることはないかと思います。 >ポインタを直接参照して書き込んでいるワケでは無い 殆どの処理系においてva_argはただのマクロで 渡された引数のアドレスを計算しているに 過ぎないので次のようなコードから 「ポインタに直接代入を行っている」と書きました 詳しくはva_argのマクロ定義を見てください。 *va_arg(Param,ULONGLONG*)=(ULONGLONG)int64Num;
Re: FileRead系関数と構造体 ( No.11 )
名前:tok 日時:2008/01/24 11:55

> *va_arg(Param,ULONGLONG*)=(ULONGLONG)int64Num; if( SkipFlag == FALSE ) の時なので、%*の指示子があった場合と限定して私は読み飛ばしてしまっていました。。 va_argの定義は単純に指定された型でva_listを順々に読むという以上のことは知りませんでした。 実際定義を読んでて読みきれなかったのですが、つまり int iI32; のときに 〜"%*I64",&iI32〜 とあるとマズいということでしょうか。 > 数値の代入においてバッファオーバフローが 今回は数値の代入と仰ってますが > va_argへのポインタに直接代入を行っていて と前回仰っていたので、アドレスに関する話だと思ってました。 総計するとポインタに数値を直接代入・・・でしょうか。危険だと思います。 理解が足りなくてすみません。 具体的なコードで教えていただけると助かります。 >バッファオーバーフロー 桁が溢れても、想定外の数値は帰ってきますが例外レベルのエラーは起こらないと判断し、 仰る「オーバーフロー=問題のない何か」と僕の中では思っていました。 これも理解が違いましたらツッコミお願いします。 > *va_arg(Param,ULONGLONG*)=(ULONGLONG)int64Num; 頭に*がついてますから、ポインタ自体ではなくポインタの指す変数領域ですね。
Re: FileRead系関数と構造体 ( No.12 )
名前:tok 日時:2008/01/24 11:58

> if( SkipFlag == FALSE ) って逆ですね(汗) %*がなかった場合、つまり "%I64",&iI32 の時に〜という場合にマズいということでしょうか。
Re: FileRead系関数と構造体 ( No.13 )
名前: 日時:2008/01/24 12:59

>総計するとポインタに数値を直接代入・・・ >でしょうか >ポインタ自体ではなくポインタの指す変数領域 言葉足らずですね;おっしゃるとおり ポインタの参照先に直接代入しているという事です > 桁が溢れても、想定外の数値は帰ってきますが > 例外レベルのエラーは起こらないと 確かに例外的なレベルのエラーが発生するわけでは ありませんが、数値の使い方によっては、 (例えば配列の添え字とか)致命的な例外に なったりします。 また、ゲームという観点で言えばイキナリHPや 経験値などが0に成ったりとかするなど意図しない 動作になるのは明白です。
Re: FileRead系関数と構造体 ( No.14 )
名前:kiyo 日時:2008/01/24 16:03

所用により返事が遅くなりました。 結局あの後はscanfの部分は全てgets+atoiでカバーしました。scanfもいろいろと試していますが、 もしかしてFileRead_getsとFileRead_scanfってそれぞれ別のファイルポインタを使用しているのでしょうか? 上のプログラウを試していると、scanfで最初の部分(一回目でいえばSkijik.jpg)を読み込んでいる気がします。 ともかく、経験を積んでいきながら、知識を蓄えていきたいと思います。 ご教授ありがとうございました。
Re: FileRead系関数と構造体 ( No.15 )
名前:tok 日時:2008/01/24 19:37

現在の状況と通様の仰ってることがわかってきました。 ■ (DxLib Ver2.24aの)FileRead_scanf_baseの実装  ・バッファオーバーフローの可能性   ○ 数値型    一度に65桁以上の数値を読み込んだ場合    配列の添え字にscanfで入力された数値を使い、結果としてアクセス違反が起こる場合   ○ 文字列    受け側の変数領域を越える文字数の文字列を読み込んだ場合  ・オーバーフローの可能性   ○ 数値型(のみ)    任意の変数型で表現の限界を超えた数値が代入されば場合 でしょうか。 いずれにしろ、僕の『内部ではscanfを使ってないので安全です』という発言は的外れでした。 > もしかしてFileRead_getsとFileRead_scanfってそれぞれ別のファイルポインタを使用しているのでしょうか? FileRead_getsとFileRead_scanfはファイルポインタを使用していません。 ファイルハンドルというものを使用しています。 しかし共通のものを使用しているかという点については「その通り」です。 なので問題が起こるとは考えにくいのですが、、一度ステップ実行で変数の中身を getsやscanfの直後に覗いてみた方がいいかもしれないですね。 では。
Re: FileRead系関数と構造体 ( No.16 )
名前: 日時:2008/01/25 14:03

>バッファオーバーフローの可能性 こちらについては、コードを詳しく追って居ない為 断言は出来ません...orz モウシワケナイ > それぞれ別のファイルポインタを使用している こちらは少し追ってみましたが、そう単純では無い ようでDXライブラリ用のアーカイブ機能の為に 内部的に拡張したファイル情報の構造体を配列にし ファイル情報と読み込むファイルのファイル情報を 配列のインデックスをハンドルとして返して、 その配列の情報から読み込んでいるような感じで、 別のハンドルの可能性もありますしそうでない場合 もあるという気がします。。。orz ~~~~~~~~~~ #思っていたより複雑だったので。。。
Re: FileRead系関数と構造体 ( No.17 )
名前:管理人 日時:2008/01/28 00:35

FileRead_xxx 系の関数は fxxx 系と同じような挙動をするだけで、 厳密な規格に沿った処理をしているわけではありません、すいません。orz 数値は 64 桁、文字列は 1024バイトを越えるとバッファオーバーランが発生します。 (これは DrawFormatString 等でも同じです) 一応 64桁以上でもバッファオーバーランは起こらないようにしましたが、 オーバーランで不正終了しないようになっただけで、64 文字以上の部分は 読みきらない、LONG_MAX は返ってこない等、その辺りの処理は現状のままです。(- -; > もしかしてFileRead_getsとFileRead_scanfってそれぞれ別のファイルポインタを使用しているのでしょうか? 何故 FileRead_gets と FileRead_scanf で別のファイルポインタを使用して いるのではとお考えになったのでしょうか?(・・; 質問の意図が理解できないと正確なご返答を差し上げることはできそうにありません。

Page: 1 |