mirror of
https://github.com/dlang/dmd.git
synced 2025-04-26 05:00:16 +03:00
65 lines
2.9 KiB
D
65 lines
2.9 KiB
D
// PERMUTE_ARGS:
|
|
// REQUIRED_ARGS:
|
|
|
|
import core.memory;
|
|
|
|
// see https://forum.dlang.org/thread/4BB6296E.6050506@digitalmars.com for more info
|
|
|
|
// failure case for bug fixed by druntime rev 282
|
|
// how it works:
|
|
// The erroneous code used the first element in the LRU cache to determine the
|
|
// length of the block, instead of looking at the current blocks blockinfo. So
|
|
// we need a somewhat alignment of moons to get this to fail. First, we create
|
|
// the perfect storm: 1) a long blockinfo at the beginning (size = 32), 2) the
|
|
// second blockinfo is smaller (16 bytes) and 3) a block of memory that is
|
|
// exactly 16 bytes past the element in the 2nd block. While this seems kind
|
|
// of unlikely, in code that uses a lot of appending, it can easily happen.
|
|
//
|
|
// The bug causes the array appender to think that a pointer in the 16 bytes
|
|
// past the second blockinfo is actually in the second blockinfo. This is
|
|
// because it uses the length from the first blockinfo for checking (which is
|
|
// 32, so since the pointer is only 16 bytes into the block, it is considered
|
|
// inside).
|
|
// On top of all this, the third block must be marked as containing pointers,
|
|
// and the second block not containing pointers. The reason is because, it's
|
|
// impossible for the runtime to append in place, since the outside pointer
|
|
// can't possibly match the array length in the block. But because the runtime
|
|
// copies the blockinfo's attributes when reallocating, it copies the
|
|
// attributes from the *wrong* block. The only way to make this cause a
|
|
// problem would be to then collect memory, which should incorrectly deallocate
|
|
// data that the array containing pointers points to, and then use those
|
|
// pointers. However, for our purposes, we know this is possible, but we can
|
|
// deterministically check the attributes of the array after appending and
|
|
// verify that they are wrong.
|
|
//
|
|
void main()
|
|
{
|
|
// fill up the cache to make it wrap, The block info cache has 8 elements,
|
|
// and the first element is not the first one filled, so we want to wrap to
|
|
// that first element we want to fill in
|
|
for(int i = 0; i < 7; i++)
|
|
{
|
|
auto n = new int[1];
|
|
n ~= 1;
|
|
}
|
|
|
|
// this will allocate a 32-byte block, and appending will insert it into
|
|
// the cache at the beginning.
|
|
auto y = new int[4];
|
|
y ~= 1;
|
|
|
|
// now, allocate a 16 byte block with pointers, this will be our block that
|
|
// gets corrupted. The current GC allocates down, so we allocate this one
|
|
// first.
|
|
auto x = new char[][1];
|
|
|
|
// this block contains no pointers, and appending will insert it into the
|
|
// second element of the cache
|
|
y = new int[1];
|
|
y ~= 1;
|
|
|
|
// verify the noscan flag is 0 on the pointer-containing blocks
|
|
assert((GC.getAttr(x.ptr) & GC.BlkAttr.NO_SCAN) == 0);
|
|
x ~= "hello".dup; // this should leave the attributes alone
|
|
assert((GC.getAttr(x.ptr) & GC.BlkAttr.NO_SCAN) == 0); // fails on 2.042
|
|
}
|