4

I have the following attribute SecurityPermission(SecurityAction.Assert) on a method in my class. I compile it (debug build) and view the output in ildasm.exe by viewing the raw heaps and looking at the blob heap containing the PermissionSet blob. What I expect (per ECMA-335) is:

2e 01 80 84 53 79 73 74  65 6d 2e 53 65 63 75 72 >.   System.Secur<
69 74 79 2e 50 65 72 6d  69 73 73 69 6f 6e 73 2e >ity.Permissions.<
53 65 63 75 72 69 74 79  50 65 72 6d 69 73 73 69 >SecurityPermissi<
6f 6e 41 74 74 72 69 62  75 74 65 2c 20 6d 73 63 >onAttribute, msc<
6f 72 6c 69 62 2c 20 56  65 72 73 69 6f 6e 3d 32 >orlib, Version=2<
2e 30 2e 30 2e 30 2c 20  43 75 6c 74 75 72 65 3d >.0.0.0, Culture=<
6e 65 75 74 72 61 6c 2c  20 50 75 62 6c 69 63 4b >neutral, PublicK<
65 79 54 6f 6b 65 6e 3d  62 37 37 61 35 63 35 36 >eyToken=b77a5c56<
31 39 33 34 65 30 38 39  00 00

But what I saw was this:

2e 01 80 84 53 79 73 74  65 6d 2e 53 65 63 75 72 >.   System.Secur<
69 74 79 2e 50 65 72 6d  69 73 73 69 6f 6e 73 2e >ity.Permissions.<
53 65 63 75 72 69 74 79  50 65 72 6d 69 73 73 69 >SecurityPermissi<
6f 6e 41 74 74 72 69 62  75 74 65 2c 20 6d 73 63 >onAttribute, msc<
6f 72 6c 69 62 2c 20 56  65 72 73 69 6f 6e 3d 32 >orlib, Version=2<
2e 30 2e 30 2e 30 2c 20  43 75 6c 74 75 72 65 3d >.0.0.0, Culture=<
6e 65 75 74 72 61 6c 2c  20 50 75 62 6c 69 63 4b >neutral, PublicK<
65 79 54 6f 6b 65 6e 3d  62 37 37 61 35 63 35 36 >eyToken=b77a5c56<
31 39 33 34 65 30 38 39  01 00

In particular, note the 01 00 at the end where I expected a 00 00. The spec says that after the counted string should be the number of named arguments. Since I am not passing in any named arguments I expected that number to be a 16-bit 0.

This is compiled against .NET 2.0 using Visual Studio 2013.

To complicate matters even more, if I add in a named argument I get this:

2e 01 80 84 53 79 73 74  65 6d 2e 53 65 63 75 72 >.   System.Secur<
69 74 79 2e 50 65 72 6d  69 73 73 69 6f 6e 73 2e >ity.Permissions.<
53 65 63 75 72 69 74 79  50 65 72 6d 69 73 73 69 >SecurityPermissi<
6f 6e 41 74 74 72 69 62  75 74 65 2c 20 6d 73 63 >onAttribute, msc<
6f 72 6c 69 62 2c 20 56  65 72 73 69 6f 6e 3d 32 >orlib, Version=2<
2e 30 2e 30 2e 30 2c 20  43 75 6c 74 75 72 65 3d >.0.0.0, Culture=<
6e 65 75 74 72 61 6c 2c  20 50 75 62 6c 69 63 4b >neutral, PublicK<
65 79 54 6f 6b 65 6e 3d  62 37 37 61 35 63 35 36 >eyToken=b77a5c56<
31 39 33 34 65 30 38 39  12 01 54 02 0d 55 6e 6d >1934e089  T  Unm<
61 6e 61 67 65 64 43 6f  64 65 01                >anagedCode      <

Once again, look at the end of the counted string for the attribute and you can see the 12 01 followed by the named argument list (a list of one item). I expected this to be 01 00, a 16-bit little endian 1 for the number of named arguments.

Based on this, I am assuming that the second byte after the counted string is the named parameter count but I still don't understand what that first byte is (0x01 in the first example, 0x12 in the next).

If I add a second named attribute the first byte changes to a 26, if I add a third named attribute it changes to a 33. I don't see an obvious pattern to the numbers other than the fact that they are increasing.

I am asking this question because I am attempting to build a PermissionSet blob by hand (I am writing a CLR profiler) and I need to know what to put in that byte.

