-
Couldn't load subscription status.
- Fork 5.2k
Description
Description
The BlobBuilder.ReserveBytes method allows for callers to observe the backing byte[] before any data is written to it. Normally that is fine because BlobBuilder instances newly allocate the byte[] on construction so the data is zero'd out. In the case the BlobBuilder is created via pooling (the type supports pooling) then the data is whatever was written by the last consumer of the instance.
Reproduction Steps
Can observe using the following steps:
// Using PooledBlobBuilder from the SMR code base. Put used content
// into the pool
var builder = PooledBlobBuilder.GetInstance();
builder.WriteBytes(42, 4);
builder.Free();
// Now grab a builder from the pool
builder = PooledBlobBuilder.GetInstance();
var blob = builder.ReserveBytes(4);
Console.WriteLine(blob.GetBytes()[0]); // prints 42Expected behavior
The consumer should not be able to meaningfully observe the content from the previous writer.
Actual behavior
The consumer can observe the content from the previous writer
Regression?
This makes it more difficult for roslyn to widely use pooled BlobBuilder instances in our code base. Consider for example the code in PEBuilder.WriteCoffHeader:
stampFixup = builder.ReserveBytes(sizeof(uint));This means that the BlobBuilder used for writing out the COFF header has four bytes that are uninitialized / non-zero. That means the underlying Blob for the COFF header is non-deterministic in the face of a pooled BlobBuilder. That non-determinism is then passed to PEBuilder.IdProvider resulting in non-deterministic MVID + timestamps being generated for the PE.
Known Workarounds
Roslyn can work around this by force zero-ing BlobBuilder that are used in COFF generation but it's better to fix the underlying bug.
Configuration
No response
Other information
No response