微软 .NET Framework 1.1 JIT Bug report (严重级别:非常高)

首先感谢拓荒者为我们提供了错误样本
其次希望微软能给我奖金,或者至少稿费。14.gif


该错误在一般使用的情况下很少遇到,但在非常特殊的使用方式下才会产生,尤其在您特意使用一些C#的副作用的时候更容易产生。不过如果遇到了,说不定真是会损失惨重。(我个人感觉这种错误跟Intel多年前奔腾芯片的浮点错误非常神似。)

 

错误简述:

如果您的程序:
1、在某个整形变量上面进行不带检查的加法操作(unchecked,默认的行为方式,不包括减法、乘法、除法操作),并且产生溢出(溢出后数值必然是负值),并且
2、立刻紧跟在该加法操作后面判断是否小于常量零(加完之后有乘除或者函数调用等,或者判断的是某个变量里面的零,或者判断的是小于其他常量值等,都不符合该条件),并且
3、在该函数当中使用了该变量的引用,例如:a.XXXX() 或者 AnotherFunction(ref a)。

或者与此相反的:
1、……减法……(……正值)……
2、……大于常量零……
3、……

则会引起上述第二步的判断与我们的期望不符——似乎该数大于等于0,并且因此没有执行该条件分支的语句或者语句块。



下面是错误重现:

1、在C#里新建一个Console项目
2、插入下列代码:

 class Test
 {
  static void Main()
  {
   int a = 0x79de61c0; //2044617152;
   a +=    0x12345678;    
   //a 应为 0x8c12b838;  //-1944930248
 
   if( a < 0 ) a = -a;
 
   System.Console.WriteLine( a );
   string str2 = a.ToString();
   Console.ReadLine();
  }
 }

3、运行后发现,if(a<0) a = -a; 这一句话出现瑕疵,a < 0 测试出错,并因此没有执行后续的  a = -a 语句。

当我们去掉 string str2 = a.ToString(); 这一句话之后,错误消失。


下面是对该问题的具体分析:

该问题实质上是由于JIT引擎翻译逻辑有瑕疵引起的。当注释掉string str2 = a.ToString() 之后,我们调试时打开反编译窗口以及寄存器窗口。在寄存器窗口上面点击右键,选上“标志”。此时我们可以看到:

TestJit_01.JPG

请注意图片黄色箭头处,if(a < 0) 实际上被翻译成jns 0016。jns机器指令的含义是,如果不是负数则跳转,实际上判断的是“符号标志”,也就是途中红圈圈上的"PL",这个标志位(以及其他一些标志位)由上一个指令add产生(这是该指令的副作用)。 由于符号为负,并没有条件转移,因此能够执行下一句a = -a (也就是neg esi)。但是请注意图中另外一个寄存器标志OV,该标志表示“溢出”。很明显我们的代码是因为相加溢出才导致结果变为负数,所以该标志位被置位。


当我们再去掉string str2 = a.ToString(); 并运行之后,我们可以看到:

TestJit_02.JPG

注意图中红圈处,原来的jns指令现在被改为jge指令。jge机器指令的含义是,如果大于等于则跳转,实际上判断的是“OV”、“PL”,相当于if ((OV ^ PL) == false) goto xxx。也就是说,多关注了一个OV标志。

很明显由于前面的溢出,造成OV标志置位,因此条件转移成立,结果没有执行后续的neg esi。所以我们从源代码的角度看,似乎此时变量a的值是非负数,这跟该数为负数的事实不符。

尽管从源代码的角度看,似乎没有任何与if(a < 0)有关系的改动,实际上由于我不清楚的理由,当该函数中使用到了a的引用,结果造成了if(a < 0)翻译成机器指令的不同,进而对一些副作用的反应不相同。

此例当中对a的引用是a.ToString(),实际上如果您使用AnotherFunction(ref a)替代这一句话,也会引起相同的问题。与此相反的减法操作的问题,原因和解释类似。


该问题的解决方法:
1、尽可能不要利用C#相加/减溢出后变成负数的副作用,例如用下列方式避免副作用:
  checked
  {
     a += 1234;
  }

2、如果您确信相加后溢出的副作用是必须的,那么请采取下列措施避免该错误:
  a += 1234;
  b = a;
  if (b < 0)
  {
    a = -a;
  }

注意,这个并非微软的官方解决方法,目前我并不清楚微软的KB里面是否有该问题的纪录。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值