-
Notifications
You must be signed in to change notification settings - Fork 5.2k
Description
This was sent to me from @tmat, quote:
We should so something about all the int <-> uint casts in the reader – some of them are actual bugs or questionable behaviors, since we shouldn’t be throwing overflow exceptions.
(found the following by looking for “internal uint” in Tables.cs)
TypeDefinition.GetLayout()
{
…
// here we overflow:
int size = (int)reader.ClassLayoutTable.GetClassSize(classLayoutRowId);
…
}
/// Returns field layout offset, or -1 if not available.
FieldDefinition.GetOffset()
{
uint layoutRowId = reader.FieldLayoutTable.FindFieldLayoutRowId(Handle);
if (layoutRowId == 0)
{
return -1;
}
uint offset = reader.FieldLayoutTable.GetOffset(layoutRowId);
// we reinterpret too big (but strictly speaking not invalid) values:
if (offset > int.MaxValue)
{
// CLI spec says:
// "Offset (a 4-byte constant)"
// "Offset shall be zero or more"
//
// Peverify fails with "Type load failed" error if offset is greater than Int32.MaxValue.
return -1;
}
return (int)offset;
}
int ExportedTypeExtensions.GetTypeDefinitionId
{
// here we overflow:
return (int)exportedType.reader.ExportedTypeTable.GetTypeDefId(exportedType.rowId);
}
internal void GetMethodRange(
TypeDefinitionHandle typeDef,
out int firstMethodRowId,
out int lastMethodRowId)
{
uint typeDefRowId = typeDef.RowId;
// here we overflow:
firstMethodRowId = (int)this.TypeDefTable.GetMethodStart(typeDefRowId);
…
}
We should probably check the range and throw BadImageFormat when passing the value out thru public API to be consistent – we’re already doing that for handles in MemoryBlock.PeekReference:
if (!TokenTypeIds.IsValidRowId(value)) ThrowReferenceOverflow();
Internally “rowId” fields of handles should probably be ints, not uints since we assert: Debug.Assert(TokenTypeIds.IsValidRowId(rowId))
“token” fields of heap handles should stay uints since we use all the bits.
Once we change the rowId to int and apply the type transitively I think we should be able to get rid of most uints.
He also shared the following implementation suggestion:
Currently we have
internal Int32 PeekInt32(int offset)
{
CheckBounds(offset, sizeof(Int32));
return *(Int32*)(Pointer + offset);
}
internal UInt32 PeekUInt32(int offset)
{
CheckBounds(offset, sizeof(UInt32));
return *(UInt32*)(Pointer + offset);}
}
on MemoryBlock. What about
internal uint PeekUnsignedInt32(int offset)
{
CheckBounds(offset, sizeof(uint));
return *(uint*)(Pointer + offset);
}
internal int PeekSignedInt32(int offset)
{
CheckBounds(offset, sizeof(int));
return *(int*)(Pointer + offset);
}
internal int PeekInt32(int offset)
{
CheckBounds(offset, sizeof(int));
var result = *(int*)(Pointer + offset);
if (result < 0) Throw();
return result;
}