翻译《有关编程、重构及其他的终极问题?》——2.比0大的并不意味着就只是1

来源:互联网 发布:批量重命名软件 编辑:程序博客网 时间:2024/06/07 01:49

翻译《有关编程、重构及其他的终极问题?》——2.比0大的并不意味着就只是1

标签(空格分隔): 翻译 技术 C/C++
作者:Andrey Karpov
翻译者:顾笑群 - Rafael Gu
校验者:高国栋
最后更新:2016年11月13日


本书背景说明、总目录等介绍,可以跳转到以下链接进行查看:
http://blog.csdn.net/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 看到上面这个问题的更多信息。

0 0
原创粉丝点击