-
Notifications
You must be signed in to change notification settings - Fork 586
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add ImportAsOperand to fix DynamicMethodBodyReader #553
Conversation
a6643c4
to
04632a7
Compare
@wtfsck What happen? |
I'll have to check this later, hopefully next week. |
The new implementation can't distinguish between ldtoken typedef and ldtoken typespec. void Method<T>()
{
_ = typeof(List<>);
_ = typeof(List<T>);
} |
Seems we can't distinguish them in dnlib, since we get the same object, see: public MemberInfo AsMember(int token) {
return m_module.ResolveMember(token, m_typeContext, m_methodContext);
} ResolveMember get the same object for 0x02000005(typedef) and 0x1b000003(typespec) |
Yes, this code can be improved: Check if there's generic parameter in the context, if no, we can't use TypeSpec, but if yes, I have no idea. |
Seems this only happen in Of course, this is not a perfect solution, if user want to get 100% correct result, they have to build one more How do you think? |
I don’t think adding a isLdtoken field when importing is a good idea. Instead the importer should guarantee that the reflection object passed into it always produces a corresponding dnlib object. It seems that in this case the ImportAsOperand method is just producing a wrong dnlib object which doesn’t match what reflection provides. This is obvious as there is a clear difference between an unbound generic type (List<>) and an instantiated generic type List where T is a method generic parameter. One should produce a uninstantiated TypeDef or TypeRef object and optionally have it wrapped in a ClassOrValueTypeSig and the second one should produce a GenericInstSig which is optionally wrapped in a TypeSped. The ImportAsOperand method doesn’t seem to handle this clear and important difference which is incorrect. Instead of trying to avoid encountering this difference by adding a “hack fix”, the root cause of the import error should be identified and resolved. |
Furthermore, I’m not sure whether adding so much additional code in the form of new ImportAsOperand methods is a good idea here. The code issue in reference mentions nothing in regard to special handling of field and types and yet here we see such changes. Why? The issue in question only mentions the importing of method operands as MemberRef/MethodSpec. More explanation as to the reasons is necessary here in order to evaluate this PR. im currently away from home but when i get back I will look more into this apparent issue with generics, dynamic methods and the importer. |
That's what
The following code is a classic DynamicMethod protector, all methods are encrypted like this, if DynamicMethodBodyReader doesn't support uninstantiation types, could you tell me how to make an unpacker to restore the code? Anyway, it's welcome if you have a better way. class My<T>
{
public bool Test<U>(T a, U b)
{
object[] args = { this, a, b };
DynamicMethod dynamicMethod = DynamicMethodBuilder.Create(751);
return (bool)dynamicMethod.Invoke(null, args);
}
}
// Generated by protector
class DynamicMethodBuilder
{
public static DynamicMethod Create(int methodID) => throw new NotImplementedException();
} |
Okay, so I've looked into this a bit and this PR seems to be fixing a problem when importing a Console.WriteLine(typeof(My<T>));
Console.WriteLine(typeof(My<>)); and debug the dynamic method body reader it is noticeable that in the dynamic method created the type used for both of these ldtokens is the EXACLTY THE SAME. There is no way to differentiate between these two cases! This is direct evidence that using As for the actual hack fix, the implementation appears to be okay to me at first glance since If we do end up going through on this PR I think the naming of the newly added members should be improved to be more clear.
Hope my little investigation helps in handling this strange and abnormal edge case! |
Don't agree, consider a man has cancer, there's a medicine, but it's not 100% useful, it can heal him in 99% cases, do you think whether we should use it when we don't have a better way? IMPORTANT, even if 1% cases happen, it doesn't make anything worse. Currently, DynamicMethodBodyReader almost completely generate wrong IL for generic types/methods. Why do I build an invalid DynamicMethod? It's not for executing purpose, it's my trick to restore IL, otherwise, how can we restore a generate type/method when it's protected to DynamicMethod? A valid and runnable DynamicMethoid always don't contain uninstantiation types, but the original method contains! This implemention only has issue when there's 100% issue on generic type/method vs 1%, how will you choice? |
This is not true, in cases where the types are actually correct, the generated code is 100% valid and correct. The reader can restore the code back to the original without issues. Issues appear when you pass in an invalid DynamicMethod. Instead of creating invalid methods for your unpacking here is a trick you can apply: Define a dynamic assembly and module, some dummy types like Furthermore, even if this is merged, using your current way of unpacking, which creates dynamic methods that are invalid, allows the possibility of corruption of IL in the case I mentioned above:
This is not true, dynamic method can contain uninstantiated generic types when using the ldtoken instruction in for example a |
Notice: what I mentioned is
Base type or interface constraint in host assembly may not be visible to unpacker assembly.
That's why there's 1 issue remaining.
There's |
This is why I suggest generating dynamic modules and assembly. If you do this, you can configure this module to skip accessibility checks to the file you are trying to unpack, meaning you can make your dummy types inherit any type in order to match generic parameter constraints. You can look into attributes like
It was just a suggestion, we could also use names like |
Seems people don't like it, just close |
Not every issue is produced from #452 #466, some issues exist a long time, close #551
DynamicTest.zip
Test code: