Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit b9a90cb

Browse files
committed
Merge pull request #2245 from nguerrera/namespace-crash
Fix issue with multiple namespaces having same suffix
2 parents 8758d0e + a4ecef2 commit b9a90cb

File tree

10 files changed

+104
-113
lines changed

10 files changed

+104
-113
lines changed

src/System.Reflection.Metadata/src/Resources/Strings.resx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,4 +300,7 @@
300300
<data name="InvalidRowCount" xml:space="preserve">
301301
<value>Invalid row count: {0}</value>
302302
</data>
303+
<data name="TooManySubnamespaces" xml:space="preserve">
304+
<value>There are too many subnamespaces.</value>
305+
</data>
303306
</root>

src/System.Reflection.Metadata/src/System/Reflection/Metadata/Handles.cs

Lines changed: 31 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2713,70 +2713,74 @@ public override int GetHashCode()
27132713
/// </summary>
27142714
public struct NamespaceDefinitionHandle : IEquatable<NamespaceDefinitionHandle>
27152715
{
2716-
// At this time, IsVirtual is always false because namespace names come from type definitions
2717-
// and type forwarders only, which never get their namespaces projected.
2718-
//
2719-
// For Namespace, the offset is to the null-terminated full name of the
2720-
// namespace in the #String heap.
2716+
// Non-virtual (namespace having at least one type or forwarder of its own)
2717+
// heap offset is to the null-terminated full name of the namespace in the
2718+
// #String heap.
27212719
//
2722-
// For SyntheticNamespace, the offset points to the dot-terminated simple name of the namespace
2723-
// in the #String heap. This is used to represent namespaces that are parents of other namespaces
2724-
// but no type definitions or forwarders of their own.
2720+
// Virtual (namespace having child namespaces but no types of its own)
2721+
// the virtual index is an auto-incremented value and serves solely to
2722+
// create unique values for indexing into the NamespaceCache.
27252723

27262724
// bits:
2727-
// 31: IsVirtual (0)
2728-
// 29..31: type (non-virtual: Namespace, SyntheticNamespace)
2725+
// 31: IsVirtual
2726+
// 29..31: 0
27292727
// 0..28: Heap offset or Virtual index
27302728
private readonly uint _value;
27312729

27322730
private NamespaceDefinitionHandle(uint value)
27332731
{
2734-
// note: no virtual namespaces allowed
2735-
Debug.Assert((value & HeapHandleType.TypeMask) == NamespaceHandleType.Namespace ||
2736-
(value & HeapHandleType.TypeMask) == NamespaceHandleType.SyntheticNamespace);
2737-
27382732
_value = value;
27392733
}
27402734

27412735
internal static NamespaceDefinitionHandle FromFullNameOffset(int stringHeapOffset)
27422736
{
2743-
return new NamespaceDefinitionHandle(NamespaceHandleType.Namespace | (uint)stringHeapOffset);
2737+
return new NamespaceDefinitionHandle((uint)stringHeapOffset);
27442738
}
27452739

2746-
internal static NamespaceDefinitionHandle FromSimpleNameOffset(int stringHeapOffset)
2740+
internal static NamespaceDefinitionHandle FromVirtualIndex(uint virtualIndex)
27472741
{
2748-
return new NamespaceDefinitionHandle(NamespaceHandleType.SyntheticNamespace | (uint)stringHeapOffset);
2742+
// we arbitrarily disallow 0 virtual index to simplify nil check.
2743+
Debug.Assert(virtualIndex != 0);
2744+
2745+
if (!HeapHandleType.IsValidHeapOffset(virtualIndex))
2746+
{
2747+
// only a pathological assembly would hit this, but it must fit in 29 bits.
2748+
ThrowTooManySubnamespaces();
2749+
}
2750+
2751+
return new NamespaceDefinitionHandle(TokenTypeIds.VirtualBit | virtualIndex);
2752+
}
2753+
2754+
[MethodImpl(MethodImplOptions.NoInlining)]
2755+
private static void ThrowTooManySubnamespaces()
2756+
{
2757+
throw new BadImageFormatException(SR.TooManySubnamespaces);
27492758
}
27502759

27512760
public static implicit operator Handle(NamespaceDefinitionHandle handle)
27522761
{
2753-
// VTT... -> V111 11TT
27542762
return new Handle(
2755-
(byte)((handle._value & HeapHandleType.VirtualBit) >> 24 | HandleType.Namespace | (handle._value & HeapHandleType.NonVirtualTypeMask) >> 2),
2763+
(byte)((handle._value & HeapHandleType.VirtualBit) >> 24 | HandleType.Namespace),
27562764
(int)(handle._value & HeapHandleType.OffsetMask));
27572765
}
27582766

27592767
public static explicit operator NamespaceDefinitionHandle(Handle handle)
27602768
{
2761-
// namespaces currently can't be virtual:
2762-
if ((handle.VType & (HandleType.VirtualBit | HandleType.StringOrNamespaceMask)) != HandleType.Namespace)
2769+
if ((handle.VType & HandleType.TypeMask) != HandleType.Namespace)
27632770
{
27642771
Handle.ThrowInvalidCast();
27652772
}
27662773

2767-
// V111 11TT -> VTT...
27682774
return new NamespaceDefinitionHandle(
2769-
(handle.VType & HandleType.VirtualBit) << 24 |
2770-
(handle.VType & HandleType.StringHeapTypeMask) << HeapHandleType.OffsetBitCount |
2775+
(handle.VType & HandleType.VirtualBit) << TokenTypeIds.RowIdBitCount |
27712776
(uint)handle.Offset);
27722777
}
27732778

27742779
public bool IsNil
27752780
{
27762781
get
27772782
{
2778-
// virtual strings are never nil, so include virtual bit
2779-
return (_value & (HeapHandleType.VirtualBit | HeapHandleType.OffsetMask)) == 0;
2783+
return _value == 0;
27802784
}
27812785
}
27822786

@@ -2791,19 +2795,13 @@ internal int GetHeapOffset()
27912795
return (int)(_value & HeapHandleType.OffsetMask);
27922796
}
27932797