Micah Zoltu
  • 6,764
  • 5
  • 44
  • 72
  • The number appears to be a count of the number of bytes in the signature. The spec doesn't mention this and in fact says that there should be a 16-bit count of attributes there, not a length (possibly compressed) followed by a 1-byte count. – Micah Zoltu Dec 24 '13 at 02:21
  • 1
    Check chapter II.23.3 and note the *Prolog* value. It is an int16 value of 1 so 0x01 0x00 in the blob heap. Do avoid parsing the raw metadata yourself, you've got both managed and unmanaged interfaces available to do this for you. – Hans Passant Jan 04 '14 at 13:30
  • I'm not looking at the prolog value, I'm looking at the value in the position of NumNamed, the two bytes after the FixedArg in that railroad diagram. Also, since these are Security Attributes and not Custom Attributes I don't believe (and the above byte snippet agrees) that there is a prolog in them. See ECMA 335 §II.22.11: "A set of properties, encoded as the named arguments to a custom attribute would be (as in §II.23.3, beginning with NumNamed)." – Micah Zoltu Jan 04 '14 at 18:48
  • I am injecting a method using the unmanaged API and if I want to attach security (or custom) attributes to it then I have to build my own signatures. I followed the spec and was running into problems so I disassembled a method with security attributes so I could see what Visual Studio was generating and found this discrepancy between it and the spec. – Micah Zoltu Jan 04 '14 at 18:50

1 Answers1

1

I think you are correct in your dismay, I remember this from my previous experience with the named parameters - the NumNamed was implemented as a compressed int instead of int16 stated in the spec, and unlike the example given §VI.B.3. I don't know if this has changed in subsequent .NET 3.0+ implementations.

For the PermissionSet you are looking for,

... * A set of properties, encoded as the named arguments to a custom attribute would be (as in §II.23.3, beginning with NumNamed).

... §II.23.3 ...

Next is a description of the optional “named” fields and properties. This starts with NumNamed – an unsigned int16 giving the number of “named” properties or fields that follow. ... In the case where NumNamed is non-zero, it is followed by NumNamed repeats of NamedArgs.

The 12 01 is the little endian integer "giving the number of properties that follow". One named property, and a total length of 18 (in decimal, inclusive).

This total length is the pattern that you were looking for, but beware since I think this length is optional and sometimes the compiler manages to pack the number of properties in the preceding int16, discarding the length. You'll have to experiment with a different number of attributes to make sure you are correctly parsing all cases.

NamesArgs:

... SerString – a PackedLen count of bytes, followed by the UTF8 characters

PROPERTY is the single byte 0x54. The FieldOrPropName is the name of the field or property, stored as a SerString (defined above)

mockinterface
  • 14,452
  • 5
  • 28
  • 49
  • I have come to the same conclusion, that it is a byte count followed by a single byte NumNamed. Is this a known bug in the VS compiler or something I should file with them? – Micah Zoltu Jan 04 '14 at 18:54
  • I am not aware of an open bug so you might as well file one, but it'd be best to check if this problem occurs if you target the more recent .NET releases. – mockinterface Jan 05 '14 at 02:52
  • 1
    Was searching on the web now to see if there is an open bug, and stumbled on this gem in what seems the mono implementation, http://stuff.mit.edu/afs/athena/software/mono_v3.0/arch/amd64_ubuntu1204/mono/external/ikvm/reflect/CustomAttributeData.cs // ... the count of named arguments is a compressed integer (instead of UInt16 as NumNamed in custom attributes) Apologies if you are already aware of this bit, HTH. – mockinterface Jan 05 '14 at 02:55
  • Good find on the mono thing. I'll work on getting a bug filed (probably Monday). – Micah Zoltu Jan 05 '14 at 06:44
  • Awesome. Anyhow I distinctly remember that it wasn't only a compressed integer + length, but sometimes the length was omitted if the compiler could manage to pack the NumNamed in the preceding integer, belonging to the public key, but I guess we can get that clarified if there would be some response from Microsoft. Cheers. – mockinterface Jan 05 '14 at 06:56
  • Bug posted to Visual Studio: https://connect.microsoft.com/VisualStudio/feedback/details/840765/compiler-generates-cil-that-is-not-to-specification – Micah Zoltu Mar 25 '14 at 20:52
  • Bug still exists in Roslyn. I filed a fix for it here, but I don't expect much traction: https://roslyn.codeplex.com/workitem/75 – Micah Zoltu Apr 12 '14 at 09:03