【问题标题】:Is it safe to have asprintf use a non-NULL target pointer that is also a input?让 asprintf 使用也是输入的非 NULL 目标指针是否安全?
【发布时间】:2013-03-26 18:46:43
【问题描述】:

tl;dr 可以在不调用临时指针的情况下天真地使用asprintf 进行连接吗?


由 GNU 引入并在其他几个 clib 实现中采用的函数 asprintf 是使用类似方案的 c 中任意连接的诱人解决方案

int i=0;
char *str = strdup(argv[i]);
while (argv[++i]) {
   asprintf(&str,"%s %s",argv[i],str);   // <=== This line
}
asprintf(&str,"%s\n",str);

当包装在 main 和必要的 include 中时,这对我来说运行良好。

但是……

是不是到处都在泄漏内存? Valgrind 说它在我的盒子上。这是一个错误吗?

我面前的手册页说

asprintf() 和 vasprintf() 函数将 *ret 设置为指向 缓冲区足够大以容纳格式化的字符串。这个指针 应该传递给 free(3) 以释放分配的存储空间 不再需要。如果无法分配足够的空间,asprintf() 并且 vasprintf() 将返回 -1 并将 ret 设置为 NULL 指针。

在没有短语 "set *ret to be a pointer to a new buffer [...]" 的情况下,我很想假设该函数使用realloc,就像getline 一样。

可能有什么问题?

使用签名int asprintf(char **ret, const char *format, ...); 表示具体性。

  1. asprintf 运行 realloc 太快了。

    想象一下,我们在一个很远的地方实现了这个函数,以至于我们可以在 realloc(*ret) 取消引用一个别名为原始缓冲区的可变参数之前运行它。该缓冲区已被释放,这在技术上是未定义的行为。这将代表一个错误。

  2. asprintf 在读取缓冲区之前先写入缓冲区。在上面的代码中,我们可以想象函数将argv[1] 的内容复制到*ret 之前的每个va_arg 上的str 参数。我引用的手册页似乎没有排除这种情况。

  3. asprintf 不直接使用free *ret,也不是通过使用realloc。这将避免问题编号 (1),但如果按上述方式使用,则会泄漏内存。再次,该手册页似乎没有排除它。

解决方法

上述所有情况都可以通过替换对asprintf 的单个调用来避免

{
  char *newStr = NULL;
  asprintf(newStr,"%s %s",argv[i],str);
  free(str);
  str = newStr;
}

但这很笨重。

共识实现是否保证第一个代码示例安全正确?

【问题讨论】:

  • 顺便问一下,好问题。 +1。
  • asprintf 不是标准函数,它是 GNU 扩展。 “是不是到处都在泄露内存?” - 当然如此。医生说要释放内存,但你没有。 asprintf 不可能 realloc 它的第一个参数——这是一个输出参数,它的内容不能被假定。
  • @JimBalter Er...是的。这是正确的。认为它们也可以在 Mac OS 现在使用的 BSD 库中找到。后续编辑。
  • 如果你将一个非空指针传递给asprintf,它会被malloc 的调用覆盖。它不使用预先分配的内存。
  • “我很想假设该函数像 getline 一样使用 realloc。” -- 做出这样的假设是完全错误的,因为getline 文档详细说明了如何使用lineptr 参数的值。只需比较文档,很明显asprintf 的工作方式不同......它从不使用*ret 的先前值。

标签: c alias stdio


【解决方案1】:

是不是到处都在泄露内存?

是的,确实如此。

char *str = strdup(argv[i]);

这里,str 包含一个指向 malloc()ated 内存的指针,它应该是 free()d。

asprintf(&str, "%s %s", argv[i], str);

这里,asprintf() 修改了str,使其指向函数本身分配的其他一些内存。现在您只是丢失了指向strdup()ped 字符串的指针,因此发生了泄漏。

【讨论】:

  • 这取决于天气asprintf 使用realloc(例如,getline 使用)。从手册页中不清楚它是否应该。只需在上面引用的段落中添加“新”作品就可以很清楚地说明这一点,但没有它......
  • @dmckee 我能找到的最接近的手册页是 OS X 的手册页。它说:“asprintf()vasprintf() 动态分配带有 malloc(3) 的新字符串。” - 所以在我看来,这意味着调整缓冲区大小涉及调用realloc()
  • 必须是比我的更新的 Mac OS,但这是我要找的词。它是笨重的版本。 ::sigh::
  • "从手册页上看不清楚是否应该这样做。" - 从手册页中可以清楚地看到......你只是在阅读它时做得非常糟糕。 “它是笨拙的版本”——如果你没有毫无意义地将newstr 初始化为NULL,它会稍微不那么笨拙。而free 是一个函数——调用它需要括号。
  • @dmckee 强调一点:ret 是一个 out 参数——*ret 已设置,但它的值从未使用过。您可以从文档中确定这一点,因为...它说 *ret 已设置,并且从不提及使用其值。因此,reallocing 这是不可能的。
猜你喜欢
  • 2019-01-17
  • 2020-12-15
  • 2015-10-14
  • 2017-01-29
  • 2016-09-11
  • 2015-04-18
  • 2023-03-28
  • 2010-10-30
相关资源
最近更新 更多