2794-
internal NamespaceKind NamespaceKind
2795-
{
2796-
get { return (NamespaceKind)(_value >> HeapHandleType.OffsetBitCount); }
2797-
}
2798-
27992798
internal bool HasFullName
28002799
{
2801-
get { return NamespaceKind != NamespaceKind.Synthetic; }
2800+
get { return !IsVirtual; }
28022801
}
28032802

28042803
internal StringHandle GetFullName()
28052804
{
2806-
Debug.Assert(!IsVirtual);
28072805
Debug.Assert(HasFullName);
28082806
return StringHandle.FromOffset(GetHeapOffset());
28092807
}

src/System.Reflection.Metadata/src/System/Reflection/Metadata/Internal/MetadataFlags.cs

Lines changed: 8 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -128,12 +128,6 @@ internal enum StringKind : byte
128128
DotTerminated = (byte)(StringHandleType.DotTerminatedString >> HeapHandleType.OffsetBitCount),
129129
}
130130

131-
internal enum NamespaceKind : byte
132-
{
133-
Plain = (byte)(NamespaceHandleType.Namespace >> HeapHandleType.OffsetBitCount),
134-
Synthetic = (byte)(NamespaceHandleType.SyntheticNamespace >> HeapHandleType.OffsetBitCount),
135-
}
136-
137131
internal static class StringHandleType
138132
{
139133
// NUL-terminated UTF8 string on a #String heap.
@@ -154,26 +148,7 @@ internal static class StringHandleType
154148

155149
// Reserved virtual strings that can be used in future:
156150
internal const uint ReservedVirtualString1 = HeapHandleType.VirtualBit | String | (2 << HeapHandleType.OffsetBitCount);
157-
internal const uint ReservedVirtaulString2 = HeapHandleType.VirtualBit | String | (3 << HeapHandleType.OffsetBitCount);
158-
}
159-
160-
internal static class NamespaceHandleType
161-
{
162-
// Namespace handle for namespace with types of its own
163-
internal const uint Namespace = 0;
164-
165-
// Namespace handle for namespace with child namespaces but no types of its own
166-
internal const uint SyntheticNamespace = Namespace | (1 << HeapHandleType.OffsetBitCount);
167-
168-
// Reserved namespaces that can be used in future:
169-
internal const uint ReservedNamespace1 = Namespace | (2 << HeapHandleType.OffsetBitCount);
170-
internal const uint ReservedNamespace2 = Namespace | (3 << HeapHandleType.OffsetBitCount);
171-
172-
// Reserved virtual namespaces that can be used in future:
173-
internal const uint ReservedVirtualNamespace1 = HeapHandleType.VirtualBit | Namespace;
174-
internal const uint ReservedVirtualNamespace2 = HeapHandleType.VirtualBit | Namespace | (1 << HeapHandleType.OffsetBitCount);
175-
internal const uint ReservedVirtualNamespace3 = HeapHandleType.VirtualBit | Namespace | (2 << HeapHandleType.OffsetBitCount);
176-
internal const uint ReservedVirtualNamespace4 = HeapHandleType.VirtualBit | Namespace | (3 << HeapHandleType.OffsetBitCount);
151+
internal const uint ReservedVirtualString2 = HeapHandleType.VirtualBit | String | (3 << HeapHandleType.OffsetBitCount);
177152
}
178153

