-
Notifications
You must be signed in to change notification settings - Fork 5.1k
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 42
Expected 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