Skip to content

Conversation

@jborean93
Copy link
Collaborator

PR Summary

Turn off the AMSI member invocation logging for non-Windows release builds. This is slightly more efficient as there's no extra work to create the AMSI string.

Fixes: #21492

PR Context

#21492 (comment)

WG reviewed this and agreed that the .NET method invocation logging for AMSI should only apply to Windows. The change should be separate and not dependent on the env var.

It doesn't make sense for this feature to be on for a proper release build as writing to stdout will affect anything calling PowerShell. The env var name also indicates this is an internal optional and not a publicly supported feature so shouldn't be under any API contract.

PR Checklist


internal static void LogMemberInvocation(string targetName, string name, object[] args)
{
#if UNIX && !DEBUG
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for tackling this, but, given that there seems to be only one call to this method (in Binders.cs), wouldn't it be better for performance reasons to conditionally exclude that call instead?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nah the JIT should optimize it out

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should have, but ... does it and at what point (interpreter)?

Copy link
Contributor

@mklement0 mklement0 Oct 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Taking a closer look at the call site, it seems that a (cached) reference to the method is being passed as an argument to another method, and it's not clear to me how that could be optimized away (but I'm not familiar with the subtleties of JIT compilation), especially since it seems desirable to prevent that other method call altogether.

// Expression block runs two expressions in order:
// - Log method invocation to AMSI Notifications (can throw PSSecurityException)
// - Invoke method
string targetName = methodInfo.ReflectedType?.FullName ?? string.Empty;
expr = Expression.Block(
Expression.Call(
CachedReflectionInfo.MemberInvocationLoggingOps_LogMemberInvocation,
Expression.Constant(targetName),
Expression.Constant(name),
Expression.NewArrayInit(
typeof(object),
args.Select(static e => e.Expression.Cast(typeof(object))))),
expr);

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mklement0 The code you referenced does only one thing - call LogMemberInvocation method. This is the code that should have been excluded.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, of course, thanks. I agree.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason why I put the change here is in case the method is called in multiple places in the future. I can certainly change it so the expression tree doesn't call it but I'm not sure if there is any actual overhead to care about with just calling a method that is ultimately a no-op. Will defer to the pwsh team to see what they decide here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, Jordan. I suppose a pragmatic way to handle this would be to to use #if UNIX && !DEBUG around both the currently known only call site as well as inside the method.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So the more I think about this the more I think it would be better to make a new method in the compiler to conditionally add this expression. The JIT should optimize the call, but it won't optimize the array creation for the argument and extra boxing involved. Should be very minimal, but might as well get rid of it if we can.

Something like this:

private Expression MaybeAddMemberInvocationLogging(Expression expr, Expression[] args, ...otherNeededState)
{
#if UNIX && !DEBUG
    return expr;
#else
    return Expression.Block( 
	    Expression.Call( 
	        CachedReflectionInfo.MemberInvocationLoggingOps_LogMemberInvocation, 
	        Expression.Constant(targetName), 
	        Expression.Constant(name), 
	        Expression.NewArrayInit( 
	            typeof(object), 
	            args.Select(static e => e.Expression.Cast(typeof(object))))), 
	    expr); 
#endif
}

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated to follow this pattern.

@microsoft-github-policy-service microsoft-github-policy-service bot added the Review - Needed The PR is being reviewed label Oct 25, 2024
@TravisEz13 TravisEz13 changed the title Turn off AMSI member invocation on nix release builds Turn off AMSI member invocation on *nix release builds Nov 5, 2024
Copy link
Collaborator

@SeeminglyScience SeeminglyScience left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feel free to ping me on discord when the changes are made (or you've otherwise responded) and I'll get this merged. Thanks Jordan! ❤️

@microsoft-github-policy-service microsoft-github-policy-service bot added Waiting on Author The PR was reviewed and requires changes or comments from the author before being accept and removed Review - Needed The PR is being reviewed labels Dec 4, 2024
Turn off the AMSI member invocation logging for non-Windows release
builds. This is slightly more efficient as there's no extra work to
create the AMSI string.
@microsoft-github-policy-service microsoft-github-policy-service bot removed the Waiting on Author The PR was reviewed and requires changes or comments from the author before being accept label Dec 8, 2024
Copy link
Collaborator

@SeeminglyScience SeeminglyScience left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Thank you Jordan!

@SeeminglyScience SeeminglyScience merged commit 06e2093 into PowerShell:master Dec 9, 2024
38 checks passed
@microsoft-github-policy-service
Copy link
Contributor

microsoft-github-policy-service bot commented Dec 9, 2024

📣 Hey @jborean93, how did we do? We would love to hear your feedback with the link below! 🗣️

🔗 https://aka.ms/PSRepoFeedback

@jborean93 jborean93 deleted the amsi-unix branch December 9, 2024 19:36
typeof(object),
args.Select(static e => e.Expression.Cast(typeof(object))))),
expr);
MaybeAddMemberInvocationLogging(expr, targetName, name, args);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I may be missing something, but it looks like the return value from this method call is never used; shouldn't that be expr = MaybeAddMemberInvocationLogging(expr, targetName, name, args);?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good pickup, sorry for missing that. @SeeminglyScience will be submitting a new PR to fix that.

Copy link
Collaborator

@iSazonov iSazonov Dec 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That says there are no tests.
And many would say thank you for such an omission 😄

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

AMSI logging implemented on Linux

4 participants