web-dev-qa-db-ja.com

strtok()が安全でないと見なされるのはなぜですか?

strtokのどの機能(バッファオーバーフローに関して)は安全で、注意が必要ですか?

私にとって少し奇妙なのはstrtok_s(「安全」です)Visual C++には追加の「コンテキスト」パラメーターがありますが、他の点では同じであるように見えます...同じですか、それとも実際に違うのですか?

29
user541686

このドキュメント のstrtok_sセクションによると:

6.7.3.1 strtok_s関数strtok_s関数は、strtok関数の2つの問題を修正します。

  1. 新しいパラメーターs1maxは、strtok_sがトークン化される文字列の外部に格納するのを防ぎます。 (トークンに分割される文字列は、strtok_sが文字列にnull文字を格納するため、関数の入力と出力の両方です。)
  2. 新しいパラメーターptrは、strtokが再入できないようにする静的な内部状態を排除します(1.1.12節)。 (ISO/IEC 9899関数wcstokとISO/IEC 9945(POSIX)関数strtok_rは、この問題を同じように修正します。)
24
Heisenbug

危険なことは何もありません。それがどのように機能し、どのように使用するかを理解する必要があるだけです。コードと単体テストを作成した後、valgrindを使用して単体テストを再実行し、メモリの範囲内で動作していることを確認するには、数分しかかかりません。 manページはそれをすべて言います:

[〜#〜]バグ[〜#〜]

これらの機能を使用するときは注意してください。それらを使用する場合は、次のことに注意してください。

  • これらの関数は、最初の引数を変更します。
  • これらの関数は定数文字列では使用できません。
  • 区切り文字のIDは失われます。
  • strtok()関数は解析中に静的バッファーを使用するため、スレッドセーフではありません。これが重要な場合は、strtok_r()を使用してください。
12
Bob

strtokは、スレッドローカルストレージを使用して呼び出し間でその状態を保存するため、Visual C++では安全です(他の場所では安全ではありません)。その他の場所では、グローバル変数はstrtok()の状態を保存するために使用されます。

ただし、strtokがスレッドセーフであるVC++でも、それはまだ少し奇妙です-同じスレッド内の異なる文字列で同時にstrtok()を使用することはできません。たとえば、これはうまく機能しません:

     token = strtok( string, seps );
     while(token)
     {
        printf("token=%s\n", token)
        token2 = strtok(string2, seps);
        while(token2)  
        {
            printf("token2=%s", token2);
            token2 = strtok( NULL, seps );
        }
        token = strtok( NULL, seps );
     }

それがうまく機能しない理由-すべてのスレッドで単一の状態のみをスレッドローカルストレージに保存でき、ここでは最初の文字列と2番目の文字列の2つの状態が必要になります。したがって、strtokはVC++でスレッドセーフですが、再入可能ではありません。

Strtok_s(または他のすべての場所でstrtok_r)が提供するもの-明示的な状態、およびそのstrtokで再入可能になります。

5

適切にnullで終了する文字列がない場合。バッファオーバーフローが発生します。また、(これは私が難しい方法で学んだことです)strtokは内部文字列を気にしていないようです。 I.E. 「hello」/「world」があると「hello」/「world」が解析され、「hello/world」は「hello world」に解析されます。 /で分割され、括弧内にあるという事実を無視していることに注意してください。

0
Suroot