gets()
GCCで関数を使用するCコードをコンパイルしようとすると、次の警告が表示されます。
(.text + 0x34):警告: `gets '関数は危険なので使用しないでください。
これはスタックの保護とセキュリティに関係していることを覚えていますが、正確な理由はわかりません。
この警告を削除するにはどうすればよいgets()
ですか。また、使用に関する警告が表示されるのはなぜですか。
gets()
とても危険なのなら、なぜそれを削除できないのでしょうか?
gets()
GCCで関数を使用するCコードをコンパイルしようとすると、次の警告が表示されます。
(.text + 0x34):警告: `gets '関数は危険なので使用しないでください。
これはスタックの保護とセキュリティに関係していることを覚えていますが、正確な理由はわかりません。
この警告を削除するにはどうすればよいgets()
ですか。また、使用に関する警告が表示されるのはなぜですか。
gets()
とても危険なのなら、なぜそれを削除できないのでしょうか?
回答:
gets
安全に使用するには、読み取る文字数を正確に把握して、バッファーを十分な大きさにする必要があります。読み取るデータが正確にわかっている場合にのみ、そのことがわかります。
を使用する代わりに、シグネチャがあるgets
を使用したいfgets
char* fgets(char *string, int length, FILE * stream);
(fgets
、行全体を読み取る場合'\n'
は、文字列にを残します。これを処理する必要があります。)
1999年のISO C標準までは言語の正式な部分でしたが、2011年の標準によって正式に削除されました。ほとんどのCの実装はまだそれをサポートしていますが、少なくともgccはそれを使用するすべてのコードに対して警告を発行します。
gets()
、使用するとコンパイラーが警告を発します。
gets()
危険なのか最初のインターネットワーム(Morris Internet Worm)は約30年前(1988-11-02)に感染しgets()
、システムからシステムへと増殖する方法の1つとしてバッファオーバーフローを使用しました。基本的な問題は、関数がバッファの大きさを知らないため、改行が見つかるか、EOFに遭遇するまで読み取りを続け、指定されたバッファの境界をオーバーフローする可能性があることです。
あなたは今までgets()
存在したと聞いたことを忘れるべきです。
C11標準ISO / IEC 9899:2011 gets()
が標準機能として削除されました。これはA Good Thing™です(ISO / IEC 9899:1999 / Cor.3:2007では「廃止予定」および「非推奨」として正式にマークされていました—技術的正誤表C99の場合は3、C11で削除)。悲しいことに、下位互換性の理由から、ライブラリは何年も(「数十年」を意味する)ライブラリに残ります。それが私次第である場合、の実装は次のgets()
ようになります。
char *gets(char *buffer)
{
assert(buffer != 0);
abort();
return 0;
}
いずれにしても、コードがクラッシュすることを考えると、遅かれ早かれ問題を回避する方が良いでしょう。エラーメッセージを追加する準備をします。
fputs("obsolete and dangerous function gets() called\n", stderr);
Linuxコンパイルシステムの最新バージョンは、リンクすると警告が生成されます。gets()
また、セキュリティ上の問題がある他のいくつかの関数(mktemp()
、…)に対しても警告が生成されます。
gets()
誰もが言ったように、標準的な選択肢はするgets()
さfgets()
を指定するstdin
ファイルストリームとして。
char buffer[BUFSIZ];
while (fgets(buffer, sizeof(buffer), stdin) != 0)
{
...process line of data...
}
まだ誰も言及してgets()
いないのは、改行は含まれていませんが含まれていますfgets()
。したがって、fgets()
改行を削除するラッパーを使用する必要がある場合があります。
char *fgets_wrapper(char *buffer, size_t buflen, FILE *fp)
{
if (fgets(buffer, buflen, fp) != 0)
{
size_t len = strlen(buffer);
if (len > 0 && buffer[len-1] == '\n')
buffer[len-1] = '\0';
return buffer;
}
return 0;
}
または、より良い:
char *fgets_wrapper(char *buffer, size_t buflen, FILE *fp)
{
if (fgets(buffer, buflen, fp) != 0)
{
buffer[strcspn(buffer, "\n")] = '\0';
return buffer;
}
return 0;
}
また、としてカフェコメントとして指摘paxdiabloで、彼の答えにショーfgets()
あなたはデータがライン上に残されている可能性があります。私のラッパーコードは、そのデータを次回読み取るために残します。必要に応じて、簡単に変更して残りのデータ行を取得することができます。
if (len > 0 && buffer[len-1] == '\n')
buffer[len-1] = '\0';
else
{
int ch;
while ((ch = getc(fp)) != EOF && ch != '\n')
;
}
残りの問題は、3つの異なる結果の状態(EOFまたはエラー、行が切り捨てられていない、および部分的に行が読み込まれたがデータが切り捨てられた)を報告する方法です。
この問題はgets()
、バッファがどこで終了しているかわからないため、端から楽に踏みつけ、美しく手入れされたメモリレイアウトに大混乱を引き起こし、バッファが割り当てられている場合、リターンスタック(スタックオーバーフロー)を台無しにすることが多いため、発生しません。スタック、またはバッファが動的に割り当てられている場合は制御情報を踏みつけ、バッファが静的に割り当てられている場合は他の貴重なグローバル(またはモジュール)変数にデータをコピーします。これらはどれも良いアイデアではありません。「未定義の動作」というフレーズの典型です。
また、以下を含むさまざまな機能のより安全な代替手段を提供するTR 24731-1(C標準委員会からの技術レポート)もありますgets()
。
§6.5.4.1
gets_s
関数あらすじ
#define __STDC_WANT_LIB_EXT1__ 1 #include <stdio.h> char *gets_s(char *s, rsize_t n);
ランタイム制約
s
nullポインタであってはなりません。n
ゼロに等しくなることも、RSIZE_MAXより大きくなることもありません。n-1
からの文字の読み取り中に、改行文字、ファイルの終わり、または読み取りエラーが発生しますstdin
。25)3ランタイム制約違反がある場合は
s[0]
、null文字に設定され、stdin
改行文字が読み取られるまで、またはファイルの終わりまたは読み取りエラーが発生するまで、文字が読み取られて破棄されます。説明
4
gets_s
関数は、で指定さn
れたストリームから、で指定された文字数より最大で1少ない文字列を、で指定さstdin
れた配列に読み取りますs
。改行文字(破棄される)の後、またはファイルの終わりの後、追加の文字は読み取られません。破棄された改行文字は、読み取られた文字数にはカウントされません。ヌル文字は、配列に読み込まれた最後の文字の直後に書き込まれます。5ファイルの終わりが検出され、配列に文字が読み込まれていない場合、または操作中に読み取りエラーが発生した場合
s[0]
は、null文字に設定され、の他の要素はs
未指定の値を取ります。推奨プラクティス
6この
fgets
関数により、適切に作成されたプログラムは、結果配列に格納するには長すぎる入力行を安全に処理できます。一般に、これには、呼び出し元がfgets
結果配列内の改行文字の有無に注意を払う必要があります。のfgets
代わりに(改行文字に基づく必要な処理とともに)の 使用を検討してくださいgets_s
。25)
gets_s
とは異なり、機能は、gets
それを格納するためのバッファをオーバーフローするための入力ラインの実行時制約違反となります。とは異なりfgets
、gets_s
は、入力行とへの正常な呼び出しとの1対1の関係を維持しますgets_s
。を使用gets
するプログラムは、そのような関係を期待しています。
Microsoft Visual Studioコンパイラーは、TR 24731-1標準への近似を実装していますが、Microsoftによって実装されたシグニチャーとTRのシグニチャーの間には違いがあります。
C11標準であるISO / IEC 9899-2011には、ライブラリのオプション部分として、付録KにTR24731が含まれています。残念ながら、Unixライクなシステムではめったに実装されていません。
getline()
— POSIXPOSIX 2008は、gets()
calledの安全な代替手段も提供しますgetline()
。それはラインのためのスペースを動的に割り当てます、従ってあなたはそれを解放する必要があることになります。したがって、行の長さの制限がなくなります。また、読み取られたデータの長さ、または-1
(ではなくEOF
)データの長さも返します。つまり、入力のnullバイトを確実に処理できます。と呼ばれる「独自の1文字の区切り文字を選択する」バリエーションもありgetdelim()
ます。これは、たとえばfind -print0
、ファイル名の末尾がASCII NUL '\0'
文字でマークされている場所からの出力を処理する場合に役立ちます。
fgets()
、fgets_wrapper()
バージョンによっては、次の入力関数によって読み取られるように、長すぎる行の末尾部分が入力バッファーに残されることにも注意してください。多くの場合、これらの文字の読み取りと破棄が必要になります。
getline()
とそのrelative getdelim()
であり、コマンドによって読み取られた「行」の長さを返し、行全体を格納できるように必要に応じてスペースを割り当てます。サイズが数ギガバイトの単一行のJSONファイルになる場合でも、問題が発生する可能性があります。あなたはそのすべての記憶を買う余裕がありますか?(私たちはそれでいる間そして、我々は持つことができますstrcpy()
し、strcat()
最後にヌルバイトへのポインタを返すバリアントを等?)
fgets()
は、ファイルにnullバイトが含まれている場合、nullバイトの後から行末(またはEOF)までのデータ量がわからないことです。 strlen()
データのnullバイトまでしか報告できません。その後、それは当て推量であるため、ほぼ間違いなく間違っています。
gets()
存在を聞いたことを忘れてください。」私がこれをするとき、私は再びそれに遭遇し、ここに戻ってきます。投票を獲得するためにstackoverflowをハッキングしていますか?
なぜならgets
、標準入力からバイトを取得してそれらをどこかに置く間、はいかなる種類のチェックも行わないからです。簡単な例:
char array1[] = "12345";
char array2[] = "67890";
gets(array1);
さて、まず第一に、あなたはあなたが望む何文字を入力することが許されます、gets
それは気にしないでしょう。次に、それらを配置した配列のサイズを超えるバイト数(この場合はarray1
)は、メモリにあるものをすべてgets
書き込みます。前の例では、これは、"abcdefghijklmnopqrts"
多分、予期せずに入力した場合、それもarray2
また何でも上書きすることを意味します。
この関数は、一貫した入力を想定しているため、安全ではありません。絶対に使用しないでください!
gets
完全に使用不能にすることは、それがかかること、配列の長さ/カウントパラメータを持っていないということです。そこにあれば、それはもう1つの通常のC標準関数になります。
gets
が、なぜ標準のfgetsバリアントが、改行が入力の一部として望まれないユースケースほど便利にならないのですか?
gets
は、その名前が示すように、から文字列を取得するように設計されていますがstdin
、サイズパラメータを持たない理由は、Cの精神によるものである可能性があります。プログラマを信頼してください。この関数はC11で削除され、指定された置換gets_s
では入力バッファーのサイズが使用されます。fgets
しかし、私はその部分についての手がかりはありません。
gets
、ある特定の長さにわたって物理的に行を送信することができなかったハードウェアラインバッファI / Oシステムを使用していて、プログラムの予定寿命があった場合です。ハードウェアの寿命よりも短かった。その場合、ハードウェアが127バイトを超える長さの行を送信できない場合、gets
128バイトのバッファーに入れるのは妥当かもしれませんが、小さい入力を期待するときに短いバッファーを指定できることの利点は、費用。
gets
とstrcat
、安全には限り収まるよう受け入れます。
gets
バッファオーバーフローを停止する方法がないため、使用しないでください。ユーザーがバッファーに収まりきらないほど多くのデータを入力すると、おそらく破損またはさらに悪い結果になります。
実際、ISOは実際にC標準から削除 するステップを踏んでいgets
ます(C99では非推奨でしたが、C11以降)。これは、下位互換性の評価の高さを考えると、その機能がどれほど悪かったかを示すものです。
ユーザーから読み取られる文字を制限できるためfgets
、stdin
ファイルハンドルで関数を使用するのが正しい方法です。
しかし、これには次のような問題もあります。
そのために、キャリアのある時点でほぼすべてのCコーダーが、より便利なラッパーfgets
も作成します。これが私のものです:
#include <stdio.h>
#include <string.h>
#define OK 0
#define NO_INPUT 1
#define TOO_LONG 2
static int getLine (char *prmpt, char *buff, size_t sz) {
int ch, extra;
// Get line with buffer overrun protection.
if (prmpt != NULL) {
printf ("%s", prmpt);
fflush (stdout);
}
if (fgets (buff, sz, stdin) == NULL)
return NO_INPUT;
// If it was too long, there'll be no newline. In that case, we flush
// to end of line so that excess doesn't affect the next call.
if (buff[strlen(buff)-1] != '\n') {
extra = 0;
while (((ch = getchar()) != '\n') && (ch != EOF))
extra = 1;
return (extra == 1) ? TOO_LONG : OK;
}
// Otherwise remove newline and give string back to caller.
buff[strlen(buff)-1] = '\0';
return OK;
}
いくつかのテストコードで:
// Test program for getLine().
int main (void) {
int rc;
char buff[10];
rc = getLine ("Enter string> ", buff, sizeof(buff));
if (rc == NO_INPUT) {
printf ("No input\n");
return 1;
}
if (rc == TOO_LONG) {
printf ("Input too long\n");
return 1;
}
printf ("OK [%s]\n", buff);
return 0;
}
fgets
バッファーオーバーフローを防ぐという点で同じ保護を提供しますが、何が起こったかについて呼び出し元に通知し、次の入力操作に影響を与えないように余分な文字を消去します。
自由に使ってください。私は、「やりたいことをやりたい」というライセンスの下でリリースします:-)
gets()
、セクション7.19.7.7で定義されている場合、またはセクション7.26.9将来のライブラリの指示とのサブセクションで明示的に非推奨になりませんでした<stdio.h>
。それが危険であることについての脚注さえありません。(それを言って、私は「:1999 / Cor.3:これは、9899 ISO / IECで廃止予定の2007年の(E)参照)」で答えによってゆうハオ。)しかし、C11は、標準からそれを削除しなかった-とない時間の前に!
int getLine (char *prmpt, char *buff, size_t sz) { ... if (fgets (buff, sz, stdin) == NULL)
の変換を非表示にsize_t
します。 の奇妙な値をキャッチします。int
sz
sz > INT_MAX || sz < 2
sz
if (buff[strlen(buff)-1] != '\n') {
入力された悪意のあるユーザーの最初の文字が埋め込まれたnull文字レンダリングbuff[strlen(buff)-1]
UBである可能性があるため、ハッカーのエクスプロイトです。 while (((ch = getchar())...
ユーザーがnull文字を入力すると問題が発生します。
私は中に、最近読んへのUSENETのポストcomp.lang.c
、gets()
標準から取り除かなっています。うん
委員会がget()をドラフトから削除することも(結局のところ全会一致で)投票したことを知ってうれしいです。
gcc -std=c2012 -pedantic ...
gets()を使用してコンパイルした場合は通過しません。(-std
パラメータを作成したところです)
C11(ISO / IEC 9899:201x)ではgets()
削除されました。(ISO / IEC 9899:1999 / Cor.3:2007(E)では非推奨)
に加えてfgets()
、C11は新しい安全な代替手段を導入しますgets_s()
。
C11 K.3.5.4.1
gets_s
関数#define __STDC_WANT_LIB_EXT1__ 1 #include <stdio.h> char *gets_s(char *s, rsize_t n);
ただし、「推奨されるプラクティス」セクションでfgets()
は、まだ推奨されています。
この
fgets
関数により、適切に作成されたプログラムは、結果の配列に格納するには長すぎる入力行を安全に処理できます。一般に、これには、呼び出し元がfgets
結果配列内の改行文字の有無に注意を払う必要があります。のfgets
代わりに(改行文字に基づく必要な処理とともに)の 使用を検討してくださいgets_s
。
gets()
ユーザーがプロンプトに入力しすぎるとプログラムがクラッシュする可能性があるため、危険です。使用可能なメモリの終わりを検出できないため、目的に対して小さすぎる量のメモリを割り当てると、セグメンテーション違反とクラッシュが発生する可能性があります。ユーザーが人の名前を示すプロンプトに1000文字を入力することはほとんどありませんが、プログラマーとして、プログラムを完全なものにする必要があります。(また、ユーザーが大量のデータを送信してシステムプログラムをクラッシュさせる可能性がある場合は、セキュリティ上のリスクとなる可能性があります)。
fgets()
標準入力バッファーから取り出す文字数を指定できるので、変数がオーバーランすることはありません。
Cの機能は危険で、非常にコストのかかる間違いです。Tony Hoareは、彼の講演「Null References:The Billion Dollar Mistake」で具体的に言及するために、それを選別しています。
http://www.infoq.com/presentations/Null-References-The-Billion-Dollar-Mistake-Tony-Hoare
1時間は一見の価値がありますが、彼のコメントでは、30分以降の具体的なコメントを見ると、約39分で批判されます。
これが全体の話に対するあなたの食欲を刺激することを願っています。これは、言語におけるより正確な正当性の証明がどのように必要であるか、そして言語デザイナーがプログラマーではなく彼らの言語の間違いに対して非難されるべきかに注意を向けます。これが、悪意のある言語の設計者が「プログラマーの自由」を装ってプログラマーに非難を強いる全体の疑わしい理由だったようです。
gets()
Buffer_overflow_attack