问题在于
ParameterBuilder.SetConstant
,而非
Activator.CreateInstance
。
SetConstant
是一个用于定义可选参数默认值的函数,并需要提供具体值作为常量。对于引用类,
null
是一个有效的具体值,但对于在创建
Nullable<>
之前的值类,
null
不是一个有效的值。例如,如何将
null
转换为
int
? 因此,
SetConstant
会检查值类型以查看传递作为常量的具体值是否为
null
,并抛出一个
ArgumentException
作为比在值类中将null取消拆装时获得的
NullReferenceException
更具描述性的错误。
在
Nullable<>
的情况下,
null
现在是值类的有效具体值。正如使用
Activator.CreateInstance
所看到的那样,
Nullable<>
本身是一个值类,并且将
null
取消打包成
Nullable<int>
是有意义的。这就是
SetConstant
出现错误的地方:它没有考虑到这一点,并为实际上不是错误的事情抛出了一个“描述性”的错误。由于缺乏来自Microsoft的错误修复,任何对带有null
Nullable<>
的
SetConstant
的调用都将不得不实现由不正确条件保护的行为。这意味着必须使用反射来深入了解
ParameterBuilder
的私有方法和字段。这是我编写的代码,只处理这种情况。在不出现错误的情况下应使用标准的
SetConstant
函数。
//ModuleBuilder module : a parameter passed into the containing function, the module which is being built (which contains the method that has the optional parameter we are setting the constant for)
//ParameterBuilder optParam : A parameter passed into the containing function, the parameter that is being built which is to have the null default value.
MethodInfo method = typeof(TypeBuilder).GetMethods(BindingFlags.Static | BindingFlags.NonPublic)
.Where(m => m.Name == "SetConstantValue" && m.GetParameters().Length > 0 && m.GetParameters()[0].ParameterType.Name == "RuntimeModule")
.Single();
var RuntimeHandle = typeof(ModuleBuilder).GetMethod("GetNativeHandle", BindingFlags.NonPublic | BindingFlags.Instance);
method.Invoke(null, new object[]
{
RuntimeHandle.Invoke(module, new object[]{}),
optParam.GetToken().Token,
0x12,
null
});
我已向微软报告了这个漏洞。他们回应说它不会在.NET 3.5中修复,但已添加到内部漏洞数据库。
更新:
该漏洞已在.NET 4.0中修复。ParameterBuilder.SetConstant现在有一个分支到constant == null条件,该条件检查值类型是否是从Nullable<>派生的通用类型,仅在不是时引发异常。