运行时和编译时的安全性检查 /RTC

RTC 表示运行时检查。RTC 有若干子选项。与 /GS 不同,/RTC 设计用于调试版本,而不用于优化代码。与 /GS 相同的是,如果您不喜欢默认对话框,可以编写自己的处理程序。

使用 Microsoft® Visual Studio® 调试应用程序时,RTC 对话框可以为您提供在错误发生之处调试应用程序的选项。此外,还可以在 Visual Studio 中创建若干个配置,每个配置都包含各种选项的不同组合,例如,对发布版本使用 /GS 选项,而对调试版本使用一个或多个 /RTC 选项。

/RTCs - 堆栈帧运行时错误检查
此选项在保护堆栈不被破坏方面采取了若干措施。

在每次调用函数时,将所有局部变量初始化为非零值。这样可以防止以前的调用对堆栈中的值的无意使用。

验证堆栈指针能够检查到堆栈破坏,例如,在一个位置将函数定义为 __stdcall,而在另一个位置将函数定义为 __cdecl 可导致堆栈破坏。

检测局部变量的溢出和不足。这与 /GS 不同,因为它仅适用于调试版本,并且检测缓冲区的两端以及所有缓冲区是否遭到破坏。

cl /Od /MLd /ZI /EHsc /RTCs GS-RTC.cpp
此命令将关闭优化 (/Od),并设置 _DEBUG 预处理器定义。

使用此命令编译样例后,请再次尝试运行 测试 1。此时它将会捕捉到覆盖操作。

 

测试 2 说明了不包括返回地址的堆栈溢出:

void Test2()
{
   char buffer1[100];
   char buffer2[100];
   buffer1[0] = 0;
   for (int i=0 ; i <= sizeof(buffer2); i++)
   {
      buffer2[i] = 'a';
   }
   buffer2[sizeof(buffer2)-1] = 0;
   cout << buffer2 << '-' << buffer1 << endl;
}
此循环使 buffer2 溢出一个字符,因为它使用 <= 代替了 <。它将覆盖 buffer1 的第一个字符。使用 /RTCs 编译器开关进行编译并使用 gs-rtc 2 运行时,系统将显示下面的对话框:

 

图 3. 使用 gs-rtc 2 选项时生成的错误。
测试 3 对缓冲区不足进行了说明:

void Test3()
{
   char buffer1[100];
   char buffer2[100];
   memset(buffer1,'a',sizeof(buffer1)-1);
   buffer1[sizeof(buffer1)-1]=0;
   memset(buffer2,'b',sizeof(buffer2)-1);
   buffer2[sizeof(buffer2)-1]=0;
   *(buffer1-1) = 'c';
   cout << buffer1 << endl;
   cout << buffer2 << endl;
 }
(要运行测试 3,请使用命令“gs-rtc 3”。)在本例中,buffer2 的最后一个字节被覆盖,并且对话框将显示 buffer1 出现的问题。如果不进行运行时检查,此测试的输出结果为:

aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaa
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbcaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
由于这一写入操作破坏了 buffer2 的终止符,因此,所有将 buffer2 用作空结束字符串的操作都将获得一个长度为期望值两倍的字符串。

测试 4 显示了 /RTCs 选项如何将未初始化变量设置为“标志”值:

void Test4()
{
   unsigned int var;
   cout << hex << var;
}
在使用 /RTCs 编译之后运行 测试 4时,将得到十六进制值 cccccccc。如果不使用 /RTCs 进行编译,将得到堆栈中剩余的随机值。

/RTCc - 检测导致数据丢失的分配
此选项将插入代码,以便在分配导致数据丢失时向您发出警报,这样可以确保在转换为较小类型时从不丢失数据。测试 5 说明以下内容。

void Test5(int value)
{
   unsigned char ch;
   ch = (unsigned char)value;
}
请执行以下命令来编译此代码:

cl /Od /MLd /ZI /EHsc /RTCc GS-RTC.cpp
请在命令提示符处使用其他数字来运行此命令。例如,此命令将触发错误:

gs-rtc 5 300
 

图 4. 在 /RTCc 开关中使用大于 255 的数值时所生成的错误。
无符号字符最多可容纳 255 个字符,因此向程序中输入 300 将导致数据丢失。使用 gs-rtc 5 200 再次运行命令,此时将不会出现错误。

如果要转换为较小类型,并要故意丢失上面的数位,则可以使用如下所示的掩码:

   ch = (unsigned char)(value & 0xFF);
/RTCu - 报告使用了未初始化的变量
当访问未初始化的变量时,此选项将会发出警告。测试 6 包含三个子测试,代码如下:

void Test6(int value)
{
   int uninitialized;
   int var;
   switch (value) {
      case 3:
         uninitialized = 4;
      case 2:
         var = 5 * uninitialized;
         break;
      case 1:
         int *var2;
         var2= &uninitialized;
         var = 5 * uninitialized;
         break;
   }
}
(请注意,执行 case 3 中的语句后,程序将直接转到 case 2 中执行。)使用以下命令编译此代码:

cl /Od /MLd /ZI /EHsc /RTCu GS-RTC.cpp
运行 gs-rtc 6 2 时,系统将显示以下对话框:

 

图 5. 使用 gs-rtc 6 2 时所生成的错误
运行 gs-rtc 6 3 时,系统将不显示对话框,因为变量已被初始化。"但是,即使使用了未初始化的变量,gs-rtc 6 1 也不会出现错误,因为编译器不跟踪可以通过指针来初始化的变量。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值