56

これは、この質問と 1 つの特定の回答に対するコメントに触発されたstrncpyもので、C の非常に安全な文字列処理関数ではなく、 に達するまでゼロをパディングすることを知りましたn

具体的には、Rを引用するには..

strncpy は null で終了せず、宛先バッファーの残り全体を null で埋めますが、これは非常に時間の無駄です。独自の null パディングを追加することで前者を回避できますが、後者は回避できません。「安全な文字列処理」関数として使用することを意図したものではなく、Unix ディレクトリ テーブルおよびデータベース ファイルの固定サイズ フィールドを操作するためのものです。snprintf(dest, n, "%s", src) は、標準 C で唯一正しい「安全な strcpy」ですが、かなり遅くなる可能性があります。ちなみに、切り捨て自体が重大なバグになる可能性があり、場合によっては特権の昇格や DoS につながる可能性があるため、問題が発生したときに出力を切り捨てる「安全な」文字列関数をスローしても、それを「安全」または「」にする方法にはなりません。安全"。その代わり、

そしてジョナサン・レフラーから

strncat() は、strncpy() よりもインターフェイスがさらにわかりにくいことに注意してください。長さの引数とは正確には何ですか? strncpy() などに基づいて期待するものではないため、strncpy() よりもエラーが発生しやすくなります。文字列をコピーする場合、 memmove() だけが必要であるという強い議論があるという意見が増えています。なぜなら、常にすべてのサイズを事前に知っており、事前に十分なスペースがあることを確認しているからです。strcpy()、strcat()、strncpy()、strncat()、memcpy() のいずれよりも memmove() を使用してください。

だから、私は明らかにC標準ライブラリに少し錆びています。したがって、私は次の質問をしたいと思います。

どの C 標準ライブラリ関数が不適切に使用されているか、またはセキュリティの問題/コードの欠陥/非効率性を引き起こす/引き起こす可能性のある方法で使用されていますか?

客観性を保つために、回答にはいくつかの基準があります。

  • 可能であれば、問題の機能の背後にある設計上の理由、つまり意図された目的を挙げてください。
  • コードが現在置かれている誤用を強調してください。
  • その誤用が問題につながる可能性がある理由を述べてください。それは明らかなはずですが、それはソフトな答えを防ぎます。

避けてください:

  • 関数の命名規則に関する議論 (これが明らかに混乱を引き起こす場合を除く)。
  • 「私は y よりも x の方が好きです」 - 好みは問題ありませんが、私は実際の予期しない副作用とそれらを防ぐ方法に興味があります。

これは主観的なものである可能性が高く、明確な答えがないため、すぐにコミュニティ wiki にフラグを立てます。

私もC99に従って働いています。

4

14 に答える 14

34

どの C 標準ライブラリ関数が不適切に使用されているか、またはセキュリティの問題/コードの欠陥/非効率性を引き起こす/引き起こす可能性のある方法で使用されていますか?

私は明白に行くつもりです:

char *gets(char *s);

それを適切に使用することは単に不可能であるという顕著な特殊性があります。

于 2011-01-03T21:50:02.557 に答える
25

この関数のよくある落とし穴は、strtok()実際には区切り文字を'\0'.

また、strtok()文字列全体がトークン化されるまで、後続の呼び出しで使用されます。一部のライブラリ実装では、 の内部ステータスがグローバル変数に格納されます。これは、 が複数のスレッドから同時に呼び出されるとstrtok()、厄介な事態を引き起こす可能性があります。strtok()

CERT C Secure Coding Standardには、質問されたこれらの落とし穴の多くがリストされています。

于 2011-01-03T22:23:44.937 に答える
21

ほとんどの場合、は使用しないでください (これは、およびatoi()にも当てはまります)。atof()atol()atoll()

