抑制警告:使用`mktemp'是危险的

12

我该如何抑制gcc链接器的以下警告:

警告:使用'mktemp'很危险,最好使用'mkstemp'

我知道使用mkstemp()更好,但由于某些原因,我必须使用mktemp()函数。


2
使用mkstemp而不是mktemp。 - Jason Coco
4
为什么你必须使用mktemp? - Jason Coco
编译Bash时出现此警告 https://www.gnu.org/software/bash/ - Scott Stensland
同时编译最近的glibc也会生成它 :)。 - pevik
6个回答

10

我猜您需要这个路径,因为您将其传递给一个只接受路径名作为参数而不是文件描述符或FILE指针的库。如果是这样,您可以使用mkdtemp创建一个临时目录,并将文件放置在其中,实际名称并不重要,因为由于目录已经独特,所以路径已经是唯一的。


9
您仍然可以使用mkstemp函数:int fd = mkstemp(template); 调用后,模板将被实际文件名替换。您将拥有文件描述符和文件路径。 - Jason Coco
@Jason Coco,你可以考虑把那个变成一个答案,这样就可以得到赞同了;-) - lothar
@Jason Coco,啊,好的,那听起来很合理:)。我同意Lothar的看法,把它做成一个答案。 - quinmars

4

如果您必须使用 mktemp,那么除了从 libc.so.6 中删除使用 mktemp 的部分之外,您无法做任何事情来抑制该警告。

为什么您必须使用 mktemp


4

有两件事情:

  • mktemp 不是标准函数。
  • 警告是作为 .gnu.warning.mktemp 部分实现的特殊警告。

如果确实需要向磁盘写入,请使用本地操作系统 API。或者使用建议的 mkstemp() 函数。


mktemp()是(或曾经是)一个标准函数,例如在POSIX.1-2001中。但无论如何都不要使用它。 - Robert Siemer

2

使用mkstemp

int fd = mkstemp(template);

调用此函数后,template将被实际文件名替换。您将获得文件描述符和文件路径。


1

如果您正在静态链接运行时,则另一个选择是编写自己的mktemp版本在对象文件中。 链接器应该优先使用您的版本而不是运行时版本。

编辑:感谢Jason Coco指出我对mktemp及其相关内容的重大误解。现在这个问题比较容易解决。由于链接器将首选对象文件中的版本,因此您只需要根据mkstemp编写mktemp即可。

唯一的困难在于清除mkstemp返回给您的文件描述符并使所有内容都线程安全。 如果您可以限制需要多少临时文件,可以使用描述符的静态数组和atexit注册函数进行清理。 如果不行,只需改用链接列表。


1
@D:向mkstemp或mktemp提供常量字符串是错误的,因为库调用将尝试更改该字符串,从而导致总线故障。 - Jason Coco

1

mktemp经常被错误使用,当一个人想要创建一个临时名称而不实际创建一个tmp文件时,就像mkstemp会做的那样。也许你想将这样的名称传递给sem_openshm_open,并且你非常清楚O_EXCL标志。有很多可能的用途,当你想要创建一个具有随机名称的对象,并且该对象不是一个tmp文件。

然而,即使在这种情况下,真的不应该使用mktemp。这是因为它检查生成的名称是否已存在文件,如果存在这样的文件,它会生成另一个名称,依此类推,在一个循环中。这真的不是你想要的,特别是如果你最终不打算创建这样一个文件。

所以最好的做法是根据您的特定需求编写自己的实现,而不是试图消除mktemp警告。我只是从glibc源代码中提取了mktemp生成器代码,并添加了%P修饰符处理,该修饰符将pid添加到模板中: https://github.com/dosemu2/dosemu2/blob/devel/src/base/misc/utilities.c#L1103 您可以将该代码用作示例,或者编写自己的代码。
在执行此类技巧时,只需遵循基本的注意事项规则即可:
  • 在对象名称中添加pid,除了随机字符之外。这样可以避免与具有相同固定部分模板的另一个程序实例发生冲突。
  • 创建对象时使用O_EXCL,以避免可能的恶意尝试让您的程序打开不应该打开的内容。
  • 如果独占创建失败并出现EEXIST错误,则可能存在一个被阻塞的对象(您在名称中有pid,并且您知道您的pid尚未创建它),因此您可以将其取消链接并重试独占创建。如果再次创建失败,则可能正在进行一些恶意操作,因此您可以选择退出。
  • 尽快取消链接对象,而不是在程序退出时。在打开此类对象(信号量、共享内存等)后,取消链接不会阻止通过已获得的文件描述符使用它们。如果要fork使用这些对象的子进程,在大多数情况下,只需在父进程中打开它们,然后立即取消链接。 子进程可以通过继承的文件描述符使用它们,而无需重新打开。

我相信上述建议足以使您自己的mktemp类似函数的使用安全可靠。但这只是我的个人意见。


所有都是好的观点,但在我看来,即使所有这些也不一定是“安全”的。将进程PID添加到临时名称中并不能使其安全 - 恶意行为者很容易获取PID。如果存在名称冲突,则删除对象似乎也容易被利用以进行拒绝服务攻击。做到这一点是困难的,这也是我不喜欢shm_open()sem_open()语义的原因之一 - 这些函数容易受到基于TOCTOU的竞态条件和基于文件名的漏洞利用的影响。 - Andrew Henle
所有都是好的观点,但在我看来,即使所有这些也不一定是“安全”的。将进程PID添加到临时名称中并不能使其安全 - 恶意行为者很容易获取PID。如果存在名称冲突,则删除对象似乎也容易被利用以进行拒绝服务攻击。做到这一点是困难的,这也是我不喜欢shm_open()sem_open()语义的原因之一 - 这些函数容易受到基于TOCTOU的竞争条件和基于文件名的攻击的影响。 - undefined
1
将进程PID添加到临时名称中并不能使其安全 - 这只是为了避免与自己的程序的另一个实例发生冲突,这是谜题中的重要一环。使其安全的是O_EXCL,以及建议永远不要在子进程中重新打开它,而是使用继承的文件描述符。 如果您遵循所有建议,那么名称冲突是不可能的,除非出现了一个被遗弃的对象和恶意创建的对象。 - stsp
我的关注主要集中在安全方面,尤其是滥用删除功能的可能性。我尚未找到一种利用该功能进行恶意操作的方法,即使只是一种令人讨厌的小型拒绝服务攻击,但我可能没有那些恶意软件编写者的创造邪恶的能力。对于在第一个评论中没有表达清楚,我表示歉意。 - Andrew Henle
如果您正在创建一些相当私密的资源,并且只打算通过fork()与子进程共享,而且如果对象名称中包含pid,那么删除操作是完全无害的,因为它只意味着该对象变成了孤立对象,而pid(也在对象名称中)被重新使用。始终处理孤立对象是很重要的,任何方案都必须考虑到可能存在孤立对象的情况。 - stsp

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接