3

man ページによると、snprintf は glibc バージョン 2.2 以降で書き込まれたバイト数を返しています。ただし、libc2.2 および HP-UX の下位バージョンでは、正の整数が返されるため、バッファ オーバーフローが発生する可能性があります。

どうすればこれを克服し、移植可能なコードを書けるでしょうか?

編集:より明確にするために

このコードはlib 2.3で完全に機能しています

    if ( snprintf( cmd, cmdLen + 1, ". %s%s", myVar1, myVar2 )  != cmdLen )
    {
        fprintf( stderr, "\nError: Unable to  copy bmake command!!!");
        returnCode = ERR_COPY_FILENAME_FAILED;
    }

Linux では文字列の長さ (10) を返します。しかし、同じコードが、HP-UX マシンで出力される文字数よりも大きい正の数を返しています。この説明でいいと思います。

4

6 に答える 6

4

バッファに十分なスペースがない場合、それぞれの場合に-1を返すsnprintfラッパーを作成できます。

その他のドキュメントについては、 manページを参照してください。また、すべてのケースを脅かす例もあります。

  while (1) {
      /* Try to print in the allocated space. */
      va_start(ap, fmt);
      n = vsnprintf (p, size, fmt, ap);
      va_end(ap);
      /* If that worked, return the string. */
      if (n > -1 && n < size)
         return p;
      /* Else try again with more space. */
      if (n > -1)    /* glibc 2.1 */
         size = n+1; /* precisely what is needed */
      else           /* glibc 2.0 */
         size *= 2;  /* twice the old size */
      if ((np = realloc (p, size)) == NULL) {
         free(p);
         return NULL;
      } else {
         p = np;
      }
   }
于 2008-09-19T10:32:47.963 に答える
2

printf の移植可能な実装を検討しましたか? 少し前に探して、トリオに落ち着きました。

http://daniel.haxx.se/projects/trio/

于 2008-09-19T11:46:51.287 に答える
1

あなたの質問はまだ不明です。にリンクされているマニュアルページは次のように語っています。

関数 snprintf() および vsnprintf()は size バイト(末尾の '\0' を含む) を超えて書き込みません。この制限のために出力が切り捨てられた場合、戻り値は、十分なスペースが利用可能であった場合に最終文字列に書き込まれる文字数 (末尾の '\0' を含まない)です。したがって、サイズ以上の戻り値は、出力が切り捨てられたことを意味します。

したがって、出力が切り捨てられたかどうかを知りたい場合は、次のようにします。

int ret = snprintf(cmd, cmdLen + 1, ". %s%s", myVar1, myVar2 ) == -1)
if(ret == -1 || ret > cmdLen)
{
    //output was truncated
}
else
{
    //everything is groovy
}
于 2008-09-19T14:53:25.783 に答える
1

*printf の移植性にはさまざまな問題があり、現実的には次の 3 つの方法のいずれかを使用することをお勧めします。

  1. c99 準拠の *printf が必要です。9 年あれば誰でも十分なはずであり、それ以外の場合はプラットフォームが壊れていると言えます。

  2. サポートしたい特定のプラットフォーム用の #ifdef の束を含む my_snprintf() を用意します。

  3. コードと一緒に vsnprintf() のコピーを持ち歩くだけです。単純なユースケースの場合、実際には非常にシンプルです。また、 vstrを見たい場合は、顧客のフォーマッタを無料で入手できます。

...他の人が示唆しているように、-1 の場合にのみ #1 と #2 をマージするハックを行うことができますが、c99 *printf は特定の条件で -1 を返すことができる/返すため、危険です。

個人的には、 ustrのような文字列ライブラリを使用することをお勧めします。これは、簡単な回避策を実行し、管理された文字列を無料で提供します。本当に気にするなら、vstrと組み合わせることができます。

于 2008-09-19T16:21:17.147 に答える
0

sprintfおよび関連する関数によって返される文字数を予測および/または制限するための移植可能な方法を見つけましたが、それは非効率的であり、多くの人がそれをエレガントでないと考えています。

tmpfile()、fprintf()を使用して一時ファイル(書き込まれたバイト数を確実に返す)を作成し、巻き戻してテキストの全部または一部をバッファーに読み込みます。

例:

int my_snprintf(char *buf, size_t n, const char *fmt, ...)
{
    va_list va;
    int nchars;
    FILE *tf = tmpfile();

    va_start(va, n);
    nchars = vfprintf(tf, fmt, va);
    if (nchars >= (int) n)
        nchars = (int) n - 1;
    va_end(va);
    memset(buf, 0, 1 + (size_t) nchars);

    if (nchars > 0)
    {
        rewind(tf);
        fread(buf, 1, (size_t) nchars, tf);
    }

    fclose(tf);

    return nchars;   
}
于 2008-09-19T10:18:44.237 に答える
0

代わりに、はるかに優れた asprintf() を使用してください。

これは GNU 拡張機能ですが、ネイティブで利用できない場合は、ターゲット プラットフォームにコピーする価値があります。

于 2008-09-19T14:58:43.243 に答える