Incorrect metadata token fixing in GetTypeSpecification #953
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Follow up issue from #931, since the problem that it exposed is not restricted to Cecil 0.11.5, and has been there for a long time.
I have made a small program where this issue can be demonstrated, its obvious side effect is the linking of the generic parameter with a wrong constraint list, which #931 described.
With the following target dll to patch:
And the following cecil procedure:
The output for the above snippet is the following (compiled with .net sdk 8.0.106):
The issue here is the appearance of any IL instructions that may have its operand serialized as a
TypeSpec. In this specific case the offender isinitobj !!0inside theSavemethod (and for #931 it was abox !!0).When reading the operand for that instruction
GetTypeSpecificationwill be called, which, if the token for the generic parameter it references was uninitialized or cleared (which is what replacing the generic parameter effectively does), it will try to fix it (source):It can be seen that when the condition is full filled the metadata token for that generic parameter will point to the
TypeSpectable.The issue with it is when the constraints for that same
GenericParameterare read, it is assumed that the generic parameter RID will point to theGenericParamtable, which is not correct and as such it will point to an arbitrary generic constraint list in the inverse mappings generated by cecil (the dictionary namedGenericConstraints) (source):Possible fixes for the issue may be:
GenericParameterdoes not have binary representation in the read file, and as such no token can be assigned to it until it is serialized.Now that the issue has been exposed in full detail I want to also explain why the commit
c4cfe16had an impact into this issue manifesting more often.That commit eliminates the removal of mappings when they have been read, since they should be cached afterwards. But a side effect that it had was making it harder for a
GenericParameterwith a wrong metadata token linking itself into some generic constraint list.This is why using Cecil 0.11.5 with MonoMod very easily triggers this issue.
It is important to point out here that the default reader settings were used, thus
ReadingMode.Deferredwas used in all my tests.Obviously using
ReadingMode.Immediatefixes all of these issues and is currently the "workaround" we are using.But this brings up the question, is patching assemblies in that default mode fully supported (either currently or planned in the future) by Cecil?
I believe we're stumbling into this in .NET nanoFramework. We are finding TypeSpec metadata tokens with RID = 0.