Stack Overflowで、一部のC関数が「時代遅れ」または「回避すべき」であることを読みました。このような機能の例とその理由を教えてください。
これらの機能に代わるものは何ですか?
それらを安全に使用できますか?
Stack Overflowで、一部のC関数が「時代遅れ」または「回避すべき」であることを読みました。このような機能の例とその理由を教えてください。
これらの機能に代わるものは何ですか?
それらを安全に使用できますか?
strncpy()
の一般的な置き換えとしては使用しませんstrcpy()
。また、考えられるstrncat()
最も直感的でないインターフェースを備えているため、使用しません。長さパラメーターが指定するものを知っていますか?
回答:
安全でない非推奨の関数
このようなの完全な例は、宛先バッファーの大きさを通知する方法がないため、gets()です。したがって、gets()を使用して入力を読み取るプログラムには、バッファオーバーフローの脆弱性があります。同様の理由で、1が使用すべきはstrncpyを()の代わりにstrcpyの()とstrncat()の代わりにstrcatは() 。
さらに、一時ファイルの上書きに関する潜在的なセキュリティの問題によるtmpfile()およびmktemp()関数が含まれる例がいくつかあり、これらはより安全なmkstemp()関数に置き換えられています。
非再入可能
他の例としては、のgethostbyaddr()とのgethostbyname() (従って、スレッドセーフであることが保証されないと、)非リエントラントであり、リエントラントに取って代わられたのgetaddrinfo()とfreeaddrinfoを() 。
ここでパターンに気づくかもしれません...セキュリティの欠如(おそらく、安全に実装するために十分な情報を署名に含めないことによる)または非再入のいずれかが非推奨の一般的な原因です。
古く、
移植できない他のいくつかの機能は、機能を複製し、他のバリアントほど移植性がないため、単に非推奨になります。たとえば、bzero()は非推奨になり、memset()が採用されました。
スレッドの安全
性と再入可能性投稿でスレッドの安全性と再入可能性について質問しました。若干の違いがあります。共有された変更可能な状態を使用しない場合、関数は再入可能です。したがって、たとえば、必要なすべての情報が関数に渡され、必要なバッファーも(関数へのすべての呼び出しで共有されるのではなく)関数に渡される場合、それは再入可能です。これは、独立したパラメーターを使用することにより、異なるスレッドが誤って状態を共有するリスクがないことを意味します。再入可能性は、スレッドの安全性よりも強力な保証です。関数は、複数のスレッドで同時に使用できる場合、スレッドセーフです。次の場合、関数はスレッドセーフです。
一般に、Single UNIX SpecificationではとIEEE 1003.1(つまり「POSIX」)では、再入可能であることが保証されていない関数は、スレッドセーフであることが保証されていません。したがって、言い換えると、再入可能であることが保証されている関数のみが、マルチスレッドアプリケーションで(外部ロックなしで)移植可能に使用できます。ただし、これは、これらの標準の実装が非再入可能関数をスレッドセーフにすることを選択できないことを意味するものではありません。たとえば、Linuxはスレッドセーフ性の保証(シングルUNIX仕様の保証を超える)を追加するために、非再入可能関数に同期を頻繁に追加します。
文字列(および一般にメモリバッファ)
列文字列/配列にいくつかの根本的な欠陥があるかどうかも尋ねました。これは事実であると主張する人もいますが、私はそうではないと主張します。言語には根本的な欠陥はありません。CおよびC ++では、配列の長さ/容量を個別に渡す必要があります(他の言語のように「.length」プロパティではありません)。これは欠点ではありません。CおよびC ++開発者は、必要に応じて長さをパラメーターとして渡すだけで、正しいコードを作成できます。問題は、この情報を必要とするいくつかのAPIがパラメーターとして指定できなかったことです。または、いくつかのMAX_BUFFER_SIZE定数が使用されると想定しました。そのようなAPIは廃止され、配列/バッファ/文字列のサイズを指定できる代替APIに置き換えられました。
Scanf(最後の質問に答えて)
個人的には、C ++ iostreamsライブラリ(std :: cin、std :: cout、<<および>>演算子、std :: getline、std :: istringstream、std :: ostringstreamを使用していますなど)、私は通常それを扱いません。ただし、純粋なCを使用せざるを得なかった場合、個人的にはfgetc()またはgetchar()をstrtol()、strtoul()などと組み合わせて使用し、手動で構文解析します。可変引数またはフォーマット文字列。そうは言っても、私の知る限りでは、[f] scanf()、[f] printf()には問題はありません。など、フォーマット文字列を自分で作成する限り、任意のフォーマット文字列を渡したり、ユーザー入力をフォーマット文字列として使用したりすることはできません、定義されたフォーマットマクロを使用し<inttypes.h>(該当する場合)。(注、snprintf() sprintf()の代わりにを使用する必要がありますが、フォーマット文字列を使用するのではなく、宛先バッファのサイズを指定できないことに関係しています)。C ++では、boost :: formatは可変引数なしでprintfのようなフォーマットを提供することも指摘しておきます。
strncpy
一般的には避けるべきです。ほとんどのプログラマーが想定しているようには動作しません。終了を保証するものではなく(バッファーオーバーランにつながる)、短い文字列を埋めます(場合によってはパフォーマンスを低下させる可能性があります)。
strncpy()
なく、さらに悪いことでもありませんstrncat()
。
繰り返しになりますが、人々はマントラのように、 "n"バージョンのstr関数は安全なバージョンであるという馬鹿げた主張を繰り返しています。
それが彼らが意図したものであるなら、彼らは常に文字列をnullで終了するでしょう。
関数の "n"バージョンは、固定長フィールド(初期のファイルシステムのディレクトリエントリなど)で使用するために記述されており、文字列がフィールドに入力されない場合にのみNULターミネータが必要です。これは、関数が奇妙な副作用を持っている理由でもあります。たとえば、置換として使用しただけでは無意味に非効率的です。たとえば、strncpy()を使用します。
s2が指す配列がnバイトより短い文字列の場合、nバイトがすべて書き込まれるまで、s1が指す配列のコピーにnullバイトが追加されます。
ファイル名を処理するために割り当てられるバッファは通常4kバイトであるため、パフォーマンスが大幅に低下する可能性があります。
「想定される」安全なバージョンが必要な場合は、常に文字列を終了し、副作用のないstrlルーチン(strlcpy、strlcatなど)を取得するか、独自に作成します。ただし、これらは文字列を黙って切り捨てることができるため、実際には安全ではないことに注意してください。これは、実際のプログラムでは、最善の方法ではありません。これで大丈夫な場合もありますが、壊滅的な結果につながる可能性のある多くの状況もあります(たとえば、処方箋を印刷するなど)。
strncpy()
、間違っていstrncat()
ます。 strncat()
は固定長フィールドで使用するように設計されていません-実際には、strcat()
連結される文字数を制限するように設計されています。strcat()
複数の連結を行うときにバッファに残っているスペースを追跡することにより、これを「安全」として使用するのは非常に簡単です。strcpy()
「宛先」の最初の文字を'\0'
前に設定することにより、「安全」としてさらに簡単に使用できます。それを呼び出す)。 strncat()
常に宛先文字列を終了し、余分なを書き込みません'\0'
。
strncat()
、宛先が常にヌル終了するとは限らないこと、および固定長フィールドで使用するように設計されているという事実は変わりません。どちらも間違っています。
strncat()
ソース文字列の長さに関係なく正しく機能しますが、機能しstrcat()
ません。ここでの問題strlcat()
は、それが標準のC関数ではないことです。
ここでのいくつかの回答は、strncat()
overの使用を提案していstrcat()
ます。strncat()
(およびstrncpy()
)も回避することをお勧めします。正しく使用できず、バグにつながる問題があります。
strncat()
は、宛先バッファーのサイズではなく、宛先にコピーできる最大文字数に関連しています(ただし、正確には3番目の点を参照してください)。これはstrncat()
、特に複数のアイテムが宛先に連結される場合は、本来よりも使いにくくなります。s1
は、配列が指すことができる最大文字数はstrlen(s1)+n+1
"です。strncat( s1, s2, n)
strncpy()
また、直感的な方法で使用しようとするとバグが発生する可能性がある問題もあります。宛先がnullで終了していることを保証するものではありません。あなたがドロップすることによってそのコーナーケースを具体的に処理することを確認する必要があることを確認する必要があることを確認するには'\0'
バッファーの最後の場所に(少なくとも特定の状況で)を。
私は、OpenBSDのようなもの使用をお勧めしたいstrlcat()
としstrlcpy()
(私は何人かの人々がこれらの機能を嫌うことを知っているのに、私は、彼らがより安全に使用する方がはるかに簡単だと信じてstrncat()
/ strncpy()
)。
ここではトッド・ミラーとテオ・デ・ラートが持つ問題について言っていたものの少しだstrncat()
とはstrncpy()
:
strncpy()
およびのstrncat()
安全なバージョンとして使用した場合に発生する問題がいくつかstrcpy()
ありstrcat()
ます。どちらの関数も、NUL終了と長さパラメーターを、経験豊富なプログラマーさえも混乱させるさまざまな非直感的な方法で処理します。また、切り捨てが発生したことを簡単に検出する方法もありません。...これらすべての問題の中で、長さパラメータによって引き起こされる混乱と、NUL終了の関連する問題が最も重要です。セキュリティホールの可能性についてOpenBSDソースツリーを監査したところ、strncpy()
およびの悪用が蔓延していstrncat()
ます。これらのすべてが悪用可能なセキュリティホールをもたらすわけではありませんが、安全な文字列操作の使用strncpy()
とstrncat()
安全性に関するルールが広く誤解されていることを明らかにしました。
OpenBSDのセキュリティ監査は、これらの機能のバグが「蔓延している」ことを発見しました。とは異なりgets()
、これらの関数は安全に使用できますが、実際にはインターフェースがわかりにくく、直感的ではなく、正しく使用することが難しいため、多くの問題があります。マイクロソフトも分析を行ったことがわかっています(ただし、彼らが公開したデータの量はわかりません)。その結果、禁止されました(または、少なくとも非常に推奨されていません-「禁止」は絶対的ではないかもしれません)。使用strncat()
とstrncpy()
(他の機能の中で)。
詳細情報のあるリンク:
memmove()
。(まあ、あなたmemcpy()
は文字列が独立している通常のケースで使用できます。)
strncat()
常に宛先文字列を終了します。
strncat()
ます。ただし、strncpy()
他のいくつかの問題があるには適切です。 strncat()
以下のための合理的な代替品ですstrcat()
が、strncpy()
のための合理的な代替ではありませんstrcpy()
。
strncat()
常にnullで終了するとは限らない私の主張について100%正しいです。私はの行動混乱してstrncat()
とstrncpy()
( - ...重要な点で異なって、彼らは同様の行動を暗示する名前を持っていますが、実際の振る舞いでは、彼らがしている機能は、回避するための別の理由)ビット。これを修正し、追加情報を追加するために、私の回答を修正しました。
char str[N] = ""; strncat(str, "long string", sizeof(str));
は、Nが十分に大きくない場合のバッファオーバーフローです。このstrncat()
関数は誤用しやすいです。使用しないでください。strncat()
安全に使用できれば、memmove()
またはmemcpy()
代わりに使用できたはずです(そしてそれらの方が効率的です)。
setjmp.h
setjmp()
。とともにlongjmp()
、これらの関数は使用するのが非常に危険であると広く認識されています。スパゲッティプログラミングにつながり、未定義の動作のさまざまな形式が発生し、スタックに格納されている値に影響を与えるなど、プログラム環境で意図しない副作用を引き起こす可能性があります。参考文献:MISRA-C:2012ルール21.4、CERT C MSC22-C。longjmp()
。を参照してくださいsetjmp()
。stdio.h
gets()
。この関数は、設計上安全ではなかったため、C言語から(C11に従って)削除されました。この関数には、C99で廃止されたフラグが既に設定されています。fgets()
代わりに使用してください。参照:ISO 9899:2011 K.3.5.4.1、ノート404も参照してください。stdlib.h
atoi()
関数のファミリー。これらにはエラー処理はありませんが、エラーが発生するたびに未定義の動作を呼び出します。関数のstrtol()
ファミリーで置き換えることができる完全に不要な関数。参照:MISRA-C:2012ルール21.7。string.h
strncat()
。しばしば誤用される厄介なインターフェースを持っています。それは主に余分な機能です。以下の備考も参照してくださいstrncpy()
。strncpy()
。この関数の意図は、の安全なバージョンになることではありませんでしたstrcpy()
。その唯一の目的は、常にUnixシステムで古代の文字列形式を処理することであり、標準ライブラリに含まれることは既知の誤りです。この関数は、文字列をnullで終了せずに残す可能性があり、プログラマが誤って使用することが多いため、危険です。参考資料:strlcpyとstrlcatはなぜ安全でないと考えられているのですか?。assert.h
assert()
。オーバーヘッドが付属しているので、通常、量産コードでは使用しないでください。エラーを表示するが、必ずしもプログラム全体を閉じるわけではない、アプリケーション固有のエラーハンドラーを使用することをお勧めします。signal.h
signal()
。参照:MISRA-C:2012ルール21.5、CERT C SIG32-C。stdarg.h
va_arg()
関数のファミリー。Cプログラムに可変長関数が存在することは、ほとんどの場合、プログラム設計が不十分であることを示しています。非常に具体的な要件がない限り、避けてください。stdio.h
一般に、このライブラリ全体は、不適切に定義された動作や不十分な型安全性の多くのケースが伴うため、量産コードには推奨されません。
fflush()
。出力ストリームに使用するのに最適です。入力ストリームに使用された場合、未定義の動作を呼び出します。gets_s()
。gets()
C11の境界チェックインターフェイスに含まれているの安全なバージョン。使用することをお勧めしますfgets()
C標準の推奨に従って、代わりをお勧めします。参照:ISO 9899:2011 K.3.5.4.1。printf()
関数のファミリー。未定義の動作が多く、型の安全性が低いリソース負荷の高い関数。sprintf()
脆弱性もあります。これらの関数は、製品コードでは避けてください。参照:MISRA-C:2012ルール21.6。scanf()
関数のファミリー。についての備考を参照してくださいprintf()
。また、- scanf()
は、正しく使用しないとバッファオーバーランに対して脆弱です。fgets()
可能な場合は使用することをお勧めします。参照:CERT C INT05-C、MISRA-C:2012ルール21.6。tmpfile()
関数のファミリー。さまざまな脆弱性の問題が付属しています。参照:CERT C FIO21-C。stdlib.h
malloc()
関数のファミリー。ホストされたシステムで使用するのにまったく問題ありませんが、C90のよく知られている問題に注意して、結果をキャストしないでください。のmalloc()
関数ファミリは、自立型アプリケーションでは使用しないでください。参照:MISRA-C:2012ルール21.3。
またrealloc()
、古いポインタをの結果で上書きする場合は危険ですrealloc()
。関数が失敗した場合、リークを作成します。
system()
。オーバーヘッドが多く、移植可能ですが、多くの場合、代わりにシステム固有のAPI関数を使用することをお勧めします。さまざまな不十分に定義された動作が付属しています。参照:CERT C ENV33-C。
string.h
strcat()
。の備考を参照してくださいstrcpy()
。strcpy()
。コピーするデータのサイズが不明であるか、コピー先のバッファーよりも大きい場合を除き、使用するのはまったく問題ありません。着信データサイズのチェックが行われない場合、バッファオーバーランが発生している可能性があります。これはstrcpy()
それ自体の障害ではありませんが、呼び出し側のアプリケーションの障害です。これはstrcpy()
安全ではありません。ほとんどがMicrosoftによって作成された神話です。strtok()
。呼び出し元の文字列を変更し、内部状態変数を使用するため、マルチスレッド環境では安全ではなくなります。static_assert()
はassert()
、の代わりに使用することをお勧めします。また、sprintf()
ほとんどの場合、交換snprintf()
することができ、少し安全です。
strtok()
関数Aで使用するというstrtok()
ことは、(a)Aが使用している間は関数が使用する他の関数を呼び出すことができないこと、および(b)Aを呼び出すstrtok()
ときにAを呼び出す関数が使用できないことを意味します。strtok()
呼び出しチェーンを汚染します。strtok()
他のユーザーがライブラリコードを呼び出せないようにするために使用することを文書化する必要があるため、ライブラリコードでは安全に使用できませんstrtok()
。
strncpy
は、ゼロで終了する文字列または少なくとも宛先と同じサイズのゼロで埋められたバッファから取得したデータでゼロで埋められた文字列バッファを書き込むときに使用する適切な関数です。ゼロが埋め込まれたバッファはそれほど一般的ではありませんが、正確に不明瞭ではありません。
一部の人々は、とを支持して、それを避けるべきであるstrcpy
とstrcat
主張します。私の意見では、これはやや主観的なものです。strncpy
strncat
ユーザー入力を処理するときは、必ずこれらを回避する必要があります-間違いなくここにあります。
コードでは「遠く」は、ユーザーからちょうど知っているバッファが十分な長され、strcpy
およびstrcat
コンピューティングので、もう少し効率的かもしれn
彼らのいとこに渡すことは余計かもしれません。
strlcat
とstrlcpy
、「n」のバージョンは、コピー先の文字列のNULL終端を保証するものではありませんので、。
strncpy()
正確にn
バイトを書き込みます。Danと同様、安全なバージョンを使用することがIMOの最良の選択です。
strncat()
正しく安全に使用するのは難しい(そして避ける必要がある)可能性があるという事実が話題になっています。
避ける
strtok
マルチスレッドプログラムでは、スレッドセーフではありません。gets
バッファオーバーフローを引き起こす可能性があるためstrtok()
スレッドの安全性を少し超えています-使用中にコードが呼び出す可能性のある関数が使用strtok()
しないことを確認しない限り、単一のスレッドのプログラムでも安全ではありません(または、それらがstrtok()
正しく機能しない場合があります)あなたの下から)。実際、マルチスレッドプラットフォームをターゲットとするほとんどのコンパイラは、静的データにstrtok()
スレッドローカルストレージを使用することで、スレッドに関する限りの潜在的な問題をstrtok()
処理します。しかし、それでも、(同じスレッドで)使用しているときに他の関数がそれを使用する問題は解決しません。
strtok()
それがとても最も良い代替品は、周りのバッファ値を維持し、それを渡す必要、で動作するように正確に1つのバッファを保持していることである。
strcspn
あなたが必要とするほとんどのことを行います-次のトークンセパレータを見つけます。あなたはstrtok
それで正気なバリアントを再実装することができます。
のstrncpy()
汎用的な置き換えではない、もう一度追加する価値があるでしょう。strcpy()
名前が示唆している可能性のあるがあります。これは、ヌルターミネータを必要としない固定長フィールド用に設計されています(元々はUNIXディレクトリエントリで使用するために設計されましたが、暗号化キーフィールドなどに役立ちます)。
ただし、strncat()
の代わりとして使用するのは簡単ですstrcpy()
。
if (dest_size > 0)
{
dest[0] = '\0';
strncat(dest, source, dest_size - 1);
}
(if
テストが明らかにdest_size
ゼロでないことがわかっている一般的なケースでは明らかにテストを落とすことができます)。
Microsoftの禁止されているAPIのリストもご覧ください。これらは、誤用されてセキュリティの問題が発生することが多いため、Microsoftのコードから禁止されているAPI(すでにここにリストされている多くのものを含む)です。
あなたはそれらのすべてに同意することはできませんが、それらはすべて検討する価値があります。それらの誤用が多くのセキュリティバグを引き起こしたとき、彼らはリストにAPIを追加します。
sprintfを忘れないでください-それは多くの問題の原因です。代替として、snprintfにはコードの移植性を損なう可能性のある異なる実装が時々あるため、これは真実です。
linux:http : //linux.die.net/man/3/snprintf
ウィンドウ:http : //msdn.microsoft.com/en-us/library/2ts7cx93%28VS.71%29.aspx
ケース1(linux)の場合、戻り値はバッファー全体を格納するために必要なデータの量です(指定されたバッファーのサイズよりも小さい場合、出力は切り捨てられます)。
ケース2(ウィンドウ)では、出力が切り捨てられる場合、戻り値は負の数になります。
一般に、次の機能以外は避けてください。
バッファオーバーフローセーフ(多くの機能はすでにここで説明されています)
スレッドセーフ/再入不可(たとえば、strtok)
各関数のマニュアルでは、安全、同期、非同期、スレッド、バッファ、バグなどのキーワードを検索する必要があります
sprintf
いくつかのケースで安全に何とか:sprintf(buffer,"%10s",input);
場合(10にコピーされたNBバイトに制限buffer
されchar buffer[11]
、データが切り捨てられる羽目になる可能性がある場合、それは安全でもあります。
scanf
安全に使用するのは非常に難しいです。をscanf
適切に使用することでバッファオーバーフローを回避できますが、要求された型に適合しない数値を読み取るときの未定義の動作に対して脆弱です。ほとんどの場合、fgets
自己解析する(使用に続いてsscanf
、strchr
等)のより良いオプションです。
しかしscanf
、「いつも避けて」とは言いません。 scanf
その用途があります。例として、char
10バイト長の配列でユーザー入力を読み取りたいとしましょう。後続の改行がある場合は削除します。ユーザーが改行の前に9文字を超える文字を入力した場合、最初の9文字をバッファーに保管し、次の改行まですべてを破棄します。できるよ:
char buf[10];
scanf("%9[^\n]%*[^\n]", buf));
getchar();
このイディオムに慣れると、以下よりも短くなり、いくつかの点でよりクリーンになります。
char buf[10];
if (fgets(buf, sizeof buf, stdin) != NULL) {
char *nl;
if ((nl = strrchr(buf, '\n')) == NULL) {
int c;
while ((c = getchar()) != EOF && c != '\n') {
;
}
} else {
*nl = 0;
}
}
すべての文字列コピー/移動シナリオ-strcat()、strncat()、strcpy()、strncpy()など- いくつかの単純なヒューリスティックが適用されている場合、状況ははるかに良くなります(より安全):
1.常にNULフィルデータを追加する前のバッファ。
2.文字バッファーをマクロ定数を使用して[SIZE + 1]として宣言します。
たとえば、次の場合:
#define BUFSIZE 10
char Buffer[BUFSIZE+1] = { 0x00 }; /* The compiler NUL-fills the rest */
次のようなコードを使用できます。
memset(Buffer,0x00,sizeof(Buffer));
strncpy(Buffer,BUFSIZE,"12345678901234567890");
比較的安全。コンパイル時にバッファを初期化した場合でも、memset()はstrncpy()の前に表示される必要があります。これは、関数が呼び出される前に他のコードがどのガベージに配置されたかがわからないためです。strncpy()はコピーされたデータを「1234567890」に切り捨て、 NULで終了しません。ただし、BUFSIZEではなく、sizeof(Buffer)のバッファ全体をすでにNULで満たしているため、BUFSIZEを使用して書き込みを制限する限り、最終的に「範囲外」でNULを終了することが保証されます。 sizeof(Buffer)の代わりに定数。
バッファとBUFSIZEも同様にsnprintf()で正常に機能します。
memset(Buffer,0x00,sizeof(Buffer));
if(snprintf(Buffer,BUFIZE,"Data: %s","Too much data") > BUFSIZE) {
/* Do some error-handling */
} /* If using MFC, you need if(... < 0), instead */
snprintf()は特にBUFIZE-1文字のみを書き込み、その後にNULを書き込む場合でも、これは安全に機能します。したがって、バッファの最後にある余分なNULバイトを「無駄に」します...かなり小さなメモリコストで、バッファオーバーフローと文字列の終端されていない状態の両方を防ぎます。
strcat()とstrncat()への私の呼び出しはもっと難しいです:それらを使用しないでください。strcat()を安全に使用することは困難であり、strncat()のAPIは直感に反しているため、適切に使用するために必要な労力は利点を打ち消します。私は次のドロップインを提案します:
#define strncat(target,source,bufsize) snprintf(target,source,"%s%s",target,source)
strcat()ドロップインを作成するのは魅力的ですが、良い考えではありません。
#define strcat(target,source) snprintf(target,sizeof(target),"%s%s",target,source)
targetがポインターである可能性があるためです(したがって、sizeof()は必要な情報を返しません)。コード内のstrcat()のインスタンスに対する適切な「ユニバーサル」ソリューションはありません。
「strFunc()対応」プログラマからよく遭遇する問題は、strlen()を使用してバッファオーバーフローから保護する試みです。内容がNULで終了することが保証されている場合は問題ありません。そうしないと、保護しようとしている「問題のある」コードに到達する前に、strlen()自体がバッファオーバーランエラー(通常、セグメンテーション違反または他のコアダンプ状況を引き起こす)を引き起こす可能性があります。
atoiはスレッドセーフではありません。代わりに、manページの推奨に従ってstrtolを使用します。
strtol()
スレッドセーフであり、atoi()
そうでない理由はありません。
man atoi
(ただし、あるはずです)。