C# 只有在启用编译优化时才会出现错误

C# 只有在启用编译优化时才会出现错误,c#,compiler-optimization,C#,Compiler Optimization,我在代码中遇到了一个bug,只有在代码构建时启用了优化,它才会被复制。我制作了一个控制台应用程序,可以复制测试逻辑(下面的代码)。您将看到,当启用优化时,在执行此无效逻辑后,“value”变为null: if ((value == null || value == new string[0]) == false) 修正是直截了当的,并在有问题的代码下面注释掉。但是我更担心的是,我可能在汇编程序中遇到了一个bug,或者其他人解释了为什么将value设置为null using System; us

我在代码中遇到了一个bug,只有在代码构建时启用了优化,它才会被复制。我制作了一个控制台应用程序,可以复制测试逻辑(下面的代码)。您将看到,当启用优化时,在执行此无效逻辑后,“value”变为null:

if ((value == null || value == new string[0]) == false)
修正是直截了当的,并在有问题的代码下面注释掉。但是我更担心的是,我可能在汇编程序中遇到了一个bug,或者其他人解释了为什么将value设置为null

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace memory_testing
{
    class Program
    {
        sta tic void Main(string[] args)
        {
            while(true)
            {
                Console.Write("Press any key to start...");
                Console.ReadKey();
                Console.WriteLine();
                PrintManagerUser c = new PrintManagerUser();
                c.MyProperty = new string[1];
            }
        }
    }

    public class PrintManager
    {
        public void Print(string key, object value)
        {
            Console.WriteLine("Key is: " + key);
            Console.WriteLine("Value is: " + value);
        }
    }

    public class PrintManagerUser
    {
        public string[] MyProperty
        {
            get { return new string[100]; }
            set
            {
                Console.WriteLine("Pre-check Value is: " + value);
                if ((value == null || value == new string[0]) == false)
                {
                    Console.WriteLine("Post-check Value is: " + value);
                    new PrintManager().Print("blah", value);
                }
                //if (value != null && value.Length > 0)
                //{
                //    new PrintManager().Print("blah", value);
                //}
            }
        }
    }
}
正常输出应为:

Pre-check Value is: System.String[]
Post-check Value is: System.String[]
Key is: blah
Value is: System.String[]
buggy的输出是:

Pre-check Value is: System.String[]
Post-check Value is:
Key is: blah
Value is:   

My Env是一个运行Windows Server 2003 R2和.NET 3.5 SP1的虚拟机。使用VS2008团队系统

谢谢


Brian

我使用的是x64,一开始无法重现该问题。然后我将目标指定为x86,我就遇到了这种情况。回到x64,它就消失了。我不确定这到底意味着什么,但我已经来回走了好几次了

value == new string[0]

在我看来,上面的说法很奇怪。您正在使用equals语句比较两个字符串数组。只有当它们都指向同一个数组时,才会出现true,这是不太可能的。这还不能解释为什么这段代码在优化版本中表现不同。

是的,您的表达式会严重混淆JIT优化器。生成的机器代码如下所示:

                if ((value == null || value == new string[0]) == false)
00000027  test        esi,esi               ; value == null?
00000029  je          00000075 
0000002b  xor         edx,edx               ; new string[0]
0000002d  mov         ecx,6D913BD2h 
00000032  call        FFD20BC8 
00000037  cmp         eax,esi               ; (value == new string[0]) == false?
00000039  je          00000075 
                {
                    Console.WriteLine("Post-check Value is: " + value);
0000003b  mov         ecx,dword ptr ds:[03532090h]  ; "Post-check value is: "
00000041  xor         edx,edx               ; BUGBUG not null!
00000043  call        6D70B7E8              ; String.Concat()
00000048  mov         esi,eax               ; 
0000004a  call        6D72BE08              ; get Console.Out
0000004f  mov         ecx,eax 
00000051  mov         edx,esi 
00000053  mov         eax,dword ptr [ecx] 
00000055  call        dword ptr [eax+000000D8h]     ; Console.WriteLine()
错误发生在地址41处,优化器得出结论,该值将始终为null,因此它直接将null传递给String.Concat()

作为比较,这是在JIT优化关闭时生成的代码:

                    Console.WriteLine("Post-check Value is: " + value);
00000056  mov         ecx,dword ptr ds:[03342090h] 
0000005c  mov         edx,dword ptr [ebp-8] 
0000005f  call        6D77B790 
代码被移动了,但请注意,在地址5c处,它现在使用局部变量(值)而不是null

您可以在connect.microsoft.com上报告此错误。解决方法很简单:

  if (value != null)
  {
    Console.WriteLine("Post-check Value is: " + value);
    new PrintManager().Print("blah", value);
  }

当然看起来像个bug,当你像这样交换操作符操作数时它会重现吗

if (false == (null == value || new string[0] == value))

这个错误似乎已经在.NET4(Beta2)中修复。下面是上面突出显示的位nobugz的优化x86反汇编:

                    Console.WriteLine("Post-check Value is: " + value);
00000056  mov         ecx,dword ptr ds:[033C2090h] 
0000005c  mov         edx,dword ptr [ebp-8] 
0000005f  call        65D8FE10

程序还以优化和未优化模式显示预期输出。

显然,优化器对您使用“==false”而不是应用!表达式的运算符。需要时Eric Lippert在哪里-pI会将此转发给jitter团队。谢谢请看,people-@Eric是甲壳虫汁,说了3次他的时间,他出现了。这已经被分配给CVE-2011-1271-4 beta 2之前的Microsoft.NET Framework中的JIT编译器,当IsJITOptimizerDisabled为false时,无法正确处理与空字符串相关的表达式,允许上下文相关攻击者利用特制的应用程序,在机会主义情况下绕过预期的访问限制,正如x86平台上的一个C#应用程序所演示的那样。我也在x64上,无法用optimization=true和target=x86重现这一点。感谢您对所发生的事情进行了精彩的解释。这是我在x64上看到的最令人印象深刻的答案,所以。。。或者我只是很容易被打动…不应该是“if(!string.IsNullOrEmpty(value)),因为OP也比较空字符串。我想不出任何方法来注入运算符重载,但这绝对是100%真实的吗?同时,我也这么想,所以+1