mirror of
https://github.com/PowerShell/PowerShell
synced 2026-06-08 12:12:50 +00:00
Fix NullReferenceException in binder (#2591)
There was a NullReferenceException after turning on constrained language mode if creating a binding on a thread with no runspace. The fix is to assume if there is no runspace that we are not in constrained language mode, and also generate a binding restriction that assumes the same.
This commit is contained in:
@@ -400,35 +400,34 @@ namespace System.Management.Automation.Language
|
||||
|
||||
internal static BindingRestrictions GetLanguageModeCheckIfHasEverUsedConstrainedLanguage()
|
||||
{
|
||||
BindingRestrictions languageModeRestriction = BindingRestrictions.Empty;
|
||||
|
||||
// Also add a language mode check to detect toggling between language modes
|
||||
if (ExecutionContext.HasEverUsedConstrainedLanguage)
|
||||
{
|
||||
var context = LocalPipeline.GetExecutionContextFromTLS();
|
||||
|
||||
switch (context.LanguageMode)
|
||||
{
|
||||
case PSLanguageMode.ConstrainedLanguage:
|
||||
languageModeRestriction = BindingRestrictions.GetExpressionRestriction(
|
||||
Expression.Equal(
|
||||
Expression.Property(
|
||||
ExpressionCache.GetExecutionContextFromTLS,
|
||||
CachedReflectionInfo.ExecutionContext_LanguageMode),
|
||||
Expression.Constant(PSLanguageMode.ConstrainedLanguage)));
|
||||
break;
|
||||
default:
|
||||
languageModeRestriction = BindingRestrictions.GetExpressionRestriction(
|
||||
Expression.NotEqual(
|
||||
Expression.Property(
|
||||
ExpressionCache.GetExecutionContextFromTLS,
|
||||
CachedReflectionInfo.ExecutionContext_LanguageMode),
|
||||
Expression.Constant(PSLanguageMode.ConstrainedLanguage)));
|
||||
break;
|
||||
}
|
||||
var tmp = Expression.Variable(typeof(ExecutionContext));
|
||||
var langModeFromContext = Expression.Property(tmp, CachedReflectionInfo.ExecutionContext_LanguageMode);
|
||||
var constrainedLanguageMode = Expression.Constant(PSLanguageMode.ConstrainedLanguage);
|
||||
|
||||
// Execution context might be null if we're called from a thread with no runspace (e.g. a PSObject
|
||||
// is used in some C# w/ dynamic). This is sometimes fine, we don't always need a runspace to access
|
||||
// properties.
|
||||
Expression test = context?.LanguageMode == PSLanguageMode.ConstrainedLanguage
|
||||
? Expression.AndAlso(
|
||||
Expression.NotEqual(tmp, ExpressionCache.NullExecutionContext),
|
||||
Expression.Equal(langModeFromContext, constrainedLanguageMode))
|
||||
: Expression.OrElse(
|
||||
Expression.Equal(tmp, ExpressionCache.NullExecutionContext),
|
||||
Expression.NotEqual(langModeFromContext, constrainedLanguageMode));
|
||||
|
||||
return BindingRestrictions.GetExpressionRestriction(
|
||||
Expression.Block(
|
||||
new[] {tmp},
|
||||
Expression.Assign(tmp, ExpressionCache.GetExecutionContextFromTLS),
|
||||
test));
|
||||
}
|
||||
|
||||
return languageModeRestriction;
|
||||
return BindingRestrictions.Empty;
|
||||
}
|
||||
|
||||
internal static BindingRestrictions GetOptionalVersionAndLanguageCheckForType(DynamicMetaObjectBinder binder, Type targetType, int expectedVersionNumber)
|
||||
@@ -5313,10 +5312,10 @@ namespace System.Management.Automation.Language
|
||||
new object[] { Name });
|
||||
}
|
||||
|
||||
internal static DynamicMetaObject EnsureAllowedInLanguageMode(PSLanguageMode languageMode, DynamicMetaObject target, Object targetValue,
|
||||
internal static DynamicMetaObject EnsureAllowedInLanguageMode(ExecutionContext context, DynamicMetaObject target, Object targetValue,
|
||||
string name, bool isStatic, DynamicMetaObject[] args, BindingRestrictions moreTests, string errorID, string resourceString)
|
||||
{
|
||||
if (languageMode == PSLanguageMode.ConstrainedLanguage)
|
||||
if (context != null && context.LanguageMode == PSLanguageMode.ConstrainedLanguage)
|
||||
{
|
||||
if (!IsAllowedInConstrainedLanguage(targetValue, name, isStatic))
|
||||
{
|
||||
@@ -5970,7 +5969,7 @@ namespace System.Management.Automation.Language
|
||||
// Validate that this is allowed in the current language mode
|
||||
var context = LocalPipeline.GetExecutionContextFromTLS();
|
||||
DynamicMetaObject runtimeError = PSGetMemberBinder.EnsureAllowedInLanguageMode(
|
||||
context.LanguageMode, target, targetValue, Name, _static, new[] { value }, restrictions,
|
||||
context, target, targetValue, Name, _static, new[] { value }, restrictions,
|
||||
"PropertySetterNotSupportedInConstrainedLanguage", ParserStrings.PropertySetConstrainedLanguage);
|
||||
if (runtimeError != null)
|
||||
{
|
||||
@@ -6460,7 +6459,7 @@ namespace System.Management.Automation.Language
|
||||
// Validate that this is allowed in the current language mode
|
||||
var context = LocalPipeline.GetExecutionContextFromTLS();
|
||||
DynamicMetaObject runtimeError = PSGetMemberBinder.EnsureAllowedInLanguageMode(
|
||||
context.LanguageMode, target, targetValue, Name, _static, args, restrictions,
|
||||
context, target, targetValue, Name, _static, args, restrictions,
|
||||
"MethodInvocationNotSupportedInConstrainedLanguage", ParserStrings.InvokeMethodConstrainedLanguage);
|
||||
if (runtimeError != null)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
|
||||
Describe "Test constrained language mode" -Tags "CI" {
|
||||
It "dynamic invocation on non-PowerShell thread should work" {
|
||||
$refAssemblies = if ($IsCoreCLR) {
|
||||
"Microsoft.CSharp","System.Dynamic.Runtime","System.Management.Automation"
|
||||
}
|
||||
else {
|
||||
"Microsoft.CSharp"
|
||||
}
|
||||
|
||||
$t,$null = Add-Type -ReferencedAssemblies $refAssemblies -WarningAction Ignore -PassThru @"
|
||||
public class BinderBug$(Get-Date -Format FileDateTime)
|
||||
{
|
||||
public static object Test(System.Management.Automation.PSObject psobj)
|
||||
{
|
||||
// Invoke a method through PSObject, but with dynamic, so we get PowerShell's dynamic site binder involved
|
||||
// And we do this on a different thread so there is no ExecutionContext/runspace to check the language mode
|
||||
// The actual method called doesn't really matter.
|
||||
|
||||
return System.Threading.Tasks.Task.Run(() => ((dynamic)psobj).AddCommand("Get-Command")).Result;
|
||||
}
|
||||
}
|
||||
"@
|
||||
|
||||
$o = [powershell]::Create()
|
||||
$t::Test($o) | Should Be $o
|
||||
|
||||
try
|
||||
{
|
||||
# Set language mode to ConstrainedLanguage on a different runspace (so it doesn't affect this runspace)
|
||||
$ps = [powershell]::Create()
|
||||
$null = $ps.AddScript('$ExecutionContext.SessionState.LanguageMode = "ConstrainedLanguage"')
|
||||
$null = $ps.Invoke()
|
||||
|
||||
$t::Test($o) | Should Be $o
|
||||
}
|
||||
finally
|
||||
{
|
||||
$ps.Dispose()
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user