java/C++这类语言对类型的要求非常严格,但是C语言对类型几乎没什么限制.
它可以非常灵活的处理不同类型之间的转换.
正因为它的这种灵活性,也导致了程序员在使用各种混合的数据类型时,并不关心这些类型混用带来的危害.
往往这些细节引起大量软件漏洞.降低了软件的安全性,而且使维护成本增高.
这也是C语言编写的程序饱受诟病的原因之一吧.
先说说整数类型转换情况:
0.从一个窄的无符号类型,转换到一个宽的有符号类型.值保留不会改变.( 比如 unsigned short --> int )
这个很简单,因为更宽的有符号类型总能容纳下比它窄的类型.哪怕是无符号数.
1.从一个窄的有符号类型,转换到一个宽的无符号类型.编译器进行符号拓展(movsx).有可能发生值改变.( 比如char -->unsigned short)
2.从一个窄的有符号类型,转换到一个宽的有符号类型.编译器进行符号拓展(movsx).转换后的值保留,不会改变.(比如short -->int)
3.从一个窄的无符号类型,转换到一个宽的无符号类型.编译器进行零拓展(movzx).转换后的值保留,不会改变.( 比如unsigned short -->unsigned int )
这个学过汇编的都知道.一条movzx指令就搞定了.
4.从一个宽类型,转换到一个窄类型.编译器进行截断.有可能发生值改变.( 比如unsigned short -->unsigned char )
5.在无符号类型和无符号类型之间转换时.他们的bit位的模式是一致的.但是有可能发生值改变.( 比如unsigned short -->short )
下面看一段有脆弱点的代码:
int read_user_data(int sockfd) { int length, sockfd, n; char buffer[1024]; length = get_user_length(sockfd); if(length > 1024){ error("illegal input, not enough room in buffer\n"); return 1; } if(read(sockfd, buffer, length) < 0){ error("read: %m"); return 1; } return 0; }
然后根据返回的length调用read函数.读取用户数据.
注意,这里发生了一次类型转换.
length是int型.read函数原型是#include ssize_t read(int fd, void *buf, size_t count).(size_t在32位机器上定义为unsigned int)
所以这里length由singed int 转换到了 unsigned int.
但是如果用户给了一个为负值的长度.
那么read函数将读取一块很大的内存.也就产生了任意读取的bug.