2009-05-22 24 views
23

私はWindowsにいくつかのコードを移植しており、Microsoftコンパイラ(Visual C++ 8)はstrerror()が危険であると私に伝えています。strerrorを使用できないのはなぜですか?

Microsoftの安全な文字列のすべてに煩わしい要素を置いておけば、廃止予定の機能の一部が危険であることが実際に分かります。しかし、strerror()で何が間違っているのか理解できません。コード(int)を受け取り、対応する文字列、またはそのコードがわからない場合は空の文字列を返します。

危険はどこですか?

Cには良い選択肢がありますか?

C++には良い選択肢がありますか?

[編集]

は、いくつかの良い答えを持っていた、そして今、いくつかの実装は、実際に共通の共有バッファに書き込むのに十分な狂気であり得ることを理解した - シングルスレッド内の再入可能に安全ではない、スレッド間で気にしません! - 私の質問は「なぜ私はそれを使うことができないのですか?そして何が選択肢ですか? 「Cおよび/またはC++にまともな、簡潔な代替案はありますか?」それはスレッドセーフではありませんので、事前

+28

マイクロソフトでは「ISO C標準は卑劣である - 私たちはプラグマで警告またはエラーを無効にしない限り、使用することはできません」と述べているため、使用できません。 memcpy()も呼び出す前にmemcpy()を禁止しました。これは、コピーするバイト数と、それについて考えることができず、ターゲットスペースに十分な空きがあることをバイト数分知っているので、ばかげています。 CまたはC++でチーム作成コードに属していないとします。 –

答えて

18

strerror

おかげで廃止されました。 strerrorは、内部の静的バッファで動作します。これは、他の同時スレッドによって上書きされる可能性があります。 strerror_sと呼ばれるセキュリティ保護されたバリアントを使用する必要があります。

セキュアなバリアントでは、バッファに書き込む前にバッファが十分に大きいことを検証するためにバッファサイズを関数に渡す必要があり、悪意のあるコードを実行する可能性のあるバッファオーバーランを回避します。

+3

Er、質問があった:_それは安全でないと考えられていますか?あなたは私に正確に何も言わなかった。 – JamieH

+10

彼は_did_理由を教えてくれます。内部静的バッファ、つまりスレッド間で共有される静的バッファで動作します。それは危険です。 – nsayer

+1

ああ、そうです。謝罪いたします。今、次の質問 - 世界全体 - は、誰がそれのようにそれを実装するのですか? – JamieH

5

strerror()によって返される文字列は、次回の関数呼び出しで変更される可能性があるため、依存することはできません。以前に返された値は、時代遅れになる可能性があります。特にマルチスレッド環境では、アクセス時に文字列が有効であることを保証することはできません。

この想像:私はMicrosoftの理由は分かりませんがstrerror()の実装に応じて

Thread #1: 
char * error = strerror(1); 
            Thread #2 
            char * error = strerror(2); 
printf(error); 

を、このコードは、エラーコード2について、いないエラーコードのエラーコード1.

+6

どのような仕組みでそれが変わるのですか?おそらく、文字列は静的にCランタイムライブラリの実装の内部で定義されているため、決して変更されません。それとも間違っているのですか?実際には動的に変化するかもしれませんか? – JamieH

+1

あなたはこれをなぜ仮定しますか?はい、それはこのようにしなければならないかもしれませんが、それは完全に異なったものになる可能性があります)。 – beef2k

+3

主な問題は、Posixはスレッドセーフであるためにstrerror()を必要としないことです。代わりにstrerror_r()を使用する必要があります。 Windowsではstrerror_s()を使用します。 –

1

を出力しますstrerrorは非const char *を返すことに注意します。これは、メリー・フランクスターがstrerrorと呼ぶリスクがあることを意味します。

+2

"const char *"の場合、同じメリー・フランクスターが(char *)にキャストして、それ以降のコードを変更しようとするかもしれません;).. "const"として何かを宣言しても、変更できないというわけではありません。コンパイラに最適化のヒントを与えるだけです。 – beef2k

+3

私は、純粋なconst-nessの観点から、これは真実であることに同意しますが、constの欠如は歴史的なものであると強く思っています。ユーザーは、文字列の内容をそのままの形で変更しないように義務付けられています。標準ライブラリの多くの非const-should-be-be部分です。その場合、strerror()の非推奨理由はまだ分かりません。 – JamieH