179154
internal static class HeapHandleType
@@ -230,26 +205,18 @@ internal static class HandleType
230205

231206
internal const uint Blob = 0x71; // #Blob heap
232207
internal const uint Guid = 0x72; // #Guid heap
208+
internal const uint Namespace = 0x73; // #String heap but known to be the full name of a namespace
233209

234-
// #String heap and its modifications (up to 8 string kinds, virtaul and non-virtual)
235-
internal const uint String = 0x78;
210+
// #String heap and its modifications (up to 8 string kinds, virtual and non-virtual)
211+
internal const uint String = 0x7c;
236212
internal const uint DotTerminatedString = String | ((StringHandleType.DotTerminatedString & ~HeapHandleType.VirtualBit) >> HeapHandleType.OffsetBitCount);
237213
internal const uint ReservedString1 = String | ((StringHandleType.ReservedString1 & ~HeapHandleType.VirtualBit) >> HeapHandleType.OffsetBitCount);
238-
internal const uint ReservedString2 = String | ((StringHandleType.ReservedString1 & ~HeapHandleType.VirtualBit) >> HeapHandleType.OffsetBitCount);
214+
internal const uint ReservedString2 = String | ((StringHandleType.ReservedString2 & ~HeapHandleType.VirtualBit) >> HeapHandleType.OffsetBitCount);
239215
internal const uint VirtualString = VirtualBit | String;
240216
internal const uint WinRTPrefixedString = VirtualBit | String | ((StringHandleType.WinRTPrefixedString & ~HeapHandleType.VirtualBit) >> HeapHandleType.OffsetBitCount);
241217
internal const uint ReservedVirtualString1 = VirtualBit | String | ((StringHandleType.ReservedString1 & ~HeapHandleType.VirtualBit) >> HeapHandleType.OffsetBitCount);
242218
internal const uint ReservedVirtualString2 = VirtualBit | String | ((StringHandleType.ReservedString2 & ~HeapHandleType.VirtualBit) >> HeapHandleType.OffsetBitCount);
243219

244-
internal const uint Namespace = 0x7c;
245-
internal const uint SyntheticNamespace = Namespace | ((NamespaceHandleType.SyntheticNamespace & ~HeapHandleType.VirtualBit) >> HeapHandleType.OffsetBitCount);
246-
internal const uint ReservedNamespace1 = Namespace | ((NamespaceHandleType.ReservedNamespace1 & ~HeapHandleType.VirtualBit) >> HeapHandleType.OffsetBitCount);
247-
internal const uint ReservedNamespace2 = Namespace | ((NamespaceHandleType.ReservedNamespace2 & ~HeapHandleType.VirtualBit) >> HeapHandleType.OffsetBitCount);
248-
internal const uint ReservedVirtualNamespace1 = VirtualBit | Namespace | ((NamespaceHandleType.ReservedVirtualNamespace1 & ~HeapHandleType.VirtualBit) >> HeapHandleType.OffsetBitCount);
249-
internal const uint ReservedVirtualNamespace2 = VirtualBit | Namespace | ((NamespaceHandleType.ReservedVirtualNamespace2 & ~HeapHandleType.VirtualBit) >> HeapHandleType.OffsetBitCount);
250-
internal const uint ReservedVirtualNamespace3 = VirtualBit | Namespace | ((NamespaceHandleType.ReservedVirtualNamespace3 & ~HeapHandleType.VirtualBit) >> HeapHandleType.OffsetBitCount);
251-
internal const uint ReservedVirtualNamespace4 = VirtualBit | Namespace | ((NamespaceHandleType.ReservedVirtualNamespace4 & ~HeapHandleType.VirtualBit) >> HeapHandleType.OffsetBitCount);
252-
253220
internal const uint StringHeapTypeMask = HeapHandleType.NonVirtualTypeMask >> HeapHandleType.OffsetBitCount;
254221
internal const uint StringOrNamespaceMask = 0x7c;
255222

