如何克服 snprintf 在不同类 UNIX 操作系统中不一致的行为?

发布于 2024-07-04 17:22:32 字数 490 浏览 12 评论 0原文

根据手册页,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 计算机上打印的字符数的正数。 希望这个解释没问题。

Per man pages, snprintf is returning number of bytes written from glibc version 2.2 onwards. But on lower versions of libc2.2 and HP-UX, it returns a positive integer, which could lead to a buffer overflow.

How can one overcome this and write portable code?

Edit : For want of more clarity

This code is working perfectly in 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;
    }

It returns the length of the string (10) on Linux. But the same code is returning a positive number that is greater than the number of characters printed on HP-UX machine. Hope this explanation is fine.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

灼疼热情 2024-07-11 17:22:33

您可以创建一个 snprintf 包装器,当缓冲区中没有足够的空间时,它为每种情况返回 -1。

有关更多文档,请参阅手册页。 它还有一个威胁所有案件的例子。

  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;
      }
   }

you could create a snprintf wrapper that returns -1 for each case when there is not enough space in the buffer.

See the man page for more docs. It has also an example which threats all the cases.

  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;
      }
   }
痴意少年 2024-07-11 17:22:33

您是否考虑过 printf 的可移植实现? 我不久前也在寻找一个,最后选定了三重奏。

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

Have you considered a portable implementation of printf? I looked for one a little while ago and settled on trio.

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

零度℉ 2024-07-11 17:22:33

你的问题还是不清楚。 链接到的手册页这样说道:

函数 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
}

Your question is still unclear. The man page linked to speaks thus:

The functions snprintf() and vsnprintf() do not write more than size bytes (including
the trailing '\0'). If the output was truncated due to this limit then the return value is the number of characters (not including the trailing '\0') which would have been written to the final string if enough space had been available. Thus, a return value of size or more means that the output was truncated.

So, if you want to know if your output was truncated:

int ret = snprintf(cmd, cmdLen + 1, ". %s%s", myVar1, myVar2 ) == -1)
if(ret == -1 || ret > cmdLen)
{
    //output was truncated
}
else
{
    //everything is groovy
}
梦归所梦 2024-07-11 17:22:33

*printf 可移植性存在很多问题,实际上您可能想遵循以下三种路径之一:

  1. 需要符合 c99 的 *printf,因为 9 年对任何人来说都足够了,只是说平台已损坏

  2. 有一个 my_snprintf() 和一堆 #ifdef,用于您想要支持所有调用下面的 vsnprintf() 的特定平台(了解最低公分母就是您所拥有的)。

  3. 只需在代码中携带一份 vsnprintf() 的副本,对于简单的用例,它实际上漂亮简单,对于其他人,您可能想查看vstr,然后您就会免费获取客户格式化程序。

...正如其他人建议的那样,您可以对 -1 情况进行黑客合并 #1 和 #2,但这是有风险的,因为 c99 *printf 在某些条件下可以/确实返回 -1。

就我个人而言,我建议使用 像 ustr 这样的字符串库,它可以为您提供简单的解决方法并免费为您提供托管字符串。 如果您真的很关心,可以与 vstr 结合使用。

There are a whole host of problems with *printf portability, and realistically you probably want to follow one of three paths:

  1. Require a c99 compliant *printf, because 9 years should be enough for anyone, and just say the platform is broken otherwise.

  2. Have a my_snprintf() with a bunch of #ifdef's for the specific platforms you want to support all calling the vsnprintf() underneath (understanding the lowest common denominator is what you have).

  3. Just carry around a copy of vsnprintf() with your code, for simple usecases it's actually pretty simple and for others you'd probably want to look at vstr and you'll get customer formatters for free.

...as other people have suggested you can do a hack merging #1 and #2, just for the -1 case, but that is risky due to the fact that c99 *printf can/does return -1 in certain conditions.

Personally I'd recommend just going with a string library like ustr, which does the simple workarounds for you and gives you managed strings for free. If you really care you can combine with vstr.

九厘米的零° 2024-07-11 17:22:33

我找到了一种可移植的方法来预测和/或限制 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;   
}

I have found one portable way to predict and/or limit the number of characters returned by sprintf and related functions, but it's inefficient and many consider it inelegant.

What you do is create a temporary file with tmpfile(), fprintf() to that (which reliably returns the number of bytes written), then rewind and read all or part of the text into a buffer.

Example:

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;   
}
倾听心声的旋律 2024-07-11 17:22:33

请使用更高级的 asprintf() 来代替。

它是一个 GNU 扩展,但如果它本身不可用,则值得将其复制到目标平台。

Use the much superior asprintf() instead.

It's a GNU extension, but it's worth copying to the target platform in the event that it's not natively available.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文