如何修复代码以避免警告-Wunsafe-buffer-usage

11
在clang 16.0之前,我能够使用-Weveything编译所有的项目而没有问题,修复我的代码并注意到这个选项提供的(有用的)警告。 然而,似乎在版本16中添加了-Wunsafe-buffer-usage(我的系统最近更新到了这个版本),然后在没有得到关于任何指针使用的巨大无用警告列表的情况下,编译任何项目都是不可能的。请参考下面的最小示例:
$ cat main.c
#include <stdio.h>

int main(int argc, char **argv) {
    if (argc != 2) {
        return 0;
    }
    printf("argv[1] = %s\n", argv[1]);
}

$ clang -Weverything main.c
main.c:3:27: warning: 'argv' is an unsafe pointer used for buffer access [-Wunsafe-buffer-usage]
int main(int argc, char **argv) {
                   ~~~~~~~^~~~
main.c:7:30: note: used in buffer access here
    printf("argv[1] = %s\n", argv[1]);
                             ^~~~
1 warning generated.

使用此警告无法合理推理代码。

clang文档如下:

由于-Weverything启用了每个诊断,我们通常不建议使用它。对于大多数项目来说,-Wall -Wextra是更好的选择。使用-Weverything意味着更新编译器更加困难,因为您会暴露于实验诊断,这些诊断可能比默认诊断质量低。如果您确实使用-Weverything,则建议您在添加到Clang的所有新编译器诊断时解决它们,无论是通过修复它们找到的东西还是通过显式禁用其相应的Wno-选项来禁用该诊断。

在这种情况下,我要如何“修复他们找到的一切”?

设置-Wno-unsafe-buffer-usage有效,但这是一个丑陋的解决方案。 它会使Makefile变得杂乱无章,并感觉像是作弊。


3
我认为使用-Weverything进行斗争可能不是你想要开始的战斗。在这种情况下,你可能能够让-Weverything满意,但是你还需要满足更多的警告,其中许多可能需要使你的代码变得更糟以进行“修复”。例如,满足-Wdeclaration-after-statement将要求你在函数顶部而不是需要时声明所有局部变量。 - Brian61354270
3
在我使用-Weverything的地方,我也会用-Wno-xyz变体来对抗一些选项。我使用的一个脚本是这样的:-Weverything -Wno-padded -Wno-vla -Wno-reserved-id-macro -Wno-documentation-unknown-command -Wno-poison-system-directories -Wno-format-nonliteral。我在许多程序中构建格式字符串,因此需要-Wno-format-nonliteral。我在矩阵操作代码中使用VLA(以及其他地方),所以需要-Wno-vla。我使用特性测试宏(用于POSIX和Solaris等系统)来确定如何编译代码,因此-Wno-reserved-id-macro是必要的。_[…继续…]_ - Jonathan Leffler
2
@lucas.mior — 毫无疑问:我禁用警告的选项是根据我的需求而定的,基于我在2022年10月找到的(针对该脚本的最新更新),但原则是一样的 — 禁用你认为无用的警告。我有理由拒绝每个我禁用选项的警告。你有不同的代码、不同的问题,以及拒绝(或接受)输出的不同理由。但要实际有用,你需要调整由“-Weverything”生成的警告。我的Mac使用的是苹果的Clang 14.0;“-Wunsafe-buffer-usage”选项无效。 - Jonathan Leffler
3
如果-Wno-unsafe-buffer-usage感觉像作弊,那么-Weverything是否感觉像一个不合理的障碍呢?正如你引用的摘录所述,-Wall -Wextra被期望成为一个有用的警告集合。 - Toby Speight
3
@lucas.mior 实际上,任何代码库都会触发成千上万个警告... 真扯淡。我负责的每个程序通常都是完全没有警告的。现在,我负责的几十万行C代码只产生了7个警告 - 这仅仅是因为代码仍然在内部使用了一个在最近发布的几周前被标记为弃用的公共API函数。这种程度的正确性需要严格遵守高标准的纪律性。一个“触发成千上万个警告”的代码库是由不守纪律的开发人员以无或非常低的标准编写的。 - Andrew Henle
显示剩余15条评论
2个回答

12

我在godbolt上尝试了多次处理这个特定的警告,即在调用printf之前检查argv != NULL && argv[1] != NULL,但都没有成功。实际上,即使是这个特定的检查也触发了警告。

考虑到文档明确建议不使用-Weverything,如果使用了该选项,则要使用相应的-Wno-选项来处理无用的警告,并且目前还不清楚如何处理这个警告(我找不到关于此选项的文档),添加-Wno-unsafe-buffer-usage可能是最好的选择。

根据LLVM讨论板上的一个链接https://discourse.llvm.org/t/rfc-c-buffer-hardening/65734/85

就目前而言,此警告只会在一个地方触发,那就是(我所知)没有办法绕过的地方:int main(int argc, char const** argv)

在这种情况下似乎无法消除警告。因此,使用-Wno-unsafe-buffer-usage似乎是您唯一的选择。


5
是的,但文档中说“要么修复所有发现的问题,要么明确禁用...”。一个警告的意义在于,即使在程序的核心方面,它所警示的代码也无法修复,这有何意义呢? - user20523087
3
@lucas.mior 我觉得在这种情况下,你可能别无选择,只能忽略它。而且看起来你并不是唯一一个遇到这个选项有问题的人。 - dbush
4
@lucas.mior 关于“一个警告的意义是什么,如果它所警示的代码在程序的核心方面都无法修复”的问题,我认为这是“实验性诊断”的一部分。 - dbush
7
@lucas.mior:关于“一个警告的意义是什么,如果它所警告的代码在程序的核心方面无法修复?”:正如文档所述,-Weverything 中的额外警告是实验性的。这个实验的目的是获取反馈:Clang 维护者测试警告选项以了解其有多大用处以及可能存在的问题。使用 -Weverything 的人应向 Clang 维护者报告问题,包括演示问题的示例代码,以便 Clang 维护者考虑改进。这就是目的。 - Eric Postpischil
2
@yvs2014:那又怎样?关于main函数的argv参数被报告出来,这是可以反馈给Clang开发人员的信息。此外,你链接中的第一项提供了使用-Weverything的另一个原因:手动使用它一次以获取所有诊断报告,自行评估每个问题,修复真正的问题,但在常规构建中不使用它。 - Eric Postpischil
显示剩余8条评论

8

这个警告似乎基本上没有记录(也许这是它最好被禁用的标志)。可用的信息表明,你没有漏掉什么;编译完整的 C 程序时不可能避免触发此警告。如果你希望在编译整个 C 程序(包括 main 函数)时使用 -Weverything 和 -Werror,似乎是必须禁用此警告。

感谢 @yvs2014 在评论中提供的链接:https://discourse.llvm.org/t/rfc-c-buffer-hardening/65734/85

在该讨论线程中建议:(1)该警告实际上是为 C++ 代码设计的,通常不应处理原始指针,而不是用于 C 代码;(2)理论上可以用于使用某些将原始指针使用抽象化的库或 API 的 C 代码;(但在编译该库的实现时绝对不能使用);(3)在编译 main 函数时绝对不能使用。

我对这个设计选择的智慧性产生了质疑,但它就是这样(而且他们非常明确地警告不要使用-Weverything)。


是的,C++再次妨碍了C语言的发展。在您提供的链接中,建议使用安全的API将指针算术操作封装起来,然后使用编译器指令在这些实现中禁用警告。事实上,除非使用其他语言编写代码,否则没有其他方法可以解决这个问题。 - user20523087

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