翻译《有关编程、重构及其他的终极问题?》——2.比0大的并不意味着就只是1
标签(空格分隔): 翻译 技术 C/C++
作者:Andrey Karpov
翻译者:顾笑群 - Rafael Gu
校验者:高国栋
最后更新:2016年11月13日
本书背景说明、总目录等介绍,可以跳转到以下链接进行查看:
http://blog.youkuaiyun.com/headman/article/details/53045891欢迎大家转载,但请附上原作者以及翻译者的名字、原文出处,以尊重光荣的劳动者。
2.比0大的并不意味着就是1
在知名的CoreCLR项目中,有如下代码段。这段代码有一个经过PVS-Studio分析诊断后的错误: V698 Expression ‘memcmp(…. == -1’ is incorrect. This function can return not only the value ‘-1’, but any negative value. Consider using ‘memcmp(….) < 0’ instead(译者注:大意是memcmp函数不只返回-1,还返回其他负数).
bool operator()(const GUID &_Key1, const GUID &_Key2) const
{
return memcmp(&_Key1, &_Key2, sizeof(GUID)) == -1;
}
解释
让我们看一下memcmp()函数的描述:
int memcmp(const void *ptr1, const void *ptr2, size_t num);
比较指针ptr1的内存块的前num个字和喝指针ptr2的内存块的前num个字节,如果相同就返回0;如果不同就根据那个更大返回非0值。
返回值:
- < 0 - 在两个内存块中第一个不相同的字节的偏移数,且ptr1的小于ptr2的(按照unsigned char来比较)。
- == 0 - 两个内存块内容完全一致。
- > 0 - 在两个内存块中第一个不相同的字节的偏移数,且ptr1的大于ptr2的(按照unsigned char来比较)。
注意,如果两个内存块不一致,这个函数的返回值是大于或者小于0,非等于1或-1。所以你不能把诸如此类的函数,如memcmy(),strcmp(),strncmp()等,和1或者-1直接比较(译者注:说实话,这个问题好像还挺普遍)。
有趣的是,上面那段喝1/-1比较的错误的代码可以如程序员预期般的正常工作很多年。但这除了幸运外,还能说什么其他呢。这个函数很可能会在以后意外的改变,比如,你使用了其他的编译器,或者有开发者用了新的方式优化了memcmp(),到那时,你的代码将会停止工作(译者注:说实话,如果那天很多标准库函数真的严格返回大于或小于0的值,而非1或者-1,我觉得全世界当大的几率有很多程序会出问题)。
正确的代码
bool operator()(const GUID &_Key1, const GUID &_Key2) const
{
return memcmp(&_Key1, &_Key2, sizeof(GUID)) < 0;
}
(译者注:注意前面的代码是比较是否等于-1,所以这里的修改应该是判断是否小于0,才能符合原函数的意思)
建议
不要依赖于函数当前工作的方式。如果相应文档说那个函数应该返回大于或小于0,那么就请遵照说明,这就意味着这个函数可以返回-10,2或者1024。而你事实上看到这个函数似乎只返回-1,0或者1不能证明任何事。
顺便说一下,如果真的返回了1024,也就意味着memcmp()的返回结果不能被存储在char类型中。这是一个更广泛的错误,其结果将更加严重。可惜,就是这样的错误,导致了MySQL/MariaDB的发布版本中,在5.1.61,5.2.11,5.3.5,5.5.22之前的版本非常脆弱(译者注:这里的意思,即在5.1.61,5.2.11,5.3.5,5.5.22发布版本才更正了这个错误,MySQL/MariaDB和很多普遍使用的系统一样,同时维护了多个版本)。具体情况就是,当一个用户连接MySQL/MariaDB时,类似的代码在使用memcmp比较预期值和token(通过SHA算法计算密码和哈希值获得)时,在某些平台上,其比较的返回值会超过[-128…127],就会导致有1/256的几率任何token和预期值比较都返回true(译者注:注意把my_bool定义为char类型了,所以当结果值溢出char类型,那么my_bool只能判断一个字节的值而非完整的正负值,只要这个字节是0x01,就是true,所以有1/256的几率返回true)。这样的话,即使不知道密码,黑客也能获得MySQL服务器的root权限。导致这个情况的具体代码如下:
typedef char my_bool;
...
my_bool check(...) {
return memcmp(...);
}
你可以在这里 http://seclists.org/oss-sec/2012/q2/49 看到上面这个问题的更多信息。
本文探讨了在 CoreCLR 项目中使用 memcmp 函数时的一个常见误区:将函数返回值直接与 -1 或 1 比较。正确的做法应当是检查返回值是否小于、等于或大于0。文章还提到了这种误用可能导致的安全隐患,例如 MySQL/MariaDB 中的一个严重漏洞。
784

被折叠的 条评论
为什么被折叠?



