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;
}
首先get_user_length()函数从用户指定的socket取得用户数据的大小并且返回一个int类型的length.
然后根据返回的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.