[深入理解计算机系统][2.2.5]有符号数和无符号数不匹配导致的安全漏洞
2013-12-24 19:58
513 查看
C语言支持所有整型数据类型的有符号和无符号运算。尽管C语言标准没有指定有符号数要采用某种表示,但是几乎所有的机器都使用补码(Two's Complement)。
C语言允许无符号数和有符号数之间的转换,转换的原则是底层的位表示保持不变(只是把二进制表示解释为另一种类型)。关于signed和unsigned转换的具体过程,可以参考《深入理解计算机系统》第二章相应章节。如果一个表达式中同时有singed和unsigned变量参与运算,C语言会隐式地将signed转换为unsigned。这时可能会导致很难察觉到bug,造成严重的安全漏洞。
首先来看第一个例子。下面几个表达式
再来看第二个例子。
第三个例子是FreeBSD开源操作系统项目2002年发现的一个安全漏洞,简化版本如下:
我们已经看到,由于unsigned运算的细微特性,尤其是signed到unsigned隐式转换,会导致严重的错误或漏洞。避免这一类错误的一种方法是绝不使用无符号数。实际上,除C以外,很少有语言支持无符号整数。很明显,这些语言的设计中认为它们带来的麻烦要远多于益处。当然,无符号数也并非全无用处,在一些特殊场合,例如在用字表示位的集合时,无符号数是非常方便和高效的。
总的来说,能用signed类型代替unsigned类型的地方,尽量用signed类型,可以减少大量难以察觉的代码bug和安全漏洞。
C语言允许无符号数和有符号数之间的转换,转换的原则是底层的位表示保持不变(只是把二进制表示解释为另一种类型)。关于signed和unsigned转换的具体过程,可以参考《深入理解计算机系统》第二章相应章节。如果一个表达式中同时有singed和unsigned变量参与运算,C语言会隐式地将signed转换为unsigned。这时可能会导致很难察觉到bug,造成严重的安全漏洞。
首先来看第一个例子。下面几个表达式
-1 < 0 ; //return true -1 < 0U; //may return false 2147483647 > -2147483647 - 1; //return true 2147483647U > -2147483647 - 1; //may return false第二个表达式中右边为unsigned类型,因此C标准会先将左边-1隐式转换为unsigned类型,结果为无符号0xFFFFFFFF(如果用4byte表示),显然0xFFFFFFFF大于0U,因此返回false。第四个表达式同理。
再来看第二个例子。
/* Warning: This is buggy code */ float sum_element(float a[], unsigned lenght) { int i; float sum = 0; for (i=0; i<=lenght-1; i++) { sum += a[i]; } return sum; }当参数length等于0时,运行这段代码应该返回0.0。但实际上,运行时会遇到一个存储器错误。原因是length声明为unsigned类型,当length=0是,计算0-1将是无符号运行,得到MAX_INT,此时 i<=length-1为真,代码将访问数组a的非法元素。
第三个例子是FreeBSD开源操作系统项目2002年发现的一个安全漏洞,简化版本如下:
/* Declaration of library function memcpy */ void *memcpy(void *dest, void *src, size_t n); /* Kernel memory region holding user-accessible data */ #define KSIZE 1024 char kbuf[KSIZE]; /* Copy at most maxlen bytes from kernel region to user buffer */ int copy_from_kernel(void *user_dest, int maxlen) { int len = KSIZE < maxlen ? KSIZE : maxlen; memcpy(user_dest, kbuf, len); return len; }如果调用copy_from_kernel时maxlen使用了负值,这个负值会传递给memcpy函数,而memcpy函数的第三个参数为size_t(32位机器上典型的定义为unsigned int)类型,该负值会被隐式转换为一个很大的正整数,用户程序就会访问非法内存或未被授权的内核存储区域!要修正这个bug,只要将maxlen的类型声明为size_t,即与memcpy的参数类型保持一致。同时,本地变量len的类型和返回值类型也应该声明为size_t。
我们已经看到,由于unsigned运算的细微特性,尤其是signed到unsigned隐式转换,会导致严重的错误或漏洞。避免这一类错误的一种方法是绝不使用无符号数。实际上,除C以外,很少有语言支持无符号整数。很明显,这些语言的设计中认为它们带来的麻烦要远多于益处。当然,无符号数也并非全无用处,在一些特殊场合,例如在用字表示位的集合时,无符号数是非常方便和高效的。
总的来说,能用signed类型代替unsigned类型的地方,尽量用signed类型,可以减少大量难以察觉的代码bug和安全漏洞。
相关文章推荐
- http://technet.microsoft.com/zh-cn/library/ms172932.aspx
- linux网络配置
- TCP-socket
- Back Track 5 之 Web踩点 && 网络漏洞
- Sobel和Roberts算子的推导过程
- vm虚拟机中三种网络连接方式的工作原理
- 编码,一点阅读(转:阮一峰的网络日志),
- boost网络编程(1)
- 下载网络资源方法-java版
- HTTP Content-type
- Httpclient 实现带参文件上传
- PHP学习笔记----http协议
- 接口测试小工具(HTTP + XML)
- 网络异常测试初探
- 命令行听歌http://www.linuxsir.org/bbs/thread280142.html?pageon=1#1584689
- yum 源安装扩展(轻松解决)
- 我心中的计算机学科(四)
- 从另外一台电脑打开别的电脑共享, 但是网络登录服务没有启动
- 未在本地计算机上注册“Microsoft.Ace.OleDb.12.0”提供程序解决办法
- java 下载网络上的图片并保存到本地目录