@@ -266,11 +233,11 @@ public static HandleKind ToHandleKind(uint handleType)
266233
{
267234
Debug.Assert((handleType & VirtualBit) == 0);
268235

269-
// Do not surface special string and namespace token sub-types (e.g. dot terminated, winrt prefixed, synthetic)
270-
// in public-facing handle type. Pretend that all strings/namespaces are just plain strings/namespaces.
236+
// Do not surface special string token sub-types (e.g. dot terminated, winrt prefixed)
237+
// in public-facing handle type. Pretend that all strings are just plain strings.
271238
if (handleType > String)
272239
{
273-
return (HandleKind)(handleType & ~StringHeapTypeMask);
240+
return HandleKind.String;
274241
}
275242

276243
return (HandleKind)handleType;

src/System.Reflection.Metadata/src/System/Reflection/Metadata/Internal/NamespaceCache.cs

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ internal class NamespaceCache
1515
private Dictionary<NamespaceDefinitionHandle, NamespaceData> _namespaceTable;
1616
private NamespaceData _rootNamespace;
1717
private ImmutableArray<NamespaceDefinitionHandle> _namespaceList;
18+
private uint _virtualNamespaceCounter;
1819

1920
internal NamespaceCache(MetadataReader reader)
2021
{
@@ -121,7 +122,7 @@ private void PopulateNamespaceTable()
121122

122123
// Make sure to add entry for root namespace. The root namespace is special in that even
123124
// though it might not have types of its own it always has an equivalent representation
124-
// as a nil handle and we don't want to handle it below as dot-terminated synthetic namespace.
125+
// as a nil handle and we don't want to handle it below as dot-terminated virtual namespace.
125126
// We use NamespaceDefinitionHandle.FromIndexOfFullName(0) instead of default(NamespaceDefinitionHandle) so
126127
// that we never hand back a handle to the user that doesn't have a typeid as that prevents
127128
// round-trip conversion to Handle and back. (We may discover other handle aliases for the
@@ -140,8 +141,8 @@ private void PopulateNamespaceTable()
140141
Dictionary<string, NamespaceDataBuilder> stringTable;
141142
MergeDuplicateNamespaces(namespaceBuilderTable, out stringTable);
142143

143-
List<NamespaceDataBuilder> syntheticNamespaces;
144-
ResolveParentChildRelationships(stringTable, out syntheticNamespaces);
144+
List<NamespaceDataBuilder> virtualNamespaces;
145+
ResolveParentChildRelationships(stringTable, out virtualNamespaces);
145146

146147
var namespaceTable = new Dictionary<NamespaceDefinitionHandle, NamespaceData>();
147148

@@ -152,11 +153,11 @@ private void PopulateNamespaceTable()
152153
namespaceTable.Add(group.Key, group.Value.Freeze());
153154
}
154155

155-
if (syntheticNamespaces != null)
156+
if (virtualNamespaces != null)
156157
{
157-
foreach (var syntheticNamespace in syntheticNamespaces)
158+
foreach (var virtualNamespace in virtualNamespaces)
158159
{
159-
namespaceTable.Add(syntheticNamespace.Handle, syntheticNamespace.Freeze());
160+
namespaceTable.Add(virtualNamespace.Handle, virtualNamespace.Freeze());
160161
}
161162
}
162163

@@ -226,7 +227,7 @@ private NamespaceDataBuilder SynthesizeNamespaceData(string fullName, NamespaceD
226227
}
227228

228229
StringHandle simpleName = GetSimpleName(realChild, numberOfSegments);
229-
var namespaceHandle = NamespaceDefinitionHandle.FromSimpleNameOffset(simpleName.GetHeapOffset());
230+
var namespaceHandle = NamespaceDefinitionHandle.FromVirtualIndex(++_virtualNamespaceCounter);
230231
return new NamespaceDataBuilder(namespaceHandle, simpleName, fullName);
231232
}
232233