これは、これらの関数が範囲外エラーをまったく検出しないためです。標準では、「結果の値を表現できない場合、動作は未定義です」と単純に述べられています。. したがって、安全に使用できるのは、入力が確実に範囲内にあることを証明できる場合のみです (たとえば、長さが 4 以下の文字列を に渡す場合、atoi()範囲外になることはありません)。

代わりに、一連strtol()の関数のいずれかを使用してください。

于 2011-01-03T23:44:14.753 に答える
11

質問をより広い意味でのインターフェースに拡張しましょう。

errno:

技術的には、変数、マクロ、暗黙の関数呼び出しなど、それが何であるかさえ明確ではありませんか? 最近のシステムでの実際には、関数呼び出しに変換されてスレッド固有のエラー状態になるマクロがほとんどです。それは悪です:

  • 呼び出し元が値にアクセスして「エラー」をチェックするためのオーバーヘッドが発生する可能性があるためです (これは単なる例外的なイベントである可能性があります)。
  • ライブラリ呼び出しを行う前に、呼び出し元がこの「変数」をクリアすることをいくつかの場所で課すことさえあるからです
  • ライブラリのグローバル状態を設定することにより、単純なエラーリターンを実装するためです。

今後の標準ではerrno、もう少しストレートな定義が得られますが、これらの醜さは残ります

于 2011-01-03T22:50:44.210 に答える
6

多くの場合、strtok_r があります。

realloc の場合、古いポインターを使用する必要がある場合、別の変数を使用することはそれほど難しくありません。プログラムが割り当てエラーで失敗した場合、多くの場合、古いポインターをクリーンアップする必要はありません。

于 2011-01-03T23:02:53.653 に答える
5

私はこのリストのかなり上位printfに入れます。scanfフォーマット指定子を正確に取得する必要があるという事実は、これらの関数を使用するのが難しく、非常に間違いやすくなっています。また、データの読み取り時にバッファ オーバーランを回避することも非常に困難です。さらに、善意のプログラマーがクライアント指定の文字列を printf の最初の引数として指定すると、おそらく「printf フォーマット文字列の脆弱性」が無数のセキュリティ ホールを引き起こし、何年も後にスタックが破壊され、セキュリティが侵害されたことがわかります。

于 2011-01-03T21:38:26.500 に答える
4

malloc一般家庭はどうですか?私が見た大規模で寿命の長いプログラムの大部分は、あたかも自由であるかのように、いたるところで動的メモリ割り当てを使用しています。もちろん、リアルタイム開発者はこれが神話であることを知っており、動的割り当てを不注意に使用すると、メモリ使用量が壊滅的に増加したり、アドレス空間が断片化されたりして、メモリが枯渇する可能性があります。

マシンレベルのポインターを持たない一部の高水準言語では、動的割り当てはそれほど悪くありません。これらのオブジェクトへの参照を最新に保つことができる限り、プログラムの存続期間中に実装がオブジェクトを移動してメモリを最適化できるからです。非従来型の C 実装でもこれを行うことができますが、詳細を検討することは自明ではなく、すべてのポインター逆参照で非常に大きなコストが発生し、ポインターがかなり大きくなるため、実用的な目的では、C では不可能です。

私の疑惑は、正しい解決策は通常、長寿命のプログラムが通常どおりに小さなルーチン割り当てを実行することmallocですが、大規模で長寿命のデータ構造を再構築して定期的に置き換えて断片化と戦うことができる形式に保つことです。アプリケーション内のデータの 1 つの大きな単位を構成する多数の構造を含む大きなmallocブロック (ブラウザーでの Web ページ全体の表示など)、または固定サイズのメモリ内キャッシュまたはメモリ マップ ファイルを含むディスク上.

于 2011-01-05T15:48:41.037 に答える
4

