这个strncmp的使用是否包含越界读取?

13

Fortify表示这是一个越界读取:

if (strncmp("test string", "less than 32 char", 32) == 0)
{
...
}

它指出该函数从小于32个字符的范围外读取数据。

如果strncmp超出32个字符并且第二个字符串少于32个字符,是否确实存在此问题?


2
Fortify是错误的。至少对于一个正确的、符合标准的strncmp()实现来说是这样的。 - alk
1
除非我非常错误,否则 strncmp() 只需要比较你的示例字符串中每个字符串的一个 char。我赞同这是误报。 - EOF
5
@Olaf: 我达到了那些要求。谢谢。 - Engineer2021
1
你确定Fortify没有对你没有发布的代码进行投诉吗? - chqrlie
3
字符串函数的实现可以进行性能优化,以处理自然对齐的4或8字节数据块。对齐保证不会触发任何多余的内存异常(每个访问完全在一个页面内),因此是“安全的”,但这种实现技术可能会引起检查越界访问的工具(即超出特定数据对象限制的范围)的抱怨。我的看法是,在as-if原则下,这样的优化是被允许的,并且符合C标准。 - njuffa
显示剩余5条评论
3个回答

13

出于性能方面的考虑,标准字符串函数的实现通常会以自然对齐寄存器宽度块的形式处理数据。这可能会导致读取超出源数据对象末尾的访问,但是对齐保证了代码在内存异常方面的行为与天真实现完全相同。每个宽度访问都包含在单个页面中,并且不会触及任何不会被字节方式实现所触及的页面。

我认为这样的实现受到C语言的"as-if"规则的覆盖,也就是说,它们的行为与抽象的功能规范一样。

这样优化的实现示例可以参考OpenSolaris的SPARC v8架构下的strcmp()。这是我十五年前编写的代码,还有其他性能优化的字符串函数。

然而,各种内存检查工具会抱怨此类代码,因为使用它可能会导致超出分配的数据对象的限制,即使由于设计原因,越界读取访问是无害的。


很遗憾,“现代”编译器的作者们对于允许程序员使用这些技术来执行标准库中未包含的任务表现出了很少的兴趣。有时,当给定简单处理顺序项的代码时,编译器可能能够找到等效或更好的优化,但在许多情况下,编译器并不会发现这样的优化。不幸的是,它们可能会发现基于UB的“优化”,这可能会随机破坏使用这些技术的用户代码。 - supercat

10

TL;DR - strncmp()将继续比较字符串的元素,直到任一字符串的结尾或32个元素(字符),以较少的那个为准。

任何字符串都是以空字符结尾的,在遇到空字符终止符后,不会进行进一步的比较。您的代码是安全的。

引用C11, 章节§7.24.4.4 (我强调的)

int strncmp(const char *s1, const char *s2, size_t n);

strncmp函数比较从指向s1的数组开始到指向s2的数组的不超过n个字符(不会比较空字符后面的字符)


6

您的代码完全有效。

要么是编译器生成了错误的目标代码,要么是Fortify报告了一个误报。

我怀疑编译器会生成错误的代码。这将会带来太多问题,并且很快就会被发现和修复。

很可能是Fortify误报了。


1
生成了什么糟糕的目标代码?为什么?是对标准库函数的函数调用吗?这是源代码分析,与编译器或目标代码完全无关。 - user207421

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