本节书摘来自华章计算机《编写高质量代码:改善c程序代码的125个建议》一书中的第2章,建议14-1,作者:马 伟 更多章节内容可以访问云栖社区“华章计算机”公众号查看。
建议14-1:尽量避免对未知的有符号数执行位操作
在C语言中,如果在未知的有符号数上执行位操作,很可能会导致缓冲区溢出,从而在某些情况下导致攻击者执行任意代码,同时,还可能会出现出乎意料的行为或编译器定义的行为。
下面来看一个简单的示例,如代码清单2-3所示。
代码清单2-3 在未知的有符号数上执行位操作示例
#include <stdio.h>
int main (void)
{
int x=0;
int y=0x80000000;
char buf[sizeof("128")];
x=sprintf(buf,"%u",y>>24);
if(x==-1||x>=sizeof(buf))
{
// 错误处理
}
printf(buf);
return 0;
}
在代码清单2-3中,y>>24的执行结果为4294967168,而sizeof(buf)的结果为4。当我们将y>>24的结果值转换为字符串“4294967168”时,超出了buf范围,所以结果值无法完全存储在buf中。因此,在执行语句“x=sprintf(buf,"%u",y>>24)”时,sprintf方法在进行写操作时就会越过buf的边界,从而产生缓冲区溢出。
如果在编译器VC++中执行这段程序,将会产生如图2-4所示的错误报告。
在C99中,要修正这样的错误,最好利用snprintf方法来代替sprintf方法。因为snprintf方法最多从源串中复制n-1个字符到目标串中,然后再从后面加一个0。因此,如果目标串的大小为n,将不会产生
溢出。
当然,如果将变量y声明成为无符号类型,那么这种缓冲区溢出错误将不会发生,如代码清单2-4所示。
代码清单2-4 代码清单2-3的改进示例
#include <stdio.h>
int main (void)
{
int x=0;
unsigned int y=0x80000000;
char buf[sizeof("128")];
x=sprintf(buf,"%u",y>>24);
if(x==-1||x>=sizeof(buf))
{
// 错误处理
}
printf(buf);
return 0;
}