+1

これをconst char *にして、変更してはいけないと言っても、スレッドセーフなものにするには十分でしょう。*呼び出しを同期してインターリーブしないようにしてください。いずれにしても、プログラマがプログラムをクラッシュさせることを禁じることはできません。彼はまだ配列を作成し、配列境界から書き出すことができます。 Terminateや何かを使って他のスレッドをkillする可能性があります:)戻り値をconst char *にすることは、偶発的な変更に対する効果的な保護です。 –

16

strerrorそれ自体では安全ではありません。スレッドする前の昔は問題ではありませんでした。スレッドでは、2つ以上のスレッドがstrerrorを呼び出して、返されたバッファを未定義の状態のままにすることができます。シングルスレッドのプログラムでは、DLL内のすべてのアプリケーションに共通のメモリのように、libcで奇妙なゲームをプレイしていない限り、strerrorを使用しないでください。これに対処するために

同じ機能に新しいインタフェースがあります:呼び出し側は、バッファ・スペースとバッファサイズを提供することを

int strerror_r(int errnum, char *buf, size_t buflen); 

は注意。これは問題を解決します。シングルスレッドアプリケーションの場合でも、それを使用することもできます。それは少し傷つくことはありません、あなたはそれをより安全な方法でやることに慣れるかもしれません。

注:上記のプロトタイプはXSI仕様です。プラットフォームごと、またはコンパイラオプションまたは#defineのシンボルによって異なる場合があります。 GNUは、例えば、簡潔なラッパーの場合#define

+1

また、strerror_s()を定義するTR24731もあります。 –

+0

「スレッドする前の昔は問題ではありませんでした」それは再入可能ではありませんが、シグナルハンドラから呼び出し可能であると主張したことはありません。 –

+0

問題ありません。シグナルハンドラからは 'longjmp'だけです;) –

2

に応じて、そのまたは独自のバージョンを利用できるように、あなたはのように、STLSoftstlsoft::error_descを使用することができます。

コードを見てみると
std::string errstr = stlsoft::error_desc(errno); 

、それはそうですstrerror()で実装されています。つまり、スレッド内での再入可能性が保証されます(つまり、指定されたステートメント内で複数回使用される場合)が、マルチスレッド問題に対処しません。

彼らは欠陥のための非常に急速なリリースサイクルを動作させるように見えるので、modを要求することができますか?

+0

ありがとうございました。私はそれを試みるかもしれない。 – JamieH

12

いくつかの実装では、共通の共有バッファに実際に書き込むほど狂っているかもしれないことを理解しています。スレッド間での再入可能性はありません。 - 私の質問は「なぜ私はそれを使うことができないのですか?そして何が選択肢ですか? 「Cおよび/またはC++にまともな、簡潔な代替案はありますか?」

POSIXではstrerror_r()を指定し、Windows上であなたは少し異なりますが、同じ目標を持っているstrerror_s()を、使用することができます。私はこれを行う:

#define BAS_PERROR(msg, err_code)\ 
    bas_perror(msg, err_code, __FILE__, __LINE__) 

void bas_perror (const char* msg, int err_code, const char* filename, 
       unsigned long line_number); 


void 
bas_perror (const char* usr_msg, int err_code, const char* filename, 
      unsigned long line_number) 
{ 
    char sys_msg[64]; 

#ifdef _WIN32 
    if (strerror_s(sys_msg, sizeof sys_msg, err_code) != 0) 
    { 
    strncpy(sys_msg, "Unknown error", taille); 
    sys_msg[sizeof sys_msg - 1] = '\0'; 
    } 
#else 
    if (strerror_r(err_code, sys_msg, sizeof sys_msg) != 0) 
    { 
    strncpy(sys_msg, "Unknown error", sizeof sys_msg); 
    sys_msg[sizeof sys_msg - 1] = '\0'; 
    } 
#endif 

    fprintf(stderr, "%s: %s (debug information: file %s, at line %lu)\n", 
      usr_msg, sys_msg, filename, line_number); 
} 

私はPOSIXスレッド機能がerrnoを変更しないので、彼らは代わりにエラーコードを返します。この機能を書きました。したがって、この機能は基本的にはperror()と同じですが、errno以外のエラーコードを指定できるほか、デバッグ情報も表示されます。あなたはあなたのニーズにそれを適応させることができます。

関連する問題