私のベテノワールの 1 つは、strtok()再入不可であり、処理中の文字列をハックして、分離する各トークンの最後に NUL を挿入するためです。これには多くの問題があります。それは問題の解決策として悲惨なほどしばしば宣伝されますが、多くの場合それ自体が問題です。常にではありません - 安全に使用できます。ただし、気をつけている場合に限ります。gets()安全に使用できない注目すべき例外を除いて、同じことがほとんどの関数に当てはまります。

于 2011-01-03T22:23:05.967 に答える
4

についてはすでに 1 つの回答がありますreallocが、私は別の見方をしています。realloc多くの場合、人々が意味のあるときに書いているのを見てきましたfreemalloc- 言い換えれば、新しいデータを保存する前にサイズを変更する必要があるゴミでいっぱいのバッファがある場合です。memcpyこれはもちろん、上書きされようとしているゴミの潜在的に大きなキャッシュ スラッシングにつながります。

成長するデータで正しく使用された場合 (O(n^2)オブジェクトを size まで成長させる最悪の場合のパフォーマンスを回避する方法n、つまり、スペースがなくなったときにバッファーを線形ではなく幾何学的に成長させる方法で) は、単に独自の newを実行するよりreallocも利点が疑わしいものになります。とサイクル。これを内部的に回避できる唯一の方法は、ヒープの一番上にある単一のオブジェクトを操作している場合です。mallocmemcpyfreerealloc

新しいオブジェクトを でゼロ フィルするのが好きな場合、それが新しい部分をゼロ フィルcallocしないことを忘れがちです。realloc

最後に、 のもう 1 つの一般的な使用法はrealloc、必要以上に割り当ててから、割り当てられたオブジェクトのサイズを必要なサイズに縮小することです。しかし、これは実際にはチャンクをサイズごとに厳密に分離する実装では有害 (追加の割り当てとmemcpy) になる可能性があり、他の場合には断片化が増加する可能性があります (既存の小さなオブジェクトを使用する代わりに、新しい小さなオブジェクトを格納するために大きな空きチャンクの一部を分割することによって)。フリーチャンク)。

悪い習慣をrealloc 助長すると言えるかどうかはわかりませんが、気をつけたい機能です。

于 2011-01-04T03:41:37.400 に答える
4

gmtime()やなど、グローバル状態を操作する関数localtime()。これらの関数は、複数のスレッドで安全に使用することはできません。

編集: rand()見た目と同じカテゴリにあります。少なくとも、スレッド セーフの保証はありません。私の Linux システムでは、man ページで再入不可であり、スレッド セーフではないことが警告されています。

于 2011-01-03T21:50:23.003 に答える
2

まったく別の方法で、私はあるatan()ときの利点を本当に理解したことがありませんatan2()。違いは、atan2()2つの引数を取り、-π..+πの範囲内の任意の角度を返すことです。さらに、ゼロ除算エラーと精度エラーの損失(非常に小さい数を非常に大きい数で除算する、またはその逆)を回避します。対照的に、この関数は-π/ 2 ..+π/2の範囲の値のみを返すため、事前に除算を行う必要があります(除算がなくても使用できるatan()シナリオは思い出せません。atan()単にアークタンジェントのテーブルを生成すること)。単純な値が与えられたときに除数として1.0を提供するatan2()ことは、限界を押し上げることではありません。

于 2011-01-03T22:44:35.887 に答える
2

これらは実際には関連していないため、別の答えrand

  • それは不特定のランダムな品質です
  • それは再入可能ではありません
于 2011-01-03T22:52:15.583 に答える
1

この関数の一部は、一部のグローバル状態を変更しています。(Windows の場合) この状態は単一のスレッドごとに共有されます。予期しない結果が生じる可能性があります。たとえば、randすべてのスレッドで を最初に呼び出すと同じ結果が得られ、疑似乱数にするために注意が必要ですが、(デバッグ目的で) 決定論的です。

于 2011-01-03T22:53:14.390 に答える
-2

basename()スレッドセーフではありdirname()ません。

于 2011-01-03T22:49:31.563 に答える