@@ -243,11 +244,11 @@ private void LinkChildDataToParentData(NamespaceDataBuilder child, NamespaceData
243244

244245
/// <summary>
245246
/// Links a child to its parent namespace. If the parent namespace doesn't exist, this will create a
246-
/// synthetic one. This will automatically link any synthetic namespaces it creates up to its parents.
247+
/// virtual one. This will automatically link any virtual namespaces it creates up to its parents.
247248
/// </summary>
248249
private void LinkChildToParentNamespace(Dictionary<string, NamespaceDataBuilder> existingNamespaces,
249250
NamespaceDataBuilder realChild,
250-
ref List<NamespaceDataBuilder> syntheticNamespaces)
251+
ref List<NamespaceDataBuilder> virtualNamespaces)
251252
{
252253
Debug.Assert(realChild.Handle.HasFullName);
253254
string childName = realChild.FullName;
@@ -284,9 +285,9 @@ private void LinkChildToParentNamespace(Dictionary<string, NamespaceDataBuilder>
284285
return;
285286
}
286287

287-
if (syntheticNamespaces != null)
288+
if (virtualNamespaces != null)
288289
{
289-
foreach (var data in syntheticNamespaces)
290+
foreach (var data in virtualNamespaces)
290291
{
291292
if (data.FullName == parentName)
292293
{
@@ -297,30 +298,30 @@ private void LinkChildToParentNamespace(Dictionary<string, NamespaceDataBuilder>
297298
}
298299
else
299300
{
300-
syntheticNamespaces = new List<NamespaceDataBuilder>();
301+
virtualNamespaces = new List<NamespaceDataBuilder>();
301302
}
302303

303-
var syntheticParent = SynthesizeNamespaceData(parentName, realChild.Handle);
304-
LinkChildDataToParentData(child, syntheticParent);
305-
syntheticNamespaces.Add(syntheticParent);
306-
childName = syntheticParent.FullName;
307-
child = syntheticParent;
304+
var virtualParent = SynthesizeNamespaceData(parentName, realChild.Handle);
305+
LinkChildDataToParentData(child, virtualParent);
306+
virtualNamespaces.Add(virtualParent);
307+
childName = virtualParent.FullName;
308+
child = virtualParent;
308309
}
309310
}
310311

311312
/// <summary>
312313
/// This will link all parents/children in the given namespaces dictionary up to each other.
313314
///
314315
/// In some cases, we need to synthesize namespaces that do not have any type definitions or forwarders
315-
/// of their own, but do have child namespaces. These are returned via the syntheticNamespaces out
316+
/// of their own, but do have child namespaces. These are returned via the virtualNamespaces out
316317
/// parameter.
317318
/// </summary>
318-
private void ResolveParentChildRelationships(Dictionary<string, NamespaceDataBuilder> namespaces, out List<NamespaceDataBuilder> syntheticNamespaces)
319+
private void ResolveParentChildRelationships(Dictionary<string, NamespaceDataBuilder> namespaces, out List<NamespaceDataBuilder> virtualNamespaces)
319320
{
320-
syntheticNamespaces = null;
321+
virtualNamespaces = null;
321322
foreach (var namespaceData in namespaces.Values)
322323
{
323-
LinkChildToParentNamespace(namespaces, namespaceData, ref syntheticNamespaces);
324+
LinkChildToParentNamespace(namespaces, namespaceData, ref virtualNamespaces);
324325
}
325326
}
326327

0 commit comments

Comments